This commit is contained in:
kunfei
2023-02-15 22:38:43 +08:00
parent c72e248b91
commit 151c3177cd
27 changed files with 229 additions and 247 deletions
@@ -4,8 +4,11 @@ import android.content.Intent
import android.os.IBinder import android.os.IBinder
import androidx.annotation.CallSuper import androidx.annotation.CallSuper
import androidx.lifecycle.LifecycleService import androidx.lifecycle.LifecycleService
import io.legado.app.R
import io.legado.app.help.LifecycleHelp import io.legado.app.help.LifecycleHelp
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.lib.permission.Permissions
import io.legado.app.lib.permission.PermissionsCompat
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
@@ -22,6 +25,7 @@ abstract class BaseService : LifecycleService(), CoroutineScope by MainScope() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
LifecycleHelp.onServiceCreate(this) LifecycleHelp.onServiceCreate(this)
checkNotificationPermission()
} }
@CallSuper @CallSuper
@@ -41,4 +45,24 @@ abstract class BaseService : LifecycleService(), CoroutineScope by MainScope() {
cancel() cancel()
LifecycleHelp.onServiceDestroy(this) LifecycleHelp.onServiceDestroy(this)
} }
/**
* 更新通知
*/
open fun upNotification() {
}
/**
* 检测通知权限
*/
private fun checkNotificationPermission() {
PermissionsCompat.Builder()
.addPermissions(Permissions.POST_NOTIFICATIONS)
.rationale(R.string.notification_permission_rationale)
.onGranted {
upNotification()
}
.request()
}
} }
@@ -1,20 +0,0 @@
package io.legado.app.lib.permission
import android.content.Context
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import java.lang.ref.WeakReference
internal class ActivitySource(activity: AppCompatActivity) : RequestSource {
private val actRef: WeakReference<AppCompatActivity> = WeakReference(activity)
override val context: Context?
get() = actRef.get()
override fun startActivity(intent: Intent) {
actRef.get()?.startActivity(intent)
}
}
@@ -1,19 +0,0 @@
package io.legado.app.lib.permission
import android.content.Context
import android.content.Intent
import androidx.fragment.app.Fragment
import java.lang.ref.WeakReference
internal class FragmentSource(fragment: Fragment) : RequestSource {
private val fragRef: WeakReference<Fragment> = WeakReference(fragment)
override val context: Context?
get() = fragRef.get()?.requireContext()
override fun startActivity(intent: Intent) {
fragRef.get()?.startActivity(intent)
}
}
@@ -7,6 +7,7 @@ import android.os.Bundle
import android.provider.Settings import android.provider.Settings
import android.view.KeyEvent import android.view.KeyEvent
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import io.legado.app.R import io.legado.app.R
@@ -15,6 +16,8 @@ import io.legado.app.utils.toastOnUi
class PermissionActivity : AppCompatActivity() { class PermissionActivity : AppCompatActivity() {
private var rationaleDialog: AlertDialog? = null
private val settingActivityResult = private val settingActivityResult =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
RequestPlugins.sRequestCallback?.onSettingActivityResult() RequestPlugins.sRequestCallback?.onSettingActivityResult()
@@ -23,42 +26,45 @@ class PermissionActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val rationale = intent.getStringExtra(KEY_RATIONALE)
val requestCode = intent.getIntExtra(KEY_INPUT_PERMISSIONS_CODE, 1000) val requestCode = intent.getIntExtra(KEY_INPUT_PERMISSIONS_CODE, 1000)
val permissions = intent.getStringArrayExtra(KEY_INPUT_PERMISSIONS) val permissions = intent.getStringArrayExtra(KEY_INPUT_PERMISSIONS)!!
when (intent.getIntExtra(KEY_INPUT_REQUEST_TYPE, Request.TYPE_REQUEST_PERMISSION)) { when (intent.getIntExtra(KEY_INPUT_REQUEST_TYPE, Request.TYPE_REQUEST_PERMISSION)) {
//权限请求 //权限请求
Request.TYPE_REQUEST_PERMISSION -> { Request.TYPE_REQUEST_PERMISSION -> showSettingDialog(permissions, rationale){
if (permissions != null) { ActivityCompat.requestPermissions(this, permissions, requestCode)
ActivityCompat.requestPermissions(this, permissions, requestCode) }
} else { //跳转到设置界面
Request.TYPE_REQUEST_SETTING -> showSettingDialog(permissions, rationale) {
openSettingsActivity()
}
//所有文件的管理权限
Request.TYPE_MANAGE_ALL_FILES_ACCESS -> showSettingDialog(permissions, rationale) {
try {
if (Permissions.isManageExternalStorage()) {
val settingIntent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
settingActivityResult.launch(settingIntent)
} else {
throw NoStackTraceException("no MANAGE_ALL_FILES_ACCESS_PERMISSION")
}
} catch (e: Exception) {
toastOnUi(e.localizedMessage)
RequestPlugins.sRequestCallback?.onError(e)
finish() finish()
} }
} }
//跳转到设置界面 Request.TYPE_REQUEST_NOTIFICATIONS -> showSettingDialog(permissions, rationale) {
Request.TYPE_REQUEST_SETTING -> openSettingsActivity() kotlin.runCatching {
//所有文件的管理权限 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Request.TYPE_MANAGE_ALL_FILES_ACCESS_PERMISSION -> try { //这种方案适用于 API 26, 即8.0(含8.0)以上可以用
if (Permissions.isManageExternalStorage()) { val intent = Intent()
val settingIntent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION) intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
settingActivityResult.launch(settingIntent) intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
} else { intent.putExtra(Settings.EXTRA_CHANNEL_ID, applicationInfo.uid)
throw NoStackTraceException("no MANAGE_ALL_FILES_ACCESS_PERMISSION") settingActivityResult.launch(intent)
} } else {
} catch (e: Exception) { openSettingsActivity()
toastOnUi(e.localizedMessage) }
RequestPlugins.sRequestCallback?.onError(e)
finish()
}
Request.TYPE_REQUEST_NOTIFICATIONS -> kotlin.runCatching {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//这种方案适用于 API 26, 即8.0(含8.0)以上可以用
val intent = Intent()
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
intent.putExtra(Settings.EXTRA_CHANNEL_ID, applicationInfo.uid)
settingActivityResult.launch(intent)
} else {
openSettingsActivity()
} }
} }
} }
@@ -106,8 +112,34 @@ class PermissionActivity : AppCompatActivity() {
} else super.onKeyDown(keyCode, event) } else super.onKeyDown(keyCode, event)
} }
private fun showSettingDialog(
permissions: Array<String>,
rationale: CharSequence?,
onOk: () -> Unit
) {
rationaleDialog?.dismiss()
if (rationale.isNullOrEmpty()) {
return
}
rationaleDialog = AlertDialog.Builder(this)
.setTitle(R.string.dialog_title)
.setMessage(rationale)
.setPositiveButton(R.string.dialog_setting) { _, _ ->
onOk.invoke()
}
.setNegativeButton(R.string.dialog_cancel) { _, _ ->
RequestPlugins.sRequestCallback?.onRequestPermissionsResult(
permissions,
IntArray(0)
)
}
.show()
}
companion object { companion object {
const val KEY_RATIONALE = "KEY_RATIONALE"
const val KEY_INPUT_REQUEST_TYPE = "KEY_INPUT_REQUEST_TYPE" const val KEY_INPUT_REQUEST_TYPE = "KEY_INPUT_REQUEST_TYPE"
const val KEY_INPUT_PERMISSIONS_CODE = "KEY_INPUT_PERMISSIONS_CODE" const val KEY_INPUT_PERMISSIONS_CODE = "KEY_INPUT_PERMISSIONS_CODE"
const val KEY_INPUT_PERMISSIONS = "KEY_INPUT_PERMISSIONS" const val KEY_INPUT_PERMISSIONS = "KEY_INPUT_PERMISSIONS"
@@ -1,8 +1,6 @@
package io.legado.app.lib.permission package io.legado.app.lib.permission
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
@Suppress("unused") @Suppress("unused")
class PermissionsCompat private constructor() { class PermissionsCompat private constructor() {
@@ -14,15 +12,7 @@ class PermissionsCompat private constructor() {
} }
class Builder { class Builder {
private val request: Request private val request: Request = Request()
constructor(activity: AppCompatActivity) {
request = Request(activity)
}
constructor(fragment: Fragment) {
request = Request(fragment)
}
fun addPermissions(vararg permissions: String): Builder { fun addPermissions(vararg permissions: String): Builder {
request.addPermissions(*permissions) request.addPermissions(*permissions)
@@ -4,48 +4,29 @@ import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import android.os.Environment import android.os.Environment
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import io.legado.app.R
import io.legado.app.utils.startActivity import io.legado.app.utils.startActivity
import splitties.init.appCtx import splitties.init.appCtx
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
internal class Request : OnRequestPermissionsResultCallback { internal class Request : OnRequestPermissionsResultCallback {
internal val requestTime: Long internal val requestTime: Long = System.currentTimeMillis()
private var requestCode: Int = TYPE_REQUEST_PERMISSION private var requestCode: Int = TYPE_REQUEST_PERMISSION
private var source: RequestSource? = null private var permissions: ArrayList<String> = ArrayList()
private var permissions: ArrayList<String>? = null
private var grantedCallback: OnPermissionsGrantedCallback? = null private var grantedCallback: OnPermissionsGrantedCallback? = null
private var deniedCallback: OnPermissionsDeniedCallback? = null private var deniedCallback: OnPermissionsDeniedCallback? = null
private var errorCallback: OnErrorCallback? = null private var errorCallback: OnErrorCallback? = null
private var rationale: CharSequence? = null private var rationale: CharSequence? = null
private var rationaleDialog: AlertDialog? = null
private val deniedPermissions: Array<String>? private val deniedPermissions: Array<String>?
get() { get() {
return getDeniedPermissions(this.permissions?.toTypedArray()) return getDeniedPermissions(this.permissions.toTypedArray())
} }
constructor(activity: AppCompatActivity) {
source = ActivitySource(activity)
permissions = ArrayList()
requestTime = System.currentTimeMillis()
}
constructor(fragment: Fragment) {
source = FragmentSource(fragment)
permissions = ArrayList()
requestTime = System.currentTimeMillis()
}
fun addPermissions(vararg permissions: String) { fun addPermissions(vararg permissions: String) {
this.permissions?.addAll(listOf(*permissions)) this.permissions.addAll(listOf(*permissions))
} }
fun setOnGrantedCallback(callback: OnPermissionsGrantedCallback) { fun setOnGrantedCallback(callback: OnPermissionsGrantedCallback) {
@@ -61,7 +42,7 @@ internal class Request : OnRequestPermissionsResultCallback {
} }
fun setRationale(@StringRes resId: Int) { fun setRationale(@StringRes resId: Int) {
rationale = source?.context?.getString(resId) rationale = appCtx.getString(resId)
} }
fun setRationale(rationale: CharSequence) { fun setRationale(rationale: CharSequence) {
@@ -82,22 +63,16 @@ internal class Request : OnRequestPermissionsResultCallback {
return return
} }
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
showSettingDialog(deniedPermissions, rationale) { toSetting()
toSetting()
}
} else { } else {
if (deniedPermissions.contains(Permissions.MANAGE_EXTERNAL_STORAGE)) { if (deniedPermissions.contains(Permissions.MANAGE_EXTERNAL_STORAGE)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
showSettingDialog(deniedPermissions, rationale) { toManageFileSetting()
toManageFileSetting()
}
} }
} else if (deniedPermissions.contains(Permissions.POST_NOTIFICATIONS)) { } else if (deniedPermissions.contains(Permissions.POST_NOTIFICATIONS)) {
showSettingDialog(deniedPermissions, rationale) { toNotificationSetting()
toNotificationSetting()
}
} else if (deniedPermissions.size > 1) { } else if (deniedPermissions.size > 1) {
source?.context?.startActivity<PermissionActivity> { appCtx.startActivity<PermissionActivity> {
putExtra(PermissionActivity.KEY_INPUT_REQUEST_TYPE, TYPE_REQUEST_PERMISSION) putExtra(PermissionActivity.KEY_INPUT_REQUEST_TYPE, TYPE_REQUEST_PERMISSION)
putExtra(PermissionActivity.KEY_INPUT_PERMISSIONS_CODE, requestCode) putExtra(PermissionActivity.KEY_INPUT_PERMISSIONS_CODE, requestCode)
putExtra(PermissionActivity.KEY_INPUT_PERMISSIONS, deniedPermissions) putExtra(PermissionActivity.KEY_INPUT_PERMISSIONS, deniedPermissions)
@@ -111,92 +86,39 @@ internal class Request : OnRequestPermissionsResultCallback {
deniedCallback = null deniedCallback = null
} }
fun getDeniedPermissions(permissions: Array<String>?): Array<String>? { fun getDeniedPermissions(permissions: Array<String>): Array<String>? {
val context = source?.context ?: return permissions val deniedPermissionList = ArrayList<String>()
if (permissions != null) { for (permission in permissions) {
val deniedPermissionList = ArrayList<String>() when (permission) {
for (permission in permissions) { Permissions.POST_NOTIFICATIONS -> {
when (permission) { if (!NotificationManagerCompat.from(appCtx).areNotificationsEnabled()) {
Permissions.POST_NOTIFICATIONS -> { deniedPermissionList.add(permission)
if (!NotificationManagerCompat.from(appCtx).areNotificationsEnabled()) {
deniedPermissionList.add(permission)
}
} }
Permissions.MANAGE_EXTERNAL_STORAGE -> { }
if (Permissions.isManageExternalStorage()) { Permissions.MANAGE_EXTERNAL_STORAGE -> {
if (!Environment.isExternalStorageManager()) { if (Permissions.isManageExternalStorage()) {
deniedPermissionList.add(permission) if (!Environment.isExternalStorageManager()) {
}
}
}
else -> {
if (
ContextCompat.checkSelfPermission(context, permission)
!= PackageManager.PERMISSION_GRANTED
) {
deniedPermissionList.add(permission) deniedPermissionList.add(permission)
} }
} }
} }
else -> {
if (
ContextCompat.checkSelfPermission(appCtx, permission)
!= PackageManager.PERMISSION_GRANTED
) {
deniedPermissionList.add(permission)
}
}
} }
val size = deniedPermissionList.size }
if (size > 0) { val size = deniedPermissionList.size
return deniedPermissionList.toTypedArray() if (size > 0) {
} return deniedPermissionList.toTypedArray()
} }
return null return null
} }
private fun showSettingDialog(
permissions: Array<String>,
rationale: CharSequence,
onOk: () -> Unit
) {
rationaleDialog?.dismiss()
source?.context?.let {
runCatching {
rationaleDialog = AlertDialog.Builder(it)
.setTitle(R.string.dialog_title)
.setMessage(rationale)
.setPositiveButton(R.string.dialog_setting) { _, _ ->
onOk.invoke()
}
.setNegativeButton(R.string.dialog_cancel) { _, _ ->
onPermissionsDenied(permissions)
}
.show()
}
}
}
private fun toSetting() {
source?.context?.startActivity<PermissionActivity> {
putExtra(
PermissionActivity.KEY_INPUT_REQUEST_TYPE,
TYPE_REQUEST_SETTING
)
}
}
private fun toManageFileSetting() {
source?.context?.startActivity<PermissionActivity> {
putExtra(
PermissionActivity.KEY_INPUT_REQUEST_TYPE,
TYPE_MANAGE_ALL_FILES_ACCESS_PERMISSION
)
putExtra(PermissionActivity.KEY_INPUT_PERMISSIONS_CODE, requestCode)
putExtra(PermissionActivity.KEY_INPUT_PERMISSIONS, deniedPermissions)
}
}
private fun toNotificationSetting() {
source?.context?.startActivity<PermissionActivity> {
putExtra(PermissionActivity.KEY_INPUT_REQUEST_TYPE, TYPE_REQUEST_NOTIFICATIONS)
putExtra(PermissionActivity.KEY_INPUT_PERMISSIONS_CODE, requestCode)
putExtra(PermissionActivity.KEY_INPUT_PERMISSIONS, deniedPermissions)
}
}
private fun onPermissionsGranted() { private fun onPermissionsGranted() {
try { try {
grantedCallback?.onPermissionsGranted() grantedCallback?.onPermissionsGranted()
@@ -215,20 +137,40 @@ internal class Request : OnRequestPermissionsResultCallback {
RequestPlugins.sResultCallback?.onPermissionsDenied(deniedPermissions) RequestPlugins.sResultCallback?.onPermissionsDenied(deniedPermissions)
} }
private fun toSetting() {
appCtx.startActivity<PermissionActivity> {
putExtra(
PermissionActivity.KEY_INPUT_REQUEST_TYPE,
TYPE_REQUEST_SETTING
)
}
}
private fun toManageFileSetting() {
appCtx.startActivity<PermissionActivity> {
putExtra(PermissionActivity.KEY_RATIONALE, rationale)
putExtra(PermissionActivity.KEY_INPUT_REQUEST_TYPE, TYPE_MANAGE_ALL_FILES_ACCESS)
putExtra(PermissionActivity.KEY_INPUT_PERMISSIONS_CODE, requestCode)
putExtra(PermissionActivity.KEY_INPUT_PERMISSIONS, deniedPermissions)
}
}
private fun toNotificationSetting() {
appCtx.startActivity<PermissionActivity> {
putExtra(PermissionActivity.KEY_RATIONALE, rationale)
putExtra(PermissionActivity.KEY_INPUT_REQUEST_TYPE, TYPE_REQUEST_NOTIFICATIONS)
putExtra(PermissionActivity.KEY_INPUT_PERMISSIONS_CODE, requestCode)
putExtra(PermissionActivity.KEY_INPUT_PERMISSIONS, deniedPermissions)
}
}
override fun onRequestPermissionsResult( override fun onRequestPermissionsResult(
permissions: Array<String>, permissions: Array<String>,
grantResults: IntArray grantResults: IntArray
) { ) {
val deniedPermissions = getDeniedPermissions(permissions) val deniedPermissions = getDeniedPermissions(permissions)
if (deniedPermissions != null) { if (deniedPermissions != null) {
val rationale = this.rationale onPermissionsDenied(deniedPermissions)
if (rationale != null) {
showSettingDialog(deniedPermissions, rationale) {
}
} else {
onPermissionsDenied(deniedPermissions)
}
} else { } else {
onPermissionsGranted() onPermissionsGranted()
} }
@@ -251,7 +193,7 @@ internal class Request : OnRequestPermissionsResultCallback {
companion object { companion object {
const val TYPE_REQUEST_PERMISSION = 1 const val TYPE_REQUEST_PERMISSION = 1
const val TYPE_REQUEST_SETTING = 2 const val TYPE_REQUEST_SETTING = 2
const val TYPE_MANAGE_ALL_FILES_ACCESS_PERMISSION = 3 const val TYPE_MANAGE_ALL_FILES_ACCESS = 3
const val TYPE_REQUEST_NOTIFICATIONS = 4 const val TYPE_REQUEST_NOTIFICATIONS = 4
} }
} }
@@ -1,12 +0,0 @@
package io.legado.app.lib.permission
import android.content.Context
import android.content.Intent
interface RequestSource {
val context: Context?
fun startActivity(intent: Intent)
}
@@ -495,7 +495,7 @@ class AudioPlayService : BaseService(),
/** /**
* 更新通知 * 更新通知
*/ */
private fun upNotification() { override fun upNotification() {
execute { execute {
var nTitle: String = when { var nTitle: String = when {
pause -> getString(R.string.audio_pause) pause -> getString(R.string.audio_pause)
@@ -355,7 +355,7 @@ abstract class BaseReadAloudService : BaseService(),
/** /**
* 更新通知 * 更新通知
*/ */
private fun upNotification() { override fun upNotification() {
execute { execute {
var nTitle: String = when { var nTitle: String = when {
pause -> getString(R.string.read_aloud_pause) pause -> getString(R.string.read_aloud_pause)
@@ -260,7 +260,7 @@ class CheckSourceService : BaseService() {
/** /**
* 更新通知 * 更新通知
*/ */
private fun upNotification() { override fun upNotification() {
notificationBuilder.setContentText(notificationMsg) notificationBuilder.setContentText(notificationMsg)
notificationBuilder.setProgress(allIds.size, checkedIds.size, false) notificationBuilder.setProgress(allIds.size, checkedIds.size, false)
postEvent(EventBus.CHECK_SOURCE, notificationMsg) postEvent(EventBus.CHECK_SOURCE, notificationMsg)
@@ -144,7 +144,7 @@ class WebService : BaseService() {
/** /**
* 更新通知 * 更新通知
*/ */
private fun upNotification() { override fun upNotification() {
val builder = NotificationCompat.Builder(this, AppConst.channelIdWeb) val builder = NotificationCompat.Builder(this, AppConst.channelIdWeb)
.setSmallIcon(R.drawable.ic_web_service_noti) .setSmallIcon(R.drawable.ic_web_service_noti)
.setOngoing(true) .setOngoing(true)
@@ -109,7 +109,7 @@ class FileAssociationActivity :
if (data.isContentScheme()) { if (data.isContentScheme()) {
viewModel.dispatchIndent(data) viewModel.dispatchIndent(data)
} else if (!AppConst.isPlayChannel || Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) { } else if (!AppConst.isPlayChannel || Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
PermissionsCompat.Builder(this) PermissionsCompat.Builder()
.addPermissions(*Permissions.Group.STORAGE) .addPermissions(*Permissions.Group.STORAGE)
.rationale(R.string.tip_perm_request_storage) .rationale(R.string.tip_perm_request_storage)
.onGranted { .onGranted {
@@ -189,7 +189,7 @@ class ImportBookActivity : BaseImportBookActivity<ActivityImportBookBinding, Imp
private fun initRootPath(path: String) { private fun initRootPath(path: String) {
binding.tvEmptyMsg.visible() binding.tvEmptyMsg.visible()
PermissionsCompat.Builder(this) PermissionsCompat.Builder()
.addPermissions(*Permissions.Group.STORAGE) .addPermissions(*Permissions.Group.STORAGE)
.rationale(R.string.tip_perm_request_storage) .rationale(R.string.tip_perm_request_storage)
.onGranted { .onGranted {
@@ -295,7 +295,7 @@ class BackupConfigFragment : PreferenceFragment(),
} }
private fun backupUsePermission(path: String) { private fun backupUsePermission(path: String) {
PermissionsCompat.Builder(this) PermissionsCompat.Builder()
.addPermissions(*Permissions.Group.STORAGE) .addPermissions(*Permissions.Group.STORAGE)
.rationale(R.string.tip_perm_request_storage) .rationale(R.string.tip_perm_request_storage)
.onGranted { .onGranted {
@@ -366,7 +366,7 @@ class BackupConfigFragment : PreferenceFragment(),
} }
private fun restoreUsePermission(path: String) { private fun restoreUsePermission(path: String) {
PermissionsCompat.Builder(this) PermissionsCompat.Builder()
.addPermissions(*Permissions.Group.STORAGE) .addPermissions(*Permissions.Group.STORAGE)
.rationale(R.string.tip_perm_request_storage) .rationale(R.string.tip_perm_request_storage)
.onGranted { .onGranted {
@@ -182,7 +182,7 @@ class HandleFileActivity :
} }
private fun checkPermissions(success: (() -> Unit)? = null) { private fun checkPermissions(success: (() -> Unit)? = null) {
PermissionsCompat.Builder(this) PermissionsCompat.Builder()
.addPermissions(*Permissions.Group.STORAGE) .addPermissions(*Permissions.Group.STORAGE)
.rationale(R.string.tip_perm_request_storage) .rationale(R.string.tip_perm_request_storage)
.onGranted { .onGranted {
@@ -127,7 +127,7 @@ class FontSelectDialog : BaseDialogFragment(R.layout.dialog_font_select),
} }
private fun loadFontFilesByPermission(path: String) { private fun loadFontFilesByPermission(path: String) {
PermissionsCompat.Builder(this@FontSelectDialog) PermissionsCompat.Builder()
.addPermissions(*Permissions.Group.STORAGE) .addPermissions(*Permissions.Group.STORAGE)
.rationale(R.string.tip_perm_request_storage) .rationale(R.string.tip_perm_request_storage)
.onGranted { .onGranted {
@@ -28,8 +28,6 @@ import io.legado.app.help.config.LocalConfig
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.help.storage.Backup import io.legado.app.help.storage.Backup
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.PermissionsCompat
import io.legado.app.lib.theme.elevation import io.legado.app.lib.theme.elevation
import io.legado.app.lib.theme.primaryColor import io.legado.app.lib.theme.primaryColor
import io.legado.app.service.BaseReadAloudService import io.legado.app.service.BaseReadAloudService
@@ -94,8 +92,6 @@ class MainActivity : VMBaseActivity<ActivityMainBinding, MainViewModel>(),
if (!privacyPolicy()) return@launch if (!privacyPolicy()) return@launch
//版本更新 //版本更新
upVersion() upVersion()
//检测通知权限
checkNotificationPermission()
//备份同步 //备份同步
backupSync() backupSync()
//自动更新书籍 //自动更新书籍
@@ -198,25 +194,6 @@ class MainActivity : VMBaseActivity<ActivityMainBinding, MainViewModel>(),
} }
} }
/**
* 检测通知权限
*/
private suspend fun checkNotificationPermission() = suspendCoroutine { block ->
PermissionsCompat.Builder(this)
.addPermissions(Permissions.POST_NOTIFICATIONS)
.rationale(R.string.notification_permission_rationale)
.onDenied {
block.resume(null)
}
.onGranted {
block.resume(null)
}
.onError {
block.resume(null)
}
.request()
}
/** /**
* 备份同步 * 备份同步
*/ */
@@ -6,6 +6,7 @@ import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.widget.EditText import android.widget.EditText
import androidx.activity.viewModels import androidx.activity.viewModels
import com.google.android.material.tabs.TabLayout
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.VMBaseActivity import io.legado.app.base.VMBaseActivity
@@ -14,6 +15,8 @@ import io.legado.app.databinding.ActivityRssSourceEditBinding
import io.legado.app.help.config.LocalConfig import io.legado.app.help.config.LocalConfig
import io.legado.app.lib.dialogs.SelectItem import io.legado.app.lib.dialogs.SelectItem
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.accentColor
import io.legado.app.lib.theme.backgroundColor
import io.legado.app.lib.theme.primaryColor import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.document.HandleFileContract import io.legado.app.ui.document.HandleFileContract
import io.legado.app.ui.login.SourceLoginActivity import io.legado.app.ui.login.SourceLoginActivity
@@ -37,6 +40,8 @@ class RssSourceEditActivity :
} }
private val adapter by lazy { RssSourceEditAdapter() } private val adapter by lazy { RssSourceEditAdapter() }
private val sourceEntities: ArrayList<EditEntity> = ArrayList() private val sourceEntities: ArrayList<EditEntity> = ArrayList()
private val listEntities: ArrayList<EditEntity> = ArrayList()
private val webViewEntities: ArrayList<EditEntity> = ArrayList()
private val selectDoc = registerForActivityResult(HandleFileContract()) { private val selectDoc = registerForActivityResult(HandleFileContract()) {
it.uri?.let { uri -> it.uri?.let { uri ->
if (uri.isContentScheme()) { if (uri.isContentScheme()) {
@@ -148,8 +153,41 @@ class RssSourceEditActivity :
} }
private fun initView() { private fun initView() {
binding.tabLayout.addTab(binding.tabLayout.newTab().apply {
setText(R.string.source_tab_base)
})
binding.tabLayout.addTab(binding.tabLayout.newTab().apply {
setText(R.string.source_tab_list)
})
binding.tabLayout.addTab(binding.tabLayout.newTab().apply {
text = "WEB_VIEW"
})
binding.recyclerView.setEdgeEffectColor(primaryColor) binding.recyclerView.setEdgeEffectColor(primaryColor)
binding.recyclerView.adapter = adapter binding.recyclerView.adapter = adapter
binding.tabLayout.setBackgroundColor(backgroundColor)
binding.tabLayout.setSelectedTabIndicatorColor(accentColor)
binding.tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabReselected(tab: TabLayout.Tab?) {
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
}
override fun onTabSelected(tab: TabLayout.Tab?) {
setEditEntities(tab?.position)
}
})
}
private fun setEditEntities(tabPosition: Int?) {
when (tabPosition) {
1 -> adapter.editEntities = listEntities
2 -> adapter.editEntities = webViewEntities
else -> adapter.editEntities = sourceEntities
}
binding.recyclerView.scrollToPosition(0)
} }
private fun upSourceView(rs: RssSource? = viewModel.rssSource) { private fun upSourceView(rs: RssSource? = viewModel.rssSource) {
@@ -175,6 +213,9 @@ class RssSourceEditActivity :
add(EditEntity("variableComment", rs?.variableComment, R.string.variable_comment)) add(EditEntity("variableComment", rs?.variableComment, R.string.variable_comment))
add(EditEntity("concurrentRate", rs?.concurrentRate, R.string.concurrent_rate)) add(EditEntity("concurrentRate", rs?.concurrentRate, R.string.concurrent_rate))
add(EditEntity("sortUrl", rs?.sortUrl, R.string.sort_url)) add(EditEntity("sortUrl", rs?.sortUrl, R.string.sort_url))
}
listEntities.clear()
listEntities.apply {
add(EditEntity("ruleArticles", rs?.ruleArticles, R.string.r_articles)) add(EditEntity("ruleArticles", rs?.ruleArticles, R.string.r_articles))
add(EditEntity("ruleNextPage", rs?.ruleNextPage, R.string.r_next)) add(EditEntity("ruleNextPage", rs?.ruleNextPage, R.string.r_next))
add(EditEntity("ruleTitle", rs?.ruleTitle, R.string.r_title)) add(EditEntity("ruleTitle", rs?.ruleTitle, R.string.r_title))
@@ -182,13 +223,17 @@ class RssSourceEditActivity :
add(EditEntity("ruleDescription", rs?.ruleDescription, R.string.r_description)) add(EditEntity("ruleDescription", rs?.ruleDescription, R.string.r_description))
add(EditEntity("ruleImage", rs?.ruleImage, R.string.r_image)) add(EditEntity("ruleImage", rs?.ruleImage, R.string.r_image))
add(EditEntity("ruleLink", rs?.ruleLink, R.string.r_link)) add(EditEntity("ruleLink", rs?.ruleLink, R.string.r_link))
}
webViewEntities.clear()
webViewEntities.apply {
add(EditEntity("ruleContent", rs?.ruleContent, R.string.r_content)) add(EditEntity("ruleContent", rs?.ruleContent, R.string.r_content))
add(EditEntity("style", rs?.style, R.string.r_style)) add(EditEntity("style", rs?.style, R.string.r_style))
add(EditEntity("injectJs", rs?.injectJs, R.string.r_inject_js)) add(EditEntity("injectJs", rs?.injectJs, R.string.r_inject_js))
add(EditEntity("contentWhitelist", rs?.contentWhitelist, R.string.c_whitelist)) add(EditEntity("contentWhitelist", rs?.contentWhitelist, R.string.c_whitelist))
add(EditEntity("contentBlacklist", rs?.contentBlacklist, R.string.c_blacklist)) add(EditEntity("contentBlacklist", rs?.contentBlacklist, R.string.c_blacklist))
} }
adapter.editEntities = sourceEntities binding.tabLayout.selectTab(binding.tabLayout.getTabAt(0))
setEditEntities(0)
} }
private fun getRssSource(): RssSource { private fun getRssSource(): RssSource {
@@ -213,6 +258,10 @@ class RssSourceEditActivity :
"variableComment" -> source.variableComment = it.value "variableComment" -> source.variableComment = it.value
"concurrentRate" -> source.concurrentRate = it.value "concurrentRate" -> source.concurrentRate = it.value
"sortUrl" -> source.sortUrl = it.value "sortUrl" -> source.sortUrl = it.value
}
}
listEntities.forEach {
when (it.key) {
"ruleArticles" -> source.ruleArticles = it.value "ruleArticles" -> source.ruleArticles = it.value
"ruleNextPage" -> source.ruleNextPage = "ruleNextPage" -> source.ruleNextPage =
viewModel.ruleComplete(it.value, source.ruleArticles, 2) viewModel.ruleComplete(it.value, source.ruleArticles, 2)
@@ -226,6 +275,10 @@ class RssSourceEditActivity :
viewModel.ruleComplete(it.value, source.ruleArticles, 3) viewModel.ruleComplete(it.value, source.ruleArticles, 3)
"ruleLink" -> source.ruleLink = "ruleLink" -> source.ruleLink =
viewModel.ruleComplete(it.value, source.ruleArticles) viewModel.ruleComplete(it.value, source.ruleArticles)
}
}
webViewEntities.forEach {
when (it.key) {
"ruleContent" -> source.ruleContent = "ruleContent" -> source.ruleContent =
viewModel.ruleComplete(it.value, source.ruleArticles) viewModel.ruleComplete(it.value, source.ruleArticles)
"style" -> source.style = it.value "style" -> source.style = it.value
@@ -41,7 +41,7 @@ fun AppCompatActivity.readUri(
success.invoke(fileDoc, inputStream) success.invoke(fileDoc, inputStream)
} }
} else { } else {
PermissionsCompat.Builder(this) PermissionsCompat.Builder()
.addPermissions( .addPermissions(
Permissions.READ_EXTERNAL_STORAGE, Permissions.READ_EXTERNAL_STORAGE,
Permissions.WRITE_EXTERNAL_STORAGE Permissions.WRITE_EXTERNAL_STORAGE
@@ -81,7 +81,7 @@ fun Fragment.readUri(uri: Uri?, success: (fileDoc: FileDoc, inputStream: InputSt
success.invoke(fileDoc, inputStream) success.invoke(fileDoc, inputStream)
} }
} else { } else {
PermissionsCompat.Builder(this) PermissionsCompat.Builder()
.addPermissions( .addPermissions(
Permissions.READ_EXTERNAL_STORAGE, Permissions.READ_EXTERNAL_STORAGE,
Permissions.WRITE_EXTERNAL_STORAGE Permissions.WRITE_EXTERNAL_STORAGE
@@ -87,6 +87,14 @@
</LinearLayout> </LinearLayout>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="36dp"
android:background="@color/background"
android:elevation="3dp"
tools:ignore="SpeakableTextPresentCheck" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view" android:id="@+id/recycler_view"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -1071,4 +1071,5 @@
<string name="jump_to_another_app">跳转其它应用</string> <string name="jump_to_another_app">跳转其它应用</string>
<string name="clear_webview_data">清除 WebView 数据</string> <string name="clear_webview_data">清除 WebView 数据</string>
<string name="clear_webview_data_summary">清除内置浏览器所有数据</string> <string name="clear_webview_data_summary">清除内置浏览器所有数据</string>
<string name="source_tab_list">列表</string>
</resources> </resources>
@@ -1074,4 +1074,5 @@
<string name="jump_to_another_app">跳转其它应用</string> <string name="jump_to_another_app">跳转其它应用</string>
<string name="clear_webview_data">清除 WebView 数据</string> <string name="clear_webview_data">清除 WebView 数据</string>
<string name="clear_webview_data_summary">清除内置浏览器所有数据</string> <string name="clear_webview_data_summary">清除内置浏览器所有数据</string>
<string name="source_tab_list">列表</string>
</resources> </resources>
@@ -1074,4 +1074,5 @@
<string name="jump_to_another_app">跳转其它应用</string> <string name="jump_to_another_app">跳转其它应用</string>
<string name="clear_webview_data">清除 WebView 数据</string> <string name="clear_webview_data">清除 WebView 数据</string>
<string name="clear_webview_data_summary">清除内置浏览器所有数据</string> <string name="clear_webview_data_summary">清除内置浏览器所有数据</string>
<string name="source_tab_list">列表</string>
</resources> </resources>
@@ -1071,4 +1071,5 @@
<string name="jump_to_another_app">跳转其它应用</string> <string name="jump_to_another_app">跳转其它应用</string>
<string name="clear_webview_data">清除 WebView 数据</string> <string name="clear_webview_data">清除 WebView 数据</string>
<string name="clear_webview_data_summary">清除内置浏览器所有数据</string> <string name="clear_webview_data_summary">清除内置浏览器所有数据</string>
<string name="source_tab_list">列表</string>
</resources> </resources>
@@ -1073,4 +1073,5 @@
<string name="jump_to_another_app">跳转其它应用</string> <string name="jump_to_another_app">跳转其它应用</string>
<string name="clear_webview_data">清除 WebView 数据</string> <string name="clear_webview_data">清除 WebView 数据</string>
<string name="clear_webview_data_summary">清除内置浏览器所有数据</string> <string name="clear_webview_data_summary">清除内置浏览器所有数据</string>
<string name="source_tab_list">列表</string>
</resources> </resources>
+1
View File
@@ -1073,4 +1073,5 @@
<string name="jump_to_another_app">跳转其它应用</string> <string name="jump_to_another_app">跳转其它应用</string>
<string name="clear_webview_data">清除 WebView 数据</string> <string name="clear_webview_data">清除 WebView 数据</string>
<string name="clear_webview_data_summary">清除内置浏览器所有数据</string> <string name="clear_webview_data_summary">清除内置浏览器所有数据</string>
<string name="source_tab_list">列表</string>
</resources> </resources>
+1
View File
@@ -1074,4 +1074,5 @@
<string name="jump_to_another_app">Jump to another app</string> <string name="jump_to_another_app">Jump to another app</string>
<string name="clear_webview_data">清除 WebView 数据</string> <string name="clear_webview_data">清除 WebView 数据</string>
<string name="clear_webview_data_summary">清除内置浏览器所有数据</string> <string name="clear_webview_data_summary">清除内置浏览器所有数据</string>
<string name="source_tab_list">List</string>
</resources> </resources>