1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
/*
* mygram-droid: Android TV client for Foldergram.
* Copyright (C) 2026 Danilo M. <danix@danix.xyz>
*
* 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<HomeRows.Row> {
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<TileAdapter.Tile> 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<Api.Folder> folders;
private final Map<String, List<Post>> samples = new HashMap<>();
HomeRows(Activity activity, List<Api.Folder> 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<Post> 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<TileAdapter.Tile> 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());
}
}
}
|