给书源点赞

pull/2208/head
shen 2 years ago
parent ca90ff24db
commit c613ba61ea
  1. 2
      app/src/main/java/io/legado/app/api/controller/BookSourceController.kt
  2. 45
      app/src/main/java/io/legado/app/help/config/SourceConfig.kt
  3. 50
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceAdapter.kt
  4. 8
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceDialog.kt
  5. 35
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceViewModel.kt
  6. 2
      app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditViewModel.kt
  7. 8
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt
  8. 2
      app/src/main/java/io/legado/app/ui/main/explore/ExploreAdapter.kt
  9. 7
      app/src/main/res/drawable/ic_praise.xml
  10. 57
      app/src/main/res/layout/item_change_source.xml

@ -5,6 +5,7 @@ import android.text.TextUtils
import io.legado.app.api.ReturnData import io.legado.app.api.ReturnData
import io.legado.app.data.appDb import io.legado.app.data.appDb
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.help.config.SourceConfig
import io.legado.app.utils.GSON import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonArray import io.legado.app.utils.fromJsonArray
@ -70,6 +71,7 @@ object BookSourceController {
GSON.fromJsonArray<BookSource>(postData).getOrThrow()?.let { GSON.fromJsonArray<BookSource>(postData).getOrThrow()?.let {
it.forEach { source -> it.forEach { source ->
appDb.bookSourceDao.delete(source) appDb.bookSourceDao.delete(source)
SourceConfig.removeSource(source.bookSourceUrl)
} }
} }
}.onFailure { }.onFailure {

@ -0,0 +1,45 @@
package io.legado.app.help.config
import android.content.Context.MODE_PRIVATE
import androidx.core.content.edit
import splitties.init.appCtx
object SourceConfig {
private val sp = appCtx.getSharedPreferences("SourceConfig", MODE_PRIVATE)
fun setBookScore(origin: String, name: String, author: String, score: Int) {
sp.edit {
val preScore = getBookScore(origin, name, author)
var newScore = score
if (preScore != 0) {
newScore = score - preScore
}
putInt(origin, getSourceScore(origin) + newScore)
putInt("${origin}_${name}_${author}", score)
}
}
fun getBookScore(origin: String, name: String, author: String): Int {
return sp.getInt("${origin}_${name}_${author}", 0)
}
fun getSourceScore(origin: String): Int {
return sp.getInt(origin, 0)
}
fun removeSource(origin: String) {
sp.all.keys.filter {
it.startsWith(origin)
}.let {
sp.edit {
it.forEach {
remove(it)
}
}
}
}
}

@ -1,16 +1,19 @@
package io.legado.app.ui.book.changesource package io.legado.app.ui.book.changesource
import android.content.Context import android.content.Context
import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.view.isVisible
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.adapter.DiffRecyclerAdapter import io.legado.app.base.adapter.DiffRecyclerAdapter
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchBook
import io.legado.app.databinding.ItemChangeSourceBinding import io.legado.app.databinding.ItemChangeSourceBinding
import io.legado.app.utils.gone
import io.legado.app.utils.invisible import io.legado.app.utils.invisible
import io.legado.app.utils.visible import io.legado.app.utils.visible
import splitties.views.onLongClick import splitties.views.onLongClick
@ -68,10 +71,55 @@ class ChangeBookSourceAdapter(
} }
} }
} }
val score = callBack.getBookScore(item)
if (score > 0) {
binding.ivBad.gone()
binding.ivGood.visible()
binding.ivGood.drawable.setTint(Color.parseColor("#D50000"))
} else if (score < 0) {
binding.ivGood.gone()
binding.ivBad.visible()
binding.ivBad.drawable.setTint(Color.parseColor("#2962FF"))
} else {
binding.ivGood.visible()
binding.ivBad.visible()
binding.ivGood.drawable.setTint(Color.parseColor("#FF8A80"))
binding.ivBad.drawable.setTint(Color.parseColor("#82B1FF"))
}
} }
} }
override fun registerListener(holder: ItemViewHolder, binding: ItemChangeSourceBinding) { override fun registerListener(holder: ItemViewHolder, binding: ItemChangeSourceBinding) {
binding.ivGood.setOnClickListener {
if (binding.ivBad.isVisible) {
binding.ivGood.drawable.setTint(Color.parseColor("#D50000"))
binding.ivBad.gone()
getItem(holder.layoutPosition)?.let {
callBack.setBookScore(it, 1)
}
} else {
binding.ivGood.drawable.setTint(Color.parseColor("#FF8A80"))
binding.ivBad.visible()
getItem(holder.layoutPosition)?.let {
callBack.setBookScore(it, 0)
}
}
}
binding.ivBad.setOnClickListener {
if (binding.ivGood.isVisible) {
binding.ivBad.drawable.setTint(Color.parseColor("#2962FF"))
binding.ivGood.gone()
getItem(holder.layoutPosition)?.let {
callBack.setBookScore(it, -1)
}
} else {
binding.ivBad.drawable.setTint(Color.parseColor("#82B1FF"))
binding.ivGood.visible()
getItem(holder.layoutPosition)?.let {
callBack.setBookScore(it, 0)
}
}
}
holder.itemView.setOnClickListener { holder.itemView.setOnClickListener {
getItem(holder.layoutPosition)?.let { getItem(holder.layoutPosition)?.let {
if (it.bookUrl != callBack.bookUrl) { if (it.bookUrl != callBack.bookUrl) {
@ -120,5 +168,7 @@ class ChangeBookSourceAdapter(
fun editSource(searchBook: SearchBook) fun editSource(searchBook: SearchBook)
fun disableSource(searchBook: SearchBook) fun disableSource(searchBook: SearchBook)
fun deleteSource(searchBook: SearchBook) fun deleteSource(searchBook: SearchBook)
fun setBookScore(searchBook: SearchBook, score: Int)
fun getBookScore(searchBook: SearchBook): Int
} }
} }

@ -294,6 +294,14 @@ class ChangeBookSourceDialog() : BaseDialogFragment(R.layout.dialog_book_change_
} }
} }
override fun setBookScore(searchBook: SearchBook, score: Int) {
viewModel.setBookScore(searchBook,score)
}
override fun getBookScore(searchBook: SearchBook): Int {
return viewModel.getBookScore(searchBook)
}
private fun changeSource(searchBook: SearchBook, onSuccess: (() -> Unit)? = null) { private fun changeSource(searchBook: SearchBook, onSuccess: (() -> Unit)? = null) {
waitDialog.setText(R.string.load_toc) waitDialog.setText(R.string.load_toc)
waitDialog.show() waitDialog.show()

@ -16,6 +16,7 @@ import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchBook
import io.legado.app.exception.NoStackTraceException import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.SourceConfig
import io.legado.app.help.coroutine.CompositeCoroutine import io.legado.app.help.coroutine.CompositeCoroutine
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.webBook.WebBook import io.legado.app.model.webBook.WebBook
@ -82,7 +83,27 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a
searchCallback = null searchCallback = null
} }
}.map { }.map {
searchBooks.sortedBy { it.originOrder } searchBooks.sortedWith(object : Comparator<SearchBook> {
override fun compare(o1: SearchBook, o2: SearchBook): Int {
val o1bs = SourceConfig.getBookScore(o1.origin, o1.name, o1.author)
val o2bs = SourceConfig.getBookScore(o2.origin, o2.name, o2.author)
if (o1bs - o2bs > 0) {
return -1
} else if (o1bs - o2bs < 0) {
return 1
} else {
val o1ss = SourceConfig.getSourceScore(o1.origin)
val o2ss = SourceConfig.getSourceScore(o2.origin)
if (o1ss - o2ss > 0) {
return -1
} else if (o1ss - o2ss < 0) {
return 1
} else {
return o1.originOrder - o2.originOrder
}
}
}
})
}.flowOn(IO) }.flowOn(IO)
@Volatile @Volatile
@ -351,6 +372,7 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a
appDb.bookSourceDao.getBookSource(searchBook.origin)?.let { source -> appDb.bookSourceDao.getBookSource(searchBook.origin)?.let { source ->
appDb.bookSourceDao.delete(source) appDb.bookSourceDao.delete(source)
appDb.searchBookDao.delete(searchBook) appDb.searchBookDao.delete(searchBook)
SourceConfig.removeSource(source.bookSourceUrl)
} }
} }
searchBooks.remove(searchBook) searchBooks.remove(searchBook)
@ -379,6 +401,17 @@ open class ChangeBookSourceViewModel(application: Application) : BaseViewModel(a
} }
} }
fun setBookScore(searchBook: SearchBook, score: Int) {
execute {
SourceConfig.setBookScore(searchBook.origin, searchBook.name, searchBook.author, score)
searchCallback?.upAdapter()
}
}
fun getBookScore(searchBook: SearchBook): Int {
return SourceConfig.getBookScore(searchBook.origin, searchBook.name, searchBook.author)
}
interface SourceCallback { interface SourceCallback {
fun searchSuccess(searchBook: SearchBook) fun searchSuccess(searchBook: SearchBook)

@ -7,6 +7,7 @@ import io.legado.app.data.appDb
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.exception.NoStackTraceException import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.RuleComplete import io.legado.app.help.RuleComplete
import io.legado.app.help.config.SourceConfig
import io.legado.app.help.http.CookieStore import io.legado.app.help.http.CookieStore
import io.legado.app.help.http.newCallStrResponse import io.legado.app.help.http.newCallStrResponse
import io.legado.app.help.http.okHttpClient import io.legado.app.help.http.okHttpClient
@ -40,6 +41,7 @@ class BookSourceEditViewModel(application: Application) : BaseViewModel(applicat
oldSourceUrl?.let { oldSourceUrl?.let {
if (oldSourceUrl != source.bookSourceUrl) { if (oldSourceUrl != source.bookSourceUrl) {
appDb.bookSourceDao.delete(it) appDb.bookSourceDao.delete(it)
SourceConfig.removeSource(it)
} }
} }
oldSourceUrl = source.bookSourceUrl oldSourceUrl = source.bookSourceUrl

@ -5,6 +5,7 @@ import android.text.TextUtils
import io.legado.app.base.BaseViewModel import io.legado.app.base.BaseViewModel
import io.legado.app.data.appDb import io.legado.app.data.appDb
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.help.config.SourceConfig
import io.legado.app.utils.* import io.legado.app.utils.*
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
@ -38,7 +39,12 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
} }
fun del(vararg sources: BookSource) { fun del(vararg sources: BookSource) {
execute { appDb.bookSourceDao.delete(*sources) } execute {
appDb.bookSourceDao.delete(*sources)
sources.forEach {
SourceConfig.removeSource(it.bookSourceUrl)
}
}
} }
fun update(vararg bookSource: BookSource) { fun update(vararg bookSource: BookSource) {

@ -15,6 +15,7 @@ import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.rule.ExploreKind import io.legado.app.data.entities.rule.ExploreKind
import io.legado.app.databinding.ItemFilletTextBinding import io.legado.app.databinding.ItemFilletTextBinding
import io.legado.app.databinding.ItemFindBookBinding import io.legado.app.databinding.ItemFindBookBinding
import io.legado.app.help.config.SourceConfig
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.lib.theme.accentColor import io.legado.app.lib.theme.accentColor
import io.legado.app.ui.login.SourceLoginActivity import io.legado.app.ui.login.SourceLoginActivity
@ -174,6 +175,7 @@ class ExploreAdapter(context: Context, val callBack: CallBack) :
} }
R.id.menu_del -> Coroutine.async(callBack.scope) { R.id.menu_del -> Coroutine.async(callBack.scope) {
appDb.bookSourceDao.delete(source) appDb.bookSourceDao.delete(source)
SourceConfig.removeSource(source.bookSourceUrl)
} }
} }
true true

@ -0,0 +1,7 @@
<vector android:height="24dp" android:viewportHeight="1024"
android:viewportWidth="1024" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M939.4,423.4c-23,-37.3 -62.9,-60.8 -107,-63.2 -2.8,-0.4 -5.5,-0.6 -8.3,-0.6l-152.2,-0.1c6.6,-29 10,-58.7 10,-88.7 0,-28.2 -3.2,-57.1 -9.6,-86 -0.4,-2 -1.1,-4 -1.9,-5.9 -15.8,-57.3 -67.4,-96.8 -127.3,-96.8 -72.8,0 -132.1,59.3 -132.1,132.1 0,3.3 0.1,6.7 0.4,10 -2.2,57.2 -32.1,109.9 -80.3,141.4 -14.4,9.4 -18.5,28.8 -9.1,43.2 9.4,14.4 28.8,18.5 43.2,9.1 65.8,-42.9 106.4,-115.3 108.7,-193.7 0,-1.3 -0,-2.7 -0.1,-4 -0.2,-2 -0.3,-4 -0.3,-6 0,-38.4 31.2,-69.6 69.6,-69.6 32.6,0 60.5,22.2 67.8,54 0.4,1.5 0.8,3 1.4,4.5 4.7,22.8 7.1,45.6 7.1,67.7 0,37.5 -6.2,74.4 -18.5,109.6 -3.3,9.5 -1.8,20.1 4,28.3 5.9,8.2 15.3,13.1 25.4,13.2l193.2,0.1c1.4,0.3 2.9,0.4 4.3,0.5 24.1,1 45.9,13.6 58.6,34.1 7.8,12.3 11.4,26.8 10.4,41.9 -0.1,0.9 -0.1,1.9 -0.1,2.9 0,0.9 0,1.7 0.1,2.5 0,0.3 -0,0.7 -0.1,1 -0.1,0.4 -0.2,0.8 -0.3,1.2l-75,330.4c-0.9,1.3 -1.7,2.6 -2.4,4 -5.9,11.8 -14.7,21.5 -25.3,28.2 -10.7,6.8 -23.1,10.6 -35.8,11.1 -0.9,-0.1 -513.8,-0.2 -513.8,-0.2 -0.5,-0 -0.9,-0 -1.4,-0 0,0 -111,0.2 -112.7,0.5 -1.9,0 -3.4,-1.5 -3.4,-3.4l0.3,-416.3c0,-1.9 1.5,-3.4 3,-3.5l1.2,0.1c1.2,0.1 2.7,0.3 3.5,0.2l83.8,-0.2 0,339.4c0,17.3 14,31.2 31.2,31.2s31.2,-14 31.2,-31.2L281,435.4c0,-1.8 0,-8.6 0,-10.4 0,-17.3 -14,-30.9 -31.2,-30.9 -1.5,0 -117.5,0.3 -119.4,0.3 -36.3,0 -65.9,29.6 -65.9,65.9l-0.3,416.3c0,36.3 29.6,65.9 65.9,65.9 2.5,0 111.4,-0.5 111.4,-0.5 0.5,0 0.9,0 1.4,0 0,0 511.5,0.3 512.5,0.3 25.5,0 50.3,-7.2 71.6,-20.7 19.6,-12.3 35.8,-29.9 46.8,-51 3.7,-5.6 6.4,-11.9 8.3,-18.6 0.1,-0.4 0.2,-0.8 0.3,-1.2l74.9,-330.3c1.6,-6.2 2.3,-12.6 2,-19C960.9,473.4 953.9,446.3 939.4,423.4z"/>
<path android:fillColor="#FF000000" android:pathData="M450,518.7c-17.3,0 -31.2,14 -31.2,31.2l0,30.5c0,17.3 14,31.2 31.2,31.2 17.3,0 31.2,-14 31.2,-31.2l0,-30.5C481.3,532.6 467.3,518.7 450,518.7z"/>
<path android:fillColor="#FF000000" android:pathData="M693.8,518.7c-17.3,0 -31.2,14 -31.2,31.2l0,30.5c0,17.3 14,31.2 31.2,31.2 17.3,0 31.2,-14 31.2,-31.2l0,-30.5C725,532.6 711.1,518.7 693.8,518.7z"/>
<path android:fillColor="#FF000000" android:pathData="M648.9,660.1c-14.3,-9.4 -33.6,-5.4 -43.2,8.8 -0.1,0.2 -13.6,19.8 -34.2,19.8 -20,0 -32.4,-18.1 -33.3,-19.4 -9.2,-14.4 -28.3,-18.8 -42.8,-9.8 -14.7,9.1 -19.2,28.4 -10.1,43 11.2,18 42,48.6 86.2,48.6 44,0 75.1,-30.3 86.6,-48.2C667.4,688.5 663.2,669.5 648.9,660.1z"/>
</vector>

@ -1,56 +1,87 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="?android:attr/selectableItemBackground" android:background="?android:attr/selectableItemBackground"
android:paddingLeft="10dp"
android:paddingTop="10dp"
android:paddingBottom="10dp"
tools:ignore="RtlHardcoded,RtlSymmetry"> tools:ignore="RtlHardcoded,RtlSymmetry">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_good"
android:layout_width="40dp"
android:layout_height="0dp"
android:layout_marginTop="5dp"
android:padding="5dp"
android:src="@drawable/ic_praise"
app:layout_constraintBottom_toTopOf="@+id/iv_bad"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tint="@color/md_red_50" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_bad"
android:layout_width="40dp"
android:layout_height="0dp"
android:layout_marginBottom="5dp"
android:padding="5dp"
android:rotationX="180"
android:src="@drawable/ic_praise"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/iv_good"
app:tint="@color/md_blue_50" />
<TextView <TextView
android:id="@+id/tv_origin" android:id="@+id/tv_origin"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="40dp"
android:layout_marginTop="10dp"
android:singleLine="true" android:singleLine="true"
android:textColor="@color/primaryText" android:textColor="@color/primaryText"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/tv_author" /> app:layout_constraintRight_toLeftOf="@+id/tv_author"
app:layout_constraintTop_toTopOf="parent" />
<TextView <TextView
android:id="@+id/tv_author" android:id="@+id/tv_author"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:maxWidth="160dp"
android:singleLine="true" android:singleLine="true"
android:textColor="@color/secondaryText" android:textColor="@color/secondaryText"
android:maxWidth="160dp" app:layout_constraintRight_toLeftOf="@+id/iv_checked"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintRight_toLeftOf="@+id/iv_checked" />
<TextView <TextView
android:id="@+id/tv_last" android:id="@+id/tv_last"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="40dp"
android:layout_marginBottom="10dp"
android:singleLine="true" android:singleLine="true"
android:textColor="@color/secondaryText" android:textColor="@color/secondaryText"
app:layout_constraintTop_toBottomOf="@+id/tv_origin"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/iv_checked" /> app:layout_constraintRight_toLeftOf="@+id/iv_checked"
app:layout_constraintTop_toBottomOf="@+id/tv_origin" />
<androidx.appcompat.widget.AppCompatImageView <androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_checked" android:id="@+id/iv_checked"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:padding="8dp" android:padding="8dp"
android:src="@drawable/ic_check" android:src="@drawable/ic_check"
android:visibility="invisible" android:visibility="invisible"
app:tint="@color/primaryText" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" /> app:tint="@color/primaryText"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
Loading…
Cancel
Save