diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a345be4fd..a55fcc8d0 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -4,6 +4,7 @@
+
@@ -368,12 +369,12 @@
-
-
+
+
-
-
-
+
+
+
diff --git a/app/src/main/java/io/legado/app/model/AudioPlay.kt b/app/src/main/java/io/legado/app/model/AudioPlay.kt
index 19c035299..5c1dbcf17 100644
--- a/app/src/main/java/io/legado/app/model/AudioPlay.kt
+++ b/app/src/main/java/io/legado/app/model/AudioPlay.kt
@@ -1,8 +1,9 @@
package io.legado.app.model
+import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
-import androidx.lifecycle.MutableLiveData
+import io.legado.app.constant.AppLog
import io.legado.app.constant.EventBus
import io.legado.app.constant.IntentAction
import io.legado.app.constant.Status
@@ -12,84 +13,176 @@ import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.BookSource
import io.legado.app.help.book.ContentProcessor
import io.legado.app.help.book.getBookSource
+import io.legado.app.help.book.readSimulating
import io.legado.app.help.book.simulatedTotalChapterNum
import io.legado.app.help.coroutine.Coroutine
+import io.legado.app.model.webBook.WebBook
import io.legado.app.service.AudioPlayService
import io.legado.app.utils.postEvent
import io.legado.app.utils.startService
+import io.legado.app.utils.toastOnUi
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancelChildren
import splitties.init.appCtx
+@SuppressLint("StaticFieldLeak")
@Suppress("unused")
-object AudioPlay {
- var titleData = MutableLiveData()
- var coverData = MutableLiveData()
+object AudioPlay : CoroutineScope by MainScope() {
var status = Status.STOP
+ private var activityContext: Context? = null
+ private var serviceContext: Context? = null
+ private val context: Context get() = activityContext ?: serviceContext ?: appCtx
+ var callback: CallBack? = null
var book: Book? = null
+ var chapterSize = 0
+ var simulatedChapterSize = 0
+ var durChapterIndex = 0
+ var durChapterPos = 0
var durChapter: BookChapter? = null
+ var durPlayUrl = ""
+ var durAudioSize = 0
var inBookshelf = false
var bookSource: BookSource? = null
val loadingChapters = arrayListOf()
- var durChapterIndex = 0
- fun upData(context: Context, book: Book) {
+ fun upData(book: Book) {
AudioPlay.book = book
- upDurChapter(book)
+ chapterSize = appDb.bookChapterDao.getChapterCount(book.bookUrl)
+ simulatedChapterSize = if (book.readSimulating()) {
+ book.simulatedTotalChapterNum()
+ } else {
+ chapterSize
+ }
if (durChapterIndex != book.durChapterIndex) {
durChapterIndex = book.durChapterIndex
- playNew(context)
+ durChapterPos = book.durChapterPos
+ durPlayUrl = ""
+ durAudioSize = 0
+ }
+ upDurChapter()
+ }
+
+ fun resetData(book: Book) {
+ stop()
+ AudioPlay.book = book
+ chapterSize = appDb.bookChapterDao.getChapterCount(book.bookUrl)
+ simulatedChapterSize = if (book.readSimulating()) {
+ book.simulatedTotalChapterNum()
+ } else {
+ chapterSize
+ }
+ bookSource = book.getBookSource()
+ durChapterIndex = book.durChapterIndex
+ durChapterPos = book.durChapterPos
+ durPlayUrl = ""
+ durAudioSize = 0
+ upDurChapter()
+ }
+
+ private fun addLoading(index: Int): Boolean {
+ synchronized(this) {
+ if (loadingChapters.contains(index)) return false
+ loadingChapters.add(index)
+ return true
}
}
- fun resetData(context: Context, book: Book) {
- stop(context)
- AudioPlay.book = book
- titleData.postValue(book.name)
- coverData.postValue(book.getDisplayCover())
- bookSource = book.getBookSource()
- durChapterIndex = book.durChapterIndex
- upDurChapter(book)
+ private fun removeLoading(index: Int) {
+ synchronized(this) {
+ loadingChapters.remove(index)
+ }
+ }
+
+ fun loadOrUpPlayUrl() {
+ if (durPlayUrl.isEmpty()) {
+ loadPlayUrl()
+ } else {
+ upPlayUrl()
+ }
+ }
+
+ /**
+ * 加载播放URL
+ */
+ private fun loadPlayUrl() {
+ val index = durChapterIndex
+ if (addLoading(index)) {
+ val book = book
+ val bookSource = bookSource
+ if (book != null && bookSource != null) {
+ upDurChapter()
+ val chapter = durChapter
+ if (chapter == null) {
+ removeLoading(index)
+ return
+ }
+ upLoading(true)
+ WebBook.getContent(this, bookSource, book, chapter)
+ .onSuccess { content ->
+ if (content.isEmpty()) {
+ appCtx.toastOnUi("未获取到资源链接")
+ } else {
+ contentLoadFinish(chapter, content)
+ }
+ }.onError {
+ AppLog.put("获取资源链接出错\n$it", it, true)
+ }.onFinally {
+ removeLoading(index)
+ }
+ } else {
+ removeLoading(index)
+ appCtx.toastOnUi("book or source is null")
+ }
+ }
+ }
+
+ /**
+ * 加载完成
+ */
+ private fun contentLoadFinish(chapter: BookChapter, content: String) {
+ if (chapter.index == book?.durChapterIndex) {
+ durPlayUrl = content
+ upPlayUrl()
+ }
+ }
+
+ private fun upPlayUrl() {
+ if (isPlayToEnd()) {
+ playNew()
+ } else {
+ play()
+ }
}
/**
* 播放当前章节
*/
- fun play(context: Context) {
- book?.let {
- if (durChapter == null) {
- upDurChapter(it)
- }
- durChapter?.let {
- context.startService {
- action = IntentAction.play
- }
- }
+ fun play() {
+ context.startService {
+ action = IntentAction.play
}
}
/**
* 从头播放新章节
*/
- fun playNew(context: Context) {
- book?.let {
- if (durChapter == null) {
- upDurChapter(it)
- }
- durChapter?.let {
- context.startService {
- action = IntentAction.playNew
- }
- }
+ private fun playNew() {
+ context.startService {
+ action = IntentAction.playNew
}
}
/**
* 更新当前章节
*/
- fun upDurChapter(book: Book) {
- durChapter = appDb.bookChapterDao.getChapter(book.bookUrl, book.durChapterIndex)
+ fun upDurChapter() {
+ val book = book ?: return
+ durChapter = appDb.bookChapterDao.getChapter(book.bookUrl, durChapterIndex)
+ durAudioSize = durChapter?.end?.toInt() ?: 0
postEvent(EventBus.AUDIO_SUB_TITLE, durChapter?.title ?: "")
- postEvent(EventBus.AUDIO_SIZE, durChapter?.end?.toInt() ?: 0)
- postEvent(EventBus.AUDIO_PROGRESS, book.durChapterPos)
+ postEvent(EventBus.AUDIO_SIZE, durAudioSize)
+ postEvent(EventBus.AUDIO_PROGRESS, durChapterPos)
}
fun pause(context: Context) {
@@ -108,7 +201,7 @@ object AudioPlay {
}
}
- fun stop(context: Context) {
+ fun stop() {
if (AudioPlayService.isRun) {
context.startService {
action = IntentAction.stop
@@ -116,7 +209,7 @@ object AudioPlay {
}
}
- fun adjustSpeed(context: Context, adjust: Float) {
+ fun adjustSpeed(adjust: Float) {
if (AudioPlayService.isRun) {
context.startService {
action = IntentAction.adjustSpeed
@@ -125,7 +218,9 @@ object AudioPlay {
}
}
- fun adjustProgress(context: Context, position: Int) {
+ fun adjustProgress(position: Int) {
+ durChapterPos = position
+ saveRead()
if (AudioPlayService.isRun) {
context.startService {
action = IntentAction.adjustProgress
@@ -134,57 +229,47 @@ object AudioPlay {
}
}
- fun skipTo(context: Context, index: Int) {
+ fun skipTo(index: Int) {
Coroutine.async {
- book?.let { book ->
- book.durChapterIndex = index
- book.durChapterPos = 0
- durChapterIndex = book.durChapterIndex
- durChapter = null
+ stop()
+ durChapterIndex = index
+ durChapterPos = 0
+ durPlayUrl = ""
+ saveRead()
+ loadPlayUrl()
+ }
+ }
+
+ fun prev() {
+ Coroutine.async {
+ stop()
+ if (durChapterIndex > 0) {
+ durChapterIndex -= 1
+ durChapterPos = 0
+ durPlayUrl = ""
saveRead()
- playNew(context)
+ loadPlayUrl()
}
}
}
- fun prev(context: Context) {
- Coroutine.async {
- book?.let { book ->
- if (book.durChapterIndex > 0) {
- book.durChapterIndex -= 1
- book.durChapterPos = 0
- durChapterIndex = book.durChapterIndex
- durChapter = null
- saveRead()
- play(context)
- } else {
- stop(context)
- }
- }
- }
- }
-
- fun next(context: Context) {
- book?.let { book ->
- if (book.durChapterIndex + 1 < book.simulatedTotalChapterNum()) {
- book.durChapterIndex += 1
- book.durChapterPos = 0
- durChapterIndex = book.durChapterIndex
- durChapter = null
- saveRead()
- play(context)
- } else {
- stop(context)
- }
+ fun next() {
+ stop()
+ if (durChapterIndex + 1 < simulatedChapterSize) {
+ durChapterIndex += 1
+ durChapterPos = 0
+ durPlayUrl = ""
+ saveRead()
+ loadPlayUrl()
}
}
fun setTimer(minute: Int) {
if (AudioPlayService.isRun) {
- val intent = Intent(appCtx, AudioPlayService::class.java)
+ val intent = Intent(context, AudioPlayService::class.java)
intent.action = IntentAction.setTimer
intent.putExtra("minute", minute)
- appCtx.startService(intent)
+ context.startService(intent)
} else {
AudioPlayService.timeMinute = minute
postEvent(EventBus.AUDIO_DS, minute)
@@ -192,24 +277,28 @@ object AudioPlay {
}
fun addTimer() {
- val intent = Intent(appCtx, AudioPlayService::class.java)
+ val intent = Intent(context, AudioPlayService::class.java)
intent.action = IntentAction.addTimer
- appCtx.startService(intent)
+ context.startService(intent)
}
fun saveRead() {
- book?.let { book ->
+ val book = book ?: return
+ Coroutine.async {
book.lastCheckCount = 0
book.durChapterTime = System.currentTimeMillis()
- Coroutine.async {
+ val chapterChanged = book.durChapterIndex != durChapterIndex
+ book.durChapterIndex = durChapterIndex
+ book.durChapterPos = durChapterPos
+ if (chapterChanged) {
appDb.bookChapterDao.getChapter(book.bookUrl, book.durChapterIndex)?.let {
book.durChapterTitle = it.getDisplayTitle(
ContentProcessor.get(book.name, book.origin).getTitleReplaceRules(),
book.getUseReplaceRule()
)
}
- book.update()
}
+ book.update()
}
}
@@ -217,11 +306,53 @@ object AudioPlay {
* 保存章节长度
*/
fun saveDurChapter(audioSize: Long) {
+ val chapter = durChapter ?: return
Coroutine.async {
- durChapter?.let {
- it.end = audioSize
- appDb.bookChapterDao.update(it)
- }
+ durAudioSize = audioSize.toInt()
+ chapter.end = audioSize
+ appDb.bookChapterDao.update(chapter)
}
}
+
+ fun playPositionChanged(position: Int) {
+ durChapterPos = position
+ saveRead()
+ }
+
+ fun upLoading(loading: Boolean) {
+ callback?.upLoading(loading)
+ }
+
+ private fun isPlayToEnd(): Boolean {
+ return durChapterIndex + 1 == simulatedChapterSize
+ && durChapterPos == durAudioSize
+ }
+
+ fun register(context: Context) {
+ activityContext = context
+ callback = context as CallBack
+ }
+
+ fun unregister(context: Context) {
+ if (activityContext === context) {
+ activityContext = null
+ callback = null
+ }
+ coroutineContext.cancelChildren()
+ }
+
+ fun registerService(context: Context) {
+ serviceContext = context
+ }
+
+ fun unregisterService() {
+ serviceContext = null
+ }
+
+ interface CallBack {
+
+ fun upLoading(loading: Boolean)
+
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/service/AudioPlayService.kt b/app/src/main/java/io/legado/app/service/AudioPlayService.kt
index db07ad1c9..32a780e60 100644
--- a/app/src/main/java/io/legado/app/service/AudioPlayService.kt
+++ b/app/src/main/java/io/legado/app/service/AudioPlayService.kt
@@ -29,16 +29,12 @@ 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.data.appDb
-import io.legado.app.data.entities.Book
-import io.legado.app.data.entities.BookChapter
import io.legado.app.help.MediaHelp
import io.legado.app.help.config.AppConfig
import io.legado.app.help.exoplayer.ExoPlayerHelper
import io.legado.app.help.glide.ImageLoader
import io.legado.app.model.AudioPlay
import io.legado.app.model.analyzeRule.AnalyzeUrl
-import io.legado.app.model.webBook.WebBook
import io.legado.app.receiver.MediaButtonReceiver
import io.legado.app.ui.book.audio.AudioPlayActivity
import io.legado.app.utils.activityPendingIntent
@@ -122,6 +118,7 @@ class AudioPlayService : BaseService(),
super.onCreate()
isRun = true
exoPlayer.addListener(this)
+ AudioPlay.registerService(this)
initMediaSession()
initBroadcastReceiver()
upMediaSessionPlaybackState(PlaybackStateCompat.STATE_PLAYING)
@@ -147,7 +144,8 @@ class AudioPlayService : BaseService(),
upPlayProgressJob?.cancel()
pause = false
position = AudioPlay.book?.durChapterPos ?: 0
- loadContent()
+ url = AudioPlay.durPlayUrl
+ play()
}
IntentAction.playNew -> {
@@ -155,13 +153,14 @@ class AudioPlayService : BaseService(),
upPlayProgressJob?.cancel()
pause = false
position = 0
- loadContent()
+ url = AudioPlay.durPlayUrl
+ play()
}
IntentAction.pause -> pause()
IntentAction.resume -> resume()
- IntentAction.prev -> AudioPlay.prev(this)
- IntentAction.next -> AudioPlay.next(this)
+ IntentAction.prev -> AudioPlay.prev()
+ IntentAction.next -> AudioPlay.next()
IntentAction.adjustSpeed -> upSpeed(intent.getFloatExtra("adjust", 1f))
IntentAction.addTimer -> addTimer()
IntentAction.setTimer -> setTimer(intent.getIntExtra("minute", 0))
@@ -189,6 +188,7 @@ class AudioPlayService : BaseService(),
upMediaSessionPlaybackState(PlaybackStateCompat.STATE_STOPPED)
AudioPlay.status = Status.STOP
postEvent(EventBus.AUDIO_STATE, Status.STOP)
+ AudioPlay.unregisterService()
}
/**
@@ -216,6 +216,7 @@ class AudioPlayService : BaseService(),
)
exoPlayer.setMediaItem(analyzeUrl.getMediaItem())
exoPlayer.playWhenReady = true
+ exoPlayer.seekTo(position.toLong())
exoPlayer.prepare()
}.onError {
AppLog.put("播放出错\n${it.localizedMessage}", it)
@@ -261,7 +262,7 @@ class AudioPlayService : BaseService(),
try {
pause = false
if (url.isEmpty()) {
- loadContent()
+ AudioPlay.loadOrUpPlayUrl()
return
}
if (!exoPlayer.isPlaying) {
@@ -317,9 +318,7 @@ class AudioPlayService : BaseService(),
Player.STATE_READY -> {
// 准备好
- if (exoPlayer.currentPosition != position.toLong()) {
- exoPlayer.seekTo(position.toLong())
- }
+ AudioPlay.upLoading(false)
if (exoPlayer.playWhenReady) {
AudioPlay.status = Status.PLAY
postEvent(EventBus.AUDIO_STATE, Status.PLAY)
@@ -336,7 +335,8 @@ class AudioPlayService : BaseService(),
Player.STATE_ENDED -> {
// 结束
upPlayProgressJob?.cancel()
- AudioPlay.next(this)
+ AudioPlay.playPositionChanged(exoPlayer.duration.toInt())
+ AudioPlay.next()
}
}
upAudioPlayNotification()
@@ -395,7 +395,7 @@ class AudioPlayService : BaseService(),
timeMinute--
}
if (timeMinute == 0) {
- AudioPlay.stop(this@AudioPlayService)
+ AudioPlay.stop()
}
}
postEvent(EventBus.AUDIO_DS, timeMinute)
@@ -411,81 +411,16 @@ class AudioPlayService : BaseService(),
upPlayProgressJob?.cancel()
upPlayProgressJob = lifecycleScope.launch {
while (isActive) {
- AudioPlay.book?.let {
- //更新buffer位置
- postEvent(EventBus.AUDIO_BUFFER_PROGRESS, exoPlayer.bufferedPosition.toInt())
- it.durChapterPos = exoPlayer.currentPosition.toInt()
- postEvent(EventBus.AUDIO_PROGRESS, it.durChapterPos)
- upMediaSessionPlaybackState(PlaybackStateCompat.STATE_PLAYING)
- saveProgress(it)
- }
+ //更新buffer位置
+ AudioPlay.playPositionChanged(exoPlayer.currentPosition.toInt())
+ postEvent(EventBus.AUDIO_BUFFER_PROGRESS, exoPlayer.bufferedPosition.toInt())
+ postEvent(EventBus.AUDIO_PROGRESS, AudioPlay.durChapterPos)
+ upMediaSessionPlaybackState(PlaybackStateCompat.STATE_PLAYING)
delay(1000)
}
}
}
- /**
- * 加载播放URL
- */
- private fun loadContent() = with(AudioPlay) {
- durChapter?.let { chapter ->
- if (addLoading(chapter.index)) {
- val book = AudioPlay.book
- val bookSource = AudioPlay.bookSource
- if (book != null && bookSource != null) {
- WebBook.getContent(lifecycleScope, bookSource, book, chapter)
- .onSuccess { content ->
- if (content.isEmpty()) {
- toastOnUi("未获取到资源链接")
- } else {
- contentLoadFinish(chapter, content)
- }
- }.onError {
- contentLoadFinish(chapter, it.localizedMessage ?: toString())
- }.onFinally {
- removeLoading(chapter.index)
- }
- } else {
- removeLoading(chapter.index)
- toastOnUi("book or source is null")
- }
- }
- }
- }
-
- private fun addLoading(index: Int): Boolean {
- synchronized(this) {
- if (AudioPlay.loadingChapters.contains(index)) return false
- AudioPlay.loadingChapters.add(index)
- return true
- }
- }
-
- private fun removeLoading(index: Int) {
- synchronized(this) {
- AudioPlay.loadingChapters.remove(index)
- }
- }
-
- /**
- * 加载完成
- */
- private fun contentLoadFinish(chapter: BookChapter, content: String) {
- if (chapter.index == AudioPlay.book?.durChapterIndex) {
- url = content
- play()
- }
- }
-
- /**
- * 保存播放进度
- */
- private fun saveProgress(book: Book) {
- execute {
- appDb.bookDao.upProgress(book.bookUrl, book.durChapterPos)
- }
- }
-
/**
* 更新媒体状态
*/
@@ -584,7 +519,6 @@ class AudioPlayService : BaseService(),
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
AppLog.put("音频焦点暂时丢失并会很快再次获得,暂停播放")
- needResumeOnAudioFocusGain = true
if (!pause) {
needResumeOnAudioFocusGain = true
pause(false)
diff --git a/app/src/main/java/io/legado/app/ui/book/audio/AudioPlayActivity.kt b/app/src/main/java/io/legado/app/ui/book/audio/AudioPlayActivity.kt
index d324afcf7..bd21808f1 100644
--- a/app/src/main/java/io/legado/app/ui/book/audio/AudioPlayActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/book/audio/AudioPlayActivity.kt
@@ -24,7 +24,6 @@ import io.legado.app.data.entities.BookSource
import io.legado.app.databinding.ActivityAudioPlayBinding
import io.legado.app.help.book.isAudio
import io.legado.app.help.book.removeType
-import io.legado.app.help.book.simulatedTotalChapterNum
import io.legado.app.help.config.AppConfig
import io.legado.app.lib.dialogs.alert
import io.legado.app.model.AudioPlay
@@ -59,7 +58,8 @@ import java.util.Locale
@SuppressLint("ObsoleteSdkInt")
class AudioPlayActivity :
VMBaseActivity(toolBarTheme = Theme.Dark),
- ChangeBookSourceDialog.CallBack {
+ ChangeBookSourceDialog.CallBack,
+ AudioPlay.CallBack {
override val binding by viewBinding(ActivityAudioPlayBinding::inflate)
override val viewModel by viewModels()
@@ -78,7 +78,7 @@ class AudioPlayActivity :
if (it.first != AudioPlay.book?.durChapterIndex
|| it.second == 0
) {
- AudioPlay.skipTo(this, it.first)
+ AudioPlay.skipTo(it.first)
}
}
}
@@ -91,10 +91,11 @@ class AudioPlayActivity :
override fun onActivityCreated(savedInstanceState: Bundle?) {
binding.titleBar.setBackgroundResource(R.color.transparent)
- AudioPlay.titleData.observe(this) {
+ AudioPlay.register(this)
+ viewModel.titleData.observe(this) {
binding.titleBar.title = it
}
- AudioPlay.coverData.observe(this) {
+ viewModel.coverData.observe(this) {
upCover(it)
}
viewModel.initData(intent)
@@ -143,13 +144,13 @@ class AudioPlayActivity :
playButton()
}
binding.fabPlayStop.onLongClick {
- AudioPlay.stop(this@AudioPlayActivity)
+ AudioPlay.stop()
}
binding.ivSkipNext.setOnClickListener {
- AudioPlay.next(this@AudioPlayActivity)
+ AudioPlay.next()
}
binding.ivSkipPrevious.setOnClickListener {
- AudioPlay.prev(this@AudioPlayActivity)
+ AudioPlay.prev()
}
binding.playerProgress.setOnSeekBarChangeListener(object : SeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
@@ -162,7 +163,7 @@ class AudioPlayActivity :
override fun onStopTrackingTouch(seekBar: SeekBar) {
adjustProgress = false
- AudioPlay.adjustProgress(this@AudioPlayActivity, seekBar.progress)
+ AudioPlay.adjustProgress(seekBar.progress)
}
})
binding.ivChapter.setOnClickListener {
@@ -175,10 +176,10 @@ class AudioPlayActivity :
binding.ivFastForward.invisible()
}
binding.ivFastForward.setOnClickListener {
- AudioPlay.adjustSpeed(this@AudioPlayActivity, 0.1f)
+ AudioPlay.adjustSpeed(0.1f)
}
binding.ivFastRewind.setOnClickListener {
- AudioPlay.adjustSpeed(this@AudioPlayActivity, -0.1f)
+ AudioPlay.adjustSpeed(-0.1f)
}
binding.ivTimer.setOnClickListener {
timerSliderPopup.showAsDropDown(it, 0, (-100).dpToPx(), Gravity.TOP)
@@ -196,7 +197,7 @@ class AudioPlayActivity :
when (AudioPlay.status) {
Status.PLAY -> AudioPlay.pause(this)
Status.PAUSE -> AudioPlay.resume(this)
- else -> AudioPlay.play(this)
+ else -> AudioPlay.loadOrUpPlayUrl()
}
}
@@ -207,7 +208,7 @@ class AudioPlayActivity :
if (book.isAudio) {
viewModel.changeTo(source, book, toc)
} else {
- AudioPlay.stop(this)
+ AudioPlay.stop()
lifecycleScope.launch {
withContext(IO) {
AudioPlay.book?.migrateTo(book, toc)
@@ -247,8 +248,9 @@ class AudioPlayActivity :
override fun onDestroy() {
super.onDestroy()
if (AudioPlay.status != Status.PLAY) {
- AudioPlay.stop(this)
+ AudioPlay.stop()
}
+ AudioPlay.unregister(this)
}
@SuppressLint("SetTextI18n")
@@ -268,11 +270,9 @@ class AudioPlayActivity :
}
observeEventSticky(EventBus.AUDIO_SUB_TITLE) {
binding.tvSubTitle.text = it
- AudioPlay.book?.let { book ->
- binding.ivSkipPrevious.isEnabled = book.durChapterIndex > 0
- binding.ivSkipNext.isEnabled =
- book.durChapterIndex < book.simulatedTotalChapterNum() - 1
- }
+ binding.ivSkipPrevious.isEnabled = AudioPlay.durChapterIndex > 0
+ binding.ivSkipNext.isEnabled =
+ AudioPlay.durChapterIndex < AudioPlay.simulatedChapterSize - 1
}
observeEventSticky(EventBus.AUDIO_SIZE) {
binding.playerProgress.max = it
@@ -296,4 +296,10 @@ class AudioPlayActivity :
}
}
+ override fun upLoading(loading: Boolean) {
+ runOnUiThread {
+ binding.progressLoading.visible(loading)
+ }
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/book/audio/AudioPlayViewModel.kt b/app/src/main/java/io/legado/app/ui/book/audio/AudioPlayViewModel.kt
index b2b6d60a9..a85438906 100644
--- a/app/src/main/java/io/legado/app/ui/book/audio/AudioPlayViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/book/audio/AudioPlayViewModel.kt
@@ -2,9 +2,10 @@ package io.legado.app.ui.book.audio
import android.app.Application
import android.content.Intent
-import androidx.lifecycle.viewModelScope
+import androidx.lifecycle.MutableLiveData
import io.legado.app.R
import io.legado.app.base.BaseViewModel
+import io.legado.app.constant.AppLog
import io.legado.app.constant.BookType
import io.legado.app.constant.EventBus
import io.legado.app.data.appDb
@@ -13,14 +14,15 @@ import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.BookSource
import io.legado.app.help.book.getBookSource
import io.legado.app.help.book.removeType
+import io.legado.app.help.book.simulatedTotalChapterNum
import io.legado.app.model.AudioPlay
-import io.legado.app.model.AudioPlay.durChapter
import io.legado.app.model.webBook.WebBook
import io.legado.app.utils.postEvent
import io.legado.app.utils.toastOnUi
-import kotlinx.coroutines.Dispatchers.IO
class AudioPlayViewModel(application: Application) : BaseViewModel(application) {
+ val titleData = MutableLiveData()
+ val coverData = MutableLiveData()
fun initData(intent: Intent) = AudioPlay.apply {
execute {
@@ -33,37 +35,53 @@ class AudioPlayViewModel(application: Application) : BaseViewModel(application)
}
}
- private fun initBook(book: Book) {
+ private suspend fun initBook(book: Book) {
val isSameBook = AudioPlay.book?.bookUrl == book.bookUrl
if (isSameBook) {
- AudioPlay.upData(context, book)
+ AudioPlay.upData(book)
} else {
- AudioPlay.resetData(context, book)
+ AudioPlay.resetData(book)
}
- if (durChapter == null) {
- if (book.tocUrl.isEmpty()) {
- loadBookInfo(book)
+ titleData.postValue(book.name)
+ coverData.postValue(book.getDisplayCover())
+ if (book.tocUrl.isEmpty() && !loadBookInfo(book)) {
+ return
+ }
+ if (AudioPlay.chapterSize == 0 && !loadChapterList(book)) {
+ return
+ }
+ }
+
+ private suspend fun loadBookInfo(book: Book): Boolean {
+ val bookSource = AudioPlay.bookSource ?: return true
+ try {
+ WebBook.getBookInfoAwait(bookSource, book)
+ return true
+ } catch (e: Exception) {
+ AppLog.put("详情页出错: ${e.localizedMessage}", e, true)
+ return false
+ }
+ }
+
+ private suspend fun loadChapterList(book: Book): Boolean {
+ val bookSource = AudioPlay.bookSource ?: return true
+ try {
+ val oldBook = book.copy()
+ val cList = WebBook.getChapterListAwait(bookSource, book).getOrThrow()
+ if (oldBook.bookUrl == book.bookUrl) {
+ appDb.bookDao.update(book)
} else {
- loadChapterList(book)
+ appDb.bookDao.insert(book)
}
- }
- }
-
- private fun loadBookInfo(book: Book) {
- val bookSource = AudioPlay.bookSource ?: return
- WebBook.getBookInfo(viewModelScope, bookSource, book).onSuccess(IO) {
- loadChapterList(book)
- }
- }
-
- private fun loadChapterList(book: Book) {
- val bookSource = AudioPlay.bookSource ?: return
- WebBook.getChapterList(viewModelScope, bookSource, book).onSuccess(IO) { cList ->
- book.save()
+ appDb.bookChapterDao.delByBook(book.bookUrl)
appDb.bookChapterDao.insert(*cList.toTypedArray())
- AudioPlay.upDurChapter(book)
- }.onError {
+ AudioPlay.chapterSize = cList.size
+ AudioPlay.simulatedChapterSize = book.simulatedTotalChapterNum()
+ AudioPlay.upDurChapter()
+ return true
+ } catch (e: Exception) {
context.toastOnUi(R.string.error_load_toc)
+ return false
}
}
@@ -83,7 +101,7 @@ class AudioPlayViewModel(application: Application) : BaseViewModel(application)
AudioPlay.book = book
AudioPlay.bookSource = source
appDb.bookChapterDao.insert(*toc.toTypedArray())
- AudioPlay.upDurChapter(book)
+ AudioPlay.upDurChapter()
}.onFinally {
postEvent(EventBus.SOURCE_CHANGED, book.bookUrl)
}
diff --git a/app/src/main/res/layout/activity_audio_play.xml b/app/src/main/res/layout/activity_audio_play.xml
index 17b4822e2..7aa5d6f69 100644
--- a/app/src/main/res/layout/activity_audio_play.xml
+++ b/app/src/main/res/layout/activity_audio_play.xml
@@ -130,9 +130,9 @@
android:id="@+id/iv_timer"
android:layout_width="46dp"
android:layout_height="46dp"
- android:padding="5dp"
android:background="@drawable/selector_circle_btn_bg"
android:contentDescription="@string/set_timer"
+ android:padding="5dp"
android:src="@drawable/ic_timer_black_24dp"
app:tint="@color/md_white_1000"
tools:ignore="ImageContrastCheck" />
@@ -146,9 +146,9 @@
android:id="@+id/iv_fast_rewind"
android:layout_width="46dp"
android:layout_height="46dp"
- android:padding="5dp"
android:background="@drawable/selector_circle_btn_bg"
android:contentDescription="@string/skip_previous"
+ android:padding="5dp"
android:src="@drawable/ic_fast_rewind"
app:tint="@color/md_white_1000"
tools:ignore="ImageContrastCheck" />
@@ -162,34 +162,56 @@
android:id="@+id/iv_skip_previous"
android:layout_width="46dp"
android:layout_height="46dp"
- android:padding="5dp"
android:background="@drawable/selector_circle_btn_bg"
android:contentDescription="@string/skip_previous"
+ android:padding="5dp"
android:src="@drawable/ic_skip_previous"
app:tint="@color/md_white_1000"
tools:ignore="ImageContrastCheck" />
-
+ android:layout_height="wrap_content">
+
+
+
+
+
+
@@ -203,9 +225,9 @@
android:id="@+id/iv_fast_forward"
android:layout_width="46dp"
android:layout_height="46dp"
- android:padding="5dp"
android:background="@drawable/selector_circle_btn_bg"
android:contentDescription="@string/skip_next"
+ android:padding="5dp"
android:src="@drawable/ic_fast_forward"
app:tint="@color/md_white_1000"
tools:ignore="ImageContrastCheck" />
@@ -219,9 +241,9 @@
android:id="@+id/iv_chapter"
android:layout_width="46dp"
android:layout_height="46dp"
- android:padding="5dp"
android:background="@drawable/selector_circle_btn_bg"
android:contentDescription="@string/chapter_list"
+ android:padding="5dp"
android:src="@drawable/ic_chapter_list"
app:tint="@color/md_white_1000"
tools:ignore="ImageContrastCheck" />
diff --git a/app/src/main/res/layout/dialog_wait.xml b/app/src/main/res/layout/dialog_wait.xml
index bc490a506..9b7e3009e 100644
--- a/app/src/main/res/layout/dialog_wait.xml
+++ b/app/src/main/res/layout/dialog_wait.xml
@@ -7,7 +7,7 @@
android:padding="20dp"
android:orientation="horizontal">
-