Skip to content

Commit 860e270

Browse files
iamitmikehardy
iamit
authored andcommitted
fix: managed backstack for parent fragments in app settings
- added compact and non-compact tests for Preference navigation.
1 parent f53c80e commit 860e270

File tree

2 files changed

+107
-0
lines changed

2 files changed

+107
-0
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/**
2+
* Copyright (c) 2025 Amit Bisht <iamitsbisht07@gmail.com>
3+
*
4+
* This program is free software; you can redistribute it and/or modify it under
5+
* the terms of the GNU General Public License as published by the Free Software
6+
* Foundation; either version 3 of the License, or (at your option) any later
7+
* version.
8+
*
9+
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
10+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
11+
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
12+
*
13+
* You should have received a copy of the GNU General Public License along with
14+
* this program. If not, see <http://www.gnu.org/licenses/>.
15+
*/
16+
package com.ichi2.anki.preferences
17+
18+
import android.content.Context
19+
import androidx.recyclerview.widget.RecyclerView
20+
import androidx.test.core.app.ActivityScenario
21+
import androidx.test.core.app.ApplicationProvider
22+
import androidx.test.espresso.Espresso.onView
23+
import androidx.test.espresso.Espresso.pressBack
24+
import androidx.test.espresso.action.ViewActions.click
25+
import androidx.test.espresso.action.ViewActions.typeText
26+
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
27+
import androidx.test.espresso.assertion.ViewAssertions.matches
28+
import androidx.test.espresso.contrib.DrawerActions
29+
import androidx.test.espresso.matcher.ViewMatchers.hasFocus
30+
import androidx.test.espresso.matcher.ViewMatchers.hasMinimumChildCount
31+
import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
32+
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
33+
import androidx.test.espresso.matcher.ViewMatchers.withClassName
34+
import androidx.test.espresso.matcher.ViewMatchers.withId
35+
import androidx.test.espresso.matcher.ViewMatchers.withResourceName
36+
import androidx.test.espresso.matcher.ViewMatchers.withText
37+
import androidx.test.ext.junit.runners.AndroidJUnit4
38+
import com.ichi2.anki.IntentHandler
39+
import com.ichi2.anki.R
40+
import com.ichi2.anki.testutil.GrantStoragePermission
41+
import com.ichi2.anki.testutil.closeGetStartedScreenIfExists
42+
import com.ichi2.anki.testutil.grantPermissions
43+
import com.ichi2.anki.utils.isWindowCompact
44+
import org.hamcrest.Matchers.allOf
45+
import org.hamcrest.Matchers.endsWith
46+
import org.junit.Assume.assumeTrue
47+
import org.junit.Rule
48+
import org.junit.Test
49+
import org.junit.runner.RunWith
50+
51+
@RunWith(AndroidJUnit4::class)
52+
class PreferencesNavigationTest {
53+
@get:Rule
54+
val runtimePermissionRule = grantPermissions(GrantStoragePermission.storagePermission)
55+
56+
/**
57+
* Verifies navigation behavior when the search bar is tapped.
58+
* - When the user searches for something using the search bar,the search view should close upon pressing the back button.
59+
* - The back button should not close the entire preferences activity.
60+
*/
61+
@Test
62+
fun testOnCompactMode() {
63+
val context = ApplicationProvider.getApplicationContext<Context>()
64+
assumeTrue(context.resources.isWindowCompact())
65+
ActivityScenario.launch(IntentHandler::class.java)
66+
closeGetStartedScreenIfExists()
67+
onView(withId(R.id.drawer_layout)).perform(DrawerActions.open())
68+
onView(withId(R.id.nav_settings)).perform(click())
69+
onView(withId(R.id.search)).perform(click())
70+
onView(allOf(withId(R.id.search), hasFocus())).perform(typeText("Controls"))
71+
pressBack()
72+
// Checking the list of Settings Categories are displayed on the basis of our search "Controls"
73+
onView(allOf(withResourceName("list"), isAssignableFrom(RecyclerView::class.java))).check(
74+
matches(
75+
hasMinimumChildCount(1),
76+
),
77+
)
78+
pressBack()
79+
onView(withId(R.id.settings_container)).check(matches(isDisplayed()))
80+
}
81+
82+
/**
83+
* Verifies navigation behavior when multiple menus are opened.
84+
* - Even after opening multiple menus, pressing the back button should
85+
* close the preference activity instead of navigating back through the previously opened menus.
86+
*/
87+
@Test
88+
fun testOnNonCompactMode() {
89+
fun isTablet(context: Context): Boolean = context.resources.configuration.smallestScreenWidthDp >= 600
90+
91+
val context = ApplicationProvider.getApplicationContext<Context>()
92+
assumeTrue(isTablet(context))
93+
ActivityScenario.launch(IntentHandler::class.java)
94+
closeGetStartedScreenIfExists()
95+
onView(withId(R.id.drawer_layout)).perform(DrawerActions.open())
96+
onView(withId(R.id.nav_settings)).perform(click())
97+
onView(withId(R.id.search)).perform(click())
98+
onView(allOf(withId(R.id.search), hasFocus())).perform(typeText("Card"))
99+
onView(withText(R.string.card_zoom)).perform(click())
100+
onView(withId(R.id.settings_container)).check(matches(isDisplayed()))
101+
onView(withText(R.string.notification_pref)).perform(click())
102+
pressBack()
103+
onView(withClassName(endsWith("PreferencesActivity"))).check(doesNotExist())
104+
}
105+
}

AnkiDroid/src/main/java/com/ichi2/anki/preferences/Preferences.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ class PreferencesFragment :
5454
override fun handleOnBackPressed() {
5555
if (!settingsIsSplit && childFragmentManager.backStackEntryCount > 0) {
5656
childFragmentManager.popBackStack()
57+
} else if (parentFragmentManager.backStackEntryCount > 0) {
58+
parentFragmentManager.popBackStack()
5759
} else {
5860
requireActivity().finish()
5961
}

0 commit comments

Comments
 (0)