This commit is contained in:
Horis
2024-08-23 17:41:14 +08:00
parent 051d8fa13f
commit 1e9dcb000c
19 changed files with 204 additions and 18 deletions

View File

@@ -6,6 +6,7 @@ import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
import android.content.res.Configuration
import android.os.Build
import com.github.liuyueyi.quick.transfer.constants.TransType
@@ -129,8 +130,13 @@ class App : Application() {
*/
private fun installGmsTlsProvider(context: Context) {
try {
val gmsPackageName = "com.google.android.gms"
val appInfo = packageManager.getApplicationInfo(gmsPackageName, 0)
if ((appInfo.flags and ApplicationInfo.FLAG_SYSTEM) == 0) {
return
}
val gms = context.createPackageContext(
"com.google.android.gms",
gmsPackageName,
CONTEXT_INCLUDE_CODE or CONTEXT_IGNORE_SECURITY
)
gms.classLoader

View File

@@ -147,6 +147,7 @@ object PreferKey {
const val optimizeRender = "optimizeRender"
const val updateToVariant = "updateToVariant"
const val streamReadAloudAudio = "streamReadAloudAudio"
const val pauseReadAloudWhilePhoneCalls = "pauseReadAloudWhilePhoneCalls"
const val cPrimary = "colorPrimary"
const val cAccent = "colorAccent"

View File

@@ -467,6 +467,10 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
val ignoreAudioFocus get() = appCtx.getPrefBoolean(PreferKey.ignoreAudioFocus, false)
var pauseReadAloudWhilePhoneCalls
get() = appCtx.getPrefBoolean(PreferKey.pauseReadAloudWhilePhoneCalls, false)
set(value) = appCtx.putPrefBoolean(PreferKey.pauseReadAloudWhilePhoneCalls, value)
val onlyLatestBackup get() = appCtx.getPrefBoolean(PreferKey.onlyLatestBackup, true)
val defaultHomePage get() = appCtx.getPrefString(PreferKey.defaultHomePage, "bookshelf")

View File

@@ -11,9 +11,9 @@ import androidx.activity.addCallback
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.lifecycle.lifecycleScope
import io.legado.app.R
import io.legado.app.constant.AppLog
import io.legado.app.exception.NoStackTraceException
import io.legado.app.utils.registerForActivityResult
import io.legado.app.utils.toastOnUi
@@ -25,13 +25,14 @@ class PermissionActivity : AppCompatActivity() {
private val settingActivityResult =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
RequestPlugins.sRequestCallback?.onSettingActivityResult()
finish()
onRequestPermissionFinish()
}
private val settingActivityResultAwait =
registerForActivityResult(ActivityResultContracts.StartActivityForResult())
private val requestPermissionResult =
registerForActivityResult(ActivityResultContracts.RequestPermission())
private val requestPermissionsResult =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions())
@SuppressLint("BatteryLife")
override fun onCreate(savedInstanceState: Bundle?) {
@@ -42,7 +43,20 @@ class PermissionActivity : AppCompatActivity() {
when (intent.getIntExtra(KEY_INPUT_REQUEST_TYPE, Request.TYPE_REQUEST_PERMISSION)) {
//权限请求
Request.TYPE_REQUEST_PERMISSION -> showSettingDialog(permissions, rationale) {
ActivityCompat.requestPermissions(this, permissions, requestCode)
lifecycleScope.launch {
try {
val result = requestPermissionsResult.launch(permissions)
if (result.values.all { it }) {
onRequestPermissionFinish()
} else {
openSettingsActivity()
}
} catch (e: Exception) {
AppLog.put("请求权限出错\n$e", e, true)
RequestPlugins.sRequestCallback?.onError(e)
finish()
}
}
}
//跳转到设置界面
Request.TYPE_REQUEST_SETTING -> showSettingDialog(permissions, rationale) {
@@ -62,7 +76,7 @@ class PermissionActivity : AppCompatActivity() {
throw NoStackTraceException("no MANAGE_ALL_FILES_ACCESS_PERMISSION")
}
} catch (e: Exception) {
toastOnUi(e.localizedMessage)
AppLog.put("请求所有文件的管理权限出错\n$e", e, true)
RequestPlugins.sRequestCallback?.onError(e)
finish()
}
@@ -70,12 +84,11 @@ class PermissionActivity : AppCompatActivity() {
Request.TYPE_REQUEST_NOTIFICATIONS -> showSettingDialog(permissions, rationale) {
lifecycleScope.launch {
kotlin.runCatching {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
&& requestPermissionResult.launch(Permissions.POST_NOTIFICATIONS)
) {
RequestPlugins.sRequestCallback?.onSettingActivityResult()
finish()
onRequestPermissionFinish()
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//这种方案适用于 API 26, 即8.0含8.0)以上可以用
val intent = Intent()
@@ -86,6 +99,10 @@ class PermissionActivity : AppCompatActivity() {
} else {
openSettingsActivity()
}
} catch (e: Exception) {
AppLog.put("请求通知权限出错\n$e", e, true)
RequestPlugins.sRequestCallback?.onError(e)
finish()
}
}
}
@@ -94,7 +111,7 @@ class PermissionActivity : AppCompatActivity() {
permissions, rationale
) {
lifecycleScope.launch {
kotlin.runCatching {
try {
val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
intent.setData(Uri.parse("package:$packageName"))
val className =
@@ -115,6 +132,10 @@ class PermissionActivity : AppCompatActivity() {
}
intent.component = null
settingActivityResult.launch(intent)
} catch (e: Exception) {
AppLog.put("请求后台权限出错\n$e", e, true)
RequestPlugins.sRequestCallback?.onError(e)
finish()
}
}
}
@@ -124,6 +145,11 @@ class PermissionActivity : AppCompatActivity() {
}
}
private fun onRequestPermissionFinish() {
RequestPlugins.sRequestCallback?.onSettingActivityResult()
finish()
}
private fun openSettingsActivity() {
try {
val settingIntent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)

View File

@@ -78,7 +78,7 @@ internal class Request : OnRequestPermissionsResultCallback {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
toIgnoreBatterySetting()
}
} else if (deniedPermissions.size > 1) {
} else if (deniedPermissions.isNotEmpty()) {
appCtx.startActivity<PermissionActivity> {
putExtra(PermissionActivity.KEY_RATIONALE, rationale)
putExtra(PermissionActivity.KEY_INPUT_REQUEST_TYPE, TYPE_REQUEST_PERMISSION)

View File

@@ -14,6 +14,8 @@ import android.os.Bundle
import android.os.PowerManager
import android.support.v4.media.session.MediaSessionCompat
import android.support.v4.media.session.PlaybackStateCompat
import android.telephony.PhoneStateListener
import android.telephony.TelephonyManager
import androidx.annotation.CallSuper
import androidx.core.app.NotificationCompat
import androidx.lifecycle.lifecycleScope
@@ -31,6 +33,8 @@ import io.legado.app.constant.Status
import io.legado.app.help.MediaHelp
import io.legado.app.help.config.AppConfig
import io.legado.app.help.glide.ImageLoader
import io.legado.app.lib.permission.Permissions
import io.legado.app.lib.permission.PermissionsCompat
import io.legado.app.model.ReadAloud
import io.legado.app.model.ReadBook
import io.legado.app.receiver.MediaButtonReceiver
@@ -41,6 +45,7 @@ import io.legado.app.utils.broadcastPendingIntent
import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.isVivoDevice
import io.legado.app.utils.observeEvent
import io.legado.app.utils.observeSharedPreferences
import io.legado.app.utils.postEvent
import io.legado.app.utils.toastOnUi
import kotlinx.coroutines.Dispatchers.IO
@@ -53,6 +58,7 @@ import splitties.init.appCtx
import splitties.systemservices.audioManager
import splitties.systemservices.notificationManager
import splitties.systemservices.powerManager
import splitties.systemservices.telephonyManager
import splitties.systemservices.wifiManager
/**
@@ -102,12 +108,16 @@ abstract class BaseReadAloudService : BaseService(),
private val mediaSessionCompat: MediaSessionCompat by lazy {
MediaSessionCompat(this, "readAloud")
}
private val phoneStateListener by lazy {
ReadAloudPhoneStateListener()
}
internal var contentList = emptyList<String>()
internal var nowSpeak: Int = 0
internal var readAloudNumber: Int = 0
internal var textChapter: TextChapter? = null
internal var pageIndex = 0
private var needResumeOnAudioFocusGain = false
private var registeredPhoneStateListener = false
private var dsJob: Job? = null
private var cover: Bitmap =
BitmapFactory.decodeResource(appCtx.resources, R.drawable.icon_read_book)
@@ -133,6 +143,7 @@ abstract class BaseReadAloudService : BaseService(),
observeLiveBus()
initMediaSession()
initBroadcastReceiver()
initPhoneStateListener()
upMediaSessionPlaybackState(PlaybackStateCompat.STATE_PLAYING)
setTimer(AppConfig.ttsTimer)
if (AppConfig.ttsTimer > 0) {
@@ -157,6 +168,13 @@ abstract class BaseReadAloudService : BaseService(),
val startPos = it.getInt("startPos")
newReadAloud(play, pageIndex, startPos)
}
observeSharedPreferences { _, key ->
when (key) {
PreferKey.pauseReadAloudWhilePhoneCalls -> {
initPhoneStateListener()
}
}
}
}
override fun onDestroy() {
@@ -174,6 +192,7 @@ abstract class BaseReadAloudService : BaseService(),
upMediaSessionPlaybackState(PlaybackStateCompat.STATE_STOPPED)
mediaSessionCompat.release()
ReadBook.uploadProgress()
unregisterPhoneStateListener(phoneStateListener)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
@@ -596,4 +615,66 @@ abstract class BaseReadAloudService : BaseService(),
}
}
private fun initPhoneStateListener() {
val needRegister = AppConfig.ignoreAudioFocus && AppConfig.pauseReadAloudWhilePhoneCalls
if (needRegister && registeredPhoneStateListener) {
return
}
if (needRegister) {
registerPhoneStateListener(phoneStateListener)
} else {
unregisterPhoneStateListener(phoneStateListener)
}
}
@Suppress("DEPRECATION")
private fun unregisterPhoneStateListener(l: PhoneStateListener) {
if (registeredPhoneStateListener) {
registerPhoneStateListener(l, true)
}
}
@Suppress("DEPRECATION")
private fun registerPhoneStateListener(l: PhoneStateListener, unregister: Boolean = false) {
try {
if (unregister) {
telephonyManager.listen(l, PhoneStateListener.LISTEN_NONE)
registeredPhoneStateListener = false
} else {
telephonyManager.listen(l, PhoneStateListener.LISTEN_CALL_STATE)
registeredPhoneStateListener = true
}
} catch (e: SecurityException) {
PermissionsCompat.Builder()
.addPermissions(Permissions.READ_PHONE_STATE)
.rationale(R.string.read_aloud_read_phone_state_permission_rationale)
.onGranted {
registerPhoneStateListener(l, unregister)
}
.request()
}
}
@Suppress("DEPRECATION", "OVERRIDE_DEPRECATION")
inner class ReadAloudPhoneStateListener : PhoneStateListener() {
override fun onCallStateChanged(state: Int, phoneNumber: String?) {
super.onCallStateChanged(state, phoneNumber)
when (state) {
TelephonyManager.CALL_STATE_IDLE -> {
AppLog.put("来电结束,继续朗读")
resumeReadAloud()
}
TelephonyManager.CALL_STATE_RINGING -> {
AppLog.put("来电响铃,暂停朗读")
pauseReadAloud()
}
TelephonyManager.CALL_STATE_OFFHOOK -> {
AppLog.put("来电接听,不做处理")
}
}
}
}
}

View File

@@ -164,12 +164,11 @@ class AboutFragment : PreferenceFragmentCompat() {
}
private fun copyLogs(doc: FileDoc) {
val files = File(appCtx.externalCacheDir, "logs").listFiles()?.toList()
if (files.isNullOrEmpty()) {
return
}
val logFiles = File(appCtx.externalCacheDir, "logs")
val crashFiles = File(appCtx.externalCacheDir, "crash")
val zipFile = File(appCtx.externalCacheDir, "logs.zip")
ZipUtils.zipFiles(files.filter { it.name.endsWith(".txt") }, zipFile)
ZipUtils.zipFiles(arrayListOf(logFiles, crashFiles), zipFile)
doc.find("logs.zip")?.delete()

View File

@@ -14,7 +14,9 @@ import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.data.appDb
import io.legado.app.help.IntentHelp
import io.legado.app.help.config.AppConfig
import io.legado.app.lib.dialogs.SelectItem
import io.legado.app.lib.prefs.SwitchPreference
import io.legado.app.lib.prefs.fragment.PreferenceFragment
import io.legado.app.lib.theme.backgroundColor
import io.legado.app.lib.theme.primaryColor
@@ -79,6 +81,9 @@ class ReadAloudConfigDialog : DialogFragment() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.pref_config_aloud)
upSpeakEngineSummary()
findPreference<SwitchPreference>(PreferKey.pauseReadAloudWhilePhoneCalls)?.let {
it.isEnabled = AppConfig.ignoreAudioFocus
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -114,6 +119,12 @@ class ReadAloudConfigDialog : DialogFragment() {
postEvent(EventBus.MEDIA_BUTTON, false)
}
}
PreferKey.ignoreAudioFocus -> {
findPreference<SwitchPreference>(PreferKey.pauseReadAloudWhilePhoneCalls)?.let {
it.isEnabled = AppConfig.ignoreAudioFocus
}
}
}
}

View File

@@ -53,7 +53,7 @@ object LogUtils {
val logFolder = FileUtils.createFolderIfNotExist(root, "logs")
val expiredTime = System.currentTimeMillis() - 7.days.inWholeMilliseconds
logFolder.listFiles()?.forEach {
if (it.lastModified() < expiredTime) {
if (it.lastModified() < expiredTime || it.name.endsWith(".lck")) {
it.delete()
}
}

View File

@@ -8,6 +8,9 @@ import android.content.Context
import android.content.ContextWrapper
import android.content.SharedPreferences
import androidx.core.content.edit
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import splitties.init.appCtx
import java.io.File
/**
@@ -100,4 +103,29 @@ fun SharedPreferences.remove(key: String) {
edit {
remove(key)
}
}
}
fun LifecycleOwner.observeSharedPreferences(
prefs: SharedPreferences = appCtx.defaultSharedPreferences,
l: SharedPreferences.OnSharedPreferenceChangeListener
) {
val observer = object : DefaultLifecycleObserver {
override fun onCreate(owner: LifecycleOwner) {
prefs.registerOnSharedPreferenceChangeListener(l)
}
override fun onDestroy(owner: LifecycleOwner) {
prefs.unregisterOnSharedPreferenceChangeListener(l)
lifecycle.removeObserver(this)
}
override fun onPause(owner: LifecycleOwner) {
prefs.unregisterOnSharedPreferenceChangeListener(l)
}
override fun onResume(owner: LifecycleOwner) {
prefs.registerOnSharedPreferenceChangeListener(l)
}
}
lifecycle.addObserver(observer)
}

View File

@@ -1161,4 +1161,7 @@
<string name="beta_releaseA_version">共存版</string>
<string name="stream_read_aloud_audio">流式播放音频</string>
<string name="stream_read_aloud_audio_summary">即边下边播网络不好时播放会断断续续仅TTS源有效</string>
<string name="pause_read_aloud_while_phone_calls_title">来电期间暂停朗读</string>
<string name="pause_read_aloud_while_phone_calls_summary">在通话期间暂停朗读,需要读取手机状态权限</string>
<string name="read_aloud_read_phone_state_permission_rationale">阅读需要读取手机状态实现来电期间暂停朗读功能</string>
</resources>

View File

@@ -1164,4 +1164,7 @@
<string name="beta_releaseA_version">共存版</string>
<string name="stream_read_aloud_audio">流式播放音频</string>
<string name="stream_read_aloud_audio_summary">即边下边播网络不好时播放会断断续续仅TTS源有效</string>
<string name="pause_read_aloud_while_phone_calls_title">来电期间暂停朗读</string>
<string name="pause_read_aloud_while_phone_calls_summary">在通话期间暂停朗读,需要读取手机状态权限</string>
<string name="read_aloud_read_phone_state_permission_rationale">阅读需要读取手机状态实现来电期间暂停朗读功能</string>
</resources>

View File

@@ -1164,4 +1164,7 @@
<string name="beta_releaseA_version">共存版</string>
<string name="stream_read_aloud_audio">流式播放音频</string>
<string name="stream_read_aloud_audio_summary">即边下边播网络不好时播放会断断续续仅TTS源有效</string>
<string name="pause_read_aloud_while_phone_calls_title">来电期间暂停朗读</string>
<string name="pause_read_aloud_while_phone_calls_summary">在通话期间暂停朗读,需要读取手机状态权限</string>
<string name="read_aloud_read_phone_state_permission_rationale">阅读需要读取手机状态实现来电期间暂停朗读功能</string>
</resources>

View File

@@ -1160,4 +1160,7 @@ Còn </string>
<string name="beta_releaseA_version">共存版</string>
<string name="stream_read_aloud_audio">流式播放音频</string>
<string name="stream_read_aloud_audio_summary">即边下边播网络不好时播放会断断续续仅TTS源有效</string>
<string name="pause_read_aloud_while_phone_calls_title">来电期间暂停朗读</string>
<string name="pause_read_aloud_while_phone_calls_summary">在通话期间暂停朗读,需要读取手机状态权限</string>
<string name="read_aloud_read_phone_state_permission_rationale">阅读需要读取手机状态实现来电期间暂停朗读功能</string>
</resources>

View File

@@ -1161,4 +1161,7 @@
<string name="beta_releaseA_version">共存版</string>
<string name="stream_read_aloud_audio">流式播放音频</string>
<string name="stream_read_aloud_audio_summary">即边下边播网络不好时播放会断断续续仅TTS源有效</string>
<string name="pause_read_aloud_while_phone_calls_title">来电期间暂停朗读</string>
<string name="pause_read_aloud_while_phone_calls_summary">在通话期间暂停朗读,需要读取手机状态权限</string>
<string name="read_aloud_read_phone_state_permission_rationale">阅读需要读取手机状态实现来电期间暂停朗读功能</string>
</resources>

View File

@@ -1163,4 +1163,7 @@
<string name="beta_releaseA_version">共存版</string>
<string name="stream_read_aloud_audio">流式播放音频</string>
<string name="stream_read_aloud_audio_summary">即边下边播网络不好时播放会断断续续仅TTS源有效</string>
<string name="pause_read_aloud_while_phone_calls_title">来电期间暂停朗读</string>
<string name="pause_read_aloud_while_phone_calls_summary">在通话期间暂停朗读,需要读取手机状态权限</string>
<string name="read_aloud_read_phone_state_permission_rationale">阅读需要读取手机状态实现来电期间暂停朗读功能</string>
</resources>

View File

@@ -1163,4 +1163,7 @@
<string name="beta_releaseA_version">共存版</string>
<string name="stream_read_aloud_audio">流式播放音频</string>
<string name="stream_read_aloud_audio_summary">即边下边播网络不好时播放会断断续续仅TTS源有效</string>
<string name="pause_read_aloud_while_phone_calls_title">来电期间暂停朗读</string>
<string name="pause_read_aloud_while_phone_calls_summary">在通话期间暂停朗读,需要读取手机状态权限</string>
<string name="read_aloud_read_phone_state_permission_rationale">阅读需要读取手机状态实现来电期间暂停朗读功能</string>
</resources>

View File

@@ -1164,4 +1164,7 @@
<string name="beta_releaseA_version">共存版</string>
<string name="stream_read_aloud_audio">流式播放音频</string>
<string name="stream_read_aloud_audio_summary">即边下边播网络不好时播放会断断续续仅TTS源有效</string>
<string name="pause_read_aloud_while_phone_calls_title">来电期间暂停朗读</string>
<string name="pause_read_aloud_while_phone_calls_summary">在通话期间暂停朗读,需要读取手机状态权限</string>
<string name="read_aloud_read_phone_state_permission_rationale">阅读需要读取手机状态实现来电期间暂停朗读功能</string>
</resources>

View File

@@ -14,6 +14,12 @@
android:summary="@string/ignore_audio_focus_summary"
android:title="@string/ignore_audio_focus_title" />
<io.legado.app.lib.prefs.SwitchPreference
android:defaultValue="false"
android:key="pauseReadAloudWhilePhoneCalls"
android:summary="@string/pause_read_aloud_while_phone_calls_summary"
android:title="@string/pause_read_aloud_while_phone_calls_title" />
<io.legado.app.lib.prefs.SwitchPreference
android:defaultValue="false"
android:title="@string/read_aloud_wake_lock"