feat(downloads): detect lost SAF grant and offer folder re-selection

A persisted SAF tree grant can die silently (folder deleted or renamed,
SD card removed, app data reset, system revocation). Downloads then fail
with no recovery path beyond digging through Settings.

- Add isSafTreeAccessible bridge method: the grant must still be in the
  system's persisted permission list and the tree writable
- Show a warning banner in Files & Folders when the saved SAF folder is
  no longer accessible, with an inline re-select action
- In the download error dialog, replace Retry with a Re-select folder
  action when the failure is a lost folder grant (Android SAF or iOS
  bookmark); a successful re-pick retries the item automatically
- Share the folder-access error messages as constants so the UI can
  match on them reliably
This commit is contained in:
zarzet
2026-07-09 19:19:01 +07:00
parent 53824b7ac2
commit 927fc8abac
22 changed files with 389 additions and 24 deletions
@@ -2414,6 +2414,27 @@ class MainActivity: FlutterFragmentActivity() {
}
result.success(exists)
}
"isSafTreeAccessible" -> {
val uriStr = call.argument<String>("tree_uri") ?: ""
val accessible = withContext(Dispatchers.IO) {
try {
val uri = Uri.parse(uriStr)
val persisted = contentResolver.persistedUriPermissions.any {
it.uri == uri && it.isReadPermission && it.isWritePermission
}
if (!persisted) {
false
} else {
val doc = DocumentFile.fromTreeUri(this@MainActivity, uri)
doc != null && doc.exists() && doc.canWrite()
}
} catch (e: Exception) {
android.util.Log.w("SpotiFLAC", "SAF tree access check failed: ${e.message}")
false
}
}
result.success(accessible)
}
"safDelete" -> {
val uriStr = call.argument<String>("uri") ?: ""
val deleted = withContext(Dispatchers.IO) {