Skip to content

Fix fragmented DeckPicker menu ownership and sw600dp split detection - #21620

Open
sanjaysargam wants to merge 2 commits into
ankidroid:mainfrom
sanjaysargam:todo/menu-options
Open

sanjaysargam wants to merge 2 commits into
ankidroid:mainfrom
sanjaysargam:todo/menu-options

Conversation

@sanjaysargam

Copy link
Copy Markdown
Member

Note

Assisted-by: GPT-5.4 Mini

Purpose / Description

This moves split pane study options menu handling from StudyOptionsFragment into DeckPicker, aligns the fragmented-layout check with the real sw600dp breakpoint, and removes screenSize from DeckPicker config handling so foldable and resizable window changes recreate the correct UI state

Fixes

Approach

Centralize fragmented menu delegation in DeckPicker

How Has This Been Tested?

Pixel 10 Pro Foldable

Checklist

Please, go through these checks before submitting the PR.

  • You have a descriptive commit message with a short title (first line, max 50 chars).
  • You have commented your code, particularly in hard-to-understand areas
  • You have performed a self-review of your own code
  • UI changes: include screenshots of all affected screens (in particular showing any new or changed strings)
  • UI Changes: You have tested your change using the Google Accessibility Scanner

@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Snapshot diff report vs main. Open screenshot-diff for diffs.

  • PreferencesScreenshotTest: 1 change
  • ReviewRemindersScreenshotTest: 16 changes
All 17 changed screenshots

PreferencesScreenshotTest

  • ScheduleRemindersFragment_compare.png

ReviewRemindersScreenshotTest

  • settingsHostTablet_scheduleReminders_compare.png
  • settingsHostTablet_troubleshooting_compare.png
  • settingsHost_landscapeCutout_collapsed_compare.png
  • settingsHost_landscapeCutout_compare.png
  • settingsHost_landscapeCutout_rtl_compare.png
  • settingsHost_scheduleReminders_compare.png
  • settingsHost_scheduleReminders_scrolled_compare.png
  • settingsHost_troubleshooting_compare.png
  • standaloneActivityHost_scheduleReminders_compare.png
  • standaloneActivityHost_systemBars_compare.png
  • standaloneActivityHost_systemBars_scrolledToEnd_compare.png
  • standaloneActivityHost_troubleshooting_compare.png
  • studyOptionsFragmentHost_scheduleReminders_compare.png
  • studyOptionsFragmentHost_troubleshooting_compare.png
  • studyOptionsFrameHost_scheduleReminders_compare.png
  • studyOptionsFrameHost_troubleshooting_compare.png

@david-allison

Copy link
Copy Markdown
Member

@ericli3690 Could you opine on the menu changes.

And probably add an issue for a unit test to ossify the decision on the displayed menu items.

@david-allison david-allison added the Needs reviewer reply Waiting for a reply from another reviewer label Aug 27, 2026

@david-allison david-allison left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry... I could have sworn I asked, could you split out the two functional changes here into separate commits

@ericli3690 ericli3690 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, this somehow slipped my notification radar somehow, apologies for the late reply.

Comment on lines +1205 to +1210
private inline fun <T> withStudyOptionsFragment(block: StudyOptionsFragment.() -> T): T? =
if (fragmented) {
fragment?.block()
} else {
null
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels... kinda hacky. Are we sure this is the intent of the TODO comment you've deleted? I was under the impression that the TODO is instructing a future developer to move the logic from the Fragment into the Activity, not just to hotwire calls to the Fragment code in.

Checking the history of the TODO comment, it's a suggestion from @david-allison (#16425), perhaps he could clarify what he initially wanted.

@david-allison david-allison Sep 2, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know a lot more about menus now. I'll change my opinion

Fragments should define their own menus (MenuProvider)

The Menu host (typically the activity) either coordinates, or provides the state to the fragments for them to hide their items

The menu host implicitly defines ordering, consider orderInCategory if you want finer grained control

Edit: I'll stand by my original point that one fragment should not own the menus of its siblings

@ericli3690 ericli3690 added Needs Author Reply Waiting for a reply from the original author and removed Needs reviewer reply Waiting for a reply from another reviewer labels Sep 2, 2026
@david-allison

Copy link
Copy Markdown
Member

Tests are failing

@david-allison david-allison removed the Needs Author Reply Waiting for a reply from the original author label Sep 6, 2026
Comment on lines 282 to 285
override var fragmented: Boolean
get() =
resources.configuration.screenLayout and Configuration.SCREENLAYOUT_SIZE_MASK ==
Configuration.SCREENLAYOUT_SIZE_XLARGE
resources.configuration.smallestScreenWidthDp >= 600
set(_) = throw UnsupportedOperationException()

@david-allison david-allison Sep 6, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel this continues to cause problems. Let's use

override var fragmented: Boolean
    get() = binding.studyoptionsFragment != null
    set(_) = throw UnsupportedOperationException()

We've discussed (maybe in Discord) that users are unhappy in 2.24 regarding both the Deck Picker and the Note Editor split screen changes, especially the modifications in the 'fragmented' threshold, and what acceptable values are for foldables.

I don't think we've done sufficient research/testing [proposed work in the linked issue] to come to a consensus on what this value should be.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. This also means you'd be able to align line 517:

if (binding.studyoptionsFragment == null) {

To instead be if (!fragmented) {

Then update unit tests to match

@david-allison david-allison added the Needs Author Reply Waiting for a reply from the original author label Sep 6, 2026
Comment on lines 282 to 285
override var fragmented: Boolean
get() =
resources.configuration.screenLayout and Configuration.SCREENLAYOUT_SIZE_MASK ==
Configuration.SCREENLAYOUT_SIZE_XLARGE
resources.configuration.smallestScreenWidthDp >= 600
set(_) = throw UnsupportedOperationException()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. This also means you'd be able to align line 517:

if (binding.studyoptionsFragment == null) {

To instead be if (!fragmented) {

Then update unit tests to match

Comment thread AnkiDroid/src/main/AndroidManifest.xml Outdated
android:theme="@style/Theme_Dark.Launcher"
android:exported="false"
android:configChanges="keyboardHidden|orientation|screenSize"
android:configChanges="keyboardHidden|orientation"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, my LLM claims that it doesn't make sense to do only orientation; double check, but apparently if you remove screenSize you should be safe to remove orientation too

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep

Comment thread AnkiDroid/src/main/AndroidManifest.xml Outdated
android:theme="@style/Theme_Dark.Launcher"
android:exported="false"
android:configChanges="keyboardHidden|orientation|screenSize"
android:configChanges="keyboardHidden"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop this, it's out of scope, (would need its own PR to discuss)

@Test
fun checkDisplayOfStudyOptionsOnTablet() {
assumeTrue("We are running on a tablet", qualifiers!!.contains("xlarge"))
assumeTrue("We are running on a tablet", resources.configuration.smallestScreenWidthDp >= 600)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still feel this is out of scope for the PR, we shouldn't be mixing redefining the layout widths in with this PR, and the prerequistite research hasn't been performed to determine the qualifier we want to use for a split screen.

`fragmented` checked `SCREENLAYOUT_SIZE_XLARGE`, which does not match
the `layout-sw600dp` resource boundary that the split-pane layout
actually uses, so the flag could disagree with the inflated layout.
Derive it from the layout itself: fragmented iff the layout provides
the side panel container, so the flag and the UI can never disagree
regardless of device geometry
fragments define their own menus via MenuProvider, and the menu host dispatches creation, preparation and
selection to them; one fragment must not own or drive the menus of its
siblings.
@sanjaysargam sanjaysargam removed the Needs Author Reply Waiting for a reply from the original author label Sep 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deck option appears on a non-split Deck Picker

3 participants