Rename
This commit is contained in:
1 parent
b86d90021c
commit
6c0757c91b
972 files changed
+6885
-7321
No files matched your search
@@ -0,0 +1,711 @@
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package io.legato.kazusa.service
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.PendingIntent
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.media.AudioManager
|
||||
import android.net.wifi.WifiManager
|
||||
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
|
||||
import androidx.media.AudioFocusRequestCompat
|
||||
import androidx.media.AudioManagerCompat
|
||||
import io.legato.kazusa.R
|
||||
import io.legato.kazusa.base.BaseService
|
||||
import io.legato.kazusa.constant.AppConst
|
||||
import io.legato.kazusa.constant.AppLog
|
||||
import io.legato.kazusa.constant.EventBus
|
||||
import io.legato.kazusa.constant.IntentAction
|
||||
import io.legato.kazusa.constant.NotificationId
|
||||
import io.legato.kazusa.constant.PreferKey
|
||||
import io.legato.kazusa.constant.Status
|
||||
import io.legato.kazusa.help.MediaHelp
|
||||
import io.legato.kazusa.help.config.AppConfig
|
||||
import io.legato.kazusa.help.coroutine.Coroutine
|
||||
import io.legato.kazusa.help.glide.ImageLoader
|
||||
import io.legato.kazusa.lib.permission.Permissions
|
||||
import io.legato.kazusa.lib.permission.PermissionsCompat
|
||||
import io.legato.kazusa.model.ReadAloud
|
||||
import io.legato.kazusa.model.ReadBook
|
||||
import io.legato.kazusa.receiver.MediaButtonReceiver
|
||||
import io.legato.kazusa.ui.book.read.ReadBookActivity
|
||||
import io.legato.kazusa.ui.book.read.page.entities.TextChapter
|
||||
import io.legato.kazusa.utils.LogUtils
|
||||
import io.legato.kazusa.utils.activityPendingIntent
|
||||
import io.legato.kazusa.utils.broadcastPendingIntent
|
||||
import io.legato.kazusa.utils.getPrefBoolean
|
||||
import io.legato.kazusa.utils.isVivoDevice
|
||||
import io.legato.kazusa.utils.observeEvent
|
||||
import io.legato.kazusa.utils.observeSharedPreferences
|
||||
import io.legato.kazusa.utils.postEvent
|
||||
import io.legato.kazusa.utils.toastOnUi
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import splitties.init.appCtx
|
||||
import splitties.systemservices.audioManager
|
||||
import splitties.systemservices.notificationManager
|
||||
import splitties.systemservices.powerManager
|
||||
import splitties.systemservices.telephonyManager
|
||||
import splitties.systemservices.wifiManager
|
||||
|
||||
/**
|
||||
* 朗读服务
|
||||
*/
|
||||
abstract class BaseReadAloudService : BaseService(),
|
||||
AudioManager.OnAudioFocusChangeListener {
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
var isRun = false
|
||||
private set
|
||||
|
||||
@JvmStatic
|
||||
var pause = true
|
||||
private set
|
||||
|
||||
@JvmStatic
|
||||
var timeMinute: Int = 0
|
||||
private set
|
||||
|
||||
fun isPlay(): Boolean {
|
||||
return isRun && !pause
|
||||
}
|
||||
|
||||
private const val TAG = "BaseReadAloudService"
|
||||
|
||||
}
|
||||
|
||||
private val useWakeLock = appCtx.getPrefBoolean(PreferKey.readAloudWakeLock, false)
|
||||
private val wakeLock by lazy {
|
||||
powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "legado:ReadAloudService")
|
||||
.apply {
|
||||
this.setReferenceCounted(false)
|
||||
}
|
||||
}
|
||||
private val wifiLock by lazy {
|
||||
@Suppress("DEPRECATION")
|
||||
wifiManager?.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "legado:AudioPlayService")
|
||||
?.apply {
|
||||
setReferenceCounted(false)
|
||||
}
|
||||
}
|
||||
private val mFocusRequest: AudioFocusRequestCompat by lazy {
|
||||
MediaHelp.buildAudioFocusRequestCompat(this)
|
||||
}
|
||||
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 needResumeOnCallStateIdle = false
|
||||
private var registeredPhoneStateListener = false
|
||||
private var dsJob: Job? = null
|
||||
private var upNotificationJob: Coroutine<*>? = null
|
||||
private var cover: Bitmap =
|
||||
BitmapFactory.decodeResource(appCtx.resources, R.drawable.icon_read_book)
|
||||
var pageChanged = false
|
||||
private var toLast = false
|
||||
var paragraphStartPos = 0
|
||||
var readAloudByPage = false
|
||||
private set
|
||||
|
||||
private val broadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (AudioManager.ACTION_AUDIO_BECOMING_NOISY == intent.action) {
|
||||
pauseReadAloud()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("WakelockTimeout")
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
isRun = true
|
||||
pause = false
|
||||
observeLiveBus()
|
||||
initMediaSession()
|
||||
initBroadcastReceiver()
|
||||
initPhoneStateListener()
|
||||
upMediaSessionPlaybackState(PlaybackStateCompat.STATE_PLAYING)
|
||||
setTimer(AppConfig.ttsTimer)
|
||||
if (AppConfig.ttsTimer > 0) {
|
||||
toastOnUi("朗读定时 ${AppConfig.ttsTimer} 分钟")
|
||||
}
|
||||
execute {
|
||||
ImageLoader
|
||||
.loadBitmap(this@BaseReadAloudService, ReadBook.book?.getDisplayCover())
|
||||
.submit()
|
||||
.get()
|
||||
}.onSuccess {
|
||||
if (it.width > 16 && it.height > 16) {
|
||||
cover = it
|
||||
upReadAloudNotification()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun observeLiveBus() {
|
||||
observeEvent<Bundle>(EventBus.READ_ALOUD_PLAY) {
|
||||
val play = it.getBoolean("play")
|
||||
val pageIndex = it.getInt("pageIndex")
|
||||
val startPos = it.getInt("startPos")
|
||||
newReadAloud(play, pageIndex, startPos)
|
||||
}
|
||||
observeSharedPreferences { _, key ->
|
||||
when (key) {
|
||||
PreferKey.ignoreAudioFocus,
|
||||
PreferKey.pauseReadAloudWhilePhoneCalls -> {
|
||||
initPhoneStateListener()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
if (useWakeLock) {
|
||||
wakeLock.release()
|
||||
wifiLock?.release()
|
||||
}
|
||||
isRun = false
|
||||
pause = true
|
||||
abandonFocus()
|
||||
unregisterReceiver(broadcastReceiver)
|
||||
postEvent(EventBus.ALOUD_STATE, Status.STOP)
|
||||
notificationManager.cancel(NotificationId.ReadAloudService)
|
||||
upMediaSessionPlaybackState(PlaybackStateCompat.STATE_STOPPED)
|
||||
mediaSessionCompat.release()
|
||||
ReadBook.uploadProgress()
|
||||
unregisterPhoneStateListener(phoneStateListener)
|
||||
upNotificationJob?.invokeOnCompletion {
|
||||
notificationManager.cancel(NotificationId.ReadAloudService)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
when (intent?.action) {
|
||||
IntentAction.play -> newReadAloud(
|
||||
intent.getBooleanExtra("play", true),
|
||||
intent.getIntExtra("pageIndex", ReadBook.durPageIndex),
|
||||
intent.getIntExtra("startPos", 0)
|
||||
)
|
||||
|
||||
IntentAction.pause -> pauseReadAloud()
|
||||
IntentAction.resume -> resumeReadAloud()
|
||||
IntentAction.upTtsSpeechRate -> upSpeechRate(true)
|
||||
IntentAction.prevParagraph -> prevP()
|
||||
IntentAction.nextParagraph -> nextP()
|
||||
IntentAction.prev -> prevChapter()
|
||||
IntentAction.next -> nextChapter()
|
||||
IntentAction.addTimer -> addTimer()
|
||||
IntentAction.setTimer -> setTimer(intent.getIntExtra("minute", 0))
|
||||
IntentAction.stop -> stopSelf()
|
||||
}
|
||||
return super.onStartCommand(intent, flags, startId)
|
||||
}
|
||||
|
||||
private fun newReadAloud(play: Boolean, pageIndex: Int, startPos: Int) {
|
||||
execute(executeContext = IO) {
|
||||
this@BaseReadAloudService.pageIndex = pageIndex
|
||||
textChapter = ReadBook.curTextChapter
|
||||
val textChapter = textChapter ?: return@execute
|
||||
if (!textChapter.isCompleted) {
|
||||
return@execute
|
||||
}
|
||||
readAloudNumber = textChapter.getReadLength(pageIndex) + startPos
|
||||
readAloudByPage = getPrefBoolean(PreferKey.readAloudByPage)
|
||||
contentList = textChapter.getNeedReadAloud(0, readAloudByPage, 0)
|
||||
.split("\n")
|
||||
.filter { it.isNotEmpty() }
|
||||
var pos = startPos
|
||||
val page = textChapter.getPage(pageIndex)!!
|
||||
if (pos > 0) {
|
||||
for (paragraph in page.paragraphs) {
|
||||
val tmp = pos - paragraph.length - 1
|
||||
if (tmp < 0) break
|
||||
pos = tmp
|
||||
}
|
||||
}
|
||||
nowSpeak = textChapter.getParagraphNum(readAloudNumber + 1, readAloudByPage) - 1
|
||||
if (!readAloudByPage && startPos == 0 && !toLast) {
|
||||
pos = page.chapterPosition -
|
||||
textChapter.paragraphs[nowSpeak].chapterPosition
|
||||
}
|
||||
if (toLast) {
|
||||
toLast = false
|
||||
readAloudNumber = textChapter.getLastParagraphPosition()
|
||||
nowSpeak = contentList.lastIndex
|
||||
if (page.paragraphs.size == 1) {
|
||||
pos = page.chapterPosition -
|
||||
textChapter.paragraphs[nowSpeak].chapterPosition
|
||||
}
|
||||
}
|
||||
paragraphStartPos = pos
|
||||
launch(Main) {
|
||||
if (play) play() else pageChanged = true
|
||||
}
|
||||
}.onError {
|
||||
AppLog.put("启动朗读出错\n${it.localizedMessage}", it, true)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("WakelockTimeout")
|
||||
open fun play() {
|
||||
if (useWakeLock) {
|
||||
wakeLock.acquire()
|
||||
wifiLock?.acquire()
|
||||
}
|
||||
isRun = true
|
||||
pause = false
|
||||
needResumeOnAudioFocusGain = false
|
||||
needResumeOnCallStateIdle = false
|
||||
upReadAloudNotification()
|
||||
postEvent(EventBus.ALOUD_STATE, Status.PLAY)
|
||||
}
|
||||
|
||||
abstract fun playStop()
|
||||
|
||||
@CallSuper
|
||||
open fun pauseReadAloud(abandonFocus: Boolean = true) {
|
||||
if (useWakeLock) {
|
||||
wakeLock.release()
|
||||
wifiLock?.release()
|
||||
}
|
||||
pause = true
|
||||
if (abandonFocus) {
|
||||
abandonFocus()
|
||||
}
|
||||
upReadAloudNotification()
|
||||
upMediaSessionPlaybackState(PlaybackStateCompat.STATE_PAUSED)
|
||||
postEvent(EventBus.ALOUD_STATE, Status.PAUSE)
|
||||
ReadBook.uploadProgress()
|
||||
doDs()
|
||||
}
|
||||
|
||||
@SuppressLint("WakelockTimeout")
|
||||
@CallSuper
|
||||
open fun resumeReadAloud() {
|
||||
pause = false
|
||||
needResumeOnAudioFocusGain = false
|
||||
needResumeOnCallStateIdle = false
|
||||
upReadAloudNotification()
|
||||
upMediaSessionPlaybackState(PlaybackStateCompat.STATE_PLAYING)
|
||||
postEvent(EventBus.ALOUD_STATE, Status.PLAY)
|
||||
}
|
||||
|
||||
abstract fun upSpeechRate(reset: Boolean = false)
|
||||
|
||||
fun upTtsProgress(progress: Int) {
|
||||
postEvent(EventBus.TTS_PROGRESS, progress)
|
||||
}
|
||||
|
||||
private fun prevP() {
|
||||
if (nowSpeak > 0) {
|
||||
playStop()
|
||||
nowSpeak--
|
||||
readAloudNumber -= contentList[nowSpeak].length + 1 + paragraphStartPos
|
||||
paragraphStartPos = 0
|
||||
textChapter?.let {
|
||||
if (readAloudByPage) {
|
||||
val paragraphs = it.getParagraphs(true)
|
||||
if (!paragraphs[nowSpeak].isParagraphEnd) readAloudNumber++
|
||||
}
|
||||
if (readAloudNumber < it.getReadLength(pageIndex)) {
|
||||
pageIndex--
|
||||
ReadBook.moveToPrevPage()
|
||||
}
|
||||
}
|
||||
upTtsProgress(readAloudNumber + 1)
|
||||
play()
|
||||
} else {
|
||||
toLast = true
|
||||
ReadBook.moveToPrevChapter(true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun nextP() {
|
||||
if (nowSpeak < contentList.size - 1) {
|
||||
playStop()
|
||||
readAloudNumber += contentList[nowSpeak].length.plus(1) - paragraphStartPos
|
||||
paragraphStartPos = 0
|
||||
nowSpeak++
|
||||
textChapter?.let {
|
||||
if (readAloudByPage) {
|
||||
val paragraphs = it.getParagraphs(true)
|
||||
if (!paragraphs[nowSpeak].isParagraphEnd) readAloudNumber--
|
||||
}
|
||||
if (readAloudNumber >= it.getReadLength(pageIndex + 1)) {
|
||||
pageIndex++
|
||||
ReadBook.moveToNextPage()
|
||||
}
|
||||
}
|
||||
upTtsProgress(readAloudNumber + 1)
|
||||
play()
|
||||
} else {
|
||||
nextChapter()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setTimer(minute: Int) {
|
||||
timeMinute = minute
|
||||
doDs()
|
||||
}
|
||||
|
||||
private fun addTimer() {
|
||||
if (timeMinute == 180) {
|
||||
timeMinute = 0
|
||||
} else {
|
||||
timeMinute += 10
|
||||
if (timeMinute > 180) timeMinute = 180
|
||||
}
|
||||
doDs()
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时
|
||||
*/
|
||||
@Synchronized
|
||||
private fun doDs() {
|
||||
postEvent(EventBus.READ_ALOUD_DS, timeMinute)
|
||||
upReadAloudNotification()
|
||||
dsJob?.cancel()
|
||||
dsJob = lifecycleScope.launch {
|
||||
while (isActive) {
|
||||
delay(60000)
|
||||
if (!pause) {
|
||||
if (timeMinute >= 0) {
|
||||
timeMinute--
|
||||
}
|
||||
if (timeMinute == 0) {
|
||||
ReadAloud.stop(this@BaseReadAloudService)
|
||||
postEvent(EventBus.READ_ALOUD_DS, timeMinute)
|
||||
break
|
||||
}
|
||||
}
|
||||
postEvent(EventBus.READ_ALOUD_DS, timeMinute)
|
||||
upReadAloudNotification()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求音频焦点
|
||||
* @return 音频焦点
|
||||
*/
|
||||
fun requestFocus(): Boolean {
|
||||
if (AppConfig.ignoreAudioFocus) {
|
||||
return true
|
||||
}
|
||||
val requestFocus = MediaHelp.requestFocus(mFocusRequest)
|
||||
if (!requestFocus) {
|
||||
pauseReadAloud(false)
|
||||
toastOnUi("未获取到音频焦点")
|
||||
}
|
||||
return requestFocus
|
||||
}
|
||||
|
||||
/**
|
||||
* 放弃音频焦点
|
||||
*/
|
||||
private fun abandonFocus() {
|
||||
AudioManagerCompat.abandonAudioFocusRequest(audioManager, mFocusRequest)
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新媒体状态
|
||||
*/
|
||||
private fun upMediaSessionPlaybackState(state: Int) {
|
||||
mediaSessionCompat.setPlaybackState(
|
||||
PlaybackStateCompat.Builder()
|
||||
.setActions(MediaHelp.MEDIA_SESSION_ACTIONS)
|
||||
.setState(state, nowSpeak.toLong(), 1f)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化MediaSession, 注册多媒体按钮
|
||||
*/
|
||||
@SuppressLint("UnspecifiedImmutableFlag")
|
||||
private fun initMediaSession() {
|
||||
mediaSessionCompat.setCallback(object : MediaSessionCompat.Callback() {
|
||||
override fun onMediaButtonEvent(mediaButtonEvent: Intent): Boolean {
|
||||
return MediaButtonReceiver.handleIntent(this@BaseReadAloudService, mediaButtonEvent)
|
||||
}
|
||||
})
|
||||
mediaSessionCompat.setMediaButtonReceiver(
|
||||
broadcastPendingIntent<MediaButtonReceiver>(Intent.ACTION_MEDIA_BUTTON)
|
||||
)
|
||||
mediaSessionCompat.isActive = true
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册多媒体按钮监听
|
||||
*/
|
||||
private fun initBroadcastReceiver() {
|
||||
val intentFilter = IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY)
|
||||
registerReceiver(broadcastReceiver, intentFilter)
|
||||
}
|
||||
|
||||
/**
|
||||
* 音频焦点变化
|
||||
*/
|
||||
override fun onAudioFocusChange(focusChange: Int) {
|
||||
if (AppConfig.ignoreAudioFocus) {
|
||||
AppLog.put("忽略音频焦点处理(TTS)")
|
||||
return
|
||||
}
|
||||
when (focusChange) {
|
||||
AudioManager.AUDIOFOCUS_GAIN -> {
|
||||
if (needResumeOnAudioFocusGain) {
|
||||
AppLog.put("音频焦点获得,继续朗读")
|
||||
resumeReadAloud()
|
||||
} else {
|
||||
AppLog.put("音频焦点获得")
|
||||
}
|
||||
}
|
||||
|
||||
AudioManager.AUDIOFOCUS_LOSS -> {
|
||||
AppLog.put("音频焦点丢失,暂停朗读")
|
||||
pauseReadAloud()
|
||||
}
|
||||
|
||||
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
|
||||
AppLog.put("音频焦点暂时丢失并会很快再次获得,暂停朗读")
|
||||
if (!pause) {
|
||||
needResumeOnAudioFocusGain = true
|
||||
pauseReadAloud(false)
|
||||
}
|
||||
}
|
||||
|
||||
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
|
||||
// 短暂丢失焦点,这种情况是被其他应用申请了短暂的焦点希望其他声音能压低音量(或者关闭声音)凸显这个声音(比如短信提示音),
|
||||
AppLog.put("音频焦点短暂丢失,不做处理")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun upReadAloudNotification() {
|
||||
upNotificationJob = execute {
|
||||
try {
|
||||
val notification = createNotification()
|
||||
notificationManager.notify(NotificationId.ReadAloudService, notification.build())
|
||||
} catch (e: Exception) {
|
||||
AppLog.put("创建朗读通知出错,${e.localizedMessage}", e, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun choiceMediaStyle(): androidx.media.app.NotificationCompat.MediaStyle {
|
||||
val mediaStyle = androidx.media.app.NotificationCompat.MediaStyle()
|
||||
.setShowActionsInCompactView(1, 2, 4)
|
||||
if (isVivoDevice) {
|
||||
//fix #4090 android 14 can not show play control in lock screen
|
||||
mediaStyle.setMediaSession(mediaSessionCompat.sessionToken)
|
||||
}
|
||||
return mediaStyle
|
||||
}
|
||||
|
||||
private fun createNotification(): NotificationCompat.Builder {
|
||||
var nTitle: String = when {
|
||||
pause -> getString(R.string.read_aloud_pause)
|
||||
timeMinute > 0 -> getString(
|
||||
R.string.read_aloud_timer,
|
||||
timeMinute
|
||||
)
|
||||
|
||||
else -> getString(R.string.read_aloud_t)
|
||||
}
|
||||
nTitle += ": ${ReadBook.book?.name}"
|
||||
var nSubtitle = ReadBook.curTextChapter?.title
|
||||
if (nSubtitle.isNullOrBlank())
|
||||
nSubtitle = getString(R.string.read_aloud_s)
|
||||
val builder = NotificationCompat
|
||||
.Builder(this, AppConst.channelIdReadAloud)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE)
|
||||
.setCategory(NotificationCompat.CATEGORY_TRANSPORT)
|
||||
.setSmallIcon(R.drawable.ic_volume_up)
|
||||
.setSubText(getString(R.string.read_aloud))
|
||||
.setOngoing(true)
|
||||
.setOnlyAlertOnce(true)
|
||||
.setContentTitle(nTitle)
|
||||
.setContentText(nSubtitle)
|
||||
.setContentIntent(
|
||||
activityPendingIntent<ReadBookActivity>("activity")
|
||||
)
|
||||
.setVibrate(null)
|
||||
.setSound(null)
|
||||
.setLights(0, 0, 0)
|
||||
builder.setLargeIcon(cover)
|
||||
// 按钮定义:上一章、播放、停止、下一章、定时
|
||||
builder.addAction(
|
||||
R.drawable.ic_skip_previous,
|
||||
getString(R.string.previous_chapter),
|
||||
aloudServicePendingIntent(IntentAction.prev)
|
||||
)
|
||||
if (pause) {
|
||||
builder.addAction(
|
||||
R.drawable.ic_play_24dp,
|
||||
getString(R.string.resume),
|
||||
aloudServicePendingIntent(IntentAction.resume)
|
||||
)
|
||||
} else {
|
||||
builder.addAction(
|
||||
R.drawable.ic_pause_24dp,
|
||||
getString(R.string.pause),
|
||||
aloudServicePendingIntent(IntentAction.pause)
|
||||
)
|
||||
}
|
||||
builder.addAction(
|
||||
R.drawable.ic_stop_black_24dp,
|
||||
getString(R.string.stop),
|
||||
aloudServicePendingIntent(IntentAction.stop)
|
||||
)
|
||||
builder.addAction(
|
||||
R.drawable.ic_skip_next,
|
||||
getString(R.string.next_chapter),
|
||||
aloudServicePendingIntent(IntentAction.next)
|
||||
)
|
||||
builder.addAction(
|
||||
R.drawable.ic_time_add_24dp,
|
||||
getString(R.string.set_timer),
|
||||
aloudServicePendingIntent(IntentAction.addTimer)
|
||||
)
|
||||
builder.setStyle(choiceMediaStyle())
|
||||
return builder
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新通知
|
||||
*/
|
||||
override fun startForegroundNotification() {
|
||||
execute {
|
||||
try {
|
||||
val notification = createNotification()
|
||||
startForeground(NotificationId.ReadAloudService, notification.build())
|
||||
} catch (e: Exception) {
|
||||
AppLog.put("创建朗读通知出错,${e.localizedMessage}", e, true)
|
||||
//创建通知出错不结束服务就会崩溃,服务必须绑定通知
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun aloudServicePendingIntent(actionStr: String): PendingIntent?
|
||||
|
||||
open fun prevChapter() {
|
||||
toLast = false
|
||||
ReadBook.moveToPrevChapter(true, toLast = false)
|
||||
}
|
||||
|
||||
open fun nextChapter() {
|
||||
ReadBook.upReadTime()
|
||||
AppLog.putDebug("${ReadBook.curTextChapter?.chapter?.title} 朗读结束跳转下一章并朗读")
|
||||
if (!ReadBook.moveToNextChapter(true)) {
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
|
||||
private fun initPhoneStateListener() {
|
||||
val needRegister = AppConfig.ignoreAudioFocus && AppConfig.pauseReadAloudWhilePhoneCalls
|
||||
if (needRegister && registeredPhoneStateListener) {
|
||||
return
|
||||
}
|
||||
if (needRegister) {
|
||||
registerPhoneStateListener(phoneStateListener)
|
||||
} else {
|
||||
unregisterPhoneStateListener(phoneStateListener)
|
||||
}
|
||||
}
|
||||
|
||||
private fun unregisterPhoneStateListener(l: PhoneStateListener) {
|
||||
if (registeredPhoneStateListener) {
|
||||
withReadPhoneStatePermission {
|
||||
telephonyManager.listen(l, PhoneStateListener.LISTEN_NONE)
|
||||
registeredPhoneStateListener = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerPhoneStateListener(l: PhoneStateListener) {
|
||||
withReadPhoneStatePermission {
|
||||
telephonyManager.listen(l, PhoneStateListener.LISTEN_CALL_STATE)
|
||||
registeredPhoneStateListener = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun withReadPhoneStatePermission(block: () -> Unit) {
|
||||
try {
|
||||
block.invoke()
|
||||
} catch (_: SecurityException) {
|
||||
PermissionsCompat.Builder()
|
||||
.addPermissions(Permissions.READ_PHONE_STATE)
|
||||
.rationale(R.string.read_aloud_read_phone_state_permission_rationale)
|
||||
.onGranted {
|
||||
try {
|
||||
block.invoke()
|
||||
} catch (_: SecurityException) {
|
||||
LogUtils.d(TAG, "Grant read phone state permission fail.")
|
||||
}
|
||||
}
|
||||
.request()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("OVERRIDE_DEPRECATION")
|
||||
inner class ReadAloudPhoneStateListener : PhoneStateListener() {
|
||||
override fun onCallStateChanged(state: Int, phoneNumber: String?) {
|
||||
super.onCallStateChanged(state, phoneNumber)
|
||||
when (state) {
|
||||
TelephonyManager.CALL_STATE_IDLE -> {
|
||||
if (needResumeOnCallStateIdle) {
|
||||
AppLog.put("来电结束,继续朗读")
|
||||
resumeReadAloud()
|
||||
} else {
|
||||
AppLog.put("来电结束")
|
||||
}
|
||||
}
|
||||
|
||||
TelephonyManager.CALL_STATE_RINGING -> {
|
||||
if (!pause) {
|
||||
AppLog.put("来电响铃,暂停朗读")
|
||||
needResumeOnCallStateIdle = true
|
||||
pauseReadAloud()
|
||||
} else {
|
||||
AppLog.put("来电响铃")
|
||||
}
|
||||
}
|
||||
|
||||
TelephonyManager.CALL_STATE_OFFHOOK -> {
|
||||
AppLog.put("来电接听,不做处理")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user