fixed SD card access grant on Android Lollipop
This commit is contained in:
parent
788241fab3
commit
fda5865e26
3 changed files with 46 additions and 9 deletions
|
@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
## <a id="unreleased"></a>[Unreleased]
|
||||
|
||||
### Fixed
|
||||
|
||||
- SD card access grant on Android Lollipop
|
||||
|
||||
## <a id="v1.7.10"></a>[v1.7.10] - 2023-01-18
|
||||
|
||||
### Added
|
||||
|
|
|
@ -41,7 +41,7 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
|
|||
"canGrantDirectoryAccess" to (sdkInt >= Build.VERSION_CODES.LOLLIPOP),
|
||||
"canPinShortcut" to ShortcutManagerCompat.isRequestPinShortcutSupported(context),
|
||||
"canPrint" to (sdkInt >= Build.VERSION_CODES.KITKAT),
|
||||
"canRenderFlagEmojis" to (sdkInt >= Build.VERSION_CODES.LOLLIPOP),
|
||||
"canRenderFlagEmojis" to (sdkInt >= Build.VERSION_CODES.M),
|
||||
"canRequestManageMedia" to (sdkInt >= Build.VERSION_CODES.S),
|
||||
"canSetLockScreenWallpaper" to (sdkInt >= Build.VERSION_CODES.N),
|
||||
"hasGeocoder" to Geocoder.isPresent(),
|
||||
|
|
|
@ -39,6 +39,7 @@ object StorageUtils {
|
|||
|
||||
private const val TREE_URI_ROOT = "content://$EXTERNAL_STORAGE_PROVIDER_AUTHORITY/tree/"
|
||||
|
||||
private val UUID_PATTERN = Regex("[A-Fa-f\\d-]+")
|
||||
private val TREE_URI_PATH_PATTERN = Pattern.compile("(.*?):(.*)")
|
||||
|
||||
const val TRASH_PATH_PLACEHOLDER = "#trash"
|
||||
|
@ -259,6 +260,7 @@ object StorageUtils {
|
|||
// e.g.
|
||||
// /storage/emulated/0/ -> primary
|
||||
// /storage/10F9-3F13/Pictures/ -> 10F9-3F13
|
||||
// /storage/extSdCard/ -> 1234-5678 [Android 5.1.1, Samsung Galaxy Core Prime]
|
||||
private fun getVolumeUuidForDocumentUri(context: Context, anyPath: String): String? {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
val sm = context.getSystemService(Context.STORAGE_SERVICE) as? StorageManager
|
||||
|
@ -278,7 +280,22 @@ object StorageUtils {
|
|||
return EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID
|
||||
}
|
||||
volumePath.split(File.separator).lastOrNull { it.isNotEmpty() }?.let { uuid ->
|
||||
return uuid.uppercase(Locale.ROOT)
|
||||
if (uuid.matches(UUID_PATTERN)) {
|
||||
return uuid.uppercase(Locale.ROOT)
|
||||
}
|
||||
}
|
||||
|
||||
// fallback when UUID does not appear in the SD card volume path
|
||||
context.contentResolver.persistedUriPermissions.firstOrNull { uriPermission ->
|
||||
convertTreeDocumentUriToDirPath(context, uriPermission.uri)?.let {
|
||||
getVolumePath(context, it)?.let { grantedVolumePath ->
|
||||
grantedVolumePath == volumePath
|
||||
}
|
||||
} ?: false
|
||||
}?.let { uriPermission ->
|
||||
splitTreeDocumentUri(uriPermission.uri)?.let { (uuid, _) ->
|
||||
return uuid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -289,6 +306,7 @@ object StorageUtils {
|
|||
// e.g.
|
||||
// primary -> /storage/emulated/0/
|
||||
// 10F9-3F13 -> /storage/10F9-3F13/
|
||||
// 1234-5678 -> /storage/extSdCard/ [Android 5.1.1, Samsung Galaxy Core Prime]
|
||||
private fun getVolumePathFromTreeDocumentUriUuid(context: Context, uuid: String): String? {
|
||||
if (uuid == EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID) {
|
||||
return getPrimaryVolumePath(context)
|
||||
|
@ -318,6 +336,10 @@ object StorageUtils {
|
|||
}
|
||||
}
|
||||
|
||||
// fallback when UUID does not appear in the SD card volume path
|
||||
val primaryVolumePath = getPrimaryVolumePath(context)
|
||||
getVolumePaths(context).firstOrNull { it != primaryVolumePath }?.let { return it }
|
||||
|
||||
Log.e(LOG_TAG, "failed to find volume path for UUID=$uuid")
|
||||
return null
|
||||
}
|
||||
|
@ -350,9 +372,9 @@ object StorageUtils {
|
|||
}
|
||||
|
||||
// e.g.
|
||||
// content://com.android.externalstorage.documents/tree/primary%3A -> /storage/emulated/0/
|
||||
// content://com.android.externalstorage.documents/tree/10F9-3F13%3APictures -> /storage/10F9-3F13/Pictures/
|
||||
fun convertTreeDocumentUriToDirPath(context: Context, treeDocumentUri: Uri): String? {
|
||||
// content://com.android.externalstorage.documents/tree/primary%3A -> ("primary", "")
|
||||
// content://com.android.externalstorage.documents/tree/10F9-3F13%3APictures -> ("10F9-3F13", "Pictures")
|
||||
private fun splitTreeDocumentUri(treeDocumentUri: Uri): Pair<String, String>? {
|
||||
val treeDocumentUriString = treeDocumentUri.toString()
|
||||
if (treeDocumentUriString.length <= TREE_URI_ROOT.length) return null
|
||||
val encoded = treeDocumentUriString.substring(TREE_URI_ROOT.length)
|
||||
|
@ -362,13 +384,24 @@ object StorageUtils {
|
|||
val uuid = group(1)
|
||||
val relativePath = group(2)
|
||||
if (uuid != null && relativePath != null) {
|
||||
val volumePath = getVolumePathFromTreeDocumentUriUuid(context, uuid)
|
||||
if (volumePath != null) {
|
||||
return ensureTrailingSeparator(volumePath + relativePath)
|
||||
}
|
||||
return Pair(uuid, relativePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.e(LOG_TAG, "failed to split treeDocumentUri=$treeDocumentUri to UUID and relative path")
|
||||
return null
|
||||
}
|
||||
|
||||
// e.g.
|
||||
// content://com.android.externalstorage.documents/tree/primary%3A -> /storage/emulated/0/
|
||||
// content://com.android.externalstorage.documents/tree/10F9-3F13%3APictures -> /storage/10F9-3F13/Pictures/
|
||||
fun convertTreeDocumentUriToDirPath(context: Context, treeDocumentUri: Uri): String? {
|
||||
splitTreeDocumentUri(treeDocumentUri)?.let { (uuid, relativePath) ->
|
||||
val volumePath = getVolumePathFromTreeDocumentUriUuid(context, uuid)
|
||||
if (volumePath != null) {
|
||||
return ensureTrailingSeparator(volumePath + relativePath)
|
||||
}
|
||||
}
|
||||
Log.e(LOG_TAG, "failed to convert treeDocumentUri=$treeDocumentUri to path")
|
||||
return null
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue