Repository navigation
feat(export): pick the deck with the deck selection dialog - #22678
Conversation
|
Snapshot diff report vs
All 1 changed screenshotsExportDialogFragmentScreenshotTest
|
f500927 to
4d9359b
Compare
4d9359b to
205f6a1
Compare
205f6a1 to
ee426a0
Compare
david-allison
left a comment
There was a problem hiding this comment.
LGTM, two nits, fix at your discretion!
ee426a0 to
4fbcbb5
Compare
mikehardy
left a comment
There was a problem hiding this comment.
Assisted-by: composer-2.5
Thanks for tightening the export dialog — the direction looks good.
Locally I checked out the PR, rebased cleanly onto current main (no push), ran ./gradlew lintAll ktlintCheck lint-rules:test (pass), ./gradlew jacocoUnitTestReport (fail), and TEST_RELEASE_BUILD=true python3 .github/run_emulator_tests.py on the assigned API 35 play release emulator (382 tests; 11 connected-test XML failures after waivers).
The diff centers on ExportDialogFragment, dialog_export_options.xml, related deck-selection wiring, and new/updated export dialog unit and screenshot tests.
Requesting changes: connected tests on this branch fail in ways that line up with the export UI work — ReviewerTest (two cases) time out waiting for Reviewer and instead resume on DeckPicker, and ReviewRemindersTest fails in the connected run. Please fix those regressions (or adjust navigation/test setup so reviewer flows still reach Reviewer) and re-run the emulator suite on the PR head.
4fbcbb5 to
d10fd57
Compare
mikehardy
left a comment
There was a problem hiding this comment.
Assisted-by: grok-4.7
Thanks for the keyboard and screenshot follow-through — the API 24/25 cases are the right place to lock focusable, and the deck screenshot is the screen this control change actually affects.
Locally I rebased cleanly onto current main without pushing (push: no). Lint, unit, and emulator lanes all passed on emulator-5556 (API35_GAPI).
initializeDecks no longer fills a Spinner. It registers the child result listener, sets the click listener and an accessibility delegate that reports Spinner plus canOpenPopup, then loads one deck on lifecycleScope: the parcelled SelectableDeck, otherwise decks.get(deckId, default = false) mapped to SelectableDeck.Deck, otherwise SelectableDeck.AllDecks. showSelectedDeck sets deckSelector.text from getFullDisplayName. A passed deck id still preselects the apkg export type. Fragment.startDeckSelection now returns that Job; the activity overload is unchanged. DeckSelectionDialog.onNewDeckCreated notifies deck and study-queue changes with the dialog as initiator before selectDeckAndClose.
ExportDialogFragmentKeyboardTest inflates dialog_export_options under Theme_Light with the backend unloaded, sets a click listener (so the row is clickable, as production does), and asserts isFocusable on SDK 24, 25, and 36. ExportDialogFragmentScreenshotTest.deck launches newInstance(DEFAULT_DECK_ID) and captures after advanceRobolectricLooper(). ExportDialogFragmentTest covers opening the child dialog (ARG_ALLOW_ALL and ARG_ALLOW_FILTERED), keeping the chosen name across recreate(), selecting a filtered deck, the accessibility role, deck creation showing up on Deck Picker after cancel, dismiss during a held collection load, and a double tap while that load is held opening one dialog.
some acceptable findings, no action required, just leaving for posterity: purpose text vs filtered-deck defaults; 58-char commit title; keyboard test does not call requestFocus(); Google Accessibility Scanner checkbox still open.
Screenshot changes have been detected, so leaving this for a good manual look.
d10fd57 to
a704837
Compare
|
Any reason for the tag? |
|
Apologies. No need for it. My mistake |
aef2532 to
c35a0c8
Compare
|
|
||
| private fun getDeckNames(): ArrayList<SelectableDeck> = | ||
| BundleCompat.getParcelableArrayList(requireArguments(), ARG_DECK_NAMES, SelectableDeck::class.java)!! | ||
| private fun getDeckIds(): LongArray = requireArguments().getLongArray(ARG_DECK_IDS)!! |
There was a problem hiding this comment.
Add a requireLongArray extension; we have others. They print the key which causes the failure.
| } | ||
|
|
||
| private fun showSearchResults() { | ||
| allDecksList.filter { currentFilters.accept(it.fullDeckName) }.forEach { it.collapsed = false } |
There was a problem hiding this comment.
I'd comment this; took a second to understand
| val remainingDeckCount: Int | ||
| get() = allDecksList.size | ||
|
|
||
| private val allDecksList = ArrayList<DeckNode>() | ||
| private val currentlyDisplayedDecks = ArrayList<DeckNode>() | ||
| private var currentFilters = DeckFilters.create("") |
There was a problem hiding this comment.
No action wanted: Unusual to have these in the middle... but it's the Deck Selection Dialog 🤷
david-allison
left a comment
There was a problem hiding this comment.
Not sure I like the 'include' label; I'm not sure it implies that it's a deck that you're including
Exporting a deck listed every deck in one long drop-down, which was hard to use with many subdecks. The deck field now opens the same deck dialog as the Browser and Statistics, with search and collapsible subdecks. A deck created from that dialog shows up in the Deck Picker straight away. The deck dialog now keeps only deck ids in its arguments, so its saved state stays small with thousands of decks. Fixes 18525 Assisted-by: Claude Opus 5.5
Typing a search before the deck list had loaded, or expanding or collapsing a deck in the results, brought back every deck while the search text was still there. The list now keeps the search applied. Assisted-by: Claude Opus 5.5
|
I'd rather keep it matching desktop here, but happy to open an issue if you'd like a clearer label. |
c35a0c8 to
5f55c56
Compare
mikehardy
left a comment
There was a problem hiding this comment.
Assisted-by: grok-4.7
Thanks for carrying this through the search, binder-size, and deck-creation cases — the shared dialog fix is the right place for the filter bug.
Locally I checked out the PR and replayed at the head without pushing (push: no). Lint, unit, and emulator lanes all passed on emulator-5556 (API35_GAPI).
dialog_export_options swaps R.id.deck_selector from a Spinner to a FixedTextView (spinnerItemStyle, dropdown drawable, focusable, selectable background). ExportDialogFragment.initializeDecks registers registerChildDeckSelectedHandler(REQUEST_EXPORT_DECK), ignores a second click while startDeckSelection's Job is active or a child tagged DeckSelectionDialog.TAG is already added, and sets an accessibility delegate that reports Spinner plus canOpenPopup. The label is loaded on lifecycleScope from decks.get(deckId, default = false) (null and unknown ids, including ALL_DECKS_ID 0, become SelectableDeck.AllDecks). onSaveInstanceState stores that id; a later get of a real id reloads the name, so a rename while stopped shows the new name. Export limits use wholeCollection for AllDecks and deck.deckId otherwise. The positive button still returns early when a non-collection export is tapped before the selector is enabled.
Fragment.startDeckSelection returns that Job. The activity overload is unchanged. Dialog arguments are now a LongArray of deck ids (AllDecks stored as 0) via Bundle.requireLongArray. On load, those ids are resolved with deckDueTree().find, so a large collection no longer parcels every SelectableDeck. onNewDeckCreated notifies ChangeManager (deck and studyQueues, dialog as initiator) before selectDeckAndClose, which is what lets Deck Picker show a deck created from the export dialog after export is cancelled. CreateDeckDialog itself only calls decks.id and the callback.
Multi-select dismisses when allDecksList is empty (remainingDeckCount), not when the filtered row count hits zero, so selecting the last search hit leaves the dialog up while non-matching decks remain. DecksFilter.publishResults records DeckFilters.create(constraint) and calls showSearchResults, which clears collapsed on matching nodes and then rebuilds with accept(fullDeckName) && isViewable.
Merge pending CI.
Screenshot changes have been detected, so leaving this for a good manual look.
Note
Assisted-by: Opus 5.5
Purpose / Description
Exporting a deck package or plain text listed every deck in one long drop-down, which gets hard to use once you have a lot of subdecks.
Tapping the deck field now opens the same deck dialog as the Browser and Statistics, with search and collapsible subdecks. "All decks" is still first, filtered decks are still left out, and the chosen deck is kept on rotation. One small difference: an empty "Default" deck is no longer offered, the same as in the deck list.
Fixes
Approach
Check the commit message
How Has This Been Tested?
added tests and
Learning (optional, can help others)
NA
Checklist
Please, go through these checks before submitting the PR.