Skip to content

Commit 45e6c4b

Browse files
authored
Merge pull request #4404 from EmmanuelMess/emmanuelmess/fix/emulator
2 parents 1cfd380 + 4e34f0a commit 45e6c4b

File tree

3 files changed

+118
-160
lines changed

3 files changed

+118
-160
lines changed
Lines changed: 45 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2014-2022 Arpit Khurana <[email protected]>, Vishal Nehra <[email protected]>,
2+
* Copyright (C) 2014-2025 Arpit Khurana <[email protected]>, Vishal Nehra <[email protected]>,
33
* Emmanuel Messulam<[email protected]>, Raymond Lai <airwave209gt at gmail.com> and Contributors.
44
*
55
* This file is part of Amaze File Manager.
@@ -20,50 +20,54 @@
2020

2121
package com.amaze.filemanager.test
2222

23-
// import android.content.Intent
24-
// import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
25-
// import android.net.Uri
26-
// import android.os.Build
27-
// import android.os.Build.VERSION.SDK_INT
28-
// import android.os.Environment
29-
// import android.provider.Settings
30-
// import android.widget.Switch
31-
// import androidx.test.platform.app.InstrumentationRegistry
32-
// import androidx.test.uiautomator.UiDevice
33-
// import androidx.test.uiautomator.UiSelector
34-
// import org.junit.Assert.assertTrue
23+
import android.content.Context
24+
import androidx.test.core.app.ActivityScenario
25+
import androidx.test.espresso.Espresso.onView
26+
import androidx.test.espresso.action.ViewActions.click
27+
import androidx.test.espresso.matcher.ViewMatchers.withText
28+
import androidx.test.platform.app.InstrumentationRegistry
29+
import androidx.test.uiautomator.By
30+
import androidx.test.uiautomator.UiDevice
31+
import androidx.test.uiautomator.UiSelector
32+
import com.amaze.filemanager.R
33+
import com.amaze.filemanager.ui.activities.MainActivity
3534

3635
object StoragePermissionHelper {
3736
/**
38-
* This method is intended for Android R or above devices to obtain MANAGE_EXTERNAL_STORAGE
39-
* permission via UI Automator framework when running relevant Espresso tests.
40-
*
41-
* This method is flat commented out because UI Automator requires Android SDK 18, while
42-
* currently we still want to support SDK 14.
37+
* From https://github.com/android/android-test/issues/1658#issue-1551755250
38+
* HACK this grants access to external storage "manually" because other solutions don't seem
39+
* to set the permission.
4340
*/
4441
@JvmStatic
45-
fun obtainManageAppAllFileAccessPermissionAutomatically() {
46-
// if (!Environment.isExternalStorageManager() && SDK_INT > Build.VERSION_CODES.R) {
47-
// InstrumentationRegistry.getInstrumentation().run {
48-
// val device = androidx.test.uiautomator.UiDevice.getInstance(this)
49-
// val context = this.targetContext
50-
// device.pressHome()
51-
// val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
52-
// .setData(Uri.parse("package:${context.packageName}"))
53-
// .addFlags(FLAG_ACTIVITY_NEW_TASK)
54-
// context.startActivity(intent)
55-
// val switch = device.findObject(
56-
// androidx.test.uiautomator.UiSelector()
57-
// .packageName("com.android.settings")
58-
// .className(Switch::class.java.name)
59-
// .resourceId("android:id/switch_widget")
60-
// )
61-
// switch.click()
62-
// assertTrue(switch.isChecked)
63-
// device.pressHome()
64-
// }
65-
// }
66-
// assertTrue(Environment.isExternalStorageManager())
67-
return // Try to get codacy happy if they ever check me... pretend I am doing something
42+
fun grantManageStoragePermission() {
43+
// Ensure that an activity that has the dialog is launched
44+
ActivityScenario.launch(MainActivity::class.java)
45+
46+
val context: Context = InstrumentationRegistry.getInstrumentation().targetContext
47+
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
48+
49+
val amazeResources = context.packageManager.getResourcesForApplication(context.packageName)
50+
val grantPermissionExplanation = amazeResources.getString(R.string.grant_all_files_permission)
51+
52+
if (device.hasObject(By.text(grantPermissionExplanation))) {
53+
// First press Amaze's grant button
54+
onView(withText(R.string.grant)).perform(click())
55+
56+
// Identifier names are taken here:
57+
// https://cs.android.com/android/platform/superproject/+/master:packages/apps/Settings/res/values/strings.xml
58+
val resources = context.packageManager.getResourcesForApplication("com.android.settings")
59+
val resId =
60+
resources.getIdentifier(
61+
"permit_manage_external_storage",
62+
"string",
63+
"com.android.settings",
64+
)
65+
val permitManageExternalStorage = resources.getString(resId)
66+
67+
val grantToggle =
68+
device.findObject(UiSelector().textMatches("(?i)$permitManageExternalStorage"))
69+
grantToggle.click()
70+
device.pressBack()
71+
}
6872
}
6973
}

app/src/androidTest/java/com/amaze/filemanager/ui/fragments/BackupPrefsFragmentTest.kt

Lines changed: 71 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (C) 2014-2022 Arpit Khurana <[email protected]>, Vishal Nehra <[email protected]>,
2+
* Copyright (C) 2014-2025 Arpit Khurana <[email protected]>, Vishal Nehra <[email protected]>,
33
* Emmanuel Messulam<[email protected]>, Raymond Lai <airwave209gt at gmail.com> and Contributors.
44
*
55
* This file is part of Amaze File Manager.
@@ -25,6 +25,8 @@ import android.content.Context
2525
import android.content.Intent
2626
import android.content.SharedPreferences
2727
import android.net.Uri
28+
import android.os.Build.VERSION.SDK_INT
29+
import android.os.Build.VERSION_CODES.TIRAMISU
2830
import androidx.lifecycle.Lifecycle
2931
import androidx.preference.PreferenceManager
3032
import androidx.test.core.app.ActivityScenario
@@ -36,13 +38,16 @@ import androidx.test.espresso.matcher.ViewMatchers.withText
3638
import androidx.test.ext.junit.runners.AndroidJUnit4
3739
import androidx.test.rule.GrantPermissionRule
3840
import com.amaze.filemanager.R
41+
import com.amaze.filemanager.test.StoragePermissionHelper
3942
import com.amaze.filemanager.ui.activities.PreferencesActivity
4043
import com.amaze.filemanager.ui.fragments.preferencefragments.BackupPrefsFragment
4144
import com.google.gson.GsonBuilder
4245
import com.google.gson.reflect.TypeToken
43-
import org.junit.Assert
4446
import org.junit.Assert.assertEquals
47+
import org.junit.Assert.assertFalse
4548
import org.junit.Assert.assertTrue
49+
import org.junit.Assert.fail
50+
import org.junit.Before
4651
import org.junit.Rule
4752
import org.junit.Test
4853
import org.junit.runner.RunWith
@@ -59,23 +64,46 @@ class BackupPrefsFragmentTest {
5964
GrantPermissionRule
6065
.grant(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
6166

62-
/** Test exporting */
67+
@Rule
68+
@JvmField
69+
val notificationPermissionRule: GrantPermissionRule =
70+
if (SDK_INT >= TIRAMISU) {
71+
GrantPermissionRule.grant(android.Manifest.permission.POST_NOTIFICATIONS)
72+
} else {
73+
GrantPermissionRule.grant()
74+
}
75+
76+
/**
77+
* Storage permission is needed for saving the preferences to a user accessible file
78+
*/
79+
@Before
80+
fun grantManageStoragePermission() {
81+
StoragePermissionHelper.grantManageStoragePermission()
82+
}
83+
84+
/** Test exporting and reimporting preferences */
6385
@Test
64-
fun testExport() {
65-
val backupPrefsFragment = BackupPrefsFragment()
86+
fun testPreferencesExportImport() {
87+
val context = ApplicationProvider.getApplicationContext<Context>()
6688

67-
val activityScenario = ActivityScenario.launch(PreferencesActivity::class.java)
89+
val exportFile = File("$storagePath${File.separator}$fileName")
90+
exportFile.delete() // delete if already exists
6891

69-
activityScenario.moveToState(Lifecycle.State.STARTED)
92+
export(context, exportFile)
93+
import(exportFile)
94+
}
7095

71-
val exportFile =
72-
File(
73-
storagePath +
74-
File.separator +
75-
fileName,
76-
)
96+
/**
97+
* Test whether the exported file contains the expected preference values
98+
*/
99+
private fun export(
100+
context: Context,
101+
exportFile: File,
102+
) {
103+
val backupPrefsFragment = BackupPrefsFragment()
104+
val activityScenario = ActivityScenario.launch(PreferencesActivity::class.java)
77105

78-
exportFile.delete() // delete if already exists
106+
activityScenario.moveToState(Lifecycle.State.STARTED)
79107

80108
activityScenario.onActivity {
81109
it.supportFragmentManager.beginTransaction()
@@ -85,54 +113,21 @@ class BackupPrefsFragmentTest {
85113
backupPrefsFragment.exportPrefs()
86114
}
87115

88-
val tempFile =
89-
File(
90-
ApplicationProvider.getApplicationContext<Context>().cacheDir.absolutePath +
91-
File.separator +
92-
fileName,
93-
)
116+
val tempFile = File("${context.cacheDir.absolutePath}${File.separator}$fileName")
94117

95-
Assert.assertTrue(tempFile.exists())
118+
assertTrue(tempFile.exists())
96119

97-
// terrible hack :cringe:
98120
onView(withId(R.id.home)).perform(ViewActions.click())
99-
Thread.sleep(500)
100-
101121
onView(withText(R.string.save)).perform(ViewActions.click())
102-
Thread.sleep(500)
103-
104-
Assert.assertTrue(exportFile.exists())
105-
}
106122

107-
/** Test whether the exported file contains the expected preference values */
108-
@Test
109-
fun verifyExportFile() {
110-
val backupPrefsFragment = BackupPrefsFragment()
111-
112-
val activityScenario = ActivityScenario.launch(PreferencesActivity::class.java)
113-
114-
activityScenario.moveToState(Lifecycle.State.STARTED)
115-
116-
val file =
117-
File(
118-
storagePath +
119-
File.separator +
120-
fileName,
121-
)
123+
assertTrue(exportFile.exists())
122124

123125
activityScenario.onActivity { preferencesActivity ->
124-
preferencesActivity.supportFragmentManager.beginTransaction()
125-
.add(backupPrefsFragment, null)
126-
.commitNow()
127-
128-
val preferences =
129-
PreferenceManager
130-
.getDefaultSharedPreferences(preferencesActivity)
131-
126+
val preferences = PreferenceManager.getDefaultSharedPreferences(preferencesActivity)
132127
val preferenceMap: Map<String?, *> = preferences.all
133128

134129
val inputString =
135-
file
130+
exportFile
136131
.inputStream()
137132
.bufferedReader()
138133
.use {
@@ -150,34 +145,27 @@ class BackupPrefsFragmentTest {
150145
)
151146

152147
for ((key, value) in preferenceMap) {
153-
var mapValue = importMap[key]
154-
155-
if (mapValue!!::class.simpleName.equals("Double")) {
156-
mapValue = (mapValue as Double).toInt() // since Gson parses Integer as Double
157-
}
148+
val importedValue = importMap[key]
149+
val mapValue =
150+
if (importedValue != null && importedValue::class.simpleName.equals("Double")) {
151+
(importedValue as Double).toInt() // since Gson parses Integer as Double
152+
} else {
153+
importedValue
154+
}
158155

159156
assertEquals("Difference found at key $key", value, mapValue)
160157
}
161158
}
162159
}
163160

164-
/** Test import */
165-
@Test
166-
fun testImport() {
167-
val backupPrefsFragment = BackupPrefsFragment()
168-
161+
/**
162+
* Test whether the imported preferences contains the expected values
163+
*/
164+
private fun import(exportFile: File) {
169165
val activityScenario = ActivityScenario.launch(PreferencesActivity::class.java)
170-
171166
activityScenario.moveToState(Lifecycle.State.STARTED)
172167

173-
val exportFile =
174-
File(
175-
storagePath +
176-
File.separator +
177-
fileName,
178-
)
179-
180-
exportFile.delete() // delete if already exists
168+
val backupPrefsFragment = BackupPrefsFragment()
181169

182170
activityScenario.onActivity { preferencesActivity ->
183171
preferencesActivity.supportFragmentManager.beginTransaction()
@@ -207,18 +195,19 @@ class BackupPrefsFragmentTest {
207195
val importMap: Map<String?, *> =
208196
GsonBuilder()
209197
.create()
210-
.fromJson(
211-
inputString,
212-
type,
213-
)
198+
.fromJson(inputString, type)
214199

215-
val preferences =
216-
PreferenceManager
217-
.getDefaultSharedPreferences(preferencesActivity)
200+
val preferences = PreferenceManager.getDefaultSharedPreferences(preferencesActivity)
218201

219202
val preferenceMap: Map<String?, *> = preferences.all
220203

221-
for ((key, value) in preferenceMap) {
204+
assertFalse(preferenceMap.containsKey(null))
205+
206+
for ((k, v) in preferenceMap) {
207+
// This cast tells the kotlin type checker that fail() never returns
208+
val key = k ?: (fail() as Nothing)
209+
val value = v ?: (fail() as Nothing)
210+
222211
assertTrue("checkPrefEqual($key) failed", checkPrefEqual(preferences, importMap, key, value))
223212
}
224213
}
@@ -227,10 +216,10 @@ class BackupPrefsFragmentTest {
227216
private fun checkPrefEqual(
228217
preferences: SharedPreferences,
229218
importMap: Map<String?, *>,
230-
key: String?,
231-
value: Any?,
219+
key: String,
220+
value: Any,
232221
): Boolean {
233-
when (value!!::class.simpleName) {
222+
when (value::class.simpleName) {
234223
"Boolean" -> return importMap[key] as Boolean ==
235224
preferences.getBoolean(key, false)
236225
"Float" ->

0 commit comments

Comments
 (0)