mirror of
https://github.com/gedoor/legado.git
synced 2025-08-10 00:52:30 +00:00
Merge branch 'gedoor:master' into master
This commit is contained in:
@@ -244,8 +244,9 @@ dependencies {
|
||||
implementation('com.github.liuyueyi.quick-chinese-transfer:quick-transfer-core:0.2.3')
|
||||
|
||||
//加解密类库
|
||||
implementation('cn.hutool:hutool-crypto:5.7.22')
|
||||
implementation('cn.hutool:hutool-crypto:5.8.0.M1')
|
||||
|
||||
//renderscriptToolkit
|
||||
implementation('com.github.android:renderscript-intrinsics-replacement-toolkit:b6363490c3')
|
||||
|
||||
//代码编辑com.github.AmrDeveloper:CodeView已集成到应用内
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
{"x86":"dad15d54e348efc1e692d72e72e9d52e","armeabi-v7a":"8745ce95fc4bc9063743a890d92bf411","x86_64":"35dd2701803d69aa66d8ac8f836e0123","arm64-v8a":"30165de860a5f288417d536d542401c3","version":"99.0.4844.88"}
|
||||
{"x86":"ff4b324fca11309c5e93a45f65970540","armeabi-v7a":"65e8bb0095d5be314ada2f523718f477","x86_64":"3dac75809f86b8edd1399a642bd26f59","arm64-v8a":"ac515c2995b4fb831419313056b1d3f0","version":"100.0.4896.58"}
|
||||
@@ -11,6 +11,13 @@
|
||||
* 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则或简繁转换出现问题。
|
||||
* 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源!
|
||||
|
||||
**2022/03/29**
|
||||
|
||||
* 更新cronet: 100.0.4896.58
|
||||
* tts朗读不在指定为中文,跟随系统设置,因为有些人需要朗读英文,如果朗读不出来可以去tts设置里看看是不是中文
|
||||
* 修复详情也封面背景模糊失效的bug
|
||||
* 优化音频界面定时设定
|
||||
|
||||
**2022/03/25**
|
||||
|
||||
* 更新cronet: 99.0.4844.88
|
||||
@@ -557,22 +564,6 @@ fun getZipByteArrayContent(url: String, path: String): ByteArray?
|
||||
* 样式属性可以搜索 [FleboxLayout子元素支持的属性介绍](https://www.jianshu.com/p/3c471953e36d)
|
||||
* 样式属性可省略,有默认值
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"title": "xxx",
|
||||
"url": "",
|
||||
"style": {
|
||||
"layout_flexGrow": 0,
|
||||
"layout_flexShrink": 1,
|
||||
"layout_alignSelf": "auto",
|
||||
"layout_flexBasisPercent": -1,
|
||||
"layout_wrapBefore": false
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**2021/07/07**
|
||||
|
||||
1. 默认规则新增类似`jsonPath`的索引写法 by bushixuanqi
|
||||
@@ -770,9 +761,6 @@ chapter.html的关键字有{title}、{content}
|
||||
|
||||
* 增加三星 S Pen 支持 by [dacer](https://github.com/dacer)
|
||||
* 订阅添加阅读下载,可以从多个渠道下载
|
||||
* 修复一些BUG
|
||||
**2020/12/30**
|
||||
|
||||
* 解决文件下载异常,在线语音可正常播放 by [Celeter](https://github.com/Celeter)
|
||||
* 更新默认在线朗读库, 默认id小于0方便下次更新时删除旧数据, 有重复的自己删除
|
||||
* 导入导出书单
|
||||
|
||||
@@ -58,7 +58,7 @@ class App : MultiDexApplication() {
|
||||
2 -> ChineseUtils.s2t("初始化")
|
||||
}
|
||||
//同步阅读记录
|
||||
if (AppWebDav.isOk && AppConfig.syncBookProgress) {
|
||||
if (AppWebDav.syncBookProgress) {
|
||||
AppWebDav.downloadAllBookProgress()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ object EventBus {
|
||||
const val BOOKSHELF_REFRESH = "bookshelfRefresh"
|
||||
const val ALOUD_STATE = "aloud_state"
|
||||
const val TTS_PROGRESS = "ttsStart"
|
||||
const val TTS_DS = "ttsDs"
|
||||
const val AUDIO_DS = "audioDs"
|
||||
const val READ_ALOUD_DS = "readAloudDs"
|
||||
const val BATTERY_CHANGED = "batteryChanged"
|
||||
const val TIME_CHANGED = "timeChanged"
|
||||
const val UP_CONFIG = "upConfig"
|
||||
@@ -17,13 +18,11 @@ object EventBus {
|
||||
const val AUDIO_PROGRESS = "audioProgress"
|
||||
const val AUDIO_SIZE = "audioSize"
|
||||
const val AUDIO_SPEED = "audioSpeed"
|
||||
const val AUDIO_ERROR = "audioError"
|
||||
const val NOTIFY_MAIN = "notifyMain"
|
||||
const val WEB_SERVICE = "webService"
|
||||
const val UP_DOWNLOAD = "upDownload"
|
||||
const val SAVE_CONTENT = "saveContent"
|
||||
const val CHECK_SOURCE = "checkSource"
|
||||
const val CHECK_SOURCE_MESSAGE = "checkSourceMessage"
|
||||
const val CHECK_SOURCE_DONE = "checkSourceDone"
|
||||
const val TIP_COLOR = "tipColor"
|
||||
const val SOURCE_CHANGED = "sourceChanged"
|
||||
|
||||
@@ -282,8 +282,6 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
val importKeepName get() = appCtx.getPrefBoolean(PreferKey.importKeepName)
|
||||
|
||||
val syncBookProgress get() = appCtx.getPrefBoolean(PreferKey.syncBookProgress, true)
|
||||
|
||||
var preDownloadNum
|
||||
get() = appCtx.getPrefInt(PreferKey.preDownloadNum, 10)
|
||||
set(value) {
|
||||
|
||||
@@ -29,6 +29,8 @@ object AppWebDav {
|
||||
private val bookProgressUrl = "${rootWebDavUrl}bookProgress/"
|
||||
private val zipFilePath = "${appCtx.externalFiles.absolutePath}${File.separator}backup.zip"
|
||||
|
||||
val syncBookProgress get() = appCtx.getPrefBoolean(PreferKey.syncBookProgress, true)
|
||||
|
||||
var isOk = false
|
||||
|
||||
init {
|
||||
@@ -167,7 +169,7 @@ object AppWebDav {
|
||||
|
||||
fun uploadBookProgress(book: Book) {
|
||||
if (!isOk) return
|
||||
if (!AppConfig.syncBookProgress) return
|
||||
if (!syncBookProgress) return
|
||||
if (!NetworkUtils.isAvailable()) return
|
||||
Coroutine.async {
|
||||
val bookProgress = BookProgress(book)
|
||||
|
||||
@@ -15,6 +15,7 @@ import io.legado.app.help.coroutine.Coroutine
|
||||
import io.legado.app.service.AudioPlayService
|
||||
import io.legado.app.utils.postEvent
|
||||
import io.legado.app.utils.startService
|
||||
import splitties.init.appCtx
|
||||
|
||||
object AudioPlay {
|
||||
var titleData = MutableLiveData<String>()
|
||||
@@ -138,10 +139,17 @@ object AudioPlay {
|
||||
}
|
||||
}
|
||||
|
||||
fun addTimer(context: Context) {
|
||||
val intent = Intent(context, AudioPlayService::class.java)
|
||||
fun addTimer() {
|
||||
val intent = Intent(appCtx, AudioPlayService::class.java)
|
||||
intent.action = IntentAction.addTimer
|
||||
context.startService(intent)
|
||||
appCtx.startService(intent)
|
||||
}
|
||||
|
||||
fun setTimer(minute: Int) {
|
||||
val intent = Intent(appCtx, AudioPlayService::class.java)
|
||||
intent.action = IntentAction.setTimer
|
||||
intent.putExtra("minute", minute)
|
||||
appCtx.startService(intent)
|
||||
}
|
||||
|
||||
fun saveRead(book: Book) {
|
||||
|
||||
@@ -16,12 +16,17 @@ import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.InputStream
|
||||
import java.util.regex.Matcher
|
||||
import java.util.regex.Pattern
|
||||
import javax.script.SimpleBindings
|
||||
|
||||
object LocalBook {
|
||||
|
||||
private val nameAuthorPatterns = arrayOf(
|
||||
Pattern.compile("(.*?)《([^《》]+)》(.*)"),
|
||||
Pattern.compile("(^)(.+) 作者:(.+)$"),
|
||||
Pattern.compile("(^)(.+) by (.+)$")
|
||||
)
|
||||
|
||||
@Throws(FileNotFoundException::class, SecurityException::class)
|
||||
fun getBookInputStream(book: Book): InputStream {
|
||||
val uri = Uri.parse(book.bookUrl)
|
||||
@@ -117,44 +122,37 @@ object LocalBook {
|
||||
val tempFileName = fileName.substringBeforeLast(".")
|
||||
var name: String
|
||||
var author: String
|
||||
//匹配(知轩藏书常用格式) 《书名》其它信息作者:作者名.txt
|
||||
val m1 = Pattern
|
||||
.compile("(.*?)《([^《》]+)》(.*)")
|
||||
.matcher(tempFileName)
|
||||
//匹配 书名 作者:作者名.txt
|
||||
val m2 = Pattern
|
||||
.compile("(^)(.+) 作者:(.+)$")
|
||||
.matcher(tempFileName)
|
||||
|
||||
(m1.takeIf { m1.find() } ?: m2.takeIf { m2.find() }).run {
|
||||
if (this is Matcher) {
|
||||
//按默认格式将文件名分解成书名、作者名
|
||||
for (pattern in nameAuthorPatterns) {
|
||||
pattern.matcher(tempFileName).takeIf { it.find() }?.run {
|
||||
name = group(2)!!
|
||||
author = BookHelp.formatBookAuthor((group(1) ?: "") + (group(3) ?: ""))
|
||||
} else if (!AppConfig.bookImportFileName.isNullOrBlank()) {
|
||||
try {
|
||||
//在脚本中定义如何分解文件名成书名、作者名
|
||||
val jsonStr = AppConst.SCRIPT_ENGINE.eval(
|
||||
//在用户脚本后添加捕获author、name的代码,只要脚本中author、name有值就会被捕获
|
||||
AppConfig.bookImportFileName + "\nJSON.stringify({author:author,name:name})",
|
||||
//将文件名注入到脚步的src变量中
|
||||
SimpleBindings().also { it["src"] = tempFileName }
|
||||
).toString()
|
||||
val bookMess = GSON.fromJsonObject<HashMap<String, String>>(jsonStr)
|
||||
.getOrThrow() ?: HashMap()
|
||||
name = bookMess["name"] ?: tempFileName
|
||||
author = bookMess["author"]?.takeIf { it.length != tempFileName.length } ?: ""
|
||||
} catch (e: Exception) {
|
||||
name = BookHelp.formatBookName(tempFileName)
|
||||
author = BookHelp.formatBookAuthor(tempFileName.replace(name, ""))
|
||||
.takeIf { it.length != tempFileName.length } ?: ""
|
||||
}
|
||||
} else {
|
||||
val group1 = group(1) ?: ""
|
||||
val group3 = group(3) ?: ""
|
||||
author = BookHelp.formatBookAuthor(group1 + group3)
|
||||
return Pair(name, author)
|
||||
}
|
||||
}
|
||||
if (!AppConfig.bookImportFileName.isNullOrBlank()) {
|
||||
try {
|
||||
//在脚本中定义如何分解文件名成书名、作者名
|
||||
val jsonStr = AppConst.SCRIPT_ENGINE.eval(
|
||||
//在用户脚本后添加捕获author、name的代码,只要脚本中author、name有值就会被捕获
|
||||
AppConfig.bookImportFileName + "\nJSON.stringify({author:author,name:name})",
|
||||
//将文件名注入到脚步的src变量中
|
||||
SimpleBindings().also { it["src"] = tempFileName }
|
||||
).toString()
|
||||
val bookMess = GSON.fromJsonObject<HashMap<String, String>>(jsonStr)
|
||||
.getOrThrow() ?: HashMap()
|
||||
name = bookMess["name"] ?: tempFileName
|
||||
author = bookMess["author"]?.takeIf { it.length != tempFileName.length } ?: ""
|
||||
} catch (e: Exception) {
|
||||
name = BookHelp.formatBookName(tempFileName)
|
||||
author = BookHelp.formatBookAuthor(tempFileName.replace(name, ""))
|
||||
.takeIf { it.length != tempFileName.length } ?: ""
|
||||
}
|
||||
|
||||
} else {
|
||||
name = BookHelp.formatBookName(tempFileName)
|
||||
author = BookHelp.formatBookAuthor(tempFileName.replace(name, ""))
|
||||
.takeIf { it.length != tempFileName.length } ?: ""
|
||||
}
|
||||
return Pair(name, author)
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ class AudioPlayService : BaseService(),
|
||||
companion object {
|
||||
var isRun = false
|
||||
private set
|
||||
var pause = false
|
||||
var pause = true
|
||||
private set
|
||||
var timeMinute: Int = 0
|
||||
private set
|
||||
@@ -59,11 +59,10 @@ class AudioPlayService : BaseService(),
|
||||
private val exoPlayer: ExoPlayer by lazy {
|
||||
ExoPlayer.Builder(this).build()
|
||||
}
|
||||
private var title: String = ""
|
||||
private var subtitle: String = ""
|
||||
private var mediaSessionCompat: MediaSessionCompat? = null
|
||||
private var broadcastReceiver: BroadcastReceiver? = null
|
||||
private var position = 0
|
||||
private var audioFocusLossTransient = false
|
||||
private var position = AudioPlay.book?.durChapterPos ?: 0
|
||||
private var dsJob: Job? = null
|
||||
private var upPlayProgressJob: Job? = null
|
||||
private var playSpeed: Float = 1f
|
||||
@@ -76,18 +75,16 @@ class AudioPlayService : BaseService(),
|
||||
initMediaSession()
|
||||
initBroadcastReceiver()
|
||||
upMediaSessionPlaybackState(PlaybackStateCompat.STATE_PLAYING)
|
||||
doDs()
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
intent?.action?.let { action ->
|
||||
when (action) {
|
||||
IntentAction.play -> {
|
||||
AudioPlay.book?.let {
|
||||
title = it.name
|
||||
subtitle = AudioPlay.durChapter?.title ?: ""
|
||||
position = it.durChapterPos
|
||||
loadContent()
|
||||
}
|
||||
pause = false
|
||||
position = AudioPlay.book?.durChapterPos ?: 0
|
||||
loadContent()
|
||||
}
|
||||
IntentAction.pause -> pause(true)
|
||||
IntentAction.resume -> resume()
|
||||
@@ -171,6 +168,10 @@ class AudioPlayService : BaseService(),
|
||||
private fun resume() {
|
||||
try {
|
||||
pause = false
|
||||
if (url.isEmpty()) {
|
||||
loadContent()
|
||||
return
|
||||
}
|
||||
if (!exoPlayer.isPlaying) {
|
||||
exoPlayer.play()
|
||||
}
|
||||
@@ -260,11 +261,11 @@ class AudioPlayService : BaseService(),
|
||||
}
|
||||
|
||||
private fun addTimer() {
|
||||
if (timeMinute == 60) {
|
||||
if (timeMinute == 180) {
|
||||
timeMinute = 0
|
||||
} else {
|
||||
timeMinute += 10
|
||||
if (timeMinute > 60) timeMinute = 60
|
||||
if (timeMinute > 180) timeMinute = 180
|
||||
}
|
||||
doDs()
|
||||
}
|
||||
@@ -273,7 +274,7 @@ class AudioPlayService : BaseService(),
|
||||
* 定时
|
||||
*/
|
||||
private fun doDs() {
|
||||
postEvent(EventBus.TTS_DS, timeMinute)
|
||||
postEvent(EventBus.AUDIO_DS, timeMinute)
|
||||
upNotification()
|
||||
dsJob?.cancel()
|
||||
dsJob = launch {
|
||||
@@ -287,7 +288,7 @@ class AudioPlayService : BaseService(),
|
||||
AudioPlay.stop(this@AudioPlayService)
|
||||
}
|
||||
}
|
||||
postEvent(EventBus.TTS_DS, timeMinute)
|
||||
postEvent(EventBus.AUDIO_DS, timeMinute)
|
||||
upNotification()
|
||||
}
|
||||
}
|
||||
@@ -360,7 +361,6 @@ class AudioPlayService : BaseService(),
|
||||
*/
|
||||
private fun contentLoadFinish(chapter: BookChapter, content: String) {
|
||||
if (chapter.index == AudioPlay.book?.durChapterIndex) {
|
||||
subtitle = chapter.title
|
||||
url = content
|
||||
play()
|
||||
}
|
||||
@@ -426,13 +426,18 @@ class AudioPlayService : BaseService(),
|
||||
when (focusChange) {
|
||||
AudioManager.AUDIOFOCUS_GAIN -> {
|
||||
// 重新获得焦点, 可做恢复播放,恢复后台音量的操作
|
||||
audioFocusLossTransient = false
|
||||
if (!pause) resume()
|
||||
}
|
||||
AudioManager.AUDIOFOCUS_LOSS -> {
|
||||
// 永久丢失焦点除非重新主动获取,这种情况是被其他播放器抢去了焦点, 为避免与其他播放器混音,可将音乐暂停
|
||||
if (audioFocusLossTransient) {
|
||||
pause(true)
|
||||
}
|
||||
}
|
||||
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
|
||||
// 暂时丢失焦点,这种情况是被其他应用申请了短暂的焦点,可压低后台音量
|
||||
audioFocusLossTransient = true
|
||||
if (!pause) pause(false)
|
||||
}
|
||||
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
|
||||
@@ -454,9 +459,9 @@ class AudioPlayService : BaseService(),
|
||||
)
|
||||
else -> getString(R.string.audio_play_t)
|
||||
}
|
||||
nTitle += ": $title"
|
||||
var nSubtitle = subtitle
|
||||
if (subtitle.isEmpty()) {
|
||||
nTitle += ": ${AudioPlay.book?.name}"
|
||||
var nSubtitle = AudioPlay.durChapter?.title
|
||||
if (nSubtitle.isNullOrEmpty()) {
|
||||
nSubtitle = getString(R.string.audio_play_s)
|
||||
}
|
||||
val builder = NotificationCompat
|
||||
|
||||
@@ -206,7 +206,7 @@ abstract class BaseReadAloudService : BaseService(),
|
||||
*/
|
||||
@Synchronized
|
||||
private fun doDs() {
|
||||
postEvent(EventBus.TTS_DS, timeMinute)
|
||||
postEvent(EventBus.READ_ALOUD_DS, timeMinute)
|
||||
upNotification()
|
||||
dsJob?.cancel()
|
||||
dsJob = launch {
|
||||
@@ -220,7 +220,7 @@ abstract class BaseReadAloudService : BaseService(),
|
||||
ReadAloud.stop(this@BaseReadAloudService)
|
||||
}
|
||||
}
|
||||
postEvent(EventBus.TTS_DS, timeMinute)
|
||||
postEvent(EventBus.READ_ALOUD_DS, timeMinute)
|
||||
upNotification()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.legado.app.ui.book.audio
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.icu.text.SimpleDateFormat
|
||||
import android.os.Build
|
||||
@@ -8,6 +9,7 @@ import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.widget.SeekBar
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import io.legado.app.R
|
||||
import io.legado.app.base.VMBaseActivity
|
||||
import io.legado.app.constant.EventBus
|
||||
@@ -25,6 +27,7 @@ import io.legado.app.ui.book.changesource.ChangeBookSourceDialog
|
||||
import io.legado.app.ui.book.source.edit.BookSourceEditActivity
|
||||
import io.legado.app.ui.book.toc.TocActivityResult
|
||||
import io.legado.app.ui.login.SourceLoginActivity
|
||||
import io.legado.app.ui.theme.AppTheme
|
||||
import io.legado.app.ui.widget.seekbar.SeekBarChangeListener
|
||||
import io.legado.app.utils.*
|
||||
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
||||
@@ -42,6 +45,7 @@ class AudioPlayActivity :
|
||||
override val viewModel by viewModels<AudioPlayViewModel>()
|
||||
private var menu: Menu? = null
|
||||
private var adjustProgress = false
|
||||
private val timerViewState = mutableStateOf(false)
|
||||
private val progressTimeFormat by lazy {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
SimpleDateFormat("mm:ss", Locale.getDefault())
|
||||
@@ -153,7 +157,19 @@ class AudioPlayActivity :
|
||||
AudioPlay.adjustSpeed(this@AudioPlayActivity, -0.1f)
|
||||
}
|
||||
binding.ivTimer.setOnClickListener {
|
||||
AudioPlay.addTimer(this@AudioPlayActivity)
|
||||
if (AudioPlayService.isRun) {
|
||||
timerViewState.value = true
|
||||
} else {
|
||||
toastOnUi(R.string.cannot_timed_non_playback)
|
||||
}
|
||||
}
|
||||
binding.composeView.setContent {
|
||||
AppTheme {
|
||||
TimerDialog(
|
||||
state = timerViewState,
|
||||
binding.ivTimer
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,6 +226,7 @@ class AudioPlayActivity :
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun observeLiveBus() {
|
||||
observeEvent<Boolean>(EventBus.MEDIA_BUTTON) {
|
||||
if (it) {
|
||||
@@ -239,6 +256,10 @@ class AudioPlayActivity :
|
||||
binding.tvSpeed.text = String.format("%.1fX", it)
|
||||
binding.tvSpeed.visible()
|
||||
}
|
||||
observeEventSticky<Int>(EventBus.AUDIO_DS) {
|
||||
binding.tvTimer.text = "${it}m"
|
||||
binding.tvTimer.visible(it > 0)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
40
app/src/main/java/io/legado/app/ui/book/audio/ComposeView.kt
Normal file
40
app/src/main/java/io/legado/app/ui/book/audio/ComposeView.kt
Normal file
@@ -0,0 +1,40 @@
|
||||
package io.legado.app.ui.book.audio
|
||||
|
||||
import android.view.View
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.Slider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import io.legado.app.model.AudioPlay
|
||||
import io.legado.app.service.AudioPlayService
|
||||
|
||||
|
||||
@Composable
|
||||
fun TimerDialog(state: MutableState<Boolean>, parent: View) {
|
||||
val intOffset = IntArray(2)
|
||||
parent.getLocationInWindow(intOffset)
|
||||
if (state.value) {
|
||||
val timeMinute = remember {
|
||||
mutableStateOf(AudioPlayService.timeMinute)
|
||||
}
|
||||
Dialog(onDismissRequest = { state.value = false }) {
|
||||
Card(Modifier.fillMaxWidth()) {
|
||||
Slider(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
value = timeMinute.value.toFloat(), onValueChange = {
|
||||
timeMinute.value = it.toInt()
|
||||
AudioPlay.setTimer(it.toInt())
|
||||
},
|
||||
valueRange = 0f..180f
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,10 +58,10 @@ class ImportBookViewModel(application: Application) : BaseViewModel(application)
|
||||
}.map { docList ->
|
||||
when (sort) {
|
||||
2 -> docList.sortedWith(
|
||||
compareBy({ !it.isDir }, { it.lastModified }, { it.name })
|
||||
compareBy({ !it.isDir }, { -it.lastModified }, { it.name })
|
||||
)
|
||||
1 -> docList.sortedWith(
|
||||
compareBy({ !it.isDir }, { it.size }, { it.name })
|
||||
compareBy({ !it.isDir }, { -it.size }, { it.name })
|
||||
)
|
||||
else -> docList.sortedWith(
|
||||
compareBy({ !it.isDir }, { it.name })
|
||||
|
||||
@@ -168,20 +168,24 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
|
||||
book: Book,
|
||||
alertSync: ((progress: BookProgress) -> Unit)? = null
|
||||
) {
|
||||
if (AppConfig.syncBookProgress)
|
||||
execute {
|
||||
execute {
|
||||
if (AppWebDav.syncBookProgress) {
|
||||
AppWebDav.getBookProgress(book)
|
||||
}.onSuccess {
|
||||
it?.let { progress ->
|
||||
if (progress.durChapterIndex < book.durChapterIndex ||
|
||||
(progress.durChapterIndex == book.durChapterIndex && progress.durChapterPos < book.durChapterPos)
|
||||
) {
|
||||
alertSync?.invoke(progress)
|
||||
} else {
|
||||
ReadBook.setProgress(progress)
|
||||
}
|
||||
}
|
||||
?: throw NoStackTraceException("没有进度")
|
||||
} else {
|
||||
throw NoStackTraceException("进度同步未启用")
|
||||
}
|
||||
}.onSuccess { progress ->
|
||||
if (progress.durChapterIndex < book.durChapterIndex ||
|
||||
(progress.durChapterIndex == book.durChapterIndex
|
||||
&& progress.durChapterPos < book.durChapterPos)
|
||||
) {
|
||||
alertSync?.invoke(progress)
|
||||
} else {
|
||||
ReadBook.setProgress(progress)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -184,7 +184,7 @@ class ReadAloudDialog : BaseDialogFragment(R.layout.dialog_read_aloud) {
|
||||
|
||||
override fun observeLiveBus() {
|
||||
observeEvent<Int>(EventBus.ALOUD_STATE) { upPlayState() }
|
||||
observeEvent<Int>(EventBus.TTS_DS) { binding.seekTimer.progress = it }
|
||||
observeEvent<Int>(EventBus.READ_ALOUD_DS) { binding.seekTimer.progress = it }
|
||||
}
|
||||
|
||||
interface CallBack {
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/background"
|
||||
android:orientation="vertical"
|
||||
tools:context=".ui.book.audio.AudioPlayActivity">
|
||||
|
||||
<ImageView
|
||||
@@ -28,15 +28,30 @@
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:themeMode="dark" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_timer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:background="@drawable/shape_fillet_btn_press"
|
||||
android:paddingLeft="6dp"
|
||||
android:paddingRight="6dp"
|
||||
android:textColor="@color/md_white_1000"
|
||||
android:visibility="invisible"
|
||||
app:drawableLeftCompat="@drawable/ic_timer_black_24dp"
|
||||
app:drawableTint="@color/md_white_1000"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/title_bar" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_speed"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:background="@drawable/shape_fillet_btn_press"
|
||||
android:paddingLeft="6dp"
|
||||
android:paddingRight="6dp"
|
||||
android:textColor="@color/md_white_1000"
|
||||
android:paddingStart="3dp"
|
||||
android:paddingEnd="3dp"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/title_bar" />
|
||||
@@ -58,12 +73,12 @@
|
||||
android:id="@+id/tv_sub_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="6dp"
|
||||
android:textColor="@color/md_white_1000"
|
||||
android:gravity="center"
|
||||
app:layout_constraintBottom_toTopOf="@+id/ll_player_progress" />
|
||||
|
||||
<LinearLayout
|
||||
@@ -106,10 +121,10 @@
|
||||
android:id="@+id/ll_play_menu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="24dp"
|
||||
android:paddingRight="24dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="24dp"
|
||||
android:paddingRight="24dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<ImageView
|
||||
@@ -208,4 +223,10 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.compose.ui.platform.ComposeView
|
||||
android:id="@+id/compose_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -964,5 +964,6 @@
|
||||
<string name="scroll_to_dur_source">定位到当前书源</string>
|
||||
<string name="sys_tts_config">系统tts设置</string>
|
||||
<string name="sys_tts_config_summary">打开系统tts设置界面</string>
|
||||
<string name="cannot_timed_non_playback">非播放状态无法定时</string>
|
||||
<!-- string end -->
|
||||
</resources>
|
||||
|
||||
@@ -967,5 +967,6 @@
|
||||
<string name="scroll_to_dur_source">定位到当前书源</string>
|
||||
<string name="sys_tts_config">系统tts设置</string>
|
||||
<string name="sys_tts_config_summary">打开系统tts设置界面</string>
|
||||
<string name="cannot_timed_non_playback">非播放状态无法定时</string>
|
||||
<!-- string end -->
|
||||
</resources>
|
||||
|
||||
@@ -967,5 +967,6 @@
|
||||
<string name="scroll_to_dur_source">定位到当前书源</string>
|
||||
<string name="sys_tts_config">系统tts设置</string>
|
||||
<string name="sys_tts_config_summary">打开系统tts设置界面</string>
|
||||
<string name="cannot_timed_non_playback">非播放状态无法定时</string>
|
||||
<!-- string end -->
|
||||
</resources>
|
||||
|
||||
@@ -964,5 +964,6 @@
|
||||
<string name="scroll_to_dur_source">定位到当前书源</string>
|
||||
<string name="sys_tts_config">系统tts设置</string>
|
||||
<string name="sys_tts_config_summary">打开系统tts设置界面</string>
|
||||
<string name="cannot_timed_non_playback">非播放状态无法定时</string>
|
||||
<!-- string end -->
|
||||
</resources>
|
||||
|
||||
@@ -966,5 +966,6 @@
|
||||
<string name="scroll_to_dur_source">定位到目前書源</string>
|
||||
<string name="sys_tts_config">系统tts设置</string>
|
||||
<string name="sys_tts_config_summary">打开系统tts设置界面</string>
|
||||
<string name="cannot_timed_non_playback">非播放状态无法定时</string>
|
||||
<!-- string end -->
|
||||
</resources>
|
||||
|
||||
@@ -966,5 +966,6 @@
|
||||
<string name="scroll_to_dur_source">定位到当前书源</string>
|
||||
<string name="sys_tts_config">系统tts设置</string>
|
||||
<string name="sys_tts_config_summary">打开系统tts设置界面</string>
|
||||
<string name="cannot_timed_non_playback">非播放状态无法定时</string>
|
||||
<!-- string end -->
|
||||
</resources>
|
||||
|
||||
@@ -967,5 +967,6 @@
|
||||
<string name="scroll_to_dur_source">定位到当前书源</string>
|
||||
<string name="sys_tts_config">系统tts设置</string>
|
||||
<string name="sys_tts_config_summary">打开系统tts设置界面</string>
|
||||
<string name="cannot_timed_non_playback">非播放状态无法定时</string>
|
||||
<!-- string end -->
|
||||
</resources>
|
||||
|
||||
@@ -23,4 +23,4 @@ kotlin.code.style=official
|
||||
android.enableResourceOptimizations=true
|
||||
|
||||
#https://chromiumdash.appspot.com/releases?platform=Android
|
||||
CronetVersion=99.0.4844.88
|
||||
CronetVersion=100.0.4896.58
|
||||
|
||||
Reference in New Issue
Block a user