/* * mygram-droid: Android TV client for Foldergram. * Copyright (C) 2026 Danilo M. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ package mygram.droid; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.view.Gravity; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; /** One folder as a grid, paged in lazily in random page order. */ public final class FolderActivity extends Activity { private FrameLayout content; private RecyclerView grid; private final List posts = new ArrayList<>(); private final List tiles = new ArrayList<>(); private Callable> more; private boolean loading; static void open(Context c, Api.Folder f) { c.startActivity(new Intent(c, FolderActivity.class).putExtra("slug", f.slug).putExtra("name", f.name) .putExtra("count", f.count)); } @Override protected void onCreate(Bundle state) { super.onCreate(state); LinearLayout column = new LinearLayout(this); column.setOrientation(LinearLayout.VERTICAL); LinearLayout header = new LinearLayout(this); header.setGravity(Gravity.CENTER_VERTICAL); int p = Ui.dp(this, 16); header.setPadding(p * 2, p, p * 2, 0); header.addView(Ui.iconButton(this, R.drawable.ic_arrow_back, "Back", v -> finish())); TextView title = Ui.text(this, getIntent().getStringExtra("name"), 24); title.setPadding(p, 0, 0, 0); header.addView(title); column.addView(header); content = new FrameLayout(this); column.addView(content, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1)); setContentView(column); show(Ui.text(this, "Loading…", 18)); more = Api.folderPages(getIntent().getStringExtra("slug"), getIntent().getIntExtra("count", 0)); loading = true; Api.async(more, page -> { loading = false; if (isDestroyed()) return; append(page); if (page.isEmpty()) { show(Ui.text(this, "Nothing to show", 18)); return; } grid = Ui.grid(this, tiles, Ui.POST_W, Ui.POST_H, 6); grid.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView rv, int dx, int dy) { loadMore(); } }); show(grid); grid.post(grid::requestFocus); // after layout }, e -> { loading = false; show(Ui.text(this, "Cannot load folder: " + e.getMessage(), 18)); }); } /** Loads the next page when the last visible tile is within 12 of the end. */ private void loadMore() { if (more == null || loading || grid == null) return; if (((GridLayoutManager) grid.getLayoutManager()).findLastVisibleItemPosition() < tiles.size() - 12) return; loading = true; Api.async(more, page -> { loading = false; if (isDestroyed()) return; if (page.isEmpty()) more = null; append(page); }, e -> { loading = false; if (!isDestroyed()) Toast.makeText(this, "Cannot load more: " + e.getMessage(), Toast.LENGTH_SHORT).show(); }); } /** Pages from one pager never overlap, so no id check is needed here. */ private void append(List page) { posts.addAll(page); addTiles(); } private void addTiles() { int old = tiles.size(); for (int i = old; i < posts.size(); i++) { int start = i; tiles.add(TileAdapter.post(posts.get(i), () -> Playlist.open(this, posts, start, more))); } if (grid != null && tiles.size() > old) grid.getAdapter().notifyItemRangeInserted(old, tiles.size() - old); } @Override protected void onResume() { super.onResume(); if (grid != null && grid.getAdapter() != null) { addTiles(); // the viewer may have appended to the shared list grid.getAdapter().notifyItemRangeChanged(0, grid.getAdapter().getItemCount()); } } private void show(android.view.View v) { content.removeAllViews(); content.addView(v); } }