aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/main/java/mygram/droid/MainActivity.java
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-22 11:19:32 +0200
committerDanilo M. <danix@danix.xyz>2026-10-22 11:19:32 +0200
commit44d4a132c193907ee53de180ad28e697828009e3 (patch)
tree001b9088b55dfd7d4ab73a1687bce1abe3da6e47 /app/src/main/java/mygram/droid/MainActivity.java
parentbf319064f127e3cf97a46d2c172bdacdc8f8a783 (diff)
downloadmygram-droid-44d4a132c193907ee53de180ad28e697828009e3.tar.gz
mygram-droid-44d4a132c193907ee53de180ad28e697828009e3.zip
Fix lifecycle, focus loss, crossfade and tab race review findings
- MainActivity: remove rotate callbacks in onDestroy, ignore connect results once destroyed, rotate the background only while resumed, guard the fade end action. - HomeRows: each row owns one TileAdapter over a mutable list; rebinds refill it and notifyDataSetChanged instead of setAdapter, so focus and scroll survive a sample arriving (change payload keeps the holder). - Crossfade: flip front only on onResourceReady, cancel the animation before resetting alpha. - Tab loads ignore errors from a tab that is no longer shown; Home does not fire a second /folders request while one is in flight. - Load the instance logo after login in connect(). - FolderActivity: request grid focus after layout. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'app/src/main/java/mygram/droid/MainActivity.java')
-rw-r--r--app/src/main/java/mygram/droid/MainActivity.java59
1 files changed, 46 insertions, 13 deletions
diff --git a/app/src/main/java/mygram/droid/MainActivity.java b/app/src/main/java/mygram/droid/MainActivity.java
index 6f8d76d..7578f05 100644
--- a/app/src/main/java/mygram/droid/MainActivity.java
+++ b/app/src/main/java/mygram/droid/MainActivity.java
@@ -59,7 +59,8 @@ public final class MainActivity extends Activity {
private RecyclerView homeList;
private HomeRows homeRows;
private int tab = HOME;
- private boolean ready;
+ private boolean ready, resumed, homeLoading;
+ private ImageView logo;
@Override
protected void onCreate(Bundle state) {
@@ -98,11 +99,9 @@ public final class MainActivity extends Activity {
bar.setGravity(Gravity.CENTER_VERTICAL);
int p = Ui.dp(this, 16);
bar.setPadding(p * 2, p, p * 2, 0);
- LinearLayout logo = Ui.iconButton(this, R.drawable.ic_home, "Home", v -> show(HOME));
- // Foldergram's logo is AGPLv3: loaded from the instance, never bundled.
- Glide.with(this).load(Api.resolve(Prefs.server(), "/icon-192.png")).error(R.drawable.ic_home)
- .into((ImageView) logo.getChildAt(0));
- bar.addView(logo);
+ LinearLayout home = Ui.iconButton(this, R.drawable.ic_home, "Home", v -> show(HOME));
+ logo = (ImageView) home.getChildAt(0);
+ bar.addView(home);
bar.addView(new Space(this), new LinearLayout.LayoutParams(0, 1, 1));
bar.addView(Ui.iconButton(this, R.drawable.ic_reels, "Reels", v -> Playlist.openReels(this)));
bar.addView(Ui.iconButton(this, R.drawable.ic_favorite, "Liked", v -> show(LIKED)));
@@ -121,12 +120,19 @@ public final class MainActivity extends Activity {
Likes.load();
return Api.randomFeed();
}, posts -> {
+ if (isDestroyed())
+ return;
ready = true;
+ // Foldergram's logo is AGPLv3: loaded from the instance after login, never bundled.
+ Glide.with(this).load(Api.resolve(Prefs.server(), "/icon-192.png")).error(R.drawable.ic_home).into(logo);
backgrounds = posts;
handler.removeCallbacks(rotate);
rotateBackground();
show(tab);
- }, this::failed);
+ }, e -> {
+ if (!isDestroyed())
+ failed(e);
+ });
}
private void failed(Exception e) {
@@ -151,20 +157,31 @@ public final class MainActivity extends Activity {
return;
}
message("Loading…");
+ if (homeLoading)
+ return; // the request in flight shows the list when it lands
+ homeLoading = true;
Api.async(Api::folders, folders -> {
+ homeLoading = false;
homeRows = new HomeRows(this, folders);
homeList = Ui.list(this, RecyclerView.VERTICAL);
homeList.setAdapter(homeRows);
if (tab == HOME)
setContent(homeList);
- }, this::failed);
+ }, e -> {
+ homeLoading = false;
+ if (tab == t)
+ failed(e);
+ });
} else if (t == LIKED) {
message("Loading…");
Api.async(Api::likes, posts -> {
if (tab == LIKED)
setContent(posts.isEmpty() ? centered("No liked posts yet")
: Ui.grid(this, Ui.postTiles(this, posts), Ui.POST_W, Ui.POST_H, 6));
- }, this::failed);
+ }, e -> {
+ if (tab == t)
+ failed(e);
+ });
} else {
message("Loading…");
Api.async(Api::folders, folders -> {
@@ -175,13 +192,17 @@ public final class MainActivity extends Activity {
tiles.add(new TileAdapter.Tile(f.avatarUrl, f.name, R.drawable.ic_folder, -1,
() -> FolderActivity.open(this, f)));
setContent(Ui.grid(this, tiles, 160, 160, 5));
- }, this::failed);
+ }, e -> {
+ if (tab == t)
+ failed(e);
+ });
}
}
@Override
protected void onResume() {
super.onResume();
+ resumed = true;
if (!ready)
return;
handler.removeCallbacks(rotate);
@@ -195,7 +216,14 @@ public final class MainActivity extends Activity {
@Override
protected void onPause() {
super.onPause();
+ resumed = false;
+ handler.removeCallbacks(rotate);
+ }
+
+ @Override
+ protected void onDestroy() {
handler.removeCallbacks(rotate);
+ super.onDestroy();
}
private void rotateBackground() {
@@ -204,6 +232,7 @@ public final class MainActivity extends Activity {
Post p = backgrounds.get(ThreadLocalRandom.current().nextInt(backgrounds.size()));
// Never pass the old drawable as a Glide placeholder: Glide may recycle its bitmap.
ImageView back = layers[1 - front], top = layers[front];
+ back.animate().cancel();
back.setAlpha(0f);
back.bringToFront();
Glide.with(this).load(p.media.get(0).thumbnailUrl).listener(new RequestListener<Drawable>() {
@@ -215,12 +244,16 @@ public final class MainActivity extends Activity {
@Override
public boolean onResourceReady(Drawable d, Object model, Target<Drawable> target, DataSource source,
boolean first) {
- back.animate().alpha(1f).setDuration(1500).withEndAction(() -> Glide.with(top).clear(top)).start();
+ front = 1 - front; // only now, so a failed load leaves the old layer on top
+ back.animate().alpha(1f).setDuration(1500).withEndAction(() -> {
+ if (!isDestroyed())
+ Glide.with(top).clear(top);
+ }).start();
return false;
}
}).into(back);
- front = 1 - front;
- handler.postDelayed(rotate, Prefs.backgroundSeconds() * 1000L);
+ if (resumed)
+ handler.postDelayed(rotate, Prefs.backgroundSeconds() * 1000L);
}
private void message(String s) {