Managed back stack for parent fragments in the app settings#18057
Conversation
|
First PR! 🚀 We sincerely appreciate that you have taken the time to propose a change to AnkiDroid! Please have patience with us as we are all volunteers - we will get to this as soon as possible. |
lukstbit
left a comment
There was a problem hiding this comment.
Hey, thank you for contributing. Pleas format the code otherwise our code style action below is going to fail.
| .setNavigationOnClickListener { onBackPressedCallback.handleOnBackPressed() } | ||
|
|
||
| requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, onBackPressedCallback) | ||
| requireActivity().onBackPressedDispatcher.addCallback( onBackPressedCallback) |
There was a problem hiding this comment.
Thank you for your feedback! I have refactored the code and added back viewLifecycleOwner to ensure proper lifecycle management. Please let me if you have any other suggestions that needs to be done.
There was a problem hiding this comment.
@iamits-07 I assume you're a gsoc candidate. If so, you're really expected to answer questions if you want to be considered.
Either there was no reason, which is a bad sign as I don't see how we can trust code you wrote (for example if you just followed a LLM advice without understanding them). Or there was a reason and then answering would allow us to evaluate how good a fit you're
Arthur-Milchior
left a comment
There was a problem hiding this comment.
Thanks for the PR.
It works great on phone. Sadly, on tablet, it breaks the "return" button when you have selected multiple settings submenu
| .setNavigationOnClickListener { onBackPressedCallback.handleOnBackPressed() } | ||
|
|
||
| requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, onBackPressedCallback) | ||
| requireActivity().onBackPressedDispatcher.addCallback( onBackPressedCallback) |
There was a problem hiding this comment.
@iamits-07 I assume you're a gsoc candidate. If so, you're really expected to answer questions if you want to be considered.
Either there was no reason, which is a bad sign as I don't see how we can trust code you wrote (for example if you just followed a LLM advice without understanding them). Or there was a reason and then answering would allow us to evaluate how good a fit you're
| object : OnBackPressedCallback(true) { | ||
| override fun handleOnBackPressed() { | ||
| if (resources.isWindowCompact() && childFragmentManager.backStackEntryCount > 0) { | ||
| if ( childFragmentManager.backStackEntryCount > 0) { |
There was a problem hiding this comment.
Why did you remove "isWindowCompact?"
Do you have any idea why it was for? Did you test your PR on both compact and non compact window? Because I just tested on tablet, and now the feature is partially broken.
There was a problem hiding this comment.
Why did you remove "isWindowCompact?" Do you have any idea why it was for? Did you test your PR on both compact and non compact window? Because I just tested on tablet, and now the feature is partially broken.
I removed isWindowCompact() because I tested it on the mobile emulators and physical mobile devices only and thought it wasn't necessary for it. However now, I understand that it affects the behavior on tablets.
I have updated the code to solve the problem in devices like tablets or foldables screens and ensures the proper functionality in both compact and non-compact windows.
Thanks for your guidance.
There was a problem hiding this comment.
Then I'd advise for a next time to ask why some code is there before removing it or editing it.
There are old code that is actually useless, it occurs. But you need to ensure it'sa ctually the case.
In particular, here, the name of the function should have been a hint that this feature is related to the size of the screen, whether it's compact or not
|
The code indeed seems to do what I want, great. Can you also add tests. Both tests that ensure that ensures that this feature works, and also a test that ensure that, on tablet, when there is no oingoing search and that you opened many submenu "back" close the settings instead of going to the previous submenu. This would ensure that the previous version of your PR would have failed the test, and thus that it would have been catched before needing our review |
Thank you for your feedback and review. I will add the tests to ensure the feature works correctly and also verify that on tablets, the back button behavior is handled as expected. I'll update the PR once the tests are added. |
| @get:Rule | ||
| val permissionRule: GrantPermissionRule = | ||
| GrantPermissionRule.grant( | ||
| Manifest.permission.WRITE_EXTERNAL_STORAGE, | ||
| Manifest.permission.READ_EXTERNAL_STORAGE, | ||
| ) |
There was a problem hiding this comment.
Why is this needed? Also, why is a new test file needed for this test when we already have PreferencesTest.kt?
There was a problem hiding this comment.
I used this rule to grant storage permission automatically during test execution (on Physical device it was creating problem ), ensuring the test doesn't fail due to permission issues.
As for the new test file, it specifically focuses on navigation behavior in compact and non compact modes, which differs from the general preferences-related tests in PreferencesTest.kt .
Would you recommend proceeding with the merge, or do you suggest any improvements?
There was a problem hiding this comment.
But why would this need to access storage?
There was a problem hiding this comment.
On running the module, the app launces the Introduction Activity, triggering a permission dialog for newly installed apps and I thought, it would be a good option to add that rule for safer side and consistent execution. However, if this permission isn't strictly necessary for the test, I can review and update the code.
There was a problem hiding this comment.
Makes sense, you can use the below:
@get:Rule
val runtimePermissionRule = grantPermissions(GrantStoragePermission.storagePermission)
There was a problem hiding this comment.
Also, you can consider merging these two new files with a name like PreferencesNavigationTest.
There was a problem hiding this comment.
Thanks for your suggestions! I'll update the PR after updating the tests.
There was a problem hiding this comment.
If you look at the error message you got, I suspect it was
java.lang.ExceptionInInitializerError
at com.ichi2.anki.preferences.PreferencesNavigationTest.(PreferencesNavigationTest.kt:31)
... 33 trimmed
Caused by: java.lang.IllegalStateException: 'All Files' access is required on your emulator/device. Please grant it manually or change Build Variant to 'playDebug' in Android Studio (Build -> Select Build Variant)
Which, if you read it, explains how to solve the isuse.
Yeah, it's not ideal that you've to switch to the play variant manually
AnkiDroid/src/androidTest/java/com/ichi2/anki/preferences/PreferencesTestNonCompact.kt
Outdated
Show resolved
Hide resolved
AnkiDroid/src/androidTest/java/com/ichi2/anki/preferences/PreferencesTestCompact.kt
Outdated
Show resolved
Hide resolved
Arthur-Milchior
left a comment
There was a problem hiding this comment.
I'm very happy with the evolution of this PR, great tests!
I'd also appreciate a test that use the search bar in non compact mode please.
And while we're at it, even if it's not related to the issue you're solving, I guess it'd be great to have a test that ensure that, if you click on a search result and then goes back, you end up in the list of settings category.
AnkiDroid/src/androidTest/java/com/ichi2/anki/preferences/PreferencesNavigationTest.kt
Show resolved
Hide resolved
| */ | ||
| @Test | ||
| fun testOnCompactMode() { | ||
| fun isCompactMode(context: Context): Boolean = context.resources.configuration.smallestScreenWidthDp < 600 |
There was a problem hiding this comment.
You can directly use "context.resources.isWindowCompact()"
| @get:Rule | ||
| val runtimePermissionRule = grantPermissions(GrantStoragePermission.storagePermission) | ||
|
|
||
| /* |
There was a problem hiding this comment.
Please use /**
The second star indicates that it's documentation. You're stating to the reader what you're testing here.
We use a single star for comment, when we are trying to indicate how the code works.
| onView(withId(R.id.drawer_layout)).perform(DrawerActions.open()) | ||
| onView(withId(R.id.nav_settings)).perform(click()) | ||
| onView(withId(R.id.search)).perform(click()) | ||
| pressBack() |
There was a problem hiding this comment.
I'd appreciate a check between both pressBack. Something that ensures that a preference such as "Notifications" is displayed.
And also a comment explaining what exactly you're testing at this place. I.e.
"The list of settings categories is displayed"
| @get:Rule | ||
| val permissionRule: GrantPermissionRule = | ||
| GrantPermissionRule.grant( | ||
| Manifest.permission.WRITE_EXTERNAL_STORAGE, | ||
| Manifest.permission.READ_EXTERNAL_STORAGE, | ||
| ) |
There was a problem hiding this comment.
If you look at the error message you got, I suspect it was
java.lang.ExceptionInInitializerError
at com.ichi2.anki.preferences.PreferencesNavigationTest.(PreferencesNavigationTest.kt:31)
... 33 trimmed
Caused by: java.lang.IllegalStateException: 'All Files' access is required on your emulator/device. Please grant it manually or change Build Variant to 'playDebug' in Android Studio (Build -> Select Build Variant)
Which, if you read it, explains how to solve the isuse.
Yeah, it's not ideal that you've to switch to the play variant manually
| onView(withId(R.id.get_started)).perform(click()) | ||
| onView(withId(R.id.drawer_layout)).perform(DrawerActions.open()) | ||
| onView(withId(R.id.nav_settings)).perform(click()) | ||
| onView(withText("Sync")).perform(click()) |
There was a problem hiding this comment.
Please don't hard code strings. Otherwise, the test will break in the device is not configured in English.
` onView(withText(R.string.pref_cat_controls)).perform(click())` works
|
Thanks for your feedback and suggestions. I'll update the PR after updating the tests. |
|
Thanks for the update.
This test is still missing. more importantly, it seems your test is flaky.
I actually had the same problem running locally the first time and then could not reproduce. I've no idea what's going on. I'll ask advice on discord. if we can't land it non flakily, then I suggest we land the non test code first and let the code for a second PR. After all, your code clearly works, and I don't want to block because of the test suite |
| /* | ||
| /** | ||
| * This test verifies the navigation when search bar is clicked. | ||
| * When user searches for something in the search bar,It should close the search view |
There was a problem hiding this comment.
I've a hard time understanding how your sentence breaks. Why is there a upper case juste after a comma?
Also, as it's mainly an app for phone and tablet, we generally use "tap" instead of "click". Even if you're right that it could be clicked if the user use a mouse
| @Test | ||
| fun testOnCompactMode() { | ||
| fun isCompactMode(context: Context): Boolean = context.resources.configuration.smallestScreenWidthDp < 600 | ||
| fun isCompactMode(context: Context): Boolean = context.resources.isWindowCompact() |
There was a problem hiding this comment.
sorry I should have been more specific. I don't really see the point of the function. I thought you could juste us isWindowCompact in the assumeTrue
|
Got it. You should have used |
|
Thanks for suggestions, as you said about the problem during the first run of the module I will try to fix it and update the others. |
This comment was marked as resolved.
This comment was marked as resolved.
AnkiDroid/src/androidTest/java/com/ichi2/anki/preferences/PreferencesNavigationTest.kt
Show resolved
Hide resolved
mikehardy
left a comment
There was a problem hiding this comment.
CI was stuck on this one, and one of the commits conflicted with main but in a way that was altered in further commits on this PR
I squashed all commits to a single one, then rebase to current main was possible
So, LGTM now, and I'm queueing for merge, thanks!
code is formatted now (or at least I think it is - CI is chewing on it)
- added compact and non-compact tests for Preference navigation.
Purpose / Description
Issue: The Settings screen closes when searching and the Back button or the Navigation icon is pressed, causing the user to restart their search.
Why it Matters: This issue creates a significant usability barrier, hindering users' ability to effectively use the settings. Addressing it will make the settings experience more efficient and less frustrating.
Fixes
Approach
Before: Child fragments were effectively managed within the handleOnBackPressed() method. However, this method did not manage the back stack for the parent fragment, which is directly hosted by the activity.
After the changes: Now, the method also manages the parent fragment's back stack, ensuring it remains on the screen when the user is searching and presses the back button or navigation icon, instead of navigating directly to the home screen.
How Has This Been Tested?
This issue has been tested on a physical device running on Android 15, as well as on Android Emulators (API 35 and API 34), with all possible navigation states.
This video clip shows how it is working now:
(Please avoid the playback speed, make it normal, due to the length of the video it is edited to 2x)
https://github.com/user-attachments/assets/dd1b8d11-c4b0-4fce-9f57-52ad939e8f82
Checklist
Please, go through these checks before submitting the PR.