优化
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
@@ -368,12 +369,12 @@
|
||||
<!-- pdf -->
|
||||
<data android:mimeType="application/pdf" />
|
||||
<!-- mobi -->
|
||||
<data android:mimeType="application/mobi"/>
|
||||
<data android:mimeType="application/x-mobipocket-ebook"/>
|
||||
<data android:mimeType="application/mobi" />
|
||||
<data android:mimeType="application/x-mobipocket-ebook" />
|
||||
<!-- azw/azw3 -->
|
||||
<data android:mimeType="application/azw"/>
|
||||
<data android:mimeType="application/azw3"/>
|
||||
<data android:mimeType="application/x-mobi8-ebook"/>
|
||||
<data android:mimeType="application/azw" />
|
||||
<data android:mimeType="application/azw3" />
|
||||
<data android:mimeType="application/x-mobi8-ebook" />
|
||||
<data android:mimeType="application/octet-stream" />
|
||||
</intent-filter>
|
||||
<!-- Works when an app doesn't know the media type, e.g. Dropbox -->
|
||||
|
||||
@@ -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<String>()
|
||||
var coverData = MutableLiveData<String>()
|
||||
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<Int>()
|
||||
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<AudioPlayService> {
|
||||
action = IntentAction.play
|
||||
}
|
||||
}
|
||||
fun play() {
|
||||
context.startService<AudioPlayService> {
|
||||
action = IntentAction.play
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从头播放新章节
|
||||
*/
|
||||
fun playNew(context: Context) {
|
||||
book?.let {
|
||||
if (durChapter == null) {
|
||||
upDurChapter(it)
|
||||
}
|
||||
durChapter?.let {
|
||||
context.startService<AudioPlayService> {
|
||||
action = IntentAction.playNew
|
||||
}
|
||||
}
|
||||
private fun playNew() {
|
||||
context.startService<AudioPlayService> {
|
||||
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<AudioPlayService> {
|
||||
action = IntentAction.stop
|
||||
@@ -116,7 +209,7 @@ object AudioPlay {
|
||||
}
|
||||
}
|
||||
|
||||
fun adjustSpeed(context: Context, adjust: Float) {
|
||||
fun adjustSpeed(adjust: Float) {
|
||||
if (AudioPlayService.isRun) {
|
||||
context.startService<AudioPlayService> {
|
||||
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<AudioPlayService> {
|
||||
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)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 | ||||