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

Merge pull request #1708 from ISuuuu/fix/tts-read-time

fix(tts): 修复后台 TTS 听书时未记录阅读时长的问题
Kudomaga 4 месяцев назад
Родитель
Сommit
98222d0721

+ 81 - 50
app/src/main/java/io/legado/app/model/ReadBook.kt

@@ -94,6 +94,10 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
     private val curChapterLoadingLock = Mutex()
     private val curChapterLoadingLock = Mutex()
     private val nextChapterLoadingLock = Mutex()
     private val nextChapterLoadingLock = Mutex()
     var readStartTime: Long = System.currentTimeMillis()
     var readStartTime: Long = System.currentTimeMillis()
+    var isUiActive = false
+    val isAutoSaveSessionRunning: Boolean
+        get() = autoSaveJob != null
+
 
 
     /* 跳转进度前进度记录 */
     /* 跳转进度前进度记录 */
     var lastBookProgress: BookProgress? = null
     var lastBookProgress: BookProgress? = null
@@ -316,69 +320,88 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
         }
         }
     }
     }
 
 
-    fun initReadTime() {
-        val currentBookName = book?.name ?: return
-        val currentBookAuthor = book?.author ?: ""
-        if (currentActiveSession != null &&
-            (currentActiveSession!!.bookName != currentBookName || currentActiveSession!!.bookAuthor != currentBookAuthor)
-        ) {
-            commitReadSession()
+    fun startReadSession() {
+        synchronized(this) {
+            readStartTime = System.currentTimeMillis()
+            initReadTime()
+            startAutoSaveSession()
         }
         }
+    }
 
 
-        if (currentActiveSession == null) {
-            lastReadLength = currentReadLength
-            currentActiveSession = ReadRecordSession(
-                deviceId = "",
-                bookName = currentBookName,
-                bookAuthor = currentBookAuthor,
-                startTime = readStartTime,
-                endTime = readStartTime,
-                words = durChapterIndex.toLong()
-            )
+    fun initReadTime() {
+        synchronized(this) {
+            val currentBookName = book?.name ?: return
+            val currentBookAuthor = book?.author ?: ""
+            if (currentActiveSession != null &&
+                (currentActiveSession!!.bookName != currentBookName || currentActiveSession!!.bookAuthor != currentBookAuthor)
+            ) {
+                commitReadSession()
+            }
+
+            if (currentActiveSession == null) {
+                lastReadLength = currentReadLength
+                currentActiveSession = ReadRecordSession(
+                    deviceId = "",
+                    bookName = currentBookName,
+                    bookAuthor = currentBookAuthor,
+                    startTime = readStartTime,
+                    endTime = readStartTime,
+                    words = durChapterIndex.toLong()
+                )
+            }
         }
         }
     }
     }
 
 
     fun upReadTime() {
     fun upReadTime() {
-        val currentLength = currentReadLength
-        val currentBookName = book?.name ?: return
-        val currentBookAuthor = book?.author ?: ""
-        val endTime = System.currentTimeMillis()
-
-        if (currentActiveSession == null ||
-            currentActiveSession!!.bookName != currentBookName ||
-            currentActiveSession!!.bookAuthor != currentBookAuthor
-        ) {
-            initReadTime()
-            return
-        }
+        synchronized(this) {
+            val currentLength = currentReadLength
+            val currentBookName = book?.name ?: return
+            val currentBookAuthor = book?.author ?: ""
+            val endTime = System.currentTimeMillis()
+
+            if (currentActiveSession == null ||
+                currentActiveSession!!.bookName != currentBookName ||
+                currentActiveSession!!.bookAuthor != currentBookAuthor
+            ) {
+                initReadTime()
+                return
+            }
 
 
-        currentActiveSession = currentActiveSession!!.copy(
-            endTime = endTime,
-            words = durChapterIndex.toLong()
-        )
+            currentActiveSession = currentActiveSession!!.copy(
+                endTime = endTime,
+                words = durChapterIndex.toLong()
+            )
 
 
-        readStartTime = endTime
-        lastReadLength = currentLength
+            readStartTime = endTime
+            lastReadLength = currentLength
+        }
     }
     }
 
 
     fun startAutoSaveSession() {
     fun startAutoSaveSession() {
-        autoSaveJob?.cancel()
-        autoSaveJob = ioScope.launch {
-            while (isActive) {
-                delay(AUTO_SAVE_INTERVAL)
-                commitSessionInternal()
+        synchronized(this) {
+            autoSaveJob?.cancel()
+            autoSaveJob = ioScope.launch {
+                while (isActive) {
+                    delay(AUTO_SAVE_INTERVAL)
+                    commitSessionInternal()
+                }
             }
             }
         }
         }
     }
     }
 
 
     fun stopAutoSaveSession() {
     fun stopAutoSaveSession() {
-        autoSaveJob?.cancel()
-        autoSaveJob = null
+        synchronized(this) {
+            autoSaveJob?.cancel()
+            autoSaveJob = null
+        }
     }
     }
 
 
     fun commitReadSession() {
     fun commitReadSession() {
-        val sessionToCommit = currentActiveSession ?: return
-        currentActiveSession = null
+        val sessionToCommit = synchronized(this) {
+            val session = currentActiveSession
+            currentActiveSession = null
+            session
+        } ?: return
         ioScope.launch {
         ioScope.launch {
             saveSessionToDb(sessionToCommit)
             saveSessionToDb(sessionToCommit)
         }
         }
@@ -388,7 +411,7 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
      * 内部提交逻辑(auto-save 专用):保存后立即创建新 session 保证连续记录
      * 内部提交逻辑(auto-save 专用):保存后立即创建新 session 保证连续记录
      */
      */
     private suspend fun commitSessionInternal() {
     private suspend fun commitSessionInternal() {
-        val sessionToSave = currentActiveSession ?: return
+        val sessionToSave = synchronized(this) { currentActiveSession } ?: return
         val sessionDuration = sessionToSave.endTime - sessionToSave.startTime
         val sessionDuration = sessionToSave.endTime - sessionToSave.startTime
         if (sessionDuration < MIN_READ_DURATION) {
         if (sessionDuration < MIN_READ_DURATION) {
             return
             return
@@ -400,11 +423,19 @@ object ReadBook : CoroutineScope by MainScope(), KoinComponent {
             return
             return
         }
         }
         // 保存成功后立即创建新 session,避免 auto-save 空窗期
         // 保存成功后立即创建新 session,避免 auto-save 空窗期
-        currentActiveSession = sessionToSave.copy(
-            startTime = sessionToSave.endTime,
-            endTime = sessionToSave.endTime,
-            words = durChapterIndex.toLong()
-        )
+        synchronized(this) {
+            val current = currentActiveSession
+            if (current != null &&
+                current.bookName == sessionToSave.bookName &&
+                current.bookAuthor == sessionToSave.bookAuthor
+            ) {
+                currentActiveSession = current.copy(
+                    startTime = sessionToSave.endTime,
+                    endTime = sessionToSave.endTime,
+                    words = durChapterIndex.toLong()
+                )
+            }
+        }
     }
     }
 
 
     /**
     /**

+ 19 - 0
app/src/main/java/io/legado/app/service/BaseReadAloudService.kt

@@ -188,6 +188,7 @@ abstract class BaseReadAloudService : BaseService(),
     }
     }
 
 
     override fun onDestroy() {
     override fun onDestroy() {
+        ReadBook.upReadTime()
         super.onDestroy()
         super.onDestroy()
         if (useWakeLock) {
         if (useWakeLock) {
             wakeLock.release()
             wakeLock.release()
@@ -203,6 +204,10 @@ abstract class BaseReadAloudService : BaseService(),
         mediaSessionCompat.release()
         mediaSessionCompat.release()
         ReadBook.uploadProgress()
         ReadBook.uploadProgress()
         unregisterPhoneStateListener(phoneStateListener)
         unregisterPhoneStateListener(phoneStateListener)
+        if (!ReadBook.isUiActive) {
+            ReadBook.stopAutoSaveSession()
+            ReadBook.commitReadSession()
+        }
         upNotificationJob?.invokeOnCompletion {
         upNotificationJob?.invokeOnCompletion {
             notificationManager.cancel(NotificationId.ReadAloudService)
             notificationManager.cancel(NotificationId.ReadAloudService)
         }
         }
@@ -288,12 +293,16 @@ abstract class BaseReadAloudService : BaseService(),
         needResumeOnCallStateIdle = false
         needResumeOnCallStateIdle = false
         upReadAloudNotification()
         upReadAloudNotification()
         postEvent(EventBus.ALOUD_STATE, Status.PLAY)
         postEvent(EventBus.ALOUD_STATE, Status.PLAY)
+        if (!ReadBook.isAutoSaveSessionRunning) {
+            ReadBook.startReadSession()
+        }
     }
     }
 
 
     abstract fun playStop()
     abstract fun playStop()
 
 
     @CallSuper
     @CallSuper
     open fun pauseReadAloud(abandonFocus: Boolean = true) {
     open fun pauseReadAloud(abandonFocus: Boolean = true) {
+        ReadBook.upReadTime()
         if (useWakeLock) {
         if (useWakeLock) {
             wakeLock.release()
             wakeLock.release()
             wifiLock?.release()
             wifiLock?.release()
@@ -307,6 +316,10 @@ abstract class BaseReadAloudService : BaseService(),
         postEvent(EventBus.ALOUD_STATE, Status.PAUSE)
         postEvent(EventBus.ALOUD_STATE, Status.PAUSE)
         ReadBook.uploadProgress()
         ReadBook.uploadProgress()
         doDs()
         doDs()
+        if (!ReadBook.isUiActive) {
+            ReadBook.stopAutoSaveSession()
+            ReadBook.commitReadSession()
+        }
     }
     }
 
 
     @SuppressLint("WakelockTimeout")
     @SuppressLint("WakelockTimeout")
@@ -322,15 +335,20 @@ abstract class BaseReadAloudService : BaseService(),
         upReadAloudNotification()
         upReadAloudNotification()
         upMediaSessionPlaybackState(PlaybackStateCompat.STATE_PLAYING)
         upMediaSessionPlaybackState(PlaybackStateCompat.STATE_PLAYING)
         postEvent(EventBus.ALOUD_STATE, Status.PLAY)
         postEvent(EventBus.ALOUD_STATE, Status.PLAY)
+        if (!ReadBook.isAutoSaveSessionRunning) {
+            ReadBook.startReadSession()
+        }
     }
     }
 
 
     abstract fun upSpeechRate(reset: Boolean = false)
     abstract fun upSpeechRate(reset: Boolean = false)
 
 
     fun upTtsProgress(progress: Int) {
     fun upTtsProgress(progress: Int) {
+        ReadBook.upReadTime()
         postEvent(EventBus.TTS_PROGRESS, progress)
         postEvent(EventBus.TTS_PROGRESS, progress)
     }
     }
 
 
     private fun prevP() {
     private fun prevP() {
+        ReadBook.upReadTime()
         if (nowSpeak > 0) {
         if (nowSpeak > 0) {
             playStop()
             playStop()
             do {
             do {
@@ -706,6 +724,7 @@ abstract class BaseReadAloudService : BaseService(),
     abstract fun aloudServicePendingIntent(actionStr: String): PendingIntent?
     abstract fun aloudServicePendingIntent(actionStr: String): PendingIntent?
 
 
     open fun prevChapter() {
     open fun prevChapter() {
+        ReadBook.upReadTime()
         toLast = false
         toLast = false
         resumeReadAloudInternal()
         resumeReadAloudInternal()
         ReadBook.moveToPrevChapter(true, toLast = false)
         ReadBook.moveToPrevChapter(true, toLast = false)

+ 7 - 5
app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt

@@ -1345,9 +1345,8 @@ class ReadBookViewModel(
 
 
     private fun handleOnResume() {
     private fun handleOnResume() {
         // Read time tracking
         // Read time tracking
-        ReadBook.readStartTime = System.currentTimeMillis()
-        ReadBook.initReadTime()
-        ReadBook.startAutoSaveSession()
+        ReadBook.isUiActive = true
+        ReadBook.startReadSession()
 
 
         // Web book progress sync
         // Web book progress sync
         ReadBook.webBookProgress?.let {
         ReadBook.webBookProgress?.let {
@@ -1372,9 +1371,12 @@ class ReadBookViewModel(
         _effects.tryEmit(ReadBookEffect.StopAutoPage)
         _effects.tryEmit(ReadBookEffect.StopAutoPage)
 
 
         // Read time tracking
         // Read time tracking
+        ReadBook.isUiActive = false
         ReadBook.saveRead()
         ReadBook.saveRead()
-        ReadBook.stopAutoSaveSession()
-        ReadBook.commitReadSession()
+        if (!BaseReadAloudService.isPlay()) {
+            ReadBook.stopAutoSaveSession()
+            ReadBook.commitReadSession()
+        }
         ReadBook.cancelPreDownloadTask()
         ReadBook.cancelPreDownloadTask()
 
 
         // View-layer
         // View-layer