aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/main/java
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-22 17:52:22 +0200
committerDanilo M. <danix@danix.xyz>2026-10-22 17:52:22 +0200
commit9fba091bb394e8e4c7e8ccd0702081bd1a939047 (patch)
treed21c451a94452d2b36dfccca35b1b45adfc2bc59 /app/src/main/java
parent899b1d9812c61a33554b67e3026850684e686ce1 (diff)
downloadmygram-droid-9fba091bb394e8e4c7e8ccd0702081bd1a939047.tar.gz
mygram-droid-9fba091bb394e8e4c7e8ccd0702081bd1a939047.zip
Folder pictures, photo/video tile marks, folder in the viewer
Home rows and the folder header show the folder's round picture next to its name. Post tiles carry a photo or video mark in the top left, on a dark dot so it reads on bright thumbnails. The viewer shows the current post's folder picture and name in the top left; pictures come from the last loaded folder list, so before one loads only the name shows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'app/src/main/java')
-rw-r--r--app/src/main/java/mygram/droid/Api.java7
-rw-r--r--app/src/main/java/mygram/droid/FolderActivity.java4
-rw-r--r--app/src/main/java/mygram/droid/HomeRows.java19
-rw-r--r--app/src/main/java/mygram/droid/TileAdapter.java28
-rw-r--r--app/src/main/java/mygram/droid/Ui.java27
-rw-r--r--app/src/main/java/mygram/droid/ViewerActivity.java20
6 files changed, 94 insertions, 11 deletions
diff --git a/app/src/main/java/mygram/droid/Api.java b/app/src/main/java/mygram/droid/Api.java
index f603b1b..c7d08eb 100644
--- a/app/src/main/java/mygram/droid/Api.java
+++ b/app/src/main/java/mygram/droid/Api.java
@@ -35,6 +35,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -46,6 +47,9 @@ final class Api {
static final ExecutorService IO = Executors.newFixedThreadPool(4);
private static final int TIMEOUT_MS = 15000;
private static final int PAGE = 60;
+ /** Folder slug to picture URL, filled by folders(); the viewer reads it. */
+ // ponytail: empty until a folder list has loaded once, so the viewer then shows the name alone.
+ static final Map<String, String> AVATARS = new ConcurrentHashMap<>();
private Api() {
}
@@ -161,6 +165,9 @@ final class Api {
List<Folder> out = new ArrayList<>();
for (int i = 0; i < items.length(); i++)
out.add(new Folder(items.getJSONObject(i), Prefs.server()));
+ for (Folder f : out)
+ if (f.avatarUrl != null)
+ AVATARS.put(f.slug, f.avatarUrl);
out.sort((a, b) -> Long.compare(b.latest, a.latest));
return out;
}
diff --git a/app/src/main/java/mygram/droid/FolderActivity.java b/app/src/main/java/mygram/droid/FolderActivity.java
index 65ac596..84e7125 100644
--- a/app/src/main/java/mygram/droid/FolderActivity.java
+++ b/app/src/main/java/mygram/droid/FolderActivity.java
@@ -42,7 +42,7 @@ public final class FolderActivity extends Activity {
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));
+ .putExtra("count", f.count).putExtra("avatar", f.avatarUrl));
}
@Override
@@ -50,7 +50,7 @@ public final class FolderActivity extends Activity {
super.onCreate(state);
LinearLayout column = new LinearLayout(this);
column.setOrientation(LinearLayout.VERTICAL);
- column.addView(Ui.header(this, getIntent().getStringExtra("name")));
+ column.addView(Ui.header(this, getIntent().getStringExtra("name"), getIntent().getStringExtra("avatar")));
content = new FrameLayout(this);
column.addView(content, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1));
setContentView(column);
diff --git a/app/src/main/java/mygram/droid/HomeRows.java b/app/src/main/java/mygram/droid/HomeRows.java
index 6105c1c..9615f6f 100644
--- a/app/src/main/java/mygram/droid/HomeRows.java
+++ b/app/src/main/java/mygram/droid/HomeRows.java
@@ -17,7 +17,9 @@ 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;
@@ -34,12 +36,14 @@ final class HomeRows extends RecyclerView.Adapter<HomeRows.Row> {
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, TextView title, RecyclerView tiles) {
+ 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));
@@ -62,19 +66,26 @@ final class HomeRows extends RecyclerView.Adapter<HomeRows.Row> {
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);
- title.setPadding(Ui.dp(c, 36), Ui.dp(c, 16), 0, 0);
- v.addView(title);
+ 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, title, tiles);
+ 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();
diff --git a/app/src/main/java/mygram/droid/TileAdapter.java b/app/src/main/java/mygram/droid/TileAdapter.java
index cf80f51..5e53109 100644
--- a/app/src/main/java/mygram/droid/TileAdapter.java
+++ b/app/src/main/java/mygram/droid/TileAdapter.java
@@ -16,6 +16,7 @@ package mygram.droid;
import android.content.Context;
import android.graphics.Color;
+import android.graphics.drawable.GradientDrawable;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
@@ -35,6 +36,8 @@ final class TileAdapter extends RecyclerView.Adapter<TileAdapter.Holder> {
final String image, label;
final int icon, postId;
final Runnable click;
+ /** Photo or video mark in the top left; 0 for none. */
+ int badge;
/** image or icon is shown; postId is -1 for tiles that are not posts. */
Tile(String image, String label, int icon, int postId, Runnable click) {
@@ -47,18 +50,21 @@ final class TileAdapter extends RecyclerView.Adapter<TileAdapter.Holder> {
}
static Tile post(Post p, Runnable click) {
- return new Tile(p.media.get(0).thumbnailUrl, null, 0, p.id, click);
+ Tile t = new Tile(p.media.get(0).thumbnailUrl, null, 0, p.id, click);
+ t.badge = p.media.get(0).video ? R.drawable.ic_videocam : R.drawable.ic_photo;
+ return t;
}
static final class Holder extends RecyclerView.ViewHolder {
- final ImageView image, heart;
+ final ImageView image, heart, badge;
final TextView label;
- Holder(View v, ImageView image, TextView label, ImageView heart) {
+ Holder(View v, ImageView image, TextView label, ImageView heart, ImageView badge) {
super(v);
this.image = image;
this.label = label;
this.heart = heart;
+ this.badge = badge;
}
}
@@ -97,8 +103,19 @@ final class TileAdapter extends RecyclerView.Adapter<TileAdapter.Holder> {
FrameLayout.LayoutParams hp = new FrameLayout.LayoutParams(s, s, Gravity.TOP | Gravity.END);
hp.setMargins(p, p, p, p);
frame.addView(heart, hp);
+ ImageView badge = new ImageView(c);
+ GradientDrawable dot = new GradientDrawable();
+ dot.setShape(GradientDrawable.OVAL);
+ dot.setColor(0x80000000); // readable on bright thumbnails
+ badge.setBackground(dot);
+ int bp = Ui.dp(c, 4);
+ badge.setPadding(bp, bp, bp, bp);
+ int bs = Ui.dp(c, 26);
+ FrameLayout.LayoutParams bl = new FrameLayout.LayoutParams(bs, bs, Gravity.TOP | Gravity.START);
+ bl.setMargins(p, p, p, p);
+ frame.addView(badge, bl);
Ui.focusable(frame);
- return new Holder(frame, image, label, heart);
+ return new Holder(frame, image, label, heart, badge);
}
@Override
@@ -116,6 +133,9 @@ final class TileAdapter extends RecyclerView.Adapter<TileAdapter.Holder> {
}
h.label.setText(t.label);
h.label.setVisibility(t.label == null ? View.GONE : View.VISIBLE);
+ h.badge.setVisibility(t.badge == 0 ? View.GONE : View.VISIBLE);
+ h.badge.setImageResource(t.badge);
+ h.badge.setContentDescription(t.badge == R.drawable.ic_videocam ? "Video" : "Photo");
h.heart.setVisibility(t.postId >= 0 && Likes.has(t.postId) ? View.VISIBLE : View.GONE);
h.itemView.setContentDescription(t.label != null ? t.label : "Post");
h.itemView.setOnClickListener(v -> t.click.run());
diff --git a/app/src/main/java/mygram/droid/Ui.java b/app/src/main/java/mygram/droid/Ui.java
index 172fbff..6667d33 100644
--- a/app/src/main/java/mygram/droid/Ui.java
+++ b/app/src/main/java/mygram/droid/Ui.java
@@ -33,6 +33,8 @@ import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import com.bumptech.glide.Glide;
+
import java.util.ArrayList;
import java.util.List;
@@ -156,17 +158,42 @@ final class Ui {
/** Back arrow and title, for screens outside the main view. */
static LinearLayout header(Activity a, String title) {
+ return header(a, title, null);
+ }
+
+ /** Back arrow, the folder picture when there is one, and title. */
+ static LinearLayout header(Activity a, String title, String avatarUrl) {
LinearLayout header = new LinearLayout(a);
header.setGravity(Gravity.CENTER_VERTICAL);
int p = dp(a, 16);
header.setPadding(p * 2, p, p * 2, 0);
header.addView(iconButton(a, R.drawable.ic_arrow_back, "Back", v -> a.finish()));
+ ImageView avatar = avatar(a, 40);
+ ((LinearLayout.LayoutParams) avatar.getLayoutParams()).leftMargin = p;
+ header.addView(avatar);
+ setAvatar(avatar, avatarUrl);
TextView t = text(a, title, 24);
t.setPadding(p, 0, 0, 0);
header.addView(t);
return header;
}
+ /** A round folder picture of size dp, for a horizontal LinearLayout. */
+ static ImageView avatar(Context c, int size) {
+ ImageView v = new ImageView(c);
+ v.setLayoutParams(new LinearLayout.LayoutParams(dp(c, size), dp(c, size)));
+ return v;
+ }
+
+ /** Loads url round, or hides the view when the folder has no picture. */
+ static void setAvatar(ImageView v, String url) {
+ v.setVisibility(url == null ? View.GONE : View.VISIBLE);
+ if (url == null)
+ Glide.with(v).clear(v);
+ else
+ Glide.with(v).load(url).circleCrop().into(v);
+ }
+
static void blur(ImageView v, float radius) {
v.setRenderEffect(RenderEffect.createBlurEffect(radius, radius, Shader.TileMode.CLAMP));
}
diff --git a/app/src/main/java/mygram/droid/ViewerActivity.java b/app/src/main/java/mygram/droid/ViewerActivity.java
index 4845cbd..369db5d 100644
--- a/app/src/main/java/mygram/droid/ViewerActivity.java
+++ b/app/src/main/java/mygram/droid/ViewerActivity.java
@@ -29,6 +29,7 @@ import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
@@ -73,7 +74,8 @@ public final class ViewerActivity extends Activity {
private PlayerView video;
private ImageView backdrop, photo, pauseIcon, heart, bigHeart;
private ProgressBar progress;
- private TextView message;
+ private TextView message, folderName;
+ private ImageView folderAvatar;
private int index, slide;
/** Bumped on every slide change; late callbacks for older slides are dropped. */
private volatile int generation;
@@ -127,6 +129,19 @@ public final class ViewerActivity extends Activity {
bigHeart = overlay(root, R.drawable.ic_favorite, "Liked", 160, Gravity.CENTER);
heart.setColorFilter(Ui.RED);
bigHeart.setColorFilter(Ui.RED);
+ LinearLayout folder = new LinearLayout(this);
+ folder.setGravity(Gravity.CENTER_VERTICAL);
+ folderAvatar = Ui.avatar(this, 40);
+ ((LinearLayout.LayoutParams) folderAvatar.getLayoutParams()).rightMargin = Ui.dp(this, 12);
+ folder.addView(folderAvatar);
+ folderName = Ui.text(this, "", 20);
+ folderName.setShadowLayer(6, 0, 0, Color.BLACK);
+ folder.addView(folderName);
+ int m = Ui.dp(this, 24);
+ FrameLayout.LayoutParams fp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.TOP | Gravity.START);
+ fp.setMargins(m, m, m, m);
+ root.addView(folder, fp);
setContentView(root);
if (!Playlist.posts.isEmpty()) {
@@ -162,6 +177,8 @@ public final class ViewerActivity extends Activity {
private void show(int i) {
index = i;
slide = 0;
+ folderName.setText(post().folderName);
+ Ui.setAvatar(folderAvatar, Api.AVATARS.get(post().folderSlug));
maybeLoadMore();
showSlide();
}
@@ -432,6 +449,7 @@ public final class ViewerActivity extends Activity {
generation++;
Glide.with(photo).clear(photo);
Glide.with(backdrop).clear(backdrop);
+ Glide.with(folderAvatar).clear(folderAvatar);
player.release();
}
}