在线朗读改为自定义

pull/332/head
gedoor 4 years ago
parent cf08b06d4e
commit aaf262fb9d
  1. 62
      app/src/main/assets/httpTTS.json
  2. 3
      app/src/main/java/io/legado/app/constant/PreferKey.kt
  3. 14
      app/src/main/java/io/legado/app/data/AppDatabase.kt
  4. 28
      app/src/main/java/io/legado/app/data/dao/HttpTTSDao.kt
  5. 12
      app/src/main/java/io/legado/app/data/entities/HttpTTS.kt
  6. 3
      app/src/main/java/io/legado/app/help/AppConfig.kt
  7. 4
      app/src/main/java/io/legado/app/help/http/HttpHelper.kt
  8. 12
      app/src/main/java/io/legado/app/help/http/api/HttpGetApi.kt
  9. 8
      app/src/main/java/io/legado/app/help/http/api/HttpPostApi.kt
  10. 2
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt
  11. 76
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt
  12. 48
      app/src/main/java/io/legado/app/service/HttpReadAloudService.kt
  13. 16
      app/src/main/java/io/legado/app/service/help/ReadAloud.kt
  14. 6
      app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverDialog.kt
  15. 9
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt
  16. 7
      app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt
  17. 7
      app/src/main/java/io/legado/app/ui/book/group/GroupSelectDialog.kt
  18. 35
      app/src/main/java/io/legado/app/ui/book/read/config/ReadAloudConfigDialog.kt
  19. 161
      app/src/main/java/io/legado/app/ui/book/read/config/SpeakEngineDialog.kt
  20. 7
      app/src/main/java/io/legado/app/ui/book/read/config/TocRegexDialog.kt
  21. 13
      app/src/main/java/io/legado/app/ui/book/source/manage/GroupManageDialog.kt
  22. 7
      app/src/main/java/io/legado/app/ui/replacerule/GroupManageDialog.kt
  23. 3
      app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt
  24. 5
      app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt
  25. 4
      app/src/main/java/io/legado/app/ui/widget/image/photo/RotateGestureDetector.kt
  26. 3
      app/src/main/java/io/legado/app/utils/ContextExtensions.kt
  27. 3
      app/src/main/res/layout/dialog_change_cover.xml
  28. 3
      app/src/main/res/layout/dialog_change_source.xml
  29. 36
      app/src/main/res/layout/dialog_http_tts_edit.xml
  30. 16
      app/src/main/res/layout/dialog_recycler_view.xml
  31. 3
      app/src/main/res/layout/dialog_replace_edit.xml
  32. 37
      app/src/main/res/layout/item_http_tts.xml
  33. 11
      app/src/main/res/menu/speak_engine.xml
  34. 15
      app/src/main/res/values-zh-rHK/arrays.xml
  35. 3
      app/src/main/res/values-zh-rHK/strings.xml
  36. 15
      app/src/main/res/values-zh-rTW/arrays.xml
  37. 3
      app/src/main/res/values-zh-rTW/strings.xml
  38. 15
      app/src/main/res/values-zh/arrays.xml
  39. 3
      app/src/main/res/values-zh/strings.xml
  40. 15
      app/src/main/res/values/array_values.xml
  41. 15
      app/src/main/res/values/arrays.xml
  42. 3
      app/src/main/res/values/strings.xml
  43. 15
      app/src/main/res/xml/pref_config_aloud.xml

@ -0,0 +1,62 @@
[
{
"id": 1,
"name": "度小美",
"url": ""
},
{
"id": 2,
"name": "度小宇",
"url": ""
},
{
"id": 3,
"name": "度逍遙",
"url": ""
},
{
"id": 4,
"name": "度丫丫",
"url": ""
},
{
"id": 5,
"name": "度小嬌",
"url": ""
},
{
"id": 6,
"name": "度米朵",
"url": ""
},
{
"id": 7,
"name": "度博文",
"url": ""
},
{
"id": 8,
"name": "度小童",
"url": ""
},
{
"id": 9,
"name": "度小萌",
"url": ""
},
{
"id": 10,
"name": "百度騷男",
"url": ""
},
{
"id": 11,
"name": "百度評書",
"url": ""
},
{
"id": 12,
"name": "百度主持",
"url": ""
}
]

@ -9,10 +9,9 @@ object PreferKey {
const val clickAllNext = "clickAllNext"
const val hideNavigationBar = "hideNavigationBar"
const val precisionSearch = "precisionSearch"
const val readAloudOnLine = "readAloudOnLine"
const val speakEngine = "speakEngine"
const val readAloudByPage = "readAloudByPage"
const val ttsSpeechRate = "ttsSpeechRate"
const val ttsSpeechPer = "ttsSpeechPer"
const val prevKey = "prevKeyCode"
const val nextKey = "nextKeyCode"
const val showRss = "showRss"

@ -14,8 +14,8 @@ import io.legado.app.data.entities.*
entities = [Book::class, BookGroup::class, BookSource::class, BookChapter::class,
ReplaceRule::class, SearchBook::class, SearchKeyword::class, Cookie::class,
RssSource::class, Bookmark::class, RssArticle::class, RssReadRecord::class,
RssStar::class, TxtTocRule::class, ReadRecord::class],
version = 17,
RssStar::class, TxtTocRule::class, ReadRecord::class, HttpTTS::class],
version = 18,
exportSchema = true
)
abstract class AppDatabase : RoomDatabase() {
@ -33,7 +33,8 @@ abstract class AppDatabase : RoomDatabase() {
migration_12_13,
migration_13_14,
migration_14_15,
migration_15_17
migration_15_17,
migration_17_18
)
.allowMainThreadQueries()
.build()
@ -94,6 +95,12 @@ abstract class AppDatabase : RoomDatabase() {
database.execSQL("CREATE TABLE IF NOT EXISTS `readRecord` (`bookName` TEXT NOT NULL, `readTime` INTEGER NOT NULL, PRIMARY KEY(`bookName`))")
}
}
private val migration_17_18 = object : Migration(17, 18) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("CREATE TABLE IF NOT EXISTS `httpTTS` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, PRIMARY KEY(`id`))")
}
}
}
abstract fun bookDao(): BookDao
@ -110,4 +117,5 @@ abstract class AppDatabase : RoomDatabase() {
abstract fun cookieDao(): CookieDao
abstract fun txtTocRule(): TxtTocRuleDao
abstract fun readRecordDao(): ReadRecordDao
abstract fun httpTTSDao(): HttpTTSDao
}

@ -0,0 +1,28 @@
package io.legado.app.data.dao
import androidx.lifecycle.LiveData
import androidx.room.*
import io.legado.app.data.entities.HttpTTS
@Dao
interface HttpTTSDao {
@get:Query("select * from httpTTS order by name")
val all: List<HttpTTS>
@Query("select * from httpTTS order by name")
fun observeAll(): LiveData<List<HttpTTS>>
@Query("select * from httpTTS where id = :id")
fun get(id: Long): HttpTTS?
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(vararg httpTTS: HttpTTS)
@Delete
fun delete(vararg httpTTS: HttpTTS)
@Update
fun update(vararg httpTTS: HttpTTS)
}

@ -0,0 +1,12 @@
package io.legado.app.data.entities
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "httpTTS")
data class HttpTTS(
@PrimaryKey
val id: Long = System.currentTimeMillis(),
var name: String = "",
var url: String = ""
)

@ -81,9 +81,6 @@ object AppConfig {
App.INSTANCE.putPrefInt(PreferKey.ttsSpeechRate, value)
}
val ttsSpeechPer: String
get() = App.INSTANCE.getPrefString(PreferKey.ttsSpeechPer) ?: "0"
val clickAllNext: Boolean get() = App.INSTANCE.getPrefBoolean(PreferKey.clickAllNext, false)
var chineseConverterType: Int

@ -83,6 +83,10 @@ object HttpHelper {
return getRetrofit(baseUrl, encode).create(T::class.java)
}
inline fun <reified T> getBytesApiService(baseUrl: String): T {
return getByteRetrofit(baseUrl).create(T::class.java)
}
fun getRetrofit(baseUrl: String, encode: String? = null): Retrofit {
return Retrofit.Builder().baseUrl(baseUrl)
//增加返回值为字符串的支持(以实体类返回)

@ -32,6 +32,12 @@ interface HttpGetApi {
@HeaderMap headers: Map<String, String>
): Call<String>
@GET
fun getByte(
@Url url: String,
@HeaderMap headers: Map<String, String>
): Call<ByteArray>
@GET
fun getMap(
@Url url: String,
@ -46,6 +52,12 @@ interface HttpGetApi {
@HeaderMap headers: Map<String, String>
): Call<ByteArray>
@GET
suspend fun getByteAsync(
@Url url: String,
@HeaderMap headers: Map<String, String>
): Response<ByteArray>
@GET
suspend fun getMapByteAsync(
@Url url: String,

@ -49,4 +49,12 @@ interface HttpPostApi {
@FieldMap(encoded = true) fieldMap: Map<String, String>,
@HeaderMap headers: Map<String, String>
): Response<ByteArray>
@POST
suspend fun postBodyByteAsync(
@Url url: String,
@Body body: RequestBody,
@HeaderMap headers: Map<String, String>
): Response<ByteArray>
}

@ -609,7 +609,7 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
*/
override fun ajax(urlStr: String): String? {
return try {
val analyzeUrl = AnalyzeUrl(urlStr, null, null, null, baseUrl, book)
val analyzeUrl = AnalyzeUrl(urlStr, baseUrl = baseUrl, book = book)
val call = analyzeUrl.getResponse(urlStr)
val response = call.execute()
response.body()

@ -34,6 +34,8 @@ class AnalyzeUrl(
var ruleUrl: String,
key: String? = null,
page: Int? = null,
speakText: String? = null,
speakSpeed: Int? = null,
headerMapF: Map<String, String>? = null,
baseUrl: String? = null,
book: BaseBook? = null,
@ -63,13 +65,19 @@ class AnalyzeUrl(
}
headerMapF?.let { headerMap.putAll(it) }
//替换参数
analyzeJs(key, page, book)
replaceKeyPageJs(key, page, book)
analyzeJs(key, page, speakText, speakSpeed, book)
replaceKeyPageJs(key, page, speakText, speakSpeed, book)
//处理URL
initUrl()
}
private fun analyzeJs(key: String?, page: Int?, book: BaseBook?) {
private fun analyzeJs(
key: String?,
page: Int?,
speakText: String?,
speakSpeed: Int?,
book: BaseBook?
) {
val ruleList = arrayListOf<String>()
var start = 0
var tmp: String
@ -96,11 +104,13 @@ class AnalyzeUrl(
when {
ruleStr.startsWith("<js>") -> {
ruleStr = ruleStr.substring(4, ruleStr.lastIndexOf("<"))
ruleUrl = evalJS(ruleStr, ruleUrl, page, key, book) as String
ruleUrl =
evalJS(ruleStr, ruleUrl, page, key, speakText, speakSpeed, book) as String
}
ruleStr.startsWith("@js", true) -> {
ruleStr = ruleStr.substring(4)
ruleUrl = evalJS(ruleStr, ruleUrl, page, key, book) as String
ruleUrl =
evalJS(ruleStr, ruleUrl, page, key, speakText, speakSpeed, book) as String
}
else -> ruleUrl = ruleStr.replace("@result", ruleUrl)
}
@ -110,7 +120,13 @@ class AnalyzeUrl(
/**
* 替换关键字,页数,JS
*/
private fun replaceKeyPageJs(key: String?, page: Int?, book: BaseBook?) {
private fun replaceKeyPageJs(
key: String?,
page: Int?,
speakText: String?,
speakSpeed: Int?,
book: BaseBook?
) {
//page
page?.let {
val matcher = pagePattern.matcher(ruleUrl)
@ -132,6 +148,8 @@ class AnalyzeUrl(
simpleBindings["baseUrl"] = baseUrl
simpleBindings["page"] = page
simpleBindings["key"] = key
simpleBindings["speakText"] = speakText
simpleBindings["speakSpeed"] = speakSpeed
simpleBindings["book"] = book
val expMatcher = EXP_PATTERN.matcher(ruleUrl)
while (expMatcher.find()) {
@ -242,12 +260,16 @@ class AnalyzeUrl(
result: Any?,
page: Int?,
key: String?,
speakText: String?,
speakSpeed: Int?,
book: BaseBook?
): Any {
val bindings = SimpleBindings()
bindings["java"] = this
bindings["page"] = page
bindings["key"] = key
bindings["speakText"] = speakText
bindings["speakSpeed"] = speakSpeed
bindings["book"] = book
bindings["result"] = result
bindings["baseUrl"] = baseUrl
@ -332,10 +354,10 @@ class AnalyzeUrl(
val cookie = CookieStore.getCookie(tag)
NetworkUtils.getBaseUrl(url)?.let {
val regex: Any
if(it.lastIndexOf(".") != it.indexOf(".")) {
regex = it.substring(it.indexOf(".")+1).toRegex()
regex = if (it.lastIndexOf(".") != it.indexOf(".")) {
it.substring(it.indexOf(".") + 1).toRegex()
} else {
regex = it.substring(it.lastIndexOf("/")+1).toRegex()
it.substring(it.lastIndexOf("/") + 1).toRegex()
}
if (regex.containsMatchIn(tag)) {
if (cookie.isNotEmpty()) {
@ -344,11 +366,41 @@ class AnalyzeUrl(
}
}
headerMap[UA_NAME] = headerMap[UA_NAME] ?: userAgent
if(fieldMap.isEmpty()) {
return HttpHelper.getBytes(url, mapOf(), headerMap)
return if (fieldMap.isEmpty()) {
HttpHelper.getBytes(url, mapOf(), headerMap)
} else {
HttpHelper.getBytes(url, fieldMap, headerMap)
}
}
@Throws(Exception::class)
suspend fun getResponseBytes(tag: String? = null): ByteArray? {
if (tag != null) {
val cookie = CookieStore.getCookie(tag)
if (cookie.isNotEmpty()) {
headerMap["Cookie"] = cookie
}
}
val response = when {
method == RequestMethod.POST -> {
if (fieldMap.isNotEmpty()) {
HttpHelper
.getBytesApiService<HttpPostApi>(baseUrl)
.postMapByteAsync(url, fieldMap, headerMap)
} else {
return HttpHelper.getBytes(url, fieldMap, headerMap)
HttpHelper
.getBytesApiService<HttpPostApi>(baseUrl)
.postBodyByteAsync(url, requestBody!!, headerMap)
}
}
fieldMap.isEmpty() -> HttpHelper
.getBytesApiService<HttpGetApi>(baseUrl)
.getByteAsync(url, headerMap)
else -> HttpHelper
.getBytesApiService<HttpGetApi>(baseUrl)
.getMapByteAsync(url, fieldMap, headerMap)
}
return response.body()
}
data class UrlOption(

@ -7,8 +7,8 @@ import io.legado.app.constant.EventBus
import io.legado.app.help.AppConfig
import io.legado.app.help.IntentHelp
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.help.http.HttpHelper
import io.legado.app.help.http.api.HttpPostApi
import io.legado.app.model.analyzeRule.AnalyzeUrl
import io.legado.app.service.help.ReadAloud
import io.legado.app.service.help.ReadBook
import io.legado.app.utils.FileUtils
import io.legado.app.utils.LogUtils
@ -17,7 +17,6 @@ import kotlinx.coroutines.isActive
import java.io.File
import java.io.FileDescriptor
import java.io.FileInputStream
import java.net.URLEncoder
class HttpReadAloudService : BaseReadAloudService(),
MediaPlayer.OnPreparedListener,
@ -31,7 +30,7 @@ class HttpReadAloudService : BaseReadAloudService(),
override fun onCreate() {
super.onCreate()
ttsFolder = cacheDir.absolutePath + File.separator + "bdTts"
ttsFolder = externalCacheDir!!.absolutePath + File.separator + "httpTTS"
mediaPlayer.setOnErrorListener(this)
mediaPlayer.setOnPreparedListener(this)
mediaPlayer.setOnCompletionListener(this)
@ -72,13 +71,13 @@ class HttpReadAloudService : BaseReadAloudService(),
FileUtils.deleteFile(ttsFolder)
for (index in 0 until contentList.size) {
if (isActive) {
val bytes = HttpHelper.getByteRetrofit("http://tts.baidu.com")
.create(HttpPostApi::class.java)
.postMapByteAsync(
"http://tts.baidu.com/text2audio",
getAudioBody(contentList[index]), mapOf()
).body()
if (bytes != null && isActive) {
ReadAloud.httpTTS?.let {
AnalyzeUrl(
it.url,
speakText = contentList[index],
speakSpeed = AppConfig.ttsSpeechRate
).getResponseBytes()?.let { bytes ->
if (isActive) {
val file = getSpeakFile(index)
file.writeBytes(bytes)
if (index == nowSpeak) {
@ -87,6 +86,8 @@ class HttpReadAloudService : BaseReadAloudService(),
playAudio(fis.fd)
}
}
}
}
} else {
break
}
@ -113,31 +114,6 @@ class HttpReadAloudService : BaseReadAloudService(),
return FileUtils.createFileIfNotExist("${ttsFolder}${File.separator}${index}.mp3")
}
private fun getAudioBody(content: String): Map<String, String> {
return mapOf(
Pair("tex", encodeTwo(content)),
Pair("spd", ((AppConfig.ttsSpeechRate + 5) / 10 + 4).toString()),
Pair("per", AppConfig.ttsSpeechPer),
Pair("cuid", "baidu_speech_demo"),
Pair("idx", "1"),
Pair("cod", "2"),
Pair("lan", "zh"),
Pair("ctp", "1"),
Pair("pdt", "1"),
Pair("vol", "5"),
Pair("pit", "5"),
Pair("_res_tag_", "audio")
)
}
private fun encodeTwo(content: String): String {
return try {
URLEncoder.encode(URLEncoder.encode(content, "UTF-8"), "UTF-8")
} catch (e: Exception) {
" "
}
}
override fun pauseReadAloud(pause: Boolean) {
super.pauseReadAloud(pause)
mediaPlayer.pause()

@ -5,22 +5,30 @@ import android.content.Intent
import io.legado.app.App
import io.legado.app.constant.IntentAction
import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.HttpTTS
import io.legado.app.service.BaseReadAloudService
import io.legado.app.service.HttpReadAloudService
import io.legado.app.service.TTSReadAloudService
import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.getPrefLong
object ReadAloud {
var aloudClass: Class<*> = getReadAloudClass()
private var aloudClass: Class<*> = getReadAloudClass()
var httpTTS: HttpTTS? = null
fun getReadAloudClass(): Class<*> {
return if (App.INSTANCE.getPrefBoolean(PreferKey.readAloudOnLine)) {
private fun getReadAloudClass(): Class<*> {
val spId = App.INSTANCE.getPrefLong(PreferKey.speakEngine)
httpTTS = App.db.httpTTSDao().get(spId)
return if (httpTTS != null) {
HttpReadAloudService::class.java
} else {
TTSReadAloudService::class.java
}
}
fun upReadAloudClass() {
aloudClass = getReadAloudClass()
}
fun play(
context: Context,
title: String,

@ -14,7 +14,7 @@ import androidx.recyclerview.widget.GridLayoutManager
import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
import io.legado.app.constant.Theme
import io.legado.app.lib.theme.bottomBackground
import io.legado.app.lib.theme.primaryColor
import io.legado.app.utils.applyTint
import io.legado.app.utils.getViewModel
import kotlinx.android.synthetic.main.dialog_change_source.*
@ -61,7 +61,7 @@ class ChangeCoverDialog : BaseDialogFragment(),
}
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
tool_bar.setBackgroundColor(bottomBackground)
tool_bar.setBackgroundColor(primaryColor)
tool_bar.setTitle(R.string.change_cover_source)
viewModel.initData(arguments)
initMenu()
@ -70,7 +70,7 @@ class ChangeCoverDialog : BaseDialogFragment(),
private fun initMenu() {
tool_bar.inflateMenu(R.menu.change_cover)
tool_bar.menu.applyTint(requireContext(), Theme.getTheme())
tool_bar.menu.applyTint(requireContext())
tool_bar.setOnMenuItemClickListener(this)
}

@ -16,10 +16,9 @@ import androidx.recyclerview.widget.RecyclerView
import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
import io.legado.app.constant.PreferKey
import io.legado.app.constant.Theme
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.SearchBook
import io.legado.app.lib.theme.bottomBackground
import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.applyTint
import io.legado.app.utils.getPrefBoolean
@ -69,7 +68,7 @@ class ChangeSourceDialog : BaseDialogFragment(),
}
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
tool_bar.setBackgroundColor(bottomBackground)
tool_bar.setBackgroundColor(primaryColor)
viewModel.initData(arguments)
showTitle()
initMenu()
@ -86,7 +85,7 @@ class ChangeSourceDialog : BaseDialogFragment(),
private fun initMenu() {
tool_bar.inflateMenu(R.menu.change_source)
tool_bar.menu.applyTint(requireContext(), Theme.getTheme())
tool_bar.menu.applyTint(requireContext())
tool_bar.setOnMenuItemClickListener(this)
tool_bar.menu.findItem(R.id.menu_load_toc)?.isChecked =
getPrefBoolean(PreferKey.changeSourceLoadToc)
@ -143,7 +142,7 @@ class ChangeSourceDialog : BaseDialogFragment(),
} else {
stopMenuItem?.setIcon(R.drawable.ic_refresh_black_24dp)
}
tool_bar.menu.applyTint(requireContext(), Theme.getTheme())
tool_bar.menu.applyTint(requireContext())
})
viewModel.searchBooksLiveData.observe(viewLifecycleOwner, Observer {
val diffResult = DiffUtil.calculateDiff(DiffCallBack(adapter.getItems(), it))

@ -20,7 +20,6 @@ import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.constant.Theme
import io.legado.app.data.entities.BookGroup
import io.legado.app.help.AppConfig
import io.legado.app.help.ItemTouchCallback
@ -30,7 +29,7 @@ import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.yesButton
import io.legado.app.lib.theme.accentColor
import io.legado.app.lib.theme.backgroundColor
import io.legado.app.lib.theme.bottomBackground
import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.applyTint
import io.legado.app.utils.getViewModel
@ -65,7 +64,7 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
}
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
tool_bar.setBackgroundColor(bottomBackground)
tool_bar.setBackgroundColor(primaryColor)
tool_bar.title = getString(R.string.group_manage)
initData()
initMenu()
@ -94,7 +93,7 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
tool_bar.setOnMenuItemClickListener(this)
tool_bar.inflateMenu(R.menu.book_group_manage)
tool_bar.menu.let {
it.applyTint(requireContext(), Theme.getTheme())
it.applyTint(requireContext())
it.findItem(R.id.menu_group_all)
.isChecked = AppConfig.bookGroupAllShow
it.findItem(R.id.menu_group_local)

@ -20,7 +20,6 @@ import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.constant.Theme
import io.legado.app.data.entities.BookGroup
import io.legado.app.help.ItemTouchCallback
import io.legado.app.lib.dialogs.alert
@ -29,7 +28,7 @@ import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.yesButton
import io.legado.app.lib.theme.accentColor
import io.legado.app.lib.theme.backgroundColor
import io.legado.app.lib.theme.bottomBackground
import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.applyTint
import io.legado.app.utils.getViewModel
@ -81,7 +80,7 @@ class GroupSelectDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
}
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
tool_bar.setBackgroundColor(bottomBackground)
tool_bar.setBackgroundColor(primaryColor)
callBack = activity as? CallBack
arguments?.let {
groupId = it.getInt("groupId")
@ -94,7 +93,7 @@ class GroupSelectDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
private fun initView() {
tool_bar.title = getString(R.string.group_select)
tool_bar.inflateMenu(R.menu.book_group_manage)
tool_bar.menu.applyTint(requireContext(), Theme.getTheme())
tool_bar.menu.applyTint(requireContext())
tool_bar.setOnMenuItemClickListener(this)
tool_bar.menu.setGroupVisible(R.id.menu_groups, false)
adapter = GroupAdapter(requireContext())

@ -10,16 +10,17 @@ import android.widget.LinearLayout
import androidx.fragment.app.DialogFragment
import androidx.preference.ListPreference
import androidx.preference.Preference
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BasePreferenceFragment
import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.help.AppConfig
import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.backgroundColor
import io.legado.app.service.BaseReadAloudService
import io.legado.app.service.help.ReadAloud
import io.legado.app.ui.book.read.Help
import io.legado.app.utils.getPrefLong
import io.legado.app.utils.postEvent
class ReadAloudConfigDialog : DialogFragment() {
@ -62,11 +63,18 @@ class ReadAloudConfigDialog : DialogFragment() {
class ReadAloudPreferenceFragment : BasePreferenceFragment(),
SharedPreferences.OnSharedPreferenceChangeListener {
private val speakEngineSummary: String
get() {
val eid = App.INSTANCE.getPrefLong(PreferKey.speakEngine)
val ht = App.db.httpTTSDao().get(eid)
return ht?.name ?: getString(R.string.local_tts)
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.pref_config_aloud)
upPreferenceSummary(
findPreference<ListPreference>(PreferKey.ttsSpeechPer),
AppConfig.ttsSpeechPer
findPreference(PreferKey.speakEngine),
speakEngineSummary
)
}
@ -85,6 +93,14 @@ class ReadAloudConfigDialog : DialogFragment() {
super.onPause()
}
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
when (preference?.key) {
PreferKey.speakEngine ->
SpeakEngineDialog().show(childFragmentManager, "speakEngine")
}
return super.onPreferenceTreeClick(preference)
}
override fun onSharedPreferenceChanged(
sharedPreferences: SharedPreferences?,
key: String?
@ -95,16 +111,9 @@ class ReadAloudConfigDialog : DialogFragment() {
postEvent(EventBus.MEDIA_BUTTON, false)
}
}
PreferKey.readAloudOnLine -> {
ReadAloud.stop(requireContext())
ReadAloud.aloudClass = ReadAloud.getReadAloudClass()
}
PreferKey.ttsSpeechPer -> {
upPreferenceSummary(
findPreference<ListPreference>(PreferKey.ttsSpeechPer),
AppConfig.ttsSpeechPer
)
ReadAloud.upTtsSpeechRate(requireContext())
PreferKey.speakEngine -> {
upPreferenceSummary(findPreference(key), speakEngineSummary)
ReadAloud.upReadAloudClass()
}
}
}

@ -0,0 +1,161 @@
package io.legado.app.ui.book.read.config
import android.annotation.SuppressLint
import android.content.Context
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar
import androidx.lifecycle.LiveData
import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.HttpTTS
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.cancelButton
import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.okButton
import io.legado.app.lib.theme.primaryColor
import io.legado.app.service.help.ReadAloud
import io.legado.app.utils.*
import kotlinx.android.synthetic.main.dialog_http_tts_edit.view.*
import kotlinx.android.synthetic.main.dialog_recycler_view.*
import kotlinx.android.synthetic.main.item_http_tts.view.*
import org.jetbrains.anko.sdk27.listeners.onClick
class SpeakEngineDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
override fun onStart() {
super.onStart()
val dm = DisplayMetrics()
activity?.windowManager?.defaultDisplay?.getMetrics(dm)
dialog?.window?.setLayout((dm.widthPixels * 0.9).toInt(), (dm.heightPixels * 0.9).toInt())
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.dialog_recycler_view, container)
}
lateinit var adapter: Adapter
private var httpTTSData: LiveData<List<HttpTTS>>? = null
var engineId = App.INSTANCE.getPrefLong(PreferKey.speakEngine)
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
initView()
initMenu()
initData()
}
private fun initView() {
tool_bar.setBackgroundColor(primaryColor)
tool_bar.setTitle(R.string.speak_engine)
recycler_view.layoutManager = LinearLayoutManager(requireContext())
adapter = Adapter(requireContext())
recycler_view.adapter = adapter
tv_footer_left.setText(R.string.local_tts)
tv_footer_left.visible()
tv_footer_left.onClick {
removePref(PreferKey.speakEngine)
dismiss()
}
tv_ok.visible()
tv_ok.onClick {
putPrefLong(PreferKey.speakEngine, engineId)
dismiss()
}
tv_cancel.visible()
tv_cancel.onClick {
dismiss()
}
}
private fun initMenu() {
tool_bar.inflateMenu(R.menu.speak_engine)
tool_bar.menu.applyTint(requireContext())
tool_bar.setOnMenuItemClickListener(this)
}
private fun initData() {
httpTTSData?.removeObservers(this)
httpTTSData = App.db.httpTTSDao().observeAll()
httpTTSData?.observe(this, {
adapter.setItems(it)
})
}
override fun onMenuItemClick(item: MenuItem?): Boolean {
when (item?.itemId) {
R.id.menu_add -> editHttpTTS()
}
return true
}
@SuppressLint("InflateParams")
private fun editHttpTTS(v: HttpTTS? = null) {
val httpTTS = v?.copy() ?: HttpTTS()
requireContext().alert(titleResource = R.string.speak_engine) {
var rootView: View? = null
customView {
LayoutInflater.from(requireContext())
.inflate(R.layout.dialog_http_tts_edit, null).apply {
rootView = this
tv_name.setText(httpTTS.name)
tv_url.setText(httpTTS.url)
}
}
cancelButton()
okButton {
rootView?.apply {
httpTTS.name = tv_name.text.toString()
httpTTS.url = tv_url.text.toString()
App.db.httpTTSDao().insert(httpTTS)
ReadAloud.upReadAloudClass()
}
}
}.show().applyTint()
}
inner class Adapter(context: Context) :
SimpleRecyclerAdapter<HttpTTS>(context, R.layout.item_http_tts) {
override fun convert(holder: ItemViewHolder, item: HttpTTS, payloads: MutableList<Any>) {
holder.itemView.apply {
cb_name.text = item.name
cb_name.isChecked = item.id == engineId
}
}
override fun registerListener(holder: ItemViewHolder) {
holder.itemView.apply {
cb_name.onClick {
getItem(holder.layoutPosition)?.let { httpTTS ->
engineId = httpTTS.id
notifyItemRangeChanged(0, getActualItemCount())
}
}
iv_edit.onClick {
editHttpTTS(getItem(holder.layoutPosition))
}
iv_menu_delete.onClick {
getItem(holder.layoutPosition)?.let { httpTTS ->
App.db.httpTTSDao().delete(httpTTS)
}
}
}
}
}
}

@ -21,7 +21,6 @@ import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.constant.Theme
import io.legado.app.data.entities.TxtTocRule
import io.legado.app.help.ItemTouchCallback
import io.legado.app.lib.dialogs.alert
@ -29,7 +28,7 @@ import io.legado.app.lib.dialogs.cancelButton
import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.okButton
import io.legado.app.lib.theme.backgroundColor
import io.legado.app.lib.theme.bottomBackground
import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.ui.widget.text.AutoCompleteTextView
import io.legado.app.utils.*
@ -68,11 +67,11 @@ class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
}
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
tool_bar.setBackgroundColor(bottomBackground)
tool_bar.setBackgroundColor(primaryColor)
durRegex = arguments?.getString("tocRegex")
tool_bar.setTitle(R.string.txt_toc_regex)
tool_bar.inflateMenu(R.menu.txt_toc_regex)
tool_bar.menu.applyTint(requireContext(), Theme.getTheme())
tool_bar.menu.applyTint(requireContext())
tool_bar.setOnMenuItemClickListener(this)
initView()
initData()

@ -17,12 +17,12 @@ import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.constant.Theme
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.yesButton
import io.legado.app.lib.theme.backgroundColor
import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.applyTint
import io.legado.app.utils.getViewModelOfActivity
@ -56,18 +56,19 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.setBackgroundColor(backgroundColor)
initData()
}
private fun initData() {
tool_bar.setBackgroundColor(primaryColor)
tool_bar.title = getString(R.string.group_manage)
tool_bar.inflateMenu(R.menu.group_manage)
tool_bar.menu.applyTint(requireContext(), Theme.getTheme())
tool_bar.menu.applyTint(requireContext())
tool_bar.setOnMenuItemClickListener(this)
adapter = GroupAdapter(requireContext())
recycler_view.layoutManager = LinearLayoutManager(requireContext())
recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
initData()
}
private fun initData() {
App.db.bookSourceDao().liveGroup().observe(viewLifecycleOwner, Observer {
val groups = linkedSetOf<String>()
it.map { group ->

@ -17,13 +17,12 @@ import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.constant.Theme
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.yesButton
import io.legado.app.lib.theme.backgroundColor
import io.legado.app.lib.theme.bottomBackground
import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.applyTint
import io.legado.app.utils.getViewModelOfActivity
@ -57,14 +56,14 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.setBackgroundColor(backgroundColor)
tool_bar.setBackgroundColor(bottomBackground)
tool_bar.setBackgroundColor(primaryColor)
initData()
}
private fun initData() {
tool_bar.title = getString(R.string.group_manage)
tool_bar.inflateMenu(R.menu.group_manage)
tool_bar.menu.applyTint(requireContext(), Theme.getTheme())
tool_bar.menu.applyTint(requireContext())
tool_bar.setOnMenuItemClickListener(this)
adapter = GroupAdapter(requireContext())
recycler_view.layoutManager = LinearLayoutManager(requireContext())

@ -15,7 +15,6 @@ import androidx.lifecycle.Observer
import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
import io.legado.app.constant.AppConst
import io.legado.app.constant.Theme
import io.legado.app.data.entities.ReplaceRule
import io.legado.app.lib.theme.bottomBackground
import io.legado.app.ui.widget.KeyboardToolPop
@ -70,7 +69,7 @@ class ReplaceEditDialog : BaseDialogFragment(),
tool_bar.setBackgroundColor(bottomBackground)
mSoftKeyboardTool = KeyboardToolPop(requireContext(), AppConst.keyboardToolChars, this)
tool_bar.inflateMenu(R.menu.replace_edit)
tool_bar.menu.applyTint(requireContext(), Theme.getTheme())
tool_bar.menu.applyTint(requireContext())
tool_bar.setOnMenuItemClickListener(this)
viewModel.replaceRuleData.observe(viewLifecycleOwner, Observer {
upReplaceView(it)

@ -17,12 +17,12 @@ import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.constant.Theme
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.yesButton
import io.legado.app.lib.theme.accentColor
import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.*
import kotlinx.android.synthetic.main.dialog_edit_text.view.*
@ -51,9 +51,10 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
}
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
tool_bar.setBackgroundColor(primaryColor)
tool_bar.title = getString(R.string.group_manage)
tool_bar.inflateMenu(R.menu.group_manage)
tool_bar.menu.applyTint(requireContext(), Theme.getTheme())
tool_bar.menu.applyTint(requireContext())
tool_bar.setOnMenuItemClickListener(this)
adapter = GroupAdapter(requireContext())
recycler_view.layoutManager = LinearLayoutManager(requireContext())

@ -4,12 +4,10 @@ import android.view.MotionEvent
import kotlin.math.abs
import kotlin.math.atan
class RotateGestureDetector(l: OnRotateListener) {
class RotateGestureDetector(private val mListener: OnRotateListener) {
private val MAX_DEGREES_STEP = 120
private val mListener: OnRotateListener? = null
private var mPrevSlope = 0f
private var mCurrSlope = 0f

@ -24,6 +24,7 @@ import io.legado.app.App
import io.legado.app.BuildConfig
import io.legado.app.R
import org.jetbrains.anko.defaultSharedPreferences
import org.jetbrains.anko.longToast
import org.jetbrains.anko.toast
import java.io.File
import java.io.FileOutputStream
@ -138,7 +139,7 @@ fun Context.sendToClip(text: String) {
val clipData = ClipData.newPlainText(null, text)
clipboard?.let {
clipboard.setPrimaryClip(clipData)
toast(R.string.copy_complete)
longToast(R.string.copy_complete)
}
}

@ -11,6 +11,9 @@
android:layout_height="wrap_content"
android:background="@color/background_menu"
android:elevation="5dp"
android:theme="?attr/actionBarStyle"
app:titleTextAppearance="@style/ToolbarTitle"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:displayHomeAsUp="false"
app:fitStatusBar="false" />

@ -12,6 +12,9 @@
android:layout_height="wrap_content"
android:background="@color/background_menu"
android:elevation="5dp"
android:theme="?attr/actionBarStyle"
app:titleTextAppearance="@style/ToolbarTitle"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:displayHomeAsUp="false"
app:fitStatusBar="false" />

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="10dp"
android:paddingStart="10dp"
android:paddingEnd="10dp">
<io.legado.app.ui.widget.text.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/name">
<io.legado.app.ui.widget.text.EditText
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</io.legado.app.ui.widget.text.TextInputLayout>
<io.legado.app.ui.widget.text.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="url"
tools:ignore="HardcodedText">
<io.legado.app.ui.widget.text.EditText
android:id="@+id/tv_url"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</io.legado.app.ui.widget.text.TextInputLayout>
</LinearLayout>

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -8,7 +9,10 @@
<androidx.appcompat.widget.Toolbar
android:id="@+id/tool_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
android:layout_height="wrap_content"
android:theme="?attr/actionBarStyle"
app:titleTextAppearance="@style/ToolbarTitle"
app:popupTheme="@style/AppTheme.PopupOverlay" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
@ -20,6 +24,16 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<io.legado.app.ui.widget.text.AccentTextView
android:id="@+id/tv_footer_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:padding="12dp"
android:text="@string/cancel"
android:visibility="gone"
tools:ignore="RtlHardcoded" />
<Space
android:layout_width="0dp"
android:layout_height="wrap_content"

@ -10,6 +10,9 @@
android:background="@color/background_menu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?attr/actionBarStyle"
app:titleTextAppearance="@style/ToolbarTitle"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:title="@string/replace_rule_edit" />
<ScrollView

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:padding="8dp">
<io.legado.app.lib.theme.view.ATERadioButton
android:id="@+id/cb_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
tools:ignore="RtlSymmetry" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_edit"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_gravity="center"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/edit"
android:padding="6dp"
android:src="@drawable/ic_edit"
android:tint="@color/primaryText" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_menu_delete"
android:layout_width="36dp"
android:layout_height="36dp"
android:padding="6dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_clear_all"
android:tint="@color/primaryText"
tools:ignore="RtlHardcoded" />
</LinearLayout>

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_add"
android:icon="@drawable/ic_add"
android:title="@string/add"
app:showAsAction="always" />
</menu>

@ -1,21 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="tts_speech_per">
<item>度小美</item>
<item>度小宇</item>
<item>度逍遙</item>
<item>度丫丫</item>
<item>度小嬌</item>
<item>度米朵</item>
<item>度博文</item>
<item>度小童</item>
<item>度小萌</item>
<item>百度騷男</item>
<item>百度評書</item>
<item>百度主持</item>
</string-array>
<string-array name="theme_mode">
<item>跟隨系統</item>
<item>亮色主題</item>

@ -543,7 +543,7 @@
<string name="timer_m">%d 分鐘</string>
<string name="brightness_auto">自動亮度 %s</string>
<string name="read_aloud_by_page">按頁朗讀</string>
<string name="read_aloud_on_line">在線朗讀</string>
<string name="speak_engine">朗讀引擎</string>
<string name="bg_image">背景圖片</string>
<string name="bg_color">背景顏色</string>
<string name="text_color">文字顏色</string>
@ -748,4 +748,5 @@
<string name="about_summary">公众号[开源阅读软件]</string>
<string name="read_record">阅读记录</string>
<string name="read_record_summary">阅读时间记录</string>
<string name="local_tts">本地TTS</string>
</resources>

@ -5,21 +5,6 @@
<item>@string/book_type_audio</item>
</string-array>
<string-array name="tts_speech_per">
<item>度小美</item>
<item>度小宇</item>
<item>度逍遙</item>
<item>度丫丫</item>
<item>度小嬌</item>
<item>度米朵</item>
<item>度博文</item>
<item>度小童</item>
<item>度小萌</item>
<item>百度騷男</item>
<item>百度評書</item>
<item>百度主持</item>
</string-array>
<string-array name="indent">
<item>@string/indent_0</item>
<item>@string/indent_1</item>

@ -544,7 +544,7 @@
<string name="timer_m">%d分鐘</string>
<string name="brightness_auto">自動亮度%s</string>
<string name="read_aloud_by_page">按頁朗讀</string>
<string name="read_aloud_on_line">線上朗讀</string>
<string name="speak_engine">朗讀引擎</string>
<string name="bg_image">背景圖片</string>
<string name="bg_color">背景顏色</string>
<string name="text_color">文字顏色</string>
@ -748,5 +748,6 @@
<string name="about_summary">公众号[开源阅读软件]</string>
<string name="read_record">阅读记录</string>
<string name="read_record_summary">阅读时间记录</string>
<string name="local_tts">本地TTS</string>
</resources>

@ -5,21 +5,6 @@
<item>@string/book_type_audio</item>
</string-array>
<string-array name="tts_speech_per">
<item>度小美</item>
<item>度小宇</item>
<item>度逍遥</item>
<item>度丫丫</item>
<item>度小娇</item>
<item>度米朵</item>
<item>度博文</item>
<item>度小童</item>
<item>度小萌</item>
<item>百度骚男</item>
<item>百度评书</item>
<item>百度主持</item>
</string-array>
<string-array name="indent">
<item>@string/indent_0</item>
<item>@string/indent_1</item>

@ -545,7 +545,7 @@
<string name="timer_m">%d分钟</string>
<string name="brightness_auto">自动亮度%s</string>
<string name="read_aloud_by_page">按页朗读</string>
<string name="read_aloud_on_line">在线朗读</string>
<string name="speak_engine">朗读引擎</string>
<string name="bg_image">背景图片</string>
<string name="bg_color">背景颜色</string>
<string name="text_color">文字颜色</string>
@ -748,4 +748,5 @@
<string name="about_summary">公众号[开源阅读软件]</string>
<string name="read_record">阅读记录</string>
<string name="read_record_summary">阅读时间记录</string>
<string name="local_tts">本地TTS</string>
</resources>

@ -26,21 +26,6 @@
<item>3</item>
</string-array>
<string-array name="tts_speech_per_value">
<item>0</item>
<item>1</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>103</item>
<item>106</item>
<item>110</item>
<item>111</item>
<item>11</item>
<item>6</item>
<item>9</item>
</string-array>
<string-array name="screen_direction_value">
<item>0</item>
<item>1</item>

@ -5,21 +5,6 @@
<item>@string/book_type_audio</item>
</string-array>
<string-array name="tts_speech_per">
<item>du Xiao Mei</item>
<item>du Xiao Yu</item>
<item>du Xiao Yao</item>
<item>du Ya Ya</item>
<item>du Xiao Jiao</item>
<item>du Mi Duo</item>
<item>du Bo Wen</item>
<item>du Xiao Tong</item>
<item>du Xiao Meng</item>
<item>baidu SaoNan</item>
<item>baidu PingShu</item>
<item>baidu ZhuChi</item>
</string-array>
<string-array name="indent">
<item>@string/indent_0</item>
<item>@string/indent_1</item>

@ -547,7 +547,7 @@
<string name="timer_m">%d min</string>
<string name="brightness_auto">Auto-Brightness %s</string>
<string name="read_aloud_by_page">Speak by pages</string>
<string name="read_aloud_on_line">Online Speech</string>
<string name="speak_engine">Speak Engine</string>
<string name="bg_image">Background images</string>
<string name="bg_color">Background color</string>
<string name="text_color">Text color</string>
@ -749,5 +749,6 @@
<string name="about_summary">公众号[开源阅读软件]</string>
<string name="read_record">阅读记录</string>
<string name="read_record_summary">阅读时间记录</string>
<string name="local_tts">本地TTS</string>
</resources>

@ -14,18 +14,11 @@
android:key="readAloudByPage"
app:iconSpaceReserved="false" />
<io.legado.app.ui.widget.prefs.SwitchPreference
<io.legado.app.ui.widget.prefs.Preference
android:defaultValue="false"
android:title="@string/read_aloud_on_line"
android:key="readAloudOnLine"
app:iconSpaceReserved="false" />
<androidx.preference.ListPreference
android:key="ttsSpeechPer"
android:title="@string/tone_colour"
android:defaultValue="0"
android:entries="@array/tts_speech_per"
android:entryValues="@array/tts_speech_per_value"
android:title="@string/speak_engine"
android:summary="本地TTS"
android:key="speakEngine"
app:iconSpaceReserved="false" />
</io.legado.app.ui.widget.prefs.PreferenceCategory>

Loading…
Cancel
Save