Просмотр исходного кода

Fix Xiaomi picker crash on non-persistable content Uri

Piktowo 8 месяцев назад
Родитель
Сommit
2cfbd91ad9

+ 3 - 5
app/src/main/java/io/legado/app/ui/config/otherConfig/OtherConfigScreen.kt

@@ -1,7 +1,6 @@
 package io.legado.app.ui.config.otherConfig
 package io.legado.app.ui.config.otherConfig
 
 
 import android.Manifest
 import android.Manifest
-import android.content.Intent
 import android.os.Build
 import android.os.Build
 import android.widget.Toast
 import android.widget.Toast
 import androidx.activity.compose.rememberLauncherForActivityResult
 import androidx.activity.compose.rememberLauncherForActivityResult
@@ -43,6 +42,7 @@ import io.legado.app.ui.widget.components.settingItem.InputSettingItem
 import io.legado.app.ui.widget.components.settingItem.SliderSettingItem
 import io.legado.app.ui.widget.components.settingItem.SliderSettingItem
 import io.legado.app.ui.widget.components.settingItem.SwitchSettingItem
 import io.legado.app.ui.widget.components.settingItem.SwitchSettingItem
 import io.legado.app.utils.restart
 import io.legado.app.utils.restart
+import io.legado.app.utils.takePersistablePermissionSafely
 import org.koin.androidx.compose.koinViewModel
 import org.koin.androidx.compose.koinViewModel
 
 
 @OptIn(ExperimentalMaterial3Api::class)
 @OptIn(ExperimentalMaterial3Api::class)
@@ -75,9 +75,7 @@ fun OtherConfigScreen(
         contract = ActivityResultContracts.OpenDocumentTree()
         contract = ActivityResultContracts.OpenDocumentTree()
     ) { uri ->
     ) { uri ->
         uri?.let {
         uri?.let {
-            val modeFlags =
-                Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-            context.contentResolver.takePersistableUriPermission(it, modeFlags)
+            it.takePersistablePermissionSafely(context)
             viewModel.updateLocalBookDir(it.toString())
             viewModel.updateLocalBookDir(it.toString())
         }
         }
     }
     }
@@ -509,4 +507,4 @@ fun ConfirmDialog(
             }
             }
         }
         }
     )
     )
-}
+}

+ 4 - 7
app/src/main/java/io/legado/app/ui/file/HandleFileActivity.kt

@@ -23,6 +23,7 @@ import io.legado.app.utils.externalFiles
 import io.legado.app.utils.getJsonArray
 import io.legado.app.utils.getJsonArray
 import io.legado.app.utils.isContentScheme
 import io.legado.app.utils.isContentScheme
 import io.legado.app.utils.launch
 import io.legado.app.utils.launch
+import io.legado.app.utils.takePersistablePermissionSafely
 import io.legado.app.utils.toastOnUi
 import io.legado.app.utils.toastOnUi
 import io.legado.app.utils.viewbindingdelegate.viewBinding
 import io.legado.app.utils.viewbindingdelegate.viewBinding
 import splitties.init.appCtx
 import splitties.init.appCtx
@@ -40,9 +41,7 @@ class HandleFileActivity :
         registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri ->
         registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri ->
             uri?.let {
             uri?.let {
                 if (uri.isContentScheme()) {
                 if (uri.isContentScheme()) {
-                    val modeFlags =
-                        Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-                    contentResolver.takePersistableUriPermission(uri, modeFlags)
+                    uri.takePersistablePermissionSafely(this)
                 }
                 }
                 onResult(Intent().setData(uri))
                 onResult(Intent().setData(uri))
             } ?: finish()
             } ?: finish()
@@ -51,9 +50,7 @@ class HandleFileActivity :
     private val selectDoc = registerForActivityResult(ActivityResultContracts.OpenDocument()) {
     private val selectDoc = registerForActivityResult(ActivityResultContracts.OpenDocument()) {
         it?.let {
         it?.let {
             if (it.isContentScheme()) {
             if (it.isContentScheme()) {
-                val modeFlags =
-                    Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-                contentResolver.takePersistableUriPermission(it, modeFlags)
+                it.takePersistablePermissionSafely(this)
             }
             }
             onResult(Intent().setData(it))
             onResult(Intent().setData(it))
         } ?: finish()
         } ?: finish()
@@ -307,4 +304,4 @@ class HandleFileActivity :
             finish()
             finish()
         }
         }
     }
     }
-}
+}

+ 37 - 0
app/src/main/java/io/legado/app/utils/UriExtensions.kt

@@ -29,6 +29,43 @@ fun Uri.isContentScheme() = this.scheme == "content"
 
 
 fun Uri.isFileScheme() = this.scheme == "file"
 fun Uri.isFileScheme() = this.scheme == "file"
 
 
+/**
+ * Some vendors return non-persistable content Uris even in system picker flows.
+ * Try to persist read/write grants separately and never throw.
+ */
+fun Uri.takePersistablePermissionSafely(
+    context: Context,
+    modeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+): Boolean {
+    if (!isContentScheme()) return false
+    var granted = false
+    if (modeFlags and Intent.FLAG_GRANT_READ_URI_PERMISSION != 0) {
+        kotlin.runCatching {
+            context.contentResolver.takePersistableUriPermission(
+                this,
+                Intent.FLAG_GRANT_READ_URI_PERMISSION
+            )
+        }.onSuccess {
+            granted = true
+        }.onFailure {
+            AppLog.put("持久化读取权限失败: $this", it)
+        }
+    }
+    if (modeFlags and Intent.FLAG_GRANT_WRITE_URI_PERMISSION != 0) {
+        kotlin.runCatching {
+            context.contentResolver.takePersistableUriPermission(
+                this,
+                Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+            )
+        }.onSuccess {
+            granted = true
+        }.onFailure {
+            AppLog.put("持久化写入权限失败: $this", it)
+        }
+    }
+    return granted
+}
+
 /**
 /**
  * 读取URI
  * 读取URI
  */
  */