Просмотр исходного кода

Merge PR #1751

# Conflicts:
#	app/src/main/java/io/legado/app/service/AudioPlayService.kt
#	app/src/main/java/io/legado/app/service/BaseReadAloudService.kt
#	app/src/main/java/io/legado/app/ui/book/audio/AudioPlayActivity.kt
HapeLee 3 месяцев назад
Родитель
Сommit
5261ca381c

+ 4 - 3
app/src/main/java/io/legado/app/data/repository/ReadAloudSettingsRepository.kt

@@ -5,6 +5,7 @@ import androidx.datastore.preferences.core.Preferences
 import androidx.datastore.preferences.core.booleanPreferencesKey
 import androidx.datastore.preferences.core.emptyPreferences
 import io.legado.app.constant.PreferKey
+import io.legado.app.domain.model.PlaybackTimer
 import io.legado.app.ui.config.readConfig.ReadConfig
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.catch
@@ -80,12 +81,12 @@ class ReadAloudSettingsRepository(
     }
 
     suspend fun setTtsTimer(value: Int) {
-        val timer = value.coerceIn(0, 180)
+        val timer = PlaybackTimer.normalize(value)
         ReadConfig.ttsTimer = timer
     }
 
     suspend fun saveTtsTimer(value: Int) {
-        settingsRepository.putInt(PreferKey.ttsTimer, value.coerceIn(0, 180))
+        settingsRepository.putInt(PreferKey.ttsTimer, PlaybackTimer.normalize(value))
     }
 
     suspend fun setTtsFollowSys(value: Boolean) {
@@ -108,7 +109,7 @@ class ReadAloudSettingsRepository(
             systemMediaControlCompatibilityChange =
                 this[Keys.SystemMediaControlCompatibilityChange] ?: true,
             streamReadAloudAudio = this[Keys.StreamReadAloudAudio] ?: false,
-            ttsTimer = this[Keys.TtsTimer] ?: 0,
+            ttsTimer = PlaybackTimer.normalize(this[Keys.TtsTimer] ?: 0),
             ttsFollowSys = this[Keys.TtsFollowSys] ?: true,
             ttsSpeechRate = this[Keys.TtsSpeechRate] ?: 5,
         )

+ 19 - 0
app/src/main/java/io/legado/app/domain/model/PlaybackTimer.kt

@@ -0,0 +1,19 @@
+package io.legado.app.domain.model
+
+object PlaybackTimer {
+
+    const val MIN_MINUTES = 0
+    const val MAX_MINUTES = 180
+    private const val INCREMENT_MINUTES = 10
+
+    fun normalize(minutes: Int): Int = minutes.coerceIn(MIN_MINUTES, MAX_MINUTES)
+
+    fun addIncrement(minutes: Int): Int {
+        val normalized = normalize(minutes)
+        return if (normalized == MAX_MINUTES) {
+            MIN_MINUTES
+        } else {
+            (normalized + INCREMENT_MINUTES).coerceAtMost(MAX_MINUTES)
+        }
+    }
+}

+ 6 - 4
app/src/main/java/io/legado/app/model/AudioPlay.kt

@@ -12,6 +12,7 @@ import io.legado.app.data.appDb
 import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.BookChapter
 import io.legado.app.data.entities.BookSource
+import io.legado.app.domain.model.PlaybackTimer
 import io.legado.app.help.book.ContentProcessor
 import io.legado.app.help.book.getBookSource
 import io.legado.app.help.book.readSimulating
@@ -335,14 +336,15 @@ object AudioPlay : CoroutineScope by MainScope() {
     }
 
     fun setTimer(minute: Int) {
+        val timer = PlaybackTimer.normalize(minute)
         if (AudioPlayService.isRun) {
             val intent = Intent(context, AudioPlayService::class.java)
             intent.action = IntentAction.setTimer
-            intent.putExtra("minute", minute)
+            intent.putExtra("minute", timer)
             context.startService(intent)
         } else {
-            AudioPlayService.timeMinute = minute
-            postEvent(EventBus.AUDIO_DS, minute)
+            AudioPlayService.timeMinute = timer
+            postEvent(EventBus.AUDIO_DS, timer)
         }
     }
 
@@ -438,4 +440,4 @@ object AudioPlay : CoroutineScope by MainScope() {
 
     }
 
-}
+}

+ 2 - 1
app/src/main/java/io/legado/app/model/ReadAloud.kt

@@ -8,6 +8,7 @@ import io.legado.app.constant.EventBus
 import io.legado.app.constant.IntentAction
 import io.legado.app.data.appDb
 import io.legado.app.data.entities.HttpTTS
+import io.legado.app.domain.model.PlaybackTimer
 import io.legado.app.service.BaseReadAloudService
 import io.legado.app.service.HttpReadAloudService
 import io.legado.app.service.TTSReadAloudService
@@ -129,7 +130,7 @@ object ReadAloud {
         if (BaseReadAloudService.isRun) {
             val intent = Intent(context, aloudClass)
             intent.action = IntentAction.setTimer
-            intent.putExtra("minute", minute)
+            intent.putExtra("minute", PlaybackTimer.normalize(minute))
             context.startForegroundServiceCompat(intent)
         }
     }

+ 12 - 12
app/src/main/java/io/legado/app/service/AudioPlayService.kt

@@ -29,6 +29,7 @@ import io.legado.app.constant.EventBus
 import io.legado.app.constant.IntentAction
 import io.legado.app.constant.NotificationId
 import io.legado.app.constant.Status
+import io.legado.app.domain.model.PlaybackTimer
 import io.legado.app.help.MediaHelp
 import io.legado.app.help.config.AppConfig
 import io.legado.app.ui.config.readConfig.ReadConfig
@@ -74,6 +75,9 @@ class AudioPlayService : BaseService(),
 
         @JvmStatic
         var timeMinute: Int = 0
+            set(value) {
+                field = PlaybackTimer.normalize(value)
+            }
 
         var url: String = ""
             private set
@@ -388,12 +392,7 @@ class AudioPlayService : BaseService(),
     }
 
     private fun addTimer() {
-        if (timeMinute == 180) {
-            timeMinute = 0
-        } else {
-            timeMinute += 10
-            if (timeMinute > 180) timeMinute = 180
-        }
+        timeMinute = PlaybackTimer.addIncrement(timeMinute)
         doDs()
     }
 
@@ -404,15 +403,15 @@ class AudioPlayService : BaseService(),
         postEvent(EventBus.AUDIO_DS, timeMinute)
         upAudioPlayNotification()
         dsJob?.cancel()
-        if (timeMinute <= 0) return
+        dsJob = null
+        if (timeMinute == PlaybackTimer.MIN_MINUTES) return
         dsJob = lifecycleScope.launch {
             while (isActive) {
                 delay(60000)
+                if (timeMinute == PlaybackTimer.MIN_MINUTES) break
                 if (!pause) {
-                    if (timeMinute > 0) {
-                        timeMinute--
-                    }
-                    if (timeMinute == 0) {
+                    timeMinute--
+                    if (timeMinute == PlaybackTimer.MIN_MINUTES) {
                         AudioPlay.stop()
                         postEvent(EventBus.AUDIO_DS, timeMinute)
                         break
@@ -573,7 +572,8 @@ class AudioPlayService : BaseService(),
     private fun createNotification(): NotificationCompat.Builder {
         val nTitle: String = when {
             pause -> getString(R.string.audio_pause)
-            timeMinute in 1..60 -> getString(R.string.playing_timer, timeMinute)
+            timeMinute > PlaybackTimer.MIN_MINUTES ->
+                getString(R.string.playing_timer, timeMinute)
             else -> getString(R.string.audio_play_t)
         } + ": ${AudioPlay.book?.name}"
 

+ 12 - 14
app/src/main/java/io/legado/app/service/BaseReadAloudService.kt

@@ -34,6 +34,7 @@ import io.legado.app.constant.IntentAction
 import io.legado.app.constant.NotificationId
 import io.legado.app.constant.PreferKey
 import io.legado.app.constant.Status
+import io.legado.app.domain.model.PlaybackTimer
 import io.legado.app.help.MediaHelp
 import io.legado.app.help.config.AppConfig
 import io.legado.app.ui.config.readConfig.ReadConfig
@@ -83,7 +84,9 @@ abstract class BaseReadAloudService : BaseService(),
 
         @JvmStatic
         var timeMinute: Int = 0
-            private set
+            private set(value) {
+                field = PlaybackTimer.normalize(value)
+            }
 
         fun isPlay(): Boolean {
             return isRun && !pause
@@ -153,8 +156,8 @@ abstract class BaseReadAloudService : BaseService(),
         initPhoneStateListener()
         upMediaSessionPlaybackState(PlaybackStateCompat.STATE_PLAYING)
         setTimer(ReadConfig.ttsTimer)
-        if (ReadConfig.ttsTimer > 0) {
-            toastOnUi("朗读定时 ${ReadConfig.ttsTimer} 分钟")
+        if (timeMinute > 0) {
+            toastOnUi("朗读定时 $timeMinute 分钟")
         }
         execute {
             ImageLoader
@@ -407,12 +410,7 @@ abstract class BaseReadAloudService : BaseService(),
     }
 
     private fun addTimer() {
-        if (timeMinute == 180) {
-            timeMinute = 0
-        } else {
-            timeMinute += 10
-            if (timeMinute > 180) timeMinute = 180
-        }
+        timeMinute = PlaybackTimer.addIncrement(timeMinute)
         doDs()
     }
 
@@ -424,15 +422,15 @@ abstract class BaseReadAloudService : BaseService(),
         postEvent(EventBus.READ_ALOUD_DS, timeMinute)
         upReadAloudNotification()
         dsJob?.cancel()
-        if (timeMinute <= 0) return
+        dsJob = null
+        if (timeMinute == PlaybackTimer.MIN_MINUTES) return
         dsJob = lifecycleScope.launch {
             while (isActive) {
                 delay(60000)
+                if (timeMinute == PlaybackTimer.MIN_MINUTES) break
                 if (!pause) {
-                    if (timeMinute > 0) {
-                        timeMinute--
-                    }
-                    if (timeMinute == 0) {
+                    timeMinute--
+                    if (timeMinute == PlaybackTimer.MIN_MINUTES) {
                         ReadAloud.stop(this@BaseReadAloudService)
                         postEvent(EventBus.READ_ALOUD_DS, timeMinute)
                         break

+ 4 - 4
app/src/main/java/io/legado/app/ui/book/audio/AudioPlayActivity.kt

@@ -43,6 +43,7 @@ import io.legado.app.data.entities.Book
 import io.legado.app.data.entities.BookChapter
 import io.legado.app.data.entities.BookSource
 import io.legado.app.databinding.ActivityAudioPlayBinding
+import io.legado.app.domain.model.PlaybackTimer
 import io.legado.app.help.book.isAudio
 import io.legado.app.help.book.removeType
 import io.legado.app.help.config.AppConfig
@@ -396,10 +397,10 @@ class AudioPlayActivity :
                 AudioPlay.setTimer(0)
             }
             binding.settingSlider.apply {
-                valueFrom = 0f
-                valueTo = 180f
+                valueFrom = PlaybackTimer.MIN_MINUTES.toFloat()
+                valueTo = PlaybackTimer.MAX_MINUTES.toFloat()
                 stepSize = 1f
-                value = AudioPlayService.timeMinute.coerceIn(0, 180).toFloat()
+                value = PlaybackTimer.normalize(AudioPlayService.timeMinute).toFloat()
 
                 addOnChangeListener { _, newValue, fromUser ->
                     if (fromUser) AudioPlay.setTimer(newValue.toInt())
@@ -798,4 +799,3 @@ private fun Int.copy(alpha: Float): Int {
     val alpha = (alpha * 255).toInt()
     return (this and 0x00FFFFFF) or (alpha shl 24)
 }
-

+ 2 - 1
app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt

@@ -40,6 +40,7 @@ import io.legado.app.data.repository.UploadRepository
 import io.legado.app.domain.gateway.AiPromptPresetGateway
 import io.legado.app.domain.gateway.BookContentProcessGateway
 import io.legado.app.domain.model.AiTaskType
+import io.legado.app.domain.model.PlaybackTimer
 import io.legado.app.domain.model.ReadingProgress
 import io.legado.app.domain.model.TextProcessAction
 import io.legado.app.domain.model.TextProcessAnchor
@@ -1954,7 +1955,7 @@ class ReadBookViewModel(
     }
 
     private fun setReadAloudTtsTimer(value: Int) {
-        val timer = value.coerceIn(0, 180)
+        val timer = PlaybackTimer.normalize(value)
         ReadAloud.setTimer(context, timer)
         _uiState.update { it.copy(readAloudTtsTimer = timer) }
     }

+ 3 - 2
app/src/main/java/io/legado/app/ui/book/read/sheet/ReadAloudSheet.kt

@@ -27,6 +27,7 @@ import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import io.legado.app.R
+import io.legado.app.domain.model.PlaybackTimer
 import io.legado.app.ui.book.read.ReadBookIntent
 import io.legado.app.ui.book.read.ReadBookUiState
 import io.legado.app.ui.widget.components.button.series.MediumTonalButton
@@ -92,8 +93,8 @@ fun ReadAloudContent(
             title = stringResource(R.string.set_timer),
             description = stringResource(R.string.timer_m, timerMinute),
             value = timerMinute.toFloat(),
-            valueRange = 0f..180f,
-            steps = 179,
+            valueRange = PlaybackTimer.MIN_MINUTES.toFloat()..PlaybackTimer.MAX_MINUTES.toFloat(),
+            steps = PlaybackTimer.MAX_MINUTES - PlaybackTimer.MIN_MINUTES - 1,
             onValueChange = {
                 onIntent(ReadBookIntent.SetReadAloudTtsTimer(it.toInt()))
             },

+ 25 - 0
app/src/test/java/io/legado/app/domain/model/PlaybackTimerTest.kt

@@ -0,0 +1,25 @@
+package io.legado.app.domain.model
+
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class PlaybackTimerTest {
+
+    @Test
+    fun normalize_clampsMinutesToSupportedRange() {
+        assertEquals(0, PlaybackTimer.normalize(-1))
+        assertEquals(0, PlaybackTimer.normalize(0))
+        assertEquals(90, PlaybackTimer.normalize(90))
+        assertEquals(180, PlaybackTimer.normalize(180))
+        assertEquals(180, PlaybackTimer.normalize(181))
+    }
+
+    @Test
+    fun addIncrement_recoversInvalidValuesAndCyclesAtMaximum() {
+        assertEquals(10, PlaybackTimer.addIncrement(-1))
+        assertEquals(10, PlaybackTimer.addIncrement(0))
+        assertEquals(180, PlaybackTimer.addIncrement(175))
+        assertEquals(0, PlaybackTimer.addIncrement(180))
+        assertEquals(0, PlaybackTimer.addIncrement(181))
+    }
+}