From 603e9b2a4e99fced8f1fcf866c7e57ebd5deadd7 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Sat, 22 Aug 2026 19:30:43 -0700 Subject: [PATCH 1/4] Add Collapse Thread comment action --- .../redreader/common/FeatureFlagHandler.java | 24 +++++ .../redreader/common/PrefsUtility.java | 3 +- .../fragments/CommentListingFragment.java | 89 ++++++++++++++++++- .../reddit/api/RedditAPICommentAction.java | 18 ++++ .../redreader/views/RedditCommentView.java | 25 ++++-- src/main/res/values/arrays.xml | 6 ++ src/main/res/values/strings.xml | 1 + 7 files changed, 155 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/quantumbadger/redreader/common/FeatureFlagHandler.java b/src/main/java/org/quantumbadger/redreader/common/FeatureFlagHandler.java index cd5495d59..1a14bcd6d 100644 --- a/src/main/java/org/quantumbadger/redreader/common/FeatureFlagHandler.java +++ b/src/main/java/org/quantumbadger/redreader/common/FeatureFlagHandler.java @@ -32,6 +32,7 @@ import org.quantumbadger.redreader.fragments.AccountListDialog; import org.quantumbadger.redreader.fragments.ChangelogDialog; +import java.util.HashSet; import java.util.Set; public final class FeatureFlagHandler { @@ -59,6 +60,7 @@ private enum FeatureFlag { DEFAULT_PREF_VIDEO_PLAYBACK_CONTROLS("defaultPrefVideoPlaybackControls"), DEFAULT_PREF_CUSTOM_TABS("defaultPrefCustomTabs"), CROSSPOST_ORIGIN_MENU_ITEM("crosspostOriginMenuItem"), + COLLAPSE_THREAD_COMMENT_MENU_ITEM("collapseThreadCommentMenuItem"), MAIN_MENU_RANDOM_REMOVED("mainMenuRandomRemoved"); @NonNull private final String id; @@ -273,6 +275,28 @@ public static void handleUpgrade(@NonNull final Context context) { .apply(); } + if(getAndSetFeatureFlag(prefs, FeatureFlag.COLLAPSE_THREAD_COMMENT_MENU_ITEM) + == FeatureFlagStatus.UPGRADE_NEEDED) { + + Log.i(TAG, "Upgrading, add collapse thread to comment action menu."); + + final Set existingCommentActionMenuItems = new HashSet<>( + getStringSet( + R.string.pref_menus_comment_context_items_key, + R.array.pref_menus_comment_context_items_return, + context, + prefs)); + + existingCommentActionMenuItems.add("collapse_thread"); + + prefs.edit() + .putStringSet( + context.getString( + R.string.pref_menus_comment_context_items_key), + existingCommentActionMenuItems) + .apply(); + } + if(getAndSetFeatureFlag(prefs, FeatureFlag.POST_TITLE_TAP_ACTION_FEATURE) == FeatureFlagStatus.UPGRADE_NEEDED) { diff --git a/src/main/java/org/quantumbadger/redreader/common/PrefsUtility.java b/src/main/java/org/quantumbadger/redreader/common/PrefsUtility.java index 3b9a032fc..5d625a039 100644 --- a/src/main/java/org/quantumbadger/redreader/common/PrefsUtility.java +++ b/src/main/java/org/quantumbadger/redreader/common/PrefsUtility.java @@ -1124,6 +1124,7 @@ public enum CommentFlingAction { COPY_URL, USER_PROFILE, COLLAPSE, + COLLAPSE_THREAD, ACTION_MENU, PROPERTIES, BACK, @@ -1143,7 +1144,7 @@ public static CommentFlingAction pref_behaviour_fling_comment_right() { } public enum CommentAction { - COLLAPSE, ACTION_MENU, NOTHING + COLLAPSE, COLLAPSE_THREAD, ACTION_MENU, NOTHING } public static CommentAction pref_behaviour_actions_comment_tap() { diff --git a/src/main/java/org/quantumbadger/redreader/fragments/CommentListingFragment.java b/src/main/java/org/quantumbadger/redreader/fragments/CommentListingFragment.java index b2a82cd89..cd23eb1e4 100644 --- a/src/main/java/org/quantumbadger/redreader/fragments/CommentListingFragment.java +++ b/src/main/java/org/quantumbadger/redreader/fragments/CommentListingFragment.java @@ -79,6 +79,7 @@ import org.quantumbadger.redreader.views.liststatus.ErrorView; import java.util.ArrayList; +import java.util.HashSet; import java.util.LinkedList; import java.util.Map; import java.util.UUID; @@ -346,6 +347,75 @@ public void handleCommentVisibilityToggle(final RedditCommentView view) { } } + public void handleThreadCollapse(final RedditCommentView view) { + + final RedditChangeDataManager changeDataManager + = RedditChangeDataManager.getInstance(mUser); + + RedditCommentListItem item = view.getComment(); + + if(!item.isComment()) { + return; + } + + final HashSet ancestors = new HashSet<>(); + + for(RedditCommentListItem ancestor = item; + ancestor != null; + ancestor = ancestor.getParent()) { + ancestors.add(ancestor); + } + + final int initialItemCount = mCommentListingManager.getAdapter().getItemCount(); + int threadPosition = -1; + + for(int position = 0; position < initialItemCount; position++) { + final GroupedRecyclerViewAdapter.Item candidate + = mCommentListingManager.getItemAtPosition(position); + + if(ancestors.contains(candidate)) { + item = (RedditCommentListItem)candidate; + threadPosition = position; + break; + } + } + + if(threadPosition < 0) { + return; + } + + if(item.asComment().isCollapsed(changeDataManager)) { + changeDataManager.markHidden( + TimestampUTC.now(), + item.asComment().getIdAndType(), + false); + + mCommentListingManager.updateHiddenStatus(); + return; + } + + changeDataManager.markHidden( + TimestampUTC.now(), + item.asComment().getIdAndType(), + true); + + mCommentListingManager.updateHiddenStatus(); + + final LinearLayoutManager layoutManager + = (LinearLayoutManager)mRecyclerView.getLayoutManager(); + final int itemCount = layoutManager.getItemCount(); + + for(int position = threadPosition + 1; position < itemCount; position++) { + if(isTopLevelComment(position)) { + jumpToPosition(threadPosition, position); + return; + } + } + + // Keep focus on a visible row when the invoking descendant disappears. + jumpToPosition(threadPosition); + } + @Override public View getListingView() { return mListingView; @@ -406,6 +476,10 @@ public void onCommentClicked(final RedditCommentView view) { handleCommentVisibilityToggle(view); break; + case COLLAPSE_THREAD: + handleThreadCollapse(view); + break; + case ACTION_MENU: { final RedditCommentListItem item = view.getComment(); if(item != null && item.isComment()) { @@ -444,6 +518,10 @@ public void onCommentLongClicked(final RedditCommentView view) { handleCommentVisibilityToggle(view); break; + case COLLAPSE_THREAD: + handleThreadCollapse(view); + break; + case NOTHING: break; } @@ -784,12 +862,19 @@ private boolean isTopLevelComment(final int position) { } private void jumpToPosition(final int position) { + jumpToPosition(position, position); + } + + private void jumpToPosition( + final int scrollPosition, + final int focusPosition) { + final LinearLayoutManager layoutManager = (LinearLayoutManager)mRecyclerView.getLayoutManager(); mParentJumpCount++; - layoutManager.scrollToPositionWithOffset(position, 0); - setAccessibilityFocusAfterLayout(position, true); + layoutManager.scrollToPositionWithOffset(scrollPosition, 0); + setAccessibilityFocusAfterLayout(focusPosition, true); } @SuppressLint("AccessibilityFocus") diff --git a/src/main/java/org/quantumbadger/redreader/reddit/api/RedditAPICommentAction.java b/src/main/java/org/quantumbadger/redreader/reddit/api/RedditAPICommentAction.java index 14aa5e8da..eb576110d 100644 --- a/src/main/java/org/quantumbadger/redreader/reddit/api/RedditAPICommentAction.java +++ b/src/main/java/org/quantumbadger/redreader/reddit/api/RedditAPICommentAction.java @@ -75,6 +75,7 @@ public enum RedditCommentAction { USER_PROFILE, COMMENT_LINKS, COLLAPSE, + COLLAPSE_THREAD, EDIT, DELETE, EXTERNAL, @@ -242,6 +243,18 @@ public static void showActionMenu( RedditCommentAction.COLLAPSE)); } + if(itemPref.contains(RedditCommentAction.COLLAPSE_THREAD) + && commentListingFragment != null + && commentView != null + && !(commentView.getComment().getIndent() == 0 + && comment.isCollapsed(changeDataManager))) { + + menu.add(new RCVMenuItem( + activity, + R.string.action_collapse_thread, + RedditCommentAction.COLLAPSE_THREAD)); + } + if(itemPref.contains(RedditCommentAction.SHARE)) { menu.add(new RCVMenuItem( activity, @@ -508,6 +521,11 @@ public static void onActionMenuItemSelected( break; } + case COLLAPSE_THREAD: { + commentListingFragment.handleThreadCollapse(commentView); + break; + } + case USER_PROFILE: LinkHandler.onLinkClicked( activity, diff --git a/src/main/java/org/quantumbadger/redreader/views/RedditCommentView.java b/src/main/java/org/quantumbadger/redreader/views/RedditCommentView.java index ab64fa239..c0f7b957d 100644 --- a/src/main/java/org/quantumbadger/redreader/views/RedditCommentView.java +++ b/src/main/java/org/quantumbadger/redreader/views/RedditCommentView.java @@ -206,6 +206,18 @@ private ActionDescriptionPair chooseFlingAction(final PrefsUtility.CommentFlingA RedditAPICommentAction.RedditCommentAction.COLLAPSE, R.string.action_collapse); + case COLLAPSE_THREAD: + + if(mFragment == null + || (mComment.getIndent() == 0 + && mComment.asComment().isCollapsed(mChangeDataManager))) { + return null; + } + + return new ActionDescriptionPair( + RedditAPICommentAction.RedditCommentAction.COLLAPSE_THREAD, + R.string.action_collapse_thread); + case ACTION_MENU: if(mFragment == null) { @@ -497,15 +509,10 @@ private void setupAccessibilityActions() { } addAccessibilityActionFromDescriptionPair( - chooseFlingAction(PrefsUtility.CommentFlingAction.COLLAPSE)); - - mAccessibilityActionManager.addAction(R.string.button_next_comment_parent, () -> { - mFragment.onNextParent(); - }); + chooseFlingAction(PrefsUtility.CommentFlingAction.COLLAPSE)); - mAccessibilityActionManager.addAction(R.string.button_prev_comment_parent, () -> { - mFragment.onPreviousParent(); - }); + addAccessibilityActionFromDescriptionPair( + chooseFlingAction(PrefsUtility.CommentFlingAction.COLLAPSE_THREAD)); if (isAuthenticated) { addAccessibilityActionFromDescriptionPair( @@ -566,6 +573,8 @@ private Integer getAccessibilityHintForActionPref( switch (pref) { case COLLAPSE: return R.string.action_collapse; + case COLLAPSE_THREAD: + return R.string.action_collapse_thread; case ACTION_MENU: return R.string.action_actionmenu; } diff --git a/src/main/res/values/arrays.xml b/src/main/res/values/arrays.xml index 0f3e81ed2..cb2ed6973 100644 --- a/src/main/res/values/arrays.xml +++ b/src/main/res/values/arrays.xml @@ -103,6 +103,7 @@ @string/action_copy_link @string/action_user_profile @string/action_collapse + @string/action_collapse_thread @string/action_properties @string/action_actionmenu @string/action_back @@ -124,6 +125,7 @@ copy_url user_profile collapse + collapse_thread properties action_menu back @@ -215,6 +217,7 @@ @string/action_collapse + @string/action_collapse_thread @string/action_actionmenu @string/action_nothing @@ -222,6 +225,7 @@ collapse + collapse_thread action_menu nothing @@ -1269,6 +1273,7 @@ @string/action_comment_go_to @string/action_comment_links @string/action_collapse + @string/action_collapse_thread @string/action_share @string/action_copy_text @string/action_copy_link @@ -1290,6 +1295,7 @@ go_to_comment comment_links collapse + collapse_thread share copy_text copy_url diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 3a5101832..13da1458c 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -240,6 +240,7 @@ Switch To Link Link Toggle Collapse + Collapse Thread Do Nothing Report From 6a81182005f5d5e2c6621140ec3a04d95f544240 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Sat, 22 Aug 2026 19:31:23 -0700 Subject: [PATCH 2/4] Make collapse comment labels state-aware --- .../reddit/api/RedditAPICommentAction.java | 6 +++++- .../redreader/views/RedditCommentView.java | 15 +++++++++++++++ src/main/res/values/arrays.xml | 2 +- src/main/res/values/strings.xml | 4 +++- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/quantumbadger/redreader/reddit/api/RedditAPICommentAction.java b/src/main/java/org/quantumbadger/redreader/reddit/api/RedditAPICommentAction.java index eb576110d..821c07f28 100644 --- a/src/main/java/org/quantumbadger/redreader/reddit/api/RedditAPICommentAction.java +++ b/src/main/java/org/quantumbadger/redreader/reddit/api/RedditAPICommentAction.java @@ -237,9 +237,13 @@ public static void showActionMenu( } if(itemPref.contains(RedditCommentAction.COLLAPSE) && commentListingFragment != null) { + final int titleRes = comment.isCollapsed(changeDataManager) + ? R.string.action_expand_comment + : R.string.action_collapse; + menu.add(new RCVMenuItem( activity, - R.string.action_collapse, + titleRes, RedditCommentAction.COLLAPSE)); } diff --git a/src/main/java/org/quantumbadger/redreader/views/RedditCommentView.java b/src/main/java/org/quantumbadger/redreader/views/RedditCommentView.java index c0f7b957d..f04ed289f 100644 --- a/src/main/java/org/quantumbadger/redreader/views/RedditCommentView.java +++ b/src/main/java/org/quantumbadger/redreader/views/RedditCommentView.java @@ -202,6 +202,12 @@ private ActionDescriptionPair chooseFlingAction(final PrefsUtility.CommentFlingA return null; } + if(mComment.asComment().isCollapsed(mChangeDataManager)) { + return new ActionDescriptionPair( + RedditAPICommentAction.RedditCommentAction.COLLAPSE, + R.string.action_expand_comment); + } + return new ActionDescriptionPair( RedditAPICommentAction.RedditCommentAction.COLLAPSE, R.string.action_collapse); @@ -572,8 +578,17 @@ private Integer getAccessibilityHintForActionPref( @NonNull final PrefsUtility.CommentAction pref) { switch (pref) { case COLLAPSE: + if(mComment.asComment().isCollapsed(mChangeDataManager)) { + return R.string.action_expand_comment; + } + return R.string.action_collapse; case COLLAPSE_THREAD: + if(mComment.getIndent() == 0 + && mComment.asComment().isCollapsed(mChangeDataManager)) { + return R.string.action_expand_comment; + } + return R.string.action_collapse_thread; case ACTION_MENU: return R.string.action_actionmenu; diff --git a/src/main/res/values/arrays.xml b/src/main/res/values/arrays.xml index cb2ed6973..141270cc8 100644 --- a/src/main/res/values/arrays.xml +++ b/src/main/res/values/arrays.xml @@ -79,7 +79,7 @@ - @string/action_collapse + @string/action_collapse_selftext @string/action_nothing diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 13da1458c..bd894e343 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -239,8 +239,10 @@ Open Link Switch To Link Link - Toggle Collapse + Collapse Comment + Collapse Post Collapse Thread + Expand Comment Do Nothing Report From a62fc226b4883b6753c6fbebb975ea5eeb2340e6 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Sat, 22 Aug 2026 19:31:36 -0700 Subject: [PATCH 3/4] Hide parent jump toolbar from accessibility --- .../redreader/fragments/CommentListingFragment.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/quantumbadger/redreader/fragments/CommentListingFragment.java b/src/main/java/org/quantumbadger/redreader/fragments/CommentListingFragment.java index cd23eb1e4..8cad1bfb7 100644 --- a/src/main/java/org/quantumbadger/redreader/fragments/CommentListingFragment.java +++ b/src/main/java/org/quantumbadger/redreader/fragments/CommentListingFragment.java @@ -196,6 +196,8 @@ public CommentListingFragment( R.layout.floating_toolbar, mOverlayFrame, false); + mFloatingToolbar.setImportantForAccessibility( + View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); if (PrefsUtility.pref_appearance_left_handed()) { final FrameLayout.LayoutParams toolBarParams = From f4ee2dd5218e42f5dbe5d00a6add9f729ef0de7f Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Sun, 23 Aug 2026 04:32:58 -0700 Subject: [PATCH 4/4] Move reading focus onward when collapsing a comment --- .../fragments/CommentListingFragment.java | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/quantumbadger/redreader/fragments/CommentListingFragment.java b/src/main/java/org/quantumbadger/redreader/fragments/CommentListingFragment.java index 8cad1bfb7..00fa838e4 100644 --- a/src/main/java/org/quantumbadger/redreader/fragments/CommentListingFragment.java +++ b/src/main/java/org/quantumbadger/redreader/fragments/CommentListingFragment.java @@ -346,6 +346,10 @@ public void handleCommentVisibilityToggle(final RedditCommentView view) { nowCollapsed ? R.string.accessibility_announcement_comment_collapsed : R.string.accessibility_announcement_comment_expanded); + + if(nowCollapsed && position != RecyclerView.NO_POSITION) { + focusNextComment(position); + } } } @@ -854,6 +858,27 @@ private void jumpToNextParent(final int startingPosition) { } } + private void focusNextComment(final int startingPosition) { + final LinearLayoutManager layoutManager + = (LinearLayoutManager)mRecyclerView.getLayoutManager(); + + for( + int pos = startingPosition + 1; + pos < layoutManager.getItemCount(); + pos++ + ) { + final GroupedRecyclerViewAdapter.Item item + = mCommentListingManager.getItemAtPosition(pos); + + if(item instanceof RedditCommentListItem + && ((RedditCommentListItem)item).isComment()) { + mParentJumpCount++; + setAccessibilityFocusAfterLayout(pos, true); + return; + } + } + } + private boolean isTopLevelComment(final int position) { final GroupedRecyclerViewAdapter.Item item = mCommentListingManager.getItemAtPosition(position); @@ -894,13 +919,18 @@ private void setAccessibilityFocusAfterLayout( final RecyclerView.ViewHolder holder = mRecyclerView.findViewHolderForAdapterPosition(position); + final LinearLayoutManager layoutManager + = (LinearLayoutManager)mRecyclerView.getLayoutManager(); - if(holder != null) { + if(holder != null + && position >= layoutManager.findFirstVisibleItemPosition() + && position <= layoutManager.findLastVisibleItemPosition()) { holder.itemView.performAccessibilityAction( AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); - } else if(allowRetry && mRecyclerView.hasPendingAdapterUpdates()) { + } else if(allowRetry && position >= 0 && position < layoutManager.getItemCount()) { + layoutManager.scrollToPosition(position); setAccessibilityFocusAfterLayout(position, false); } });