Skip to content

Commit 445d12a

Browse files
committed
More friendly access to data/obb folder on Android 14+
1 parent dc0e5cb commit 445d12a

3 files changed

Lines changed: 110 additions & 22 deletions

File tree

commons/src/main/kotlin/org/fossify/commons/activities/BaseSimpleActivity.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -751,12 +751,12 @@ abstract class BaseSimpleActivity : AppCompatActivity() {
751751
}
752752
}
753753

754-
fun handleAndroidSAFDialog(path: String, callback: (success: Boolean) -> Unit): Boolean {
754+
fun handleAndroidSAFDialog(path: String, openInSystemAppAllowed: Boolean = false, callback: (success: Boolean) -> Unit): Boolean {
755755
hideKeyboard()
756756
return if (!packageName.startsWith("org.fossify")) {
757757
callback(true)
758758
false
759-
} else if (isShowingAndroidSAFDialog(path)) {
759+
} else if (isShowingAndroidSAFDialog(path, openInSystemAppAllowed)) {
760760
funAfterSAFPermission = callback
761761
true
762762
} else {

commons/src/main/kotlin/org/fossify/commons/extensions/Activity.kt

Lines changed: 104 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -226,30 +226,58 @@ fun BaseSimpleActivity.isShowingSAFCreateDocumentDialogSdk30(path: String): Bool
226226
}
227227
}
228228

229-
fun BaseSimpleActivity.isShowingAndroidSAFDialog(path: String): Boolean {
229+
fun BaseSimpleActivity.isShowingAndroidSAFDialog(path: String, openInSystemAppAllowed: Boolean = false): Boolean {
230230
return if (isRestrictedSAFOnlyRoot(path) && (getAndroidTreeUri(path).isEmpty() || !hasProperStoredAndroidTreeUri(path))) {
231231
runOnUiThread {
232232
if (!isDestroyed && !isFinishing) {
233-
ConfirmationAdvancedDialog(this, "", R.string.confirm_storage_access_android_text, R.string.ok, R.string.cancel) { success ->
234-
if (success) {
235-
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
236-
putExtra(EXTRA_SHOW_ADVANCED, true)
237-
putExtra(DocumentsContract.EXTRA_INITIAL_URI, createAndroidDataOrObbUri(path))
238-
try {
239-
startActivityForResult(this, OPEN_DOCUMENT_TREE_FOR_ANDROID_DATA_OR_OBB)
240-
checkedDocumentPath = path
241-
return@apply
242-
} catch (e: Exception) {
243-
type = "*/*"
244-
}
233+
if (isUpsideDownCakePlus() && !openInSystemAppAllowed) {
234+
ConfirmationDialog(
235+
this,
236+
"",
237+
R.string.confirm_storage_access_restricted_text,
238+
positive = android.R.string.ok,
239+
negative = 0
240+
) {}
241+
} else {
242+
ConfirmationAdvancedDialog(
243+
this,
244+
"",
245+
R.string.confirm_storage_access_restricted_text,
246+
if (isUpsideDownCakePlus()) {
247+
R.string.confirm_storage_access_restricted_text_open_system
248+
} else {
249+
R.string.confirm_storage_access_restricted_text_request_access
250+
},
251+
R.string.cancel
252+
) { success ->
253+
if (success) {
254+
val uri = createAndroidDataOrObbUri(path)
245255

246-
try {
247-
startActivityForResult(this, OPEN_DOCUMENT_TREE_FOR_ANDROID_DATA_OR_OBB)
248-
checkedDocumentPath = path
249-
} catch (e: ActivityNotFoundException) {
250-
toast(R.string.system_service_disabled, Toast.LENGTH_LONG)
251-
} catch (e: Exception) {
252-
toast(R.string.unknown_error_occurred)
256+
// On Android 14+, there is no longer any workaround to access the data/ and obb/ folders
257+
// Open the system app instead
258+
if (isUpsideDownCakePlus()) {
259+
launchSystemFileManager(uri)
260+
} else {
261+
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
262+
putExtra(EXTRA_SHOW_ADVANCED, true)
263+
putExtra(DocumentsContract.EXTRA_INITIAL_URI, uri)
264+
try {
265+
startActivityForResult(this, OPEN_DOCUMENT_TREE_FOR_ANDROID_DATA_OR_OBB)
266+
checkedDocumentPath = path
267+
return@apply
268+
} catch (e: Exception) {
269+
type = "*/*"
270+
}
271+
272+
try {
273+
startActivityForResult(this, OPEN_DOCUMENT_TREE_FOR_ANDROID_DATA_OR_OBB)
274+
checkedDocumentPath = path
275+
} catch (e: ActivityNotFoundException) {
276+
toast(R.string.system_service_disabled, Toast.LENGTH_LONG)
277+
} catch (e: Exception) {
278+
toast(R.string.unknown_error_occurred)
279+
}
280+
}
253281
}
254282
}
255283
}
@@ -262,6 +290,62 @@ fun BaseSimpleActivity.isShowingAndroidSAFDialog(path: String): Boolean {
262290
}
263291
}
264292

293+
/**
294+
* Launch system file manager by testing different possible intents depending on the device
295+
* Each intent is tested in a OR condition which allows to stop at the first successful one
296+
*/
297+
fun BaseSimpleActivity.launchSystemFileManager(uri: Uri) {
298+
if (
299+
startIntentForUriAction(
300+
uri,
301+
"android.intent.action.VIEW",
302+
ComponentName("com.google.android.documentsui", "com.android.documentsui.files.FilesActivity")
303+
) ||
304+
startIntentForUriAction(
305+
uri,
306+
"android.intent.action.VIEW",
307+
ComponentName("com.android.documentsui", "com.android.documentsui.files.FilesActivity")
308+
) ||
309+
startIntentForUriAction(
310+
uri,
311+
"android.intent.action.VIEW",
312+
ComponentName("com.android.documentsui", "com.android.documentsui.FilesActivity")
313+
) ||
314+
startIntentForUriAction(uri, "android.intent.action.VIEW", null) ||
315+
startIntentForUriAction(uri, "android.provider.action.BROWSE", null) ||
316+
startIntentForUriAction(uri, "android.provider.action.BROWSE_DOCUMENT_ROOT", null)
317+
) {
318+
return
319+
} else {
320+
toast(R.string.confirm_storage_access_restricted_text_not_found)
321+
}
322+
}
323+
324+
/**
325+
* Start the intent
326+
* @param uri URI to pass to the intent
327+
* @param action Action of the intent
328+
* @param componentName Optional ComponentName
329+
* @return true if the intent was successful
330+
*/
331+
fun BaseSimpleActivity.startIntentForUriAction(
332+
uri: Uri,
333+
action: String,
334+
componentName: ComponentName?
335+
): Boolean {
336+
val intent = Intent(action, uri)
337+
if (componentName != null) {
338+
intent.setComponent(componentName)
339+
}
340+
return try {
341+
startActivity(intent)
342+
true
343+
} catch (e: java.lang.Exception) {
344+
e.printStackTrace()
345+
false
346+
}
347+
}
348+
265349
fun BaseSimpleActivity.isShowingOTGDialog(path: String): Boolean {
266350
return if (!isRPlus() && isPathOnOTG(path) && (baseConfig.OTGTreeUri.isEmpty() || !hasProperStoredTreeUri(true))) {
267351
showOTGPermissionDialog(path)

commons/src/main/res/values/strings.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,10 @@
290290
<string name="confirm_storage_access_text_sd">If you don\'t see the SD card, try this</string>
291291
<string name="confirm_storage_access_android_text">Please allow the app accessing the selected storage on the next screen by pressing \'Use this folder\' at the bottom.</string>
292292
<string name="confirm_storage_access_android_text_specific">Please allow access to \'&lt;b&gt;%s&lt;/b&gt;\' on the next screen by pressing \'&lt;b&gt;Use this folder&lt;/b&gt;\' at the bottom.</string>
293+
<string name="confirm_storage_access_restricted_text">Due to system restrictions, this folder is not available.</string>
294+
<string name="confirm_storage_access_restricted_text_request_access">Request access</string>
295+
<string name="confirm_storage_access_restricted_text_open_system">Open in system app</string>
296+
<string name="confirm_storage_access_restricted_text_not_found">A system file manager app could not be found.</string>
293297
<string name="confirm_create_doc_for_new_folder_text">Please press \'&lt;b&gt;Save&lt;/b&gt;\' at the bottom of the next screen to create the new folder.</string>
294298
<string name="confirm_selection">Confirm selection</string>
295299
<string name="loading">Loading…</string>

0 commit comments

Comments
 (0)