Skip to content

Commit c64b2cc

Browse files
authored
Merge pull request #4374 from TranceLove/bugfix/1555
2 parents c7e46f1 + e435871 commit c64b2cc

File tree

5 files changed

+475
-123
lines changed

5 files changed

+475
-123
lines changed
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package com.amaze.filemanager.ui.fragments
2+
3+
import android.content.Context
4+
import android.content.pm.ActivityInfo
5+
import android.os.Build.VERSION.SDK_INT
6+
import android.os.Build.VERSION_CODES.TIRAMISU
7+
import androidx.test.espresso.Espresso.onView
8+
import androidx.test.espresso.action.ViewActions.click
9+
import androidx.test.espresso.action.ViewActions.swipeLeft
10+
import androidx.test.espresso.action.ViewActions.swipeRight
11+
import androidx.test.espresso.matcher.ViewMatchers.withId
12+
import androidx.test.espresso.matcher.ViewMatchers.withText
13+
import androidx.test.ext.junit.runners.AndroidJUnit4
14+
import androidx.test.platform.app.InstrumentationRegistry
15+
import androidx.test.rule.ActivityTestRule
16+
import androidx.test.rule.GrantPermissionRule
17+
import androidx.test.uiautomator.By
18+
import androidx.test.uiautomator.UiDevice
19+
import androidx.test.uiautomator.UiSelector
20+
import com.amaze.filemanager.R
21+
import com.amaze.filemanager.ui.activities.MainActivity
22+
import org.junit.Before
23+
import org.junit.Rule
24+
import org.junit.Test
25+
import org.junit.runner.RunWith
26+
27+
/**
28+
* Tests for [TabFragment] functionality, mainly for
29+
* https://github.com/TeamAmaze/AmazeFileManager/issues/1555.
30+
*
31+
* Note: deprecated methods and classes are used here for best reproducing the issues.
32+
*/
33+
@Suppress("DEPRECATION")
34+
@RunWith(AndroidJUnit4::class)
35+
class TabFragmentTest {
36+
@get:Rule
37+
val activityRule = ActivityTestRule(MainActivity::class.java)
38+
39+
@Rule
40+
@JvmField
41+
val storagePermissionRule: GrantPermissionRule =
42+
GrantPermissionRule
43+
.grant(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
44+
45+
@Rule
46+
@JvmField
47+
val notificationPermissionRule: GrantPermissionRule =
48+
if (SDK_INT >= TIRAMISU) {
49+
GrantPermissionRule.grant(android.Manifest.permission.POST_NOTIFICATIONS)
50+
} else {
51+
GrantPermissionRule.grant()
52+
}
53+
54+
/**
55+
* From https://github.com/android/android-test/issues/1658#issue-1551755250
56+
* HACK this grants access to external storage "manually" because other solutions don't seem
57+
* to set the permission.
58+
*/
59+
@Before
60+
fun grantManageStoragePermission() {
61+
val context: Context = InstrumentationRegistry.getInstrumentation().targetContext
62+
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
63+
64+
val amazeResources = context.packageManager.getResourcesForApplication(context.packageName)
65+
val grantPermissionExplanation = amazeResources.getString(R.string.grant_all_files_permission)
66+
67+
if (device.hasObject(By.text(grantPermissionExplanation))) {
68+
// First press Amaze's grant button
69+
onView(withText(R.string.grant)).perform(click())
70+
71+
// Identifier names are taken here:
72+
// https://cs.android.com/android/platform/superproject/+/master:packages/apps/Settings/res/values/strings.xml
73+
val resources = context.packageManager.getResourcesForApplication("com.android.settings")
74+
val resId =
75+
resources.getIdentifier(
76+
"permit_manage_external_storage",
77+
"string",
78+
"com.android.settings",
79+
)
80+
val permitManageExternalStorage = resources.getString(resId)
81+
82+
val grantToggle =
83+
device.findObject(UiSelector().textMatches("(?i)$permitManageExternalStorage"))
84+
grantToggle.click()
85+
device.pressBack()
86+
}
87+
}
88+
89+
/**
90+
* This test causes a rotation to happen while the MainFragment detaches, to check if it
91+
* fails. This could happen in reality, but should be very rare
92+
*/
93+
@Test
94+
fun testFragmentStateSavingDuringDetachment() {
95+
activityRule.activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
96+
97+
// Get the TabFragment
98+
InstrumentationRegistry.getInstrumentation().runOnMainSync {
99+
val activity = activityRule.activity
100+
val tabFragment =
101+
activity.supportFragmentManager
102+
.findFragmentById(R.id.content_frame) as TabFragment
103+
104+
// Detach fragment through FragmentManager
105+
activity.supportFragmentManager.beginTransaction().apply {
106+
tabFragment.fragments.forEach { detach(it) }
107+
commit()
108+
}
109+
}
110+
}
111+
112+
/**
113+
* Check if the fragment state is saved correctly during a configuration change
114+
* by rotate the screen while swiping between the tabs.
115+
*/
116+
@Test
117+
fun testFragmentStateSavingDuringConfigChange() {
118+
// First perform the swipe action
119+
onView(withId(R.id.pager)).perform(swipeLeft())
120+
121+
// Force a configuration change by rotating the screen
122+
activityRule.activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
123+
Thread.sleep(1000) // Give time for the rotation to complete
124+
activityRule.activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
125+
}
126+
127+
/**
128+
* Check if the fragment state is saved correctly during rapid tab swiping.
129+
*/
130+
@Test
131+
fun testRapidTabSwitchingAndStateSaving() {
132+
// Perform rapid tab switches
133+
repeat(10) {
134+
onView(withId(R.id.pager)).perform(swipeLeft())
135+
Thread.sleep(100) // Small delay to ensure swipe completes
136+
onView(withId(R.id.pager)).perform(swipeRight())
137+
Thread.sleep(100) // Small delay to ensure swipe completes
138+
}
139+
140+
// Force a save state by rotating
141+
activityRule.activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
142+
}
143+
144+
/**
145+
* Check if the fragment state is saved correctly when the fragment is detached.
146+
*/
147+
@Test
148+
fun testFragmentDetachmentAndStateSaving() {
149+
// First switch to a different tab
150+
onView(withId(R.id.pager)).perform(swipeLeft())
151+
152+
// Get the TabFragment
153+
InstrumentationRegistry.getInstrumentation().runOnMainSync {
154+
val activity = activityRule.activity
155+
val tabFragment =
156+
activity.supportFragmentManager
157+
.findFragmentById(R.id.content_frame) as TabFragment
158+
159+
// Detach fragment through FragmentManager
160+
activity.supportFragmentManager.beginTransaction().apply {
161+
tabFragment.fragments.firstOrNull()?.let { detach(it) }
162+
commit()
163+
}
164+
}
165+
166+
// Force state save through configuration change
167+
activityRule.activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
168+
}
169+
}

app/src/main/java/com/amaze/filemanager/adapters/RecyclerAdapter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ public void createHeaders(boolean invalidate, List<IconDataParcelable> uris) {
683683

684684
@Override
685685
public int getItemCount() {
686-
return getItemsDigested().size();
686+
return getItemsDigested() != null ? getItemsDigested().size() : 0;
687687
}
688688

689689
@Override

0 commit comments

Comments
 (0)