Merge pull request #656

Fix Xiaomi picker crash on non-persistable content Uri
This commit is contained in:
Kudomaga authored and GitHub committed 2026-02-26 01:23:55 +08:00
commit 8cc0932bef
4 files changed
+45 -14

No files matched your search

@@ -34,8 +34,7 @@ import java.io.InputStream
import java.net.MalformedURLException import java.net.MalformedURLException
import java.net.URL import java.net.URL
import java.net.URLEncoder import java.net.URLEncoder
import java.time.LocalDateTime import java.time.ZonedDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.coroutines.coroutineContext import kotlin.coroutines.coroutineContext
@@ -219,8 +218,8 @@ open class WebDav(
val lastModify: Long = kotlin.runCatching { val lastModify: Long = kotlin.runCatching {
element.findNS("getlastmodified", ns) element.findNS("getlastmodified", ns)
.firstOrNull()?.text()?.let { .firstOrNull()?.text()?.let {
LocalDateTime.parse(it, dateTimeFormatter) ZonedDateTime.parse(it, dateTimeFormatter)
.toInstant(ZoneOffset.of("+8")).toEpochMilli() .toInstant().toEpochMilli()
} }
}.getOrNull() ?: 0 }.getOrNull() ?: 0
var fullURL = NetworkUtils.getAbsoluteURL(baseUrl, hrefDecode) var fullURL = NetworkUtils.getAbsoluteURL(baseUrl, hrefDecode)
@@ -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 = it.takePersistablePermissionSafely(context)
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
context.contentResolver.takePersistableUriPermission(it, modeFlags)
viewModel.updateLocalBookDir(it.toString()) viewModel.updateLocalBookDir(it.toString())
} }
} }
@@ -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 = uri.takePersistablePermissionSafely(this)
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
contentResolver.takePersistableUriPermission(uri, modeFlags)
} }
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 = it.takePersistablePermissionSafely(this)
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
contentResolver.takePersistableUriPermission(it, modeFlags)
} }
onResult(Intent().setData(it)) onResult(Intent().setData(it))
} ?: finish() } ?: finish()
@@ -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
*/ */