pull/1866/head
kunfei 3 years ago
parent f1ad8cb94d
commit 75f5cd1178
  1. 3
      app/src/main/java/io/legado/app/data/dao/TxtTocRuleDao.kt
  2. 19
      app/src/main/java/io/legado/app/data/entities/TxtTocRule.kt
  3. 21
      app/src/main/java/io/legado/app/help/config/ThemeConfig.kt
  4. 46
      app/src/main/java/io/legado/app/ui/association/BaseAssociationViewModel.kt
  5. 36
      app/src/main/java/io/legado/app/ui/association/FileAssociationActivity.kt
  6. 25
      app/src/main/java/io/legado/app/ui/association/FileAssociationViewModel.kt
  7. 15
      app/src/main/java/io/legado/app/ui/association/ImportHttpTtsViewModel.kt
  8. 179
      app/src/main/java/io/legado/app/ui/association/ImportThemeDialog.kt
  9. 106
      app/src/main/java/io/legado/app/ui/association/ImportThemeViewModel.kt
  10. 179
      app/src/main/java/io/legado/app/ui/association/ImportTxtRuleDialog.kt
  11. 107
      app/src/main/java/io/legado/app/ui/association/ImportTxtRuleViewModel.kt
  12. 6
      app/src/main/java/io/legado/app/ui/association/OnLineImportActivity.kt
  13. 19
      app/src/main/java/io/legado/app/ui/association/OnLineImportViewModel.kt

@ -19,6 +19,9 @@ interface TxtTocRuleDao {
@get:Query("select * from txtTocRules where enable != 1 order by serialNumber")
val disabled: List<TxtTocRule>
@Query("select * from txtTocRules where id = :id")
fun get(id: Long): TxtTocRule?
@get:Query("select ifNull(min(serialNumber), 0) from txtTocRules")
val minOrder: Int

@ -1,8 +1,8 @@
package io.legado.app.data.entities
import androidx.room.Entity
import androidx.room.Ignore
import androidx.room.PrimaryKey
import io.legado.app.utils.GSON
@Entity(tableName = "txtTocRules")
@ -15,7 +15,20 @@ data class TxtTocRule(
var enable: Boolean = true
) {
@Ignore
constructor() : this(name = "")
override fun hashCode(): Int {
return GSON.toJson(this).hashCode()
}
override fun equals(other: Any?): Boolean {
other ?: return false
if (other is TxtTocRule) {
return id == other.id
&& name == other.name
&& rule == other.rule
&& serialNumber == other.serialNumber
&& enable == other.enable
}
return false
}
}

@ -256,6 +256,25 @@ object ThemeConfig {
var accentColor: String,
var backgroundColor: String,
var bottomBackground: String
)
) {
override fun hashCode(): Int {
return GSON.toJson(this).hashCode()
}
override fun equals(other: Any?): Boolean {
other ?: return false
if (other is Config) {
return other.themeName == themeName
&& other.isNightTheme == isNightTheme
&& other.primaryColor == primaryColor
&& other.accentColor == accentColor
&& other.backgroundColor == backgroundColor
&& other.bottomBackground == bottomBackground
}
return false
}
}
}

@ -1,10 +1,10 @@
package io.legado.app.ui.association
import android.app.Application
import androidx.lifecycle.MutableLiveData
import io.legado.app.R
import io.legado.app.base.BaseViewModel
import io.legado.app.data.appDb
import io.legado.app.data.entities.HttpTTS
import io.legado.app.data.entities.TxtTocRule
import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.config.ThemeConfig
@ -15,6 +15,27 @@ import io.legado.app.utils.isJsonArray
abstract class BaseAssociationViewModel(application: Application) : BaseViewModel(application) {
val successLive = MutableLiveData<Pair<String, String>>()
val errorLive = MutableLiveData<String>()
fun importJson(json: String) {
//暂时根据文件内容判断属于什么
when {
json.contains("bookSourceUrl") ->
successLive.postValue(Pair("bookSource", json))
json.contains("sourceUrl") ->
successLive.postValue(Pair("rssSource", json))
json.contains("pattern") ->
successLive.postValue(Pair("replaceRule", json))
json.contains("themeName") ->
successLive.postValue(Pair("theme", json))
json.contains("name") && json.contains("rule") ->
successLive.postValue(Pair("txtRule", json))
json.contains("name") && json.contains("url") ->
successLive.postValue(Pair("httpTts", json))
else -> errorLive.postValue("格式不对")
}
}
fun importTextTocRule(json: String, finally: (title: String, msg: String) -> Unit) {
execute {
@ -37,29 +58,6 @@ abstract class BaseAssociationViewModel(application: Application) : BaseViewMode
}
}
fun importHttpTTS(json: String, finally: (title: String, msg: String) -> Unit) {
execute {
if (json.isJsonArray()) {
HttpTTS.fromJsonArray(json).getOrThrow().let {
appDb.httpTTSDao.insert(*it.toTypedArray())
return@execute it.size
}
} else {
HttpTTS.fromJson(json).getOrThrow().let {
appDb.httpTTSDao.insert(it)
return@execute 1
}
}
}.onSuccess {
finally.invoke(context.getString(R.string.success), "导入${it}朗读引擎")
}.onError {
finally.invoke(
context.getString(R.string.error),
it.localizedMessage ?: context.getString(R.string.unknown_error)
)
}
}
fun importTheme(json: String, finally: (title: String, msg: String) -> Unit) {
execute {
if (json.isJsonArray()) {

@ -70,19 +70,29 @@ class FileAssociationActivity :
}
finish()
}
viewModel.importBookSourceLive.observe(this) {
binding.rotateLoading.hide()
showDialogFragment(ImportBookSourceDialog(it, true))
}
viewModel.importRssSourceLive.observe(this) {
binding.rotateLoading.hide()
showDialogFragment(ImportRssSourceDialog(it, true))
}
viewModel.importReplaceRuleLive.observe(this) {
binding.rotateLoading.hide()
showDialogFragment(ImportReplaceRuleDialog(it, true))
viewModel.successLive.observe(this) {
when (it.first) {
"bookSource" -> showDialogFragment(
ImportBookSourceDialog(it.second, true)
)
"rssSource" -> showDialogFragment(
ImportRssSourceDialog(it.second, true)
)
"replaceRule" -> showDialogFragment(
ImportReplaceRuleDialog(it.second, true)
)
"httpTts" -> showDialogFragment(
ImportHttpTtsDialog(it.second, true)
)
"theme" -> showDialogFragment(
ImportThemeDialog(it.second, true)
)
"txtRule" -> showDialogFragment(
ImportTxtRuleDialog(it.second, true)
)
}
}
viewModel.errorLiveData.observe(this) {
viewModel.errorLive.observe(this) {
binding.rotateLoading.hide()
toastOnUi(it)
finish()
@ -95,7 +105,7 @@ class FileAssociationActivity :
finish()
}
intent.data?.let { data ->
viewModel.dispatchIndent(data, this::finallyDialog)
viewModel.dispatchIndent(data)
}
}

@ -15,14 +15,10 @@ import java.io.File
class FileAssociationViewModel(application: Application) : BaseAssociationViewModel(application) {
val importBookLiveData = MutableLiveData<Uri>()
val onLineImportLive = MutableLiveData<Uri>()
val importBookSourceLive = MutableLiveData<String>()
val importRssSourceLive = MutableLiveData<String>()
val importReplaceRuleLive = MutableLiveData<String>()
val openBookLiveData = MutableLiveData<String>()
val errorLiveData = MutableLiveData<String>()
@Suppress("BlockingMethodInNonBlockingContext")
fun dispatchIndent(uri: Uri, finally: (title: String, msg: String) -> Unit) {
fun dispatchIndent(uri: Uri) {
execute {
lateinit var fileName: String
lateinit var content: String
@ -39,22 +35,7 @@ class FileAssociationViewModel(application: Application) : BaseAssociationViewMo
}
when {
content.isJson() -> {
//暂时根据文件内容判断属于什么
when {
content.contains("bookSourceUrl") ->
importBookSourceLive.postValue(content)
content.contains("sourceUrl") ->
importRssSourceLive.postValue(content)
content.contains("pattern") ->
importReplaceRuleLive.postValue(content)
content.contains("themeName") ->
importTheme(content, finally)
content.contains("name") && content.contains("rule") ->
importTextTocRule(content, finally)
content.contains("name") && content.contains("url") ->
importHttpTTS(content, finally)
else -> errorLiveData.postValue("格式不对")
}
importJson(content)
}
fileName.matches(bookFileRegex) -> {
importBookLiveData.postValue(uri)
@ -68,7 +49,7 @@ class FileAssociationViewModel(application: Application) : BaseAssociationViewMo
}
}.onError {
it.printOnDebug()
errorLiveData.postValue(it.localizedMessage)
errorLive.postValue(it.localizedMessage)
}
}

@ -2,13 +2,11 @@ package io.legado.app.ui.association
import android.app.Application
import androidx.lifecycle.MutableLiveData
import com.jayway.jsonpath.JsonPath
import io.legado.app.R
import io.legado.app.base.BaseViewModel
import io.legado.app.data.appDb
import io.legado.app.data.entities.HttpTTS
import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.ContentProcessor
import io.legado.app.help.http.newCallResponseBody
import io.legado.app.help.http.okHttpClient
import io.legado.app.help.http.text
@ -56,7 +54,6 @@ class ImportHttpTtsViewModel(app: Application) : BaseViewModel(app) {
}
}
appDb.httpTTSDao.insert(*selectSource.toTypedArray())
ContentProcessor.upReplaceRules()
}.onFinally {
finally.invoke()
}
@ -67,16 +64,8 @@ class ImportHttpTtsViewModel(app: Application) : BaseViewModel(app) {
val mText = text.trim()
when {
mText.isJsonObject() -> {
val json = JsonPath.parse(mText)
val urls = json.read<List<String>>("$.sourceUrls")
if (!urls.isNullOrEmpty()) {
urls.forEach {
importSourceUrl(it)
}
} else {
HttpTTS.fromJson(mText).getOrThrow().let {
allSources.add(it)
}
HttpTTS.fromJson(mText).getOrThrow().let {
allSources.add(it)
}
}
mText.isJsonArray() -> HttpTTS.fromJsonArray(mText).getOrThrow().let { items ->

@ -0,0 +1,179 @@
package io.legado.app.ui.association
import android.annotation.SuppressLint
import android.content.Context
import android.content.DialogInterface
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.RecyclerAdapter
import io.legado.app.databinding.DialogRecyclerViewBinding
import io.legado.app.databinding.ItemSourceImportBinding
import io.legado.app.help.config.ThemeConfig
import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.widget.dialog.CodeDialog
import io.legado.app.ui.widget.dialog.WaitDialog
import io.legado.app.utils.GSON
import io.legado.app.utils.setLayout
import io.legado.app.utils.showDialogFragment
import io.legado.app.utils.viewbindingdelegate.viewBinding
import io.legado.app.utils.visible
import splitties.views.onClick
class ImportThemeDialog() : BaseDialogFragment(R.layout.dialog_recycler_view) {
constructor(source: String, finishOnDismiss: Boolean = false) : this() {
arguments = Bundle().apply {
putString("source", source)
putBoolean("finishOnDismiss", finishOnDismiss)
}
}
private val binding by viewBinding(DialogRecyclerViewBinding::bind)
private val viewModel by viewModels<ImportThemeViewModel>()
private val adapter by lazy { SourcesAdapter(requireContext()) }
override fun onStart() {
super.onStart()
setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
if (arguments?.getBoolean("finishOnDismiss") == true) {
activity?.finish()
}
}
@SuppressLint("NotifyDataSetChanged")
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
binding.toolBar.setBackgroundColor(primaryColor)
binding.toolBar.setTitle(R.string.import_book_source)
binding.rotateLoading.show()
binding.recyclerView.layoutManager = LinearLayoutManager(requireContext())
binding.recyclerView.adapter = adapter
binding.tvCancel.visible()
binding.tvCancel.setOnClickListener {
dismissAllowingStateLoss()
}
binding.tvOk.visible()
binding.tvOk.setOnClickListener {
val waitDialog = WaitDialog(requireContext())
waitDialog.show()
viewModel.importSelect {
waitDialog.dismiss()
dismissAllowingStateLoss()
}
}
binding.tvFooterLeft.visible()
binding.tvFooterLeft.setOnClickListener {
val selectAll = viewModel.isSelectAll
viewModel.selectStatus.forEachIndexed { index, b ->
if (b != !selectAll) {
viewModel.selectStatus[index] = !selectAll
}
}
adapter.notifyDataSetChanged()
upSelectText()
}
viewModel.errorLiveData.observe(this) {
binding.rotateLoading.hide()
binding.tvMsg.apply {
text = it
visible()
}
}
viewModel.successLiveData.observe(this) {
binding.rotateLoading.hide()
if (it > 0) {
adapter.setItems(viewModel.allSources)
upSelectText()
} else {
binding.tvMsg.apply {
setText(R.string.wrong_format)
visible()
}
}
}
val source = arguments?.getString("source")
if (source.isNullOrEmpty()) {
dismiss()
return
}
viewModel.importSource(source)
}
private fun upSelectText() {
if (viewModel.isSelectAll) {
binding.tvFooterLeft.text = getString(
R.string.select_cancel_count,
viewModel.selectCount,
viewModel.allSources.size
)
} else {
binding.tvFooterLeft.text = getString(
R.string.select_all_count,
viewModel.selectCount,
viewModel.allSources.size
)
}
}
inner class SourcesAdapter(context: Context) :
RecyclerAdapter<ThemeConfig.Config, ItemSourceImportBinding>(context) {
override fun getViewBinding(parent: ViewGroup): ItemSourceImportBinding {
return ItemSourceImportBinding.inflate(inflater, parent, false)
}
override fun convert(
holder: ItemViewHolder,
binding: ItemSourceImportBinding,
item: ThemeConfig.Config,
payloads: MutableList<Any>
) {
binding.apply {
cbSourceName.isChecked = viewModel.selectStatus[holder.layoutPosition]
cbSourceName.text = item.themeName
val localSource = viewModel.checkSources[holder.layoutPosition]
tvSourceState.text = when {
localSource == null -> "新增"
localSource != item -> "更新"
else -> "已有"
}
}
}
override fun registerListener(holder: ItemViewHolder, binding: ItemSourceImportBinding) {
binding.apply {
cbSourceName.setOnCheckedChangeListener { buttonView, isChecked ->
if (buttonView.isPressed) {
viewModel.selectStatus[holder.layoutPosition] = isChecked
upSelectText()
}
}
root.onClick {
cbSourceName.isChecked = !cbSourceName.isChecked
viewModel.selectStatus[holder.layoutPosition] = cbSourceName.isChecked
upSelectText()
}
tvOpen.setOnClickListener {
val source = viewModel.allSources[holder.layoutPosition]
showDialogFragment(
CodeDialog(
GSON.toJson(source),
disableEdit = false,
requestId = holder.layoutPosition.toString()
)
)
}
}
}
}
}

@ -0,0 +1,106 @@
package io.legado.app.ui.association
import android.app.Application
import androidx.lifecycle.MutableLiveData
import io.legado.app.R
import io.legado.app.base.BaseViewModel
import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.config.ThemeConfig
import io.legado.app.help.http.newCallResponseBody
import io.legado.app.help.http.okHttpClient
import io.legado.app.help.http.text
import io.legado.app.utils.*
class ImportThemeViewModel(app: Application) : BaseViewModel(app) {
val errorLiveData = MutableLiveData<String>()
val successLiveData = MutableLiveData<Int>()
val allSources = arrayListOf<ThemeConfig.Config>()
val checkSources = arrayListOf<ThemeConfig.Config?>()
val selectStatus = arrayListOf<Boolean>()
val isSelectAll: Boolean
get() {
selectStatus.forEach {
if (!it) {
return false
}
}
return true
}
val selectCount: Int
get() {
var count = 0
selectStatus.forEach {
if (it) {
count++
}
}
return count
}
fun importSelect(finally: () -> Unit) {
execute {
selectStatus.forEachIndexed { index, b ->
if (b) {
ThemeConfig.addConfig(allSources[index])
}
}
}.onFinally {
finally.invoke()
}
}
fun importSource(text: String) {
execute {
importSourceAwait(text.trim())
}.onError {
it.printOnDebug()
errorLiveData.postValue(it.localizedMessage ?: "")
}.onSuccess {
comparisonSource()
}
}
private suspend fun importSourceAwait(text: String) {
when {
text.isJsonObject() -> {
GSON.fromJsonObject<ThemeConfig.Config>(text).getOrThrow()?.let {
allSources.add(it)
}
}
text.isJsonArray() -> GSON.fromJsonArray<ThemeConfig.Config>(text).getOrThrow()
?.let { items ->
allSources.addAll(items)
}
text.isAbsUrl() -> {
importSourceUrl(text)
}
else -> throw NoStackTraceException(context.getString(R.string.wrong_format))
}
}
private suspend fun importSourceUrl(url: String) {
okHttpClient.newCallResponseBody {
url(url)
}.text().let {
importSourceAwait(it)
}
}
private fun comparisonSource() {
execute {
allSources.forEach { config ->
val source = ThemeConfig.configList.find {
it.themeName == config.themeName
}
checkSources.add(source)
selectStatus.add(source == null || source != config)
}
successLiveData.postValue(allSources.size)
}
}
}

@ -0,0 +1,179 @@
package io.legado.app.ui.association
import android.annotation.SuppressLint
import android.content.Context
import android.content.DialogInterface
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.RecyclerAdapter
import io.legado.app.data.entities.TxtTocRule
import io.legado.app.databinding.DialogRecyclerViewBinding
import io.legado.app.databinding.ItemSourceImportBinding
import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.widget.dialog.CodeDialog
import io.legado.app.ui.widget.dialog.WaitDialog
import io.legado.app.utils.GSON
import io.legado.app.utils.setLayout
import io.legado.app.utils.showDialogFragment
import io.legado.app.utils.viewbindingdelegate.viewBinding
import io.legado.app.utils.visible
import splitties.views.onClick
class ImportTxtRuleDialog() : BaseDialogFragment(R.layout.dialog_recycler_view) {
constructor(source: String, finishOnDismiss: Boolean = false) : this() {
arguments = Bundle().apply {
putString("source", source)
putBoolean("finishOnDismiss", finishOnDismiss)
}
}
private val binding by viewBinding(DialogRecyclerViewBinding::bind)
private val viewModel by viewModels<ImportTxtRuleViewModel>()
private val adapter by lazy { SourcesAdapter(requireContext()) }
override fun onStart() {
super.onStart()
setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
if (arguments?.getBoolean("finishOnDismiss") == true) {
activity?.finish()
}
}
@SuppressLint("NotifyDataSetChanged")
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
binding.toolBar.setBackgroundColor(primaryColor)
binding.toolBar.setTitle(R.string.import_book_source)
binding.rotateLoading.show()
binding.recyclerView.layoutManager = LinearLayoutManager(requireContext())
binding.recyclerView.adapter = adapter
binding.tvCancel.visible()
binding.tvCancel.setOnClickListener {
dismissAllowingStateLoss()
}
binding.tvOk.visible()
binding.tvOk.setOnClickListener {
val waitDialog = WaitDialog(requireContext())
waitDialog.show()
viewModel.importSelect {
waitDialog.dismiss()
dismissAllowingStateLoss()
}
}
binding.tvFooterLeft.visible()
binding.tvFooterLeft.setOnClickListener {
val selectAll = viewModel.isSelectAll
viewModel.selectStatus.forEachIndexed { index, b ->
if (b != !selectAll) {
viewModel.selectStatus[index] = !selectAll
}
}
adapter.notifyDataSetChanged()
upSelectText()
}
viewModel.errorLiveData.observe(this) {
binding.rotateLoading.hide()
binding.tvMsg.apply {
text = it
visible()
}
}
viewModel.successLiveData.observe(this) {
binding.rotateLoading.hide()
if (it > 0) {
adapter.setItems(viewModel.allSources)
upSelectText()
} else {
binding.tvMsg.apply {
setText(R.string.wrong_format)
visible()
}
}
}
val source = arguments?.getString("source")
if (source.isNullOrEmpty()) {
dismiss()
return
}
viewModel.importSource(source)
}
private fun upSelectText() {
if (viewModel.isSelectAll) {
binding.tvFooterLeft.text = getString(
R.string.select_cancel_count,
viewModel.selectCount,
viewModel.allSources.size
)
} else {
binding.tvFooterLeft.text = getString(
R.string.select_all_count,
viewModel.selectCount,
viewModel.allSources.size
)
}
}
inner class SourcesAdapter(context: Context) :
RecyclerAdapter<TxtTocRule, ItemSourceImportBinding>(context) {
override fun getViewBinding(parent: ViewGroup): ItemSourceImportBinding {
return ItemSourceImportBinding.inflate(inflater, parent, false)
}
override fun convert(
holder: ItemViewHolder,
binding: ItemSourceImportBinding,
item: TxtTocRule,
payloads: MutableList<Any>
) {
binding.apply {
cbSourceName.isChecked = viewModel.selectStatus[holder.layoutPosition]
cbSourceName.text = item.name
val localSource = viewModel.checkSources[holder.layoutPosition]
tvSourceState.text = when {
localSource == null -> "新增"
item != localSource -> "更新"
else -> "已有"
}
}
}
override fun registerListener(holder: ItemViewHolder, binding: ItemSourceImportBinding) {
binding.apply {
cbSourceName.setOnCheckedChangeListener { buttonView, isChecked ->
if (buttonView.isPressed) {
viewModel.selectStatus[holder.layoutPosition] = isChecked
upSelectText()
}
}
root.onClick {
cbSourceName.isChecked = !cbSourceName.isChecked
viewModel.selectStatus[holder.layoutPosition] = cbSourceName.isChecked
upSelectText()
}
tvOpen.setOnClickListener {
val source = viewModel.allSources[holder.layoutPosition]
showDialogFragment(
CodeDialog(
GSON.toJson(source),
disableEdit = false,
requestId = holder.layoutPosition.toString()
)
)
}
}
}
}
}

@ -0,0 +1,107 @@
package io.legado.app.ui.association
import android.app.Application
import androidx.lifecycle.MutableLiveData
import io.legado.app.R
import io.legado.app.base.BaseViewModel
import io.legado.app.data.appDb
import io.legado.app.data.entities.TxtTocRule
import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.http.newCallResponseBody
import io.legado.app.help.http.okHttpClient
import io.legado.app.help.http.text
import io.legado.app.utils.*
class ImportTxtRuleViewModel(app: Application) : BaseViewModel(app) {
val errorLiveData = MutableLiveData<String>()
val successLiveData = MutableLiveData<Int>()
val allSources = arrayListOf<TxtTocRule>()
val checkSources = arrayListOf<TxtTocRule?>()
val selectStatus = arrayListOf<Boolean>()
val isSelectAll: Boolean
get() {
selectStatus.forEach {
if (!it) {
return false
}
}
return true
}
val selectCount: Int
get() {
var count = 0
selectStatus.forEach {
if (it) {
count++
}
}
return count
}
fun importSelect(finally: () -> Unit) {
execute {
val selectSource = arrayListOf<TxtTocRule>()
selectStatus.forEachIndexed { index, b ->
if (b) {
selectSource.add(allSources[index])
}
}
appDb.txtTocRuleDao.insert(*selectSource.toTypedArray())
}.onFinally {
finally.invoke()
}
}
fun importSource(text: String) {
execute {
importSourceAwait(text.trim())
}.onError {
it.printOnDebug()
errorLiveData.postValue(it.localizedMessage ?: "")
}.onSuccess {
comparisonSource()
}
}
private suspend fun importSourceAwait(text: String) {
when {
text.isJsonObject() -> {
GSON.fromJsonObject<TxtTocRule>(text).getOrThrow()?.let {
allSources.add(it)
}
}
text.isJsonArray() -> GSON.fromJsonArray<TxtTocRule>(text).getOrThrow()
?.let { items ->
allSources.addAll(items)
}
text.isAbsUrl() -> {
importSourceUrl(text)
}
else -> throw NoStackTraceException(context.getString(R.string.wrong_format))
}
}
private suspend fun importSourceUrl(url: String) {
okHttpClient.newCallResponseBody {
url(url)
}.text().let {
importSourceAwait(it)
}
}
private fun comparisonSource() {
execute {
allSources.forEach {
val source = appDb.txtTocRuleDao.get(it.id)
checkSources.add(source)
selectStatus.add(source == null || it != source)
}
successLiveData.postValue(allSources.size)
}
}
}

@ -34,6 +34,12 @@ class OnLineImportActivity :
"httpTts" -> showDialogFragment(
ImportHttpTtsDialog(it.second, true)
)
"theme" -> showDialogFragment(
ImportThemeDialog(it.second, true)
)
"txtRule" -> showDialogFragment(
ImportTxtRuleDialog(it.second, true)
)
}
}
viewModel.errorLive.observe(this) {

@ -1,7 +1,6 @@
package io.legado.app.ui.association
import android.app.Application
import androidx.lifecycle.MutableLiveData
import io.legado.app.R
import io.legado.app.help.config.ReadBookConfig
import io.legado.app.help.http.newCallResponseBody
@ -10,8 +9,6 @@ import io.legado.app.help.http.text
import okhttp3.MediaType.Companion.toMediaType
class OnLineImportViewModel(app: Application) : BaseAssociationViewModel(app) {
val successLive = MutableLiveData<Pair<String, String>>()
val errorLive = MutableLiveData<String>()
fun getText(url: String, success: (text: String) -> Unit) {
execute {
@ -76,21 +73,7 @@ class OnLineImportViewModel(app: Application) : BaseAssociationViewModel(app) {
}
else -> {
val json = rs.text("utf-8")
when {
json.contains("bookSourceUrl") ->
successLive.postValue(Pair("bookSource", json))
json.contains("sourceUrl") ->
successLive.postValue(Pair("rssSource", json))
json.contains("replacement") ->
successLive.postValue(Pair("replaceRule", json))
json.contains("themeName") ->
importTheme(json, finally)
json.contains("name") && json.contains("rule") ->
importTextTocRule(json, finally)
json.contains("name") && json.contains("url") ->
successLive.postValue(Pair("httpTts", json))
else -> errorLive.postValue("格式不对")
}
importJson(json)
}
}
}

Loading…
Cancel
Save