优化
This commit is contained in:
@@ -23,13 +23,13 @@
|
|||||||
<application
|
<application
|
||||||
android:name=".App"
|
android:name=".App"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
android:enableOnBackInvokedCallback="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:networkSecurityConfig="@xml/network_security_config"
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
android:requestLegacyExternalStorage="true"
|
android:requestLegacyExternalStorage="true"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme.Light"
|
android:theme="@style/AppTheme.Light"
|
||||||
android:enableOnBackInvokedCallback="true"
|
|
||||||
tools:ignore="AllowBackup,GoogleAppIndexingWarning,UnusedAttribute">
|
tools:ignore="AllowBackup,GoogleAppIndexingWarning,UnusedAttribute">
|
||||||
<!-- 主入口 -->
|
<!-- 主入口 -->
|
||||||
<activity
|
<activity
|
||||||
@@ -367,6 +367,7 @@
|
|||||||
<data android:mimeType="application/epub+zip" />
|
<data android:mimeType="application/epub+zip" />
|
||||||
<!-- pdf -->
|
<!-- pdf -->
|
||||||
<data android:mimeType="application/pdf" />
|
<data android:mimeType="application/pdf" />
|
||||||
|
<data android:mimeType="application/octet-stream" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<!-- Works when an app doesn't know the media type, e.g. Dropbox -->
|
<!-- Works when an app doesn't know the media type, e.g. Dropbox -->
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
@@ -428,9 +429,9 @@
|
|||||||
<service
|
<service
|
||||||
android:name=".service.WebTileService"
|
android:name=".service.WebTileService"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
android:foregroundServiceType="dataSync"
|
||||||
android:icon="@drawable/ic_web_service_noti"
|
android:icon="@drawable/ic_web_service_noti"
|
||||||
android:label="legado Web Service"
|
android:label="legado Web Service"
|
||||||
android:foregroundServiceType="dataSync"
|
|
||||||
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
|
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.service.quicksettings.action.QS_TILE" />
|
<action android:name="android.service.quicksettings.action.QS_TILE" />
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package io.legado.app.exception
|
||||||
|
|
||||||
|
class InvalidBooksDirException(msg: String) : NoStackTraceException(msg)
|
||||||
@@ -43,7 +43,10 @@ class PermissionActivity : AppCompatActivity() {
|
|||||||
try {
|
try {
|
||||||
if (Permissions.isManageExternalStorage()) {
|
if (Permissions.isManageExternalStorage()) {
|
||||||
val settingIntent =
|
val settingIntent =
|
||||||
Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
|
Intent(
|
||||||
|
Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION,
|
||||||
|
Uri.parse("package:$packageName")
|
||||||
|
)
|
||||||
settingActivityResult.launch(settingIntent)
|
settingActivityResult.launch(settingIntent)
|
||||||
} else {
|
} else {
|
||||||
throw NoStackTraceException("no MANAGE_ALL_FILES_ACCESS_PERMISSION")
|
throw NoStackTraceException("no MANAGE_ALL_FILES_ACCESS_PERMISSION")
|
||||||
|
|||||||
@@ -4,12 +4,14 @@ import android.net.Uri
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
|
import androidx.core.os.postDelayed
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.base.VMBaseActivity
|
import io.legado.app.base.VMBaseActivity
|
||||||
import io.legado.app.constant.AppLog
|
import io.legado.app.constant.AppLog
|
||||||
import io.legado.app.databinding.ActivityTranslucenceBinding
|
import io.legado.app.databinding.ActivityTranslucenceBinding
|
||||||
|
import io.legado.app.exception.InvalidBooksDirException
|
||||||
import io.legado.app.help.config.AppConfig
|
import io.legado.app.help.config.AppConfig
|
||||||
import io.legado.app.lib.dialogs.alert
|
import io.legado.app.lib.dialogs.alert
|
||||||
import io.legado.app.lib.permission.Permissions
|
import io.legado.app.lib.permission.Permissions
|
||||||
@@ -17,6 +19,8 @@ import io.legado.app.lib.permission.PermissionsCompat
|
|||||||
import io.legado.app.ui.book.read.ReadBookActivity
|
import io.legado.app.ui.book.read.ReadBookActivity
|
||||||
import io.legado.app.ui.file.HandleFileContract
|
import io.legado.app.ui.file.HandleFileContract
|
||||||
import io.legado.app.utils.FileUtils
|
import io.legado.app.utils.FileUtils
|
||||||
|
import io.legado.app.utils.buildMainHandler
|
||||||
|
import io.legado.app.utils.canRead
|
||||||
import io.legado.app.utils.checkWrite
|
import io.legado.app.utils.checkWrite
|
||||||
import io.legado.app.utils.getFile
|
import io.legado.app.utils.getFile
|
||||||
import io.legado.app.utils.isContentScheme
|
import io.legado.app.utils.isContentScheme
|
||||||
@@ -43,7 +47,7 @@ class FileAssociationActivity :
|
|||||||
} ?: let {
|
} ?: let {
|
||||||
val storageHelp = String(assets.open("storageHelp.md").readBytes())
|
val storageHelp = String(assets.open("storageHelp.md").readBytes())
|
||||||
toastOnUi(storageHelp)
|
toastOnUi(storageHelp)
|
||||||
viewModel.importBook(uri)
|
importBook(null, uri)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,6 +56,10 @@ class FileAssociationActivity :
|
|||||||
|
|
||||||
override val viewModel by viewModels<FileAssociationViewModel>()
|
override val viewModel by viewModels<FileAssociationViewModel>()
|
||||||
|
|
||||||
|
private val handler by lazy {
|
||||||
|
buildMainHandler()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
binding.rotateLoading.visible()
|
binding.rotateLoading.visible()
|
||||||
viewModel.importBookLiveData.observe(this) { uri ->
|
viewModel.importBookLiveData.observe(this) { uri ->
|
||||||
@@ -114,21 +122,20 @@ class FileAssociationActivity :
|
|||||||
noButton {
|
noButton {
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
onCancelled {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
intent.data?.let { data ->
|
intent.data?.let { data ->
|
||||||
if (data.isContentScheme()) {
|
if (data.isContentScheme()) {
|
||||||
viewModel.dispatchIndent(data)
|
if (data.canRead()) {
|
||||||
|
viewModel.dispatchIntent(data)
|
||||||
|
} else {
|
||||||
|
dispatchWithPermission(data)
|
||||||
|
}
|
||||||
} else if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
|
} else if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
|
||||||
PermissionsCompat.Builder()
|
dispatchWithPermission(data)
|
||||||
.addPermissions(*Permissions.Group.STORAGE)
|
|
||||||
.rationale(R.string.tip_perm_request_storage)
|
|
||||||
.onGranted {
|
|
||||||
viewModel.dispatchIndent(data)
|
|
||||||
}.onDenied {
|
|
||||||
toastOnUi("请求存储权限失败。")
|
|
||||||
finish()
|
|
||||||
}.request()
|
|
||||||
} else {
|
} else {
|
||||||
toastOnUi("由于安卓系统限制,请使用系统文件管理重新打开。")
|
toastOnUi("由于安卓系统限制,请使用系统文件管理重新打开。")
|
||||||
finish()
|
finish()
|
||||||
@@ -136,6 +143,18 @@ class FileAssociationActivity :
|
|||||||
} ?: finish()
|
} ?: finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun dispatchWithPermission(data: Uri) {
|
||||||
|
PermissionsCompat.Builder()
|
||||||
|
.addPermissions(*Permissions.Group.STORAGE)
|
||||||
|
.rationale(R.string.tip_perm_request_storage)
|
||||||
|
.onGranted {
|
||||||
|
viewModel.dispatchIntent(data)
|
||||||
|
}.onDenied {
|
||||||
|
toastOnUi("请求存储权限失败。")
|
||||||
|
finish()
|
||||||
|
}.request()
|
||||||
|
}
|
||||||
|
|
||||||
private fun importBook(uri: Uri) {
|
private fun importBook(uri: Uri) {
|
||||||
if (uri.isContentScheme()) {
|
if (uri.isContentScheme()) {
|
||||||
val treeUriStr = AppConfig.defaultBookTreeUri
|
val treeUriStr = AppConfig.defaultBookTreeUri
|
||||||
@@ -148,19 +167,21 @@ class FileAssociationActivity :
|
|||||||
importBook(Uri.parse(treeUriStr), uri)
|
importBook(Uri.parse(treeUriStr), uri)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
viewModel.importBook(uri)
|
importBook(null, uri)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun importBook(treeUri: Uri, uri: Uri) {
|
private fun importBook(treeUri: Uri?, uri: Uri) {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
runCatching {
|
runCatching {
|
||||||
withContext(IO) {
|
withContext(IO) {
|
||||||
if (treeUri.isContentScheme()) {
|
if (treeUri == null) {
|
||||||
|
viewModel.importBook(uri)
|
||||||
|
} else if (treeUri.isContentScheme()) {
|
||||||
val treeDoc =
|
val treeDoc =
|
||||||
DocumentFile.fromTreeUri(this@FileAssociationActivity, treeUri)
|
DocumentFile.fromTreeUri(this@FileAssociationActivity, treeUri)
|
||||||
if (!treeDoc!!.checkWrite()) {
|
if (!treeDoc!!.checkWrite()) {
|
||||||
throw SecurityException("请重新设置书籍保存位置\nPermission Denial")
|
throw InvalidBooksDirException("请重新设置书籍保存位置\nPermission Denial")
|
||||||
}
|
}
|
||||||
readUri(uri) { fileDoc, inputStream ->
|
readUri(uri) { fileDoc, inputStream ->
|
||||||
val name = fileDoc.name
|
val name = fileDoc.name
|
||||||
@@ -168,7 +189,7 @@ class FileAssociationActivity :
|
|||||||
if (doc == null || fileDoc.lastModified > doc.lastModified()) {
|
if (doc == null || fileDoc.lastModified > doc.lastModified()) {
|
||||||
if (doc == null) {
|
if (doc == null) {
|
||||||
doc = treeDoc.createFile(FileUtils.getMimeType(name), name)
|
doc = treeDoc.createFile(FileUtils.getMimeType(name), name)
|
||||||
?: throw SecurityException("请重新设置书籍保存位置\nPermission Denial")
|
?: throw InvalidBooksDirException("请重新设置书籍保存位置\nPermission Denial")
|
||||||
}
|
}
|
||||||
contentResolver.openOutputStream(doc.uri)!!.use { oStream ->
|
contentResolver.openOutputStream(doc.uri)!!.use { oStream ->
|
||||||
inputStream.copyTo(oStream)
|
inputStream.copyTo(oStream)
|
||||||
@@ -180,7 +201,7 @@ class FileAssociationActivity :
|
|||||||
} else {
|
} else {
|
||||||
val treeFile = File(treeUri.path ?: treeUri.toString())
|
val treeFile = File(treeUri.path ?: treeUri.toString())
|
||||||
if (!treeFile.checkWrite()) {
|
if (!treeFile.checkWrite()) {
|
||||||
throw SecurityException("请重新设置书籍保存位置\nPermission Denial")
|
throw InvalidBooksDirException("请重新设置书籍保存位置\nPermission Denial")
|
||||||
}
|
}
|
||||||
readUri(uri) { fileDoc, inputStream ->
|
readUri(uri) { fileDoc, inputStream ->
|
||||||
val name = fileDoc.name
|
val name = fileDoc.name
|
||||||
@@ -197,7 +218,7 @@ class FileAssociationActivity :
|
|||||||
}
|
}
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
when (it) {
|
when (it) {
|
||||||
is SecurityException -> localBookTreeSelect.launch {
|
is InvalidBooksDirException -> localBookTreeSelect.launch {
|
||||||
title = getString(R.string.select_book_folder)
|
title = getString(R.string.select_book_folder)
|
||||||
mode = HandleFileContract.DIR_SYS
|
mode = HandleFileContract.DIR_SYS
|
||||||
}
|
}
|
||||||
@@ -206,7 +227,9 @@ class FileAssociationActivity :
|
|||||||
val msg = "导入书籍失败\n${it.localizedMessage}"
|
val msg = "导入书籍失败\n${it.localizedMessage}"
|
||||||
AppLog.put(msg, it)
|
AppLog.put(msg, it)
|
||||||
toastOnUi(msg)
|
toastOnUi(msg)
|
||||||
finish()
|
handler.postDelayed(2000) {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class FileAssociationViewModel(application: Application) : BaseAssociationViewMo
|
|||||||
val openBookLiveData = MutableLiveData<String>()
|
val openBookLiveData = MutableLiveData<String>()
|
||||||
val notSupportedLiveData = MutableLiveData<Pair<Uri, String>>()
|
val notSupportedLiveData = MutableLiveData<Pair<Uri, String>>()
|
||||||
|
|
||||||
fun dispatchIndent(uri: Uri) {
|
fun dispatchIntent(uri: Uri) {
|
||||||
execute {
|
execute {
|
||||||
lateinit var fileName: String
|
lateinit var fileName: String
|
||||||
//如果是普通的url,需要根据返回的内容判断是什么
|
//如果是普通的url,需要根据返回的内容判断是什么
|
||||||
|
|||||||
@@ -159,6 +159,9 @@ fun Context.getCompatDrawable(@DrawableRes id: Int): Drawable? = ContextCompat.g
|
|||||||
fun Context.getCompatColorStateList(@ColorRes id: Int): ColorStateList? =
|
fun Context.getCompatColorStateList(@ColorRes id: Int): ColorStateList? =
|
||||||
ContextCompat.getColorStateList(this, id)
|
ContextCompat.getColorStateList(this, id)
|
||||||
|
|
||||||
|
fun Context.checkSelfUriPermission(uri: Uri, modeFlags: Int): Int =
|
||||||
|
checkUriPermission(uri, Process.myPid(), Process.myUid(), modeFlags)
|
||||||
|
|
||||||
fun Context.restart() {
|
fun Context.restart() {
|
||||||
val intent: Intent? = packageManager.getLaunchIntentForPackage(packageName)
|
val intent: Intent? = packageManager.getLaunchIntentForPackage(packageName)
|
||||||
intent?.let {
|
intent?.let {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package io.legado.app.utils
|
package io.legado.app.utils
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
@@ -63,7 +65,7 @@ fun AppCompatActivity.readUri(
|
|||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printOnDebug()
|
e.printOnDebug()
|
||||||
toastOnUi(e.localizedMessage ?: "read uri error")
|
toastOnUi("读取Uri出错\n${e.localizedMessage}")
|
||||||
if (e is SecurityException) {
|
if (e is SecurityException) {
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
@@ -104,7 +106,7 @@ fun Fragment.readUri(uri: Uri?, success: (fileDoc: FileDoc, inputStream: InputSt
|
|||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printOnDebug()
|
e.printOnDebug()
|
||||||
toastOnUi(e.localizedMessage ?: "read uri error")
|
toastOnUi("读取Uri出错\n${e.localizedMessage}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,3 +314,10 @@ fun Uri.toRequestBody(contentType: MediaType? = null): RequestBody {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Uri.canRead(): Boolean {
|
||||||
|
return appCtx.checkSelfUriPermission(
|
||||||
|
this,
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
|
) == PackageManager.PERMISSION_GRANTED
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user