/* * 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.view.Gravity; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import androidx.recyclerview.widget.RecyclerView; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** Home: one row per folder, a random sample of its posts, then a Continue tile. */ final class HomeRows extends RecyclerView.Adapter { private static final int SAMPLE = 10; static final class Row extends RecyclerView.ViewHolder { final TextView title; final ImageView avatar; final RecyclerView tiles; /** Backing list of the row's one adapter: rebinds refill it instead of replacing the adapter. */ final List list = new ArrayList<>(); Row(LinearLayout v, ImageView avatar, TextView title, RecyclerView tiles) { super(v); this.avatar = avatar; this.title = title; this.tiles = tiles; tiles.setAdapter(new TileAdapter(list, Ui.POST_W, Ui.POST_H)); } } private final Activity activity; private final List folders; private final Map> samples = new HashMap<>(); HomeRows(Activity activity, List folders) { this.activity = activity; this.folders = folders; } @Override public Row onCreateViewHolder(ViewGroup parent, int type) { Context c = parent.getContext(); LinearLayout v = new LinearLayout(c); v.setOrientation(LinearLayout.VERTICAL); v.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); LinearLayout head = new LinearLayout(c); head.setGravity(Gravity.CENTER_VERTICAL); head.setPadding(Ui.dp(c, 36), Ui.dp(c, 16), 0, 0); ImageView avatar = Ui.avatar(c, 32); ((LinearLayout.LayoutParams) avatar.getLayoutParams()).rightMargin = Ui.dp(c, 12); head.addView(avatar); TextView title = Ui.text(c, "", 18); head.addView(title); v.addView(head); RecyclerView tiles = Ui.list(c, RecyclerView.HORIZONTAL); v.addView(tiles, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Ui.dp(c, Ui.POST_H + 24))); return new Row(v, avatar, title, tiles); } @Override public void onBindViewHolder(Row row, int position) { Api.Folder f = folders.get(position); row.title.setText(f.name); Ui.setAvatar(row.avatar, f.avatarUrl); List posts = samples.get(f.slug); if (posts == null) { posts = List.of(); samples.put(f.slug, posts); // marks it loading, so it is fetched once Api.async(() -> Api.folderSample(f, SAMPLE), p -> { samples.put(f.slug, p); notifyItemChanged(folders.indexOf(f), Boolean.TRUE); // payload: keep the holder, so focus stays }, e -> { // the row keeps only its Continue tile }); } List tiles = row.list; tiles.clear(); for (Post p : posts) tiles.add(TileAdapter.post(p, () -> open(f, p))); tiles.add(new TileAdapter.Tile(null, "Continue", R.drawable.ic_arrow_forward, -1, () -> FolderActivity.open(activity, f))); row.tiles.getAdapter().notifyDataSetChanged(); } @Override public int getItemCount() { return folders.size(); } /** Viewer at p at once; the rest of the folder pages in lazily, in random page order. */ private void open(Api.Folder f, Post p) { Playlist.open(activity, new ArrayList<>(List.of(p)), 0, Api.folderPages(f.slug, f.count)); } /** Rebinds visible tiles so hearts follow likes changed in the viewer. */ void refreshHearts(RecyclerView list) { for (int i = 0; i < list.getChildCount(); i++) { RecyclerView.Adapter a = ((Row) list.getChildViewHolder(list.getChildAt(i))).tiles.getAdapter(); if (a != null) a.notifyItemRangeChanged(0, a.getItemCount()); } } }