Skip to content

Commit 5191625

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

3 files changed

Lines changed: 104 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: 98 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -226,30 +226,82 @@ 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 (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && !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 (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
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)
255+
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 (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
259+
// Using a if with OR conditions allows to test them one by one, and stops at the first successful one
260+
if (
261+
startIntentForUriAction(
262+
uri,
263+
"android.intent.action.VIEW",
264+
ComponentName("com.google.android.documentsui", "com.android.documentsui.files.FilesActivity")
265+
) ||
266+
startIntentForUriAction(
267+
uri,
268+
"android.intent.action.VIEW",
269+
ComponentName("com.android.documentsui", "com.android.documentsui.files.FilesActivity")
270+
) ||
271+
startIntentForUriAction(
272+
uri,
273+
"android.intent.action.VIEW",
274+
ComponentName("com.android.documentsui", "com.android.documentsui.FilesActivity")
275+
) ||
276+
startIntentForUriAction(uri, "android.intent.action.VIEW", null) ||
277+
startIntentForUriAction(uri, "android.provider.action.BROWSE", null) ||
278+
startIntentForUriAction(uri, "android.provider.action.BROWSE_DOCUMENT_ROOT", null)
279+
) {
280+
return@ConfirmationAdvancedDialog
281+
} else {
282+
toast(R.string.confirm_storage_access_restricted_text_not_found)
283+
}
284+
} else {
285+
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
286+
putExtra(EXTRA_SHOW_ADVANCED, true)
287+
putExtra(DocumentsContract.EXTRA_INITIAL_URI, uri)
288+
try {
289+
startActivityForResult(this, OPEN_DOCUMENT_TREE_FOR_ANDROID_DATA_OR_OBB)
290+
checkedDocumentPath = path
291+
return@apply
292+
} catch (e: Exception) {
293+
type = "*/*"
294+
}
245295

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)
296+
try {
297+
startActivityForResult(this, OPEN_DOCUMENT_TREE_FOR_ANDROID_DATA_OR_OBB)
298+
checkedDocumentPath = path
299+
} catch (e: ActivityNotFoundException) {
300+
toast(R.string.system_service_disabled, Toast.LENGTH_LONG)
301+
} catch (e: Exception) {
302+
toast(R.string.unknown_error_occurred)
303+
}
304+
}
253305
}
254306
}
255307
}
@@ -262,6 +314,32 @@ fun BaseSimpleActivity.isShowingAndroidSAFDialog(path: String): Boolean {
262314
}
263315
}
264316

317+
/**
318+
* Start the intent
319+
* @param uri URI to pass to the intent
320+
* @param action Action of the intent
321+
* @param componentName Optional ComponentName
322+
* @return true if the intent was successful
323+
*/
324+
fun BaseSimpleActivity.startIntentForUriAction(
325+
uri: Uri,
326+
action: String,
327+
componentName: ComponentName?
328+
): Boolean {
329+
val intent = Intent(action, uri)
330+
if (componentName != null) {
331+
intent.setComponent(componentName)
332+
}
333+
return try {
334+
startActivity(intent)
335+
finish()
336+
true
337+
} catch (e: java.lang.Exception) {
338+
e.printStackTrace()
339+
false
340+
}
341+
}
342+
265343
fun BaseSimpleActivity.isShowingOTGDialog(path: String): Boolean {
266344
return if (!isRPlus() && isPathOnOTG(path) && (baseConfig.OTGTreeUri.isEmpty() || !hasProperStoredTreeUri(true))) {
267345
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)