在线朗读改为自定义

pull/332/head
gedoor 5 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. 80
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt
  12. 60
      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 clickAllNext = "clickAllNext"
const val hideNavigationBar = "hideNavigationBar" const val hideNavigationBar = "hideNavigationBar"
const val precisionSearch = "precisionSearch" const val precisionSearch = "precisionSearch"
const val readAloudOnLine = "readAloudOnLine" const val speakEngine = "speakEngine"
const val readAloudByPage = "readAloudByPage" const val readAloudByPage = "readAloudByPage"
const val ttsSpeechRate = "ttsSpeechRate" const val ttsSpeechRate = "ttsSpeechRate"
const val ttsSpeechPer = "ttsSpeechPer"
const val prevKey = "prevKeyCode" const val prevKey = "prevKeyCode"
const val nextKey = "nextKeyCode" const val nextKey = "nextKeyCode"
const val showRss = "showRss" const val showRss = "showRss"

@ -14,8 +14,8 @@ import io.legado.app.data.entities.*
entities = [Book::class, BookGroup::class, BookSource::class, BookChapter::class, entities = [Book::class, BookGroup::class, BookSource::class, BookChapter::class,
ReplaceRule::class, SearchBook::class, SearchKeyword::class, Cookie::class, ReplaceRule::class, SearchBook::class, SearchKeyword::class, Cookie::class,
RssSource::class, Bookmark::class, RssArticle::class, RssReadRecord::class, RssSource::class, Bookmark::class, RssArticle::class, RssReadRecord::class,
RssStar::class, TxtTocRule::class, ReadRecord::class], RssStar::class, TxtTocRule::class, ReadRecord::class, HttpTTS::class],
version = 17, version = 18,
exportSchema = true exportSchema = true
) )
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
@ -33,7 +33,8 @@ abstract class AppDatabase : RoomDatabase() {
migration_12_13, migration_12_13,
migration_13_14, migration_13_14,
migration_14_15, migration_14_15,
migration_15_17 migration_15_17,
migration_17_18
) )
.allowMainThreadQueries() .allowMainThreadQueries()
.build() .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`))") 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 abstract fun bookDao(): BookDao
@ -110,4 +117,5 @@ abstract class AppDatabase : RoomDatabase() {
abstract fun cookieDao(): CookieDao abstract fun cookieDao(): CookieDao
abstract fun txtTocRule(): TxtTocRuleDao abstract fun txtTocRule(): TxtTocRuleDao
abstract fun readRecordDao(): ReadRecordDao 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) 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) val clickAllNext: Boolean get() = App.INSTANCE.getPrefBoolean(PreferKey.clickAllNext, false)
var chineseConverterType: Int var chineseConverterType: Int

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

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

@ -49,4 +49,12 @@ interface HttpPostApi {
@FieldMap(encoded = true) fieldMap: Map<String, String>, @FieldMap(encoded = true) fieldMap: Map<String, String>,
@HeaderMap headers: Map<String, String> @HeaderMap headers: Map<String, String>
): Response<ByteArray> ): 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? { override fun ajax(urlStr: String): String? {
return try { 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 call = analyzeUrl.getResponse(urlStr)
val response = call.execute() val response = call.execute()
response.body() response.body()

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

@ -7,8 +7,8 @@ import io.legado.app.constant.EventBus
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import io.legado.app.help.IntentHelp import io.legado.app.help.IntentHelp
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.help.http.HttpHelper import io.legado.app.model.analyzeRule.AnalyzeUrl
import io.legado.app.help.http.api.HttpPostApi import io.legado.app.service.help.ReadAloud
import io.legado.app.service.help.ReadBook import io.legado.app.service.help.ReadBook
import io.legado.app.utils.FileUtils import io.legado.app.utils.FileUtils
import io.legado.app.utils.LogUtils import io.legado.app.utils.LogUtils
@ -17,7 +17,6 @@ import kotlinx.coroutines.isActive
import java.io.File import java.io.File
import java.io.FileDescriptor import java.io.FileDescriptor
import java.io.FileInputStream import java.io.FileInputStream
import java.net.URLEncoder
class HttpReadAloudService : BaseReadAloudService(), class HttpReadAloudService : BaseReadAloudService(),
MediaPlayer.OnPreparedListener, MediaPlayer.OnPreparedListener,
@ -31,7 +30,7 @@ class HttpReadAloudService : BaseReadAloudService(),
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
ttsFolder = cacheDir.absolutePath + File.separator + "bdTts" ttsFolder = externalCacheDir!!.absolutePath + File.separator + "httpTTS"
mediaPlayer.setOnErrorListener(this) mediaPlayer.setOnErrorListener(this)
mediaPlayer.setOnPreparedListener(this) mediaPlayer.setOnPreparedListener(this)
mediaPlayer.setOnCompletionListener(this) mediaPlayer.setOnCompletionListener(this)
@ -72,19 +71,21 @@ class HttpReadAloudService : BaseReadAloudService(),
FileUtils.deleteFile(ttsFolder) FileUtils.deleteFile(ttsFolder)
for (index in 0 until contentList.size) { for (index in 0 until contentList.size) {
if (isActive) { if (isActive) {
val bytes = HttpHelper.getByteRetrofit("http://tts.baidu.com") ReadAloud.httpTTS?.let {
.create(HttpPostApi::class.java) AnalyzeUrl(
.postMapByteAsync( it.url,
"http://tts.baidu.com/text2audio", speakText = contentList[index],
getAudioBody(contentList[index]), mapOf() speakSpeed = AppConfig.ttsSpeechRate
).body() ).getResponseBytes()?.let { bytes ->
if (bytes != null && isActive) { if (isActive) {
val file = getSpeakFile(index) val file = getSpeakFile(index)
file.writeBytes(bytes) file.writeBytes(bytes)
if (index == nowSpeak) { if (index == nowSpeak) {
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")
val fis = FileInputStream(file) val fis = FileInputStream(file)
playAudio(fis.fd) playAudio(fis.fd)
}
}
} }
} }
} else { } else {
@ -113,31 +114,6 @@ class HttpReadAloudService : BaseReadAloudService(),
return FileUtils.createFileIfNotExist("${ttsFolder}${File.separator}${index}.mp3") 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) { override fun pauseReadAloud(pause: Boolean) {
super.pauseReadAloud(pause) super.pauseReadAloud(pause)
mediaPlayer.pause() mediaPlayer.pause()

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

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

@ -16,10 +16,9 @@ import androidx.recyclerview.widget.RecyclerView
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseDialogFragment import io.legado.app.base.BaseDialogFragment
import io.legado.app.constant.PreferKey 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.Book
import io.legado.app.data.entities.SearchBook 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.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.applyTint import io.legado.app.utils.applyTint
import io.legado.app.utils.getPrefBoolean import io.legado.app.utils.getPrefBoolean
@ -69,7 +68,7 @@ class ChangeSourceDialog : BaseDialogFragment(),
} }
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
tool_bar.setBackgroundColor(bottomBackground) tool_bar.setBackgroundColor(primaryColor)
viewModel.initData(arguments) viewModel.initData(arguments)
showTitle() showTitle()
initMenu() initMenu()
@ -86,7 +85,7 @@ class ChangeSourceDialog : BaseDialogFragment(),
private fun initMenu() { private fun initMenu() {
tool_bar.inflateMenu(R.menu.change_source) 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.setOnMenuItemClickListener(this)
tool_bar.menu.findItem(R.id.menu_load_toc)?.isChecked = tool_bar.menu.findItem(R.id.menu_load_toc)?.isChecked =
getPrefBoolean(PreferKey.changeSourceLoadToc) getPrefBoolean(PreferKey.changeSourceLoadToc)
@ -143,7 +142,7 @@ class ChangeSourceDialog : BaseDialogFragment(),
} else { } else {
stopMenuItem?.setIcon(R.drawable.ic_refresh_black_24dp) 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 { viewModel.searchBooksLiveData.observe(viewLifecycleOwner, Observer {
val diffResult = DiffUtil.calculateDiff(DiffCallBack(adapter.getItems(), it)) 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.BaseDialogFragment
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.constant.Theme
import io.legado.app.data.entities.BookGroup import io.legado.app.data.entities.BookGroup
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import io.legado.app.help.ItemTouchCallback 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.dialogs.yesButton
import io.legado.app.lib.theme.accentColor import io.legado.app.lib.theme.accentColor
import io.legado.app.lib.theme.backgroundColor 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.recycler.VerticalDivider
import io.legado.app.utils.applyTint import io.legado.app.utils.applyTint
import io.legado.app.utils.getViewModel import io.legado.app.utils.getViewModel
@ -65,7 +64,7 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
} }
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
tool_bar.setBackgroundColor(bottomBackground) tool_bar.setBackgroundColor(primaryColor)
tool_bar.title = getString(R.string.group_manage) tool_bar.title = getString(R.string.group_manage)
initData() initData()
initMenu() initMenu()
@ -94,7 +93,7 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
tool_bar.setOnMenuItemClickListener(this) tool_bar.setOnMenuItemClickListener(this)
tool_bar.inflateMenu(R.menu.book_group_manage) tool_bar.inflateMenu(R.menu.book_group_manage)
tool_bar.menu.let { tool_bar.menu.let {
it.applyTint(requireContext(), Theme.getTheme()) it.applyTint(requireContext())
it.findItem(R.id.menu_group_all) it.findItem(R.id.menu_group_all)
.isChecked = AppConfig.bookGroupAllShow .isChecked = AppConfig.bookGroupAllShow
it.findItem(R.id.menu_group_local) 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.BaseDialogFragment
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.constant.Theme
import io.legado.app.data.entities.BookGroup import io.legado.app.data.entities.BookGroup
import io.legado.app.help.ItemTouchCallback import io.legado.app.help.ItemTouchCallback
import io.legado.app.lib.dialogs.alert 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.dialogs.yesButton
import io.legado.app.lib.theme.accentColor import io.legado.app.lib.theme.accentColor
import io.legado.app.lib.theme.backgroundColor 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.recycler.VerticalDivider
import io.legado.app.utils.applyTint import io.legado.app.utils.applyTint
import io.legado.app.utils.getViewModel import io.legado.app.utils.getViewModel
@ -81,7 +80,7 @@ class GroupSelectDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
} }
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
tool_bar.setBackgroundColor(bottomBackground) tool_bar.setBackgroundColor(primaryColor)
callBack = activity as? CallBack callBack = activity as? CallBack
arguments?.let { arguments?.let {
groupId = it.getInt("groupId") groupId = it.getInt("groupId")
@ -94,7 +93,7 @@ class GroupSelectDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
private fun initView() { private fun initView() {
tool_bar.title = getString(R.string.group_select) tool_bar.title = getString(R.string.group_select)
tool_bar.inflateMenu(R.menu.book_group_manage) 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.setOnMenuItemClickListener(this)
tool_bar.menu.setGroupVisible(R.id.menu_groups, false) tool_bar.menu.setGroupVisible(R.id.menu_groups, false)
adapter = GroupAdapter(requireContext()) adapter = GroupAdapter(requireContext())

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

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

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

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

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

@ -17,12 +17,12 @@ import io.legado.app.R
import io.legado.app.base.BaseDialogFragment import io.legado.app.base.BaseDialogFragment
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter 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.alert
import io.legado.app.lib.dialogs.customView import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.noButton import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.yesButton import io.legado.app.lib.dialogs.yesButton
import io.legado.app.lib.theme.accentColor 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.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.* import io.legado.app.utils.*
import kotlinx.android.synthetic.main.dialog_edit_text.view.* 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?) { override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
tool_bar.setBackgroundColor(primaryColor)
tool_bar.title = getString(R.string.group_manage) tool_bar.title = getString(R.string.group_manage)
tool_bar.inflateMenu(R.menu.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) tool_bar.setOnMenuItemClickListener(this)
adapter = GroupAdapter(requireContext()) adapter = GroupAdapter(requireContext())
recycler_view.layoutManager = LinearLayoutManager(requireContext()) recycler_view.layoutManager = LinearLayoutManager(requireContext())

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

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

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

@ -12,6 +12,9 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/background_menu" android:background="@color/background_menu"
android:elevation="5dp" android:elevation="5dp"
android:theme="?attr/actionBarStyle"
app:titleTextAppearance="@style/ToolbarTitle"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:displayHomeAsUp="false" app:displayHomeAsUp="false"
app:fitStatusBar="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"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -8,7 +9,10 @@
<androidx.appcompat.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/tool_bar" android:id="@+id/tool_bar"
android:layout_width="match_parent" 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 <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view" android:id="@+id/recycler_view"
@ -20,6 +24,16 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> 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 <Space
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"

@ -10,6 +10,9 @@
android:background="@color/background_menu" android:background="@color/background_menu"
android:layout_width="match_parent" 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"
app:title="@string/replace_rule_edit" /> app:title="@string/replace_rule_edit" />
<ScrollView <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"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <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"> <string-array name="theme_mode">
<item>跟隨系統</item> <item>跟隨系統</item>
<item>亮色主題</item> <item>亮色主題</item>

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

@ -5,21 +5,6 @@
<item>@string/book_type_audio</item> <item>@string/book_type_audio</item>
</string-array> </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"> <string-array name="indent">
<item>@string/indent_0</item> <item>@string/indent_0</item>
<item>@string/indent_1</item> <item>@string/indent_1</item>

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

@ -5,21 +5,6 @@
<item>@string/book_type_audio</item> <item>@string/book_type_audio</item>
</string-array> </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"> <string-array name="indent">
<item>@string/indent_0</item> <item>@string/indent_0</item>
<item>@string/indent_1</item> <item>@string/indent_1</item>

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

@ -26,21 +26,6 @@
<item>3</item> <item>3</item>
</string-array> </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"> <string-array name="screen_direction_value">
<item>0</item> <item>0</item>
<item>1</item> <item>1</item>

@ -5,21 +5,6 @@
<item>@string/book_type_audio</item> <item>@string/book_type_audio</item>
</string-array> </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"> <string-array name="indent">
<item>@string/indent_0</item> <item>@string/indent_0</item>
<item>@string/indent_1</item> <item>@string/indent_1</item>

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

@ -14,18 +14,11 @@
android:key="readAloudByPage" android:key="readAloudByPage"
app:iconSpaceReserved="false" /> app:iconSpaceReserved="false" />
<io.legado.app.ui.widget.prefs.SwitchPreference <io.legado.app.ui.widget.prefs.Preference
android:defaultValue="false" android:defaultValue="false"
android:title="@string/read_aloud_on_line" android:title="@string/speak_engine"
android:key="readAloudOnLine" android:summary="本地TTS"
app:iconSpaceReserved="false" /> android:key="speakEngine"
<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"
app:iconSpaceReserved="false" /> app:iconSpaceReserved="false" />
</io.legado.app.ui.widget.prefs.PreferenceCategory> </io.legado.app.ui.widget.prefs.PreferenceCategory>

Loading…
Cancel
Save