pull/2178/head
821938089 2 years ago
parent c80a0905d6
commit 51fbd1f7ba
  1. 1
      app/src/main/java/io/legado/app/constant/PreferKey.kt
  2. 12
      app/src/main/java/io/legado/app/data/dao/RssSourceDao.kt
  3. 4
      app/src/main/java/io/legado/app/help/config/AppConfig.kt
  4. 7
      app/src/main/java/io/legado/app/model/localBook/EpubFile.kt
  5. 15
      app/src/main/java/io/legado/app/model/localBook/LocalBook.kt
  6. 4
      app/src/main/java/io/legado/app/service/CheckSourceService.kt
  7. 21
      app/src/main/java/io/legado/app/ui/book/manage/BookAdapter.kt
  8. 1
      app/src/main/java/io/legado/app/ui/book/manage/BookshelfManageActivity.kt
  9. 5
      app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
  10. 6
      app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt
  11. 9
      app/src/main/java/io/legado/app/ui/config/BackupConfigFragment.kt
  12. 2
      app/src/main/java/io/legado/app/ui/main/rss/RssAdapter.kt
  13. 6
      app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt
  14. 6
      app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt
  15. 31
      app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt
  16. 20
      app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceAdapter.kt
  17. 7
      app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt
  18. 3
      app/src/main/java/io/legado/app/utils/StringExtensions.kt
  19. 4
      app/src/main/res/menu/bookshelf_menage_sel.xml
  20. 4
      app/src/main/res/menu/rss_main_item.xml
  21. 16
      app/src/main/res/menu/rss_source.xml
  22. 5
      app/src/main/res/menu/rss_source_sel.xml
  23. 2
      app/src/main/res/values-es-rES/strings.xml
  24. 2
      app/src/main/res/values-ja-rJP/strings.xml
  25. 2
      app/src/main/res/values-pt-rBR/strings.xml
  26. 2
      app/src/main/res/values-zh-rHK/strings.xml
  27. 2
      app/src/main/res/values-zh-rTW/strings.xml
  28. 2
      app/src/main/res/values-zh/strings.xml
  29. 2
      app/src/main/res/values/strings.xml
  30. 6
      app/src/main/res/xml/pref_config_other.xml

@ -109,6 +109,7 @@ object PreferKey {
const val welcomeShowIcon = "welcomeShowIcon" const val welcomeShowIcon = "welcomeShowIcon"
const val welcomeShowIconDark = "welcomeShowIconDark" const val welcomeShowIconDark = "welcomeShowIconDark"
const val pageTouchSlop = "pageTouchSlop" const val pageTouchSlop = "pageTouchSlop"
const val showAddToShelfAlert = "showAddToShelfAlert"
const val cPrimary = "colorPrimary" const val cPrimary = "colorPrimary"
const val cAccent = "colorAccent" const val cAccent = "colorAccent"

@ -44,6 +44,15 @@ interface RssSourceDao {
@Query("SELECT * FROM rssSources where enabled = 1 order by customOrder") @Query("SELECT * FROM rssSources where enabled = 1 order by customOrder")
fun flowEnabled(): Flow<List<RssSource>> fun flowEnabled(): Flow<List<RssSource>>
@Query("SELECT * FROM rssSources where enabled = 0 order by customOrder")
fun flowDisabled(): Flow<List<RssSource>>
@Query("select * from rssSources where loginUrl is not null and loginUrl != ''")
fun flowLogin(): Flow<List<RssSource>>
@Query("select * from rssSources where sourceGroup is null or sourceGroup = '' or sourceGroup like '%未分组%'")
fun flowNoGroup(): Flow<List<RssSource>>
@Query( @Query(
"""SELECT * FROM rssSources """SELECT * FROM rssSources
where enabled = 1 where enabled = 1
@ -67,6 +76,9 @@ interface RssSourceDao {
@Query("select distinct sourceGroup from rssSources where trim(sourceGroup) <> ''") @Query("select distinct sourceGroup from rssSources where trim(sourceGroup) <> ''")
fun flowGroup(): Flow<List<String>> fun flowGroup(): Flow<List<String>>
@Query("select distinct sourceGroup from rssSources where trim(sourceGroup) <> '' and enabled = 1")
fun flowGroupEnabled(): Flow<List<String>>
@get:Query("select distinct sourceGroup from rssSources where trim(sourceGroup) <> ''") @get:Query("select distinct sourceGroup from rssSources where trim(sourceGroup) <> ''")
val allGroup: List<String> val allGroup: List<String>

@ -310,7 +310,9 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
val recordLog get() = appCtx.getPrefBoolean(PreferKey.recordLog) val recordLog get() = appCtx.getPrefBoolean(PreferKey.recordLog)
val loadCoverOnlyWifi = appCtx.getPrefBoolean(PreferKey.loadCoverOnlyWifi, false) val loadCoverOnlyWifi get() = appCtx.getPrefBoolean(PreferKey.loadCoverOnlyWifi, false)
val showAddToShelfAlert get() = appCtx.getPrefBoolean(PreferKey.showAddToShelfAlert, true)
val doublePageHorizontal: String? val doublePageHorizontal: String?
get() = appCtx.getPrefString(PreferKey.doublePageHorizontal) get() = appCtx.getPrefString(PreferKey.doublePageHorizontal)

@ -18,6 +18,7 @@ import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.net.URLDecoder
import java.nio.charset.Charset import java.nio.charset.Charset
class EpubFile(var book: Book) { class EpubFile(var book: Book) {
@ -117,7 +118,9 @@ class EpubFile(var book: Book) {
* <image width="1038" height="670" xlink:href="..."/> * <image width="1038" height="670" xlink:href="..."/>
* ...titlepage.xhtml * ...titlepage.xhtml
*/ */
if (chapter.url.contains("titlepage.xhtml")) { if (chapter.url.contains("titlepage.xhtml") ||
chapter.url.contains("cover.xhtml")
) {
return "<img src=\"cover.jpeg\" />" return "<img src=\"cover.jpeg\" />"
} }
/*获取当前章节文本*/ /*获取当前章节文本*/
@ -190,7 +193,7 @@ class EpubFile(var book: Book) {
private fun getImage(href: String): InputStream? { private fun getImage(href: String): InputStream? {
if (href == "cover.jpeg") return epubBook?.coverImage?.inputStream if (href == "cover.jpeg") return epubBook?.coverImage?.inputStream
val abHref = href.replace("../", "") val abHref = URLDecoder.decode(href.replace("../", ""), "UTF-8")
return epubBook?.resources?.getByHref(abHref)?.inputStream return epubBook?.resources?.getByHref(abHref)?.inputStream
} }

@ -6,6 +6,7 @@ import androidx.documentfile.provider.DocumentFile
import com.script.SimpleBindings import com.script.SimpleBindings
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.AppLog
import io.legado.app.data.appDb import io.legado.app.data.appDb
import io.legado.app.data.entities.BaseSource import io.legado.app.data.entities.BaseSource
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
@ -16,6 +17,7 @@ import io.legado.app.help.BookHelp
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.model.analyzeRule.AnalyzeUrl import io.legado.app.model.analyzeRule.AnalyzeUrl
import io.legado.app.utils.* import io.legado.app.utils.*
import org.jsoup.nodes.Entities
import splitties.init.appCtx import splitties.init.appCtx
import java.io.* import java.io.*
import java.util.regex.Pattern import java.util.regex.Pattern
@ -81,7 +83,7 @@ object LocalBook {
} }
fun getContent(book: Book, chapter: BookChapter): String? { fun getContent(book: Book, chapter: BookChapter): String? {
return try { val content = try {
when { when {
book.isEpub() -> { book.isEpub() -> {
EpubFile.getContent(book, chapter) EpubFile.getContent(book, chapter)
@ -95,8 +97,15 @@ object LocalBook {
} }
} catch (e: Exception) { } catch (e: Exception) {
e.printOnDebug() e.printOnDebug()
e.localizedMessage AppLog.put("获取本地书籍内容失败\n${e.localizedMessage}", e)
} "获取本地书籍内容失败\n${e.localizedMessage}"
}?.replace("&lt;img", "&lt; img", true)
content ?: return null
return kotlin.runCatching {
Entities.unescape(content)
}.onFailure {
AppLog.put("HTML实体解码失败\n${it.localizedMessage}", it)
}.getOrElse { content }
} }
/** /**

@ -136,7 +136,7 @@ class CheckSourceService : BaseService() {
source.bookSourceComment = source.bookSourceComment source.bookSourceComment = source.bookSourceComment
?.split("\n\n") ?.split("\n\n")
?.filterNot { ?.filterNot {
it.startsWith("Error: ") it.startsWith("// Error: ")
}?.joinToString("\n") }?.joinToString("\n")
//校验搜索书籍 //校验搜索书籍
if (CheckSource.checkSearch) { if (CheckSource.checkSearch) {
@ -186,7 +186,7 @@ class CheckSourceService : BaseService() {
!is NoStackTraceException -> source.addGroup("网站失效") !is NoStackTraceException -> source.addGroup("网站失效")
} }
source.bookSourceComment = source.bookSourceComment =
"Error: ${it.localizedMessage}" + if (source.bookSourceComment.isNullOrBlank()) "// Error: ${it.localizedMessage}" + if (source.bookSourceComment.isNullOrBlank())
"" else "\n\n${source.bookSourceComment}" "" else "\n\n${source.bookSourceComment}"
Debug.updateFinalMessage(source.bookSourceUrl, "校验失败:${it.localizedMessage}") Debug.updateFinalMessage(source.bookSourceUrl, "校验失败:${it.localizedMessage}")
}.onSuccess(searchCoroutine) { }.onSuccess(searchCoroutine) {

@ -14,6 +14,8 @@ import io.legado.app.databinding.ItemArrangeBookBinding
import io.legado.app.lib.theme.backgroundColor import io.legado.app.lib.theme.backgroundColor
import io.legado.app.ui.widget.recycler.DragSelectTouchHelper import io.legado.app.ui.widget.recycler.DragSelectTouchHelper
import io.legado.app.ui.widget.recycler.ItemTouchCallback import io.legado.app.ui.widget.recycler.ItemTouchCallback
import java.util.*
import kotlin.collections.HashSet
class BookAdapter(context: Context, val callBack: CallBack) : class BookAdapter(context: Context, val callBack: CallBack) :
RecyclerAdapter<Book, ItemArrangeBookBinding>(context), RecyclerAdapter<Book, ItemArrangeBookBinding>(context),
@ -121,6 +123,25 @@ class BookAdapter(context: Context, val callBack: CallBack) :
callBack.upSelectCount() callBack.upSelectCount()
} }
fun checkSelectedInterval() {
val selectedPosition = linkedSetOf<Int>()
getItems().forEachIndexed { index, it ->
if (selectedBooks.contains(it)) {
selectedPosition.add(index)
}
}
val minPosition = Collections.min(selectedPosition)
val maxPosition = Collections.max(selectedPosition)
val itemCount = maxPosition - minPosition + 1
for (i in minPosition..maxPosition) {
getItem(i)?.let {
selectedBooks.add(it)
}
}
notifyItemRangeChanged(minPosition, itemCount, bundleOf(Pair("selected", null)))
callBack.upSelectCount()
}
private fun getGroupList(groupId: Long): List<String> { private fun getGroupList(groupId: Long): List<String> {
val groupNames = arrayListOf<String>() val groupNames = arrayListOf<String>()
callBack.groupList.forEach { callBack.groupList.forEach {

@ -192,6 +192,7 @@ class BookshelfManageActivity :
viewModel.upCanUpdate(adapter.selection, false) viewModel.upCanUpdate(adapter.selection, false)
R.id.menu_add_to_group -> selectGroup(addToGroupRequestCode, 0) R.id.menu_add_to_group -> selectGroup(addToGroupRequestCode, 0)
R.id.menu_change_source -> showDialogFragment<SourcePickerDialog>() R.id.menu_change_source -> showDialogFragment<SourcePickerDialog>()
R.id.menu_check_selected_interval -> adapter.checkSelectedInterval()
} }
return false return false
} }

@ -25,6 +25,7 @@ import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.AppWebDav import io.legado.app.help.AppWebDav
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.help.IntentData import io.legado.app.help.IntentData
import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.ReadBookConfig import io.legado.app.help.config.ReadBookConfig
import io.legado.app.help.config.ReadTipConfig import io.legado.app.help.config.ReadTipConfig
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
@ -1147,6 +1148,9 @@ class ReadBookActivity : BaseReadBookActivity(),
override fun finish() { override fun finish() {
ReadBook.book?.let { ReadBook.book?.let {
if (!ReadBook.inBookshelf) { if (!ReadBook.inBookshelf) {
if (!AppConfig.showAddToShelfAlert) {
viewModel.removeFromBookshelf { super.finish() }
} else {
alert(title = getString(R.string.add_to_shelf)) { alert(title = getString(R.string.add_to_shelf)) {
setMessage(getString(R.string.check_add_bookshelf, it.name)) setMessage(getString(R.string.check_add_bookshelf, it.name))
okButton { okButton {
@ -1155,6 +1159,7 @@ class ReadBookActivity : BaseReadBookActivity(),
} }
noButton { viewModel.removeFromBookshelf { super.finish() } } noButton { viewModel.removeFromBookshelf { super.finish() } }
} }
}
} else { } else {
super.finish() super.finish()
} }

@ -66,6 +66,7 @@ class SearchActivity : VMBaseActivity<ActivityBookSearchBinding, SearchViewModel
private var menu: Menu? = null private var menu: Menu? = null
private var precisionSearchMenuItem: MenuItem? = null private var precisionSearchMenuItem: MenuItem? = null
private var groups = linkedSetOf<String>() private var groups = linkedSetOf<String>()
private var isManualStopSearch = false
private val searchFinishCallback: (isEmpty: Boolean) -> Unit = searchFinish@{ isEmpty -> private val searchFinishCallback: (isEmpty: Boolean) -> Unit = searchFinish@{ isEmpty ->
val searchGroup = AppConfig.searchGroup val searchGroup = AppConfig.searchGroup
if (!isEmpty || searchGroup.isEmpty()) return@searchFinish if (!isEmpty || searchGroup.isEmpty()) return@searchFinish
@ -155,6 +156,7 @@ class SearchActivity : VMBaseActivity<ActivityBookSearchBinding, SearchViewModel
override fun onQueryTextSubmit(query: String?): Boolean { override fun onQueryTextSubmit(query: String?): Boolean {
searchView.clearFocus() searchView.clearFocus()
query?.let { query?.let {
isManualStopSearch = false
viewModel.saveSearchKey(query) viewModel.saveSearchKey(query)
viewModel.searchKey = "" viewModel.searchKey = ""
viewModel.search(it) viewModel.search(it)
@ -221,6 +223,7 @@ class SearchActivity : VMBaseActivity<ActivityBookSearchBinding, SearchViewModel
.setPressedColor(ColorUtils.darkenColor(accentColor)) .setPressedColor(ColorUtils.darkenColor(accentColor))
.create() .create()
binding.fbStop.setOnClickListener { binding.fbStop.setOnClickListener {
isManualStopSearch = true
viewModel.stop() viewModel.stop()
binding.refreshProgressBar.isAutoLoading = false binding.refreshProgressBar.isAutoLoading = false
} }
@ -266,6 +269,9 @@ class SearchActivity : VMBaseActivity<ActivityBookSearchBinding, SearchViewModel
* 滚动到底部事件 * 滚动到底部事件
*/ */
private fun scrollToBottom() { private fun scrollToBottom() {
if (isManualStopSearch) {
return
}
if (viewModel.isSearchLiveData.value == false if (viewModel.isSearchLiveData.value == false
&& viewModel.searchKey.isNotEmpty() && viewModel.searchKey.isNotEmpty()
&& loadMoreView.hasMore && loadMoreView.hasMore

@ -3,6 +3,7 @@ package io.legado.app.ui.config
import android.content.SharedPreferences import android.content.SharedPreferences
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.text.Editable
import android.text.InputType import android.text.InputType
import android.view.Menu import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
@ -116,6 +117,14 @@ class BackupConfigFragment : PreferenceFragment(),
InputType.TYPE_TEXT_VARIATION_PASSWORD or InputType.TYPE_CLASS_TEXT InputType.TYPE_TEXT_VARIATION_PASSWORD or InputType.TYPE_CLASS_TEXT
} }
} }
findPreference<EditTextPreference>(PreferKey.webDavDir)?.let {
it.setOnBindEditTextListener { editText ->
editText.text = AppConfig.webDavDir?.toEditable()
}
it.setOnPreferenceChangeListener { _, newValue ->
(newValue as String).isNotBlank()
}
}
upPreferenceSummary(PreferKey.webDavUrl, getPrefString(PreferKey.webDavUrl)) upPreferenceSummary(PreferKey.webDavUrl, getPrefString(PreferKey.webDavUrl))
upPreferenceSummary(PreferKey.webDavAccount, getPrefString(PreferKey.webDavAccount)) upPreferenceSummary(PreferKey.webDavAccount, getPrefString(PreferKey.webDavAccount))
upPreferenceSummary(PreferKey.webDavPassword, getPrefString(PreferKey.webDavPassword)) upPreferenceSummary(PreferKey.webDavPassword, getPrefString(PreferKey.webDavPassword))

@ -63,6 +63,7 @@ class RssAdapter(context: Context, val callBack: CallBack) :
R.id.menu_top -> callBack.toTop(rssSource) R.id.menu_top -> callBack.toTop(rssSource)
R.id.menu_edit -> callBack.edit(rssSource) R.id.menu_edit -> callBack.edit(rssSource)
R.id.menu_del -> callBack.del(rssSource) R.id.menu_del -> callBack.del(rssSource)
R.id.menu_disable -> callBack.disable(rssSource)
} }
true true
} }
@ -74,5 +75,6 @@ class RssAdapter(context: Context, val callBack: CallBack) :
fun toTop(rssSource: RssSource) fun toTop(rssSource: RssSource)
fun edit(rssSource: RssSource) fun edit(rssSource: RssSource)
fun del(rssSource: RssSource) fun del(rssSource: RssSource)
fun disable(rssSource: RssSource)
} }
} }

@ -123,7 +123,7 @@ class RssFragment : VMBaseFragment<RssSourceViewModel>(R.layout.fragment_rss),
private fun initGroupData() { private fun initGroupData() {
groupsFlowJob?.cancel() groupsFlowJob?.cancel()
groupsFlowJob = launch { groupsFlowJob = launch {
appDb.rssSourceDao.flowGroup().conflate().collect { appDb.rssSourceDao.flowGroupEnabled().conflate().collect {
groups.clear() groups.clear()
it.map { group -> it.map { group ->
groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex))
@ -181,4 +181,8 @@ class RssFragment : VMBaseFragment<RssSourceViewModel>(R.layout.fragment_rss),
override fun del(rssSource: RssSource) { override fun del(rssSource: RssSource) {
viewModel.del(rssSource) viewModel.del(rssSource)
} }
override fun disable(rssSource: RssSource) {
viewModel.disable(rssSource)
}
} }

@ -65,7 +65,11 @@ class GroupManageDialog : BaseDialogFragment(R.layout.dialog_recycler_view),
it.map { group -> it.map { group ->
groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex))
} }
adapter.setItems(groups.toList()) adapter.setItems(
groups.sortedWith { o1, o2 ->
o1.cnCompare(o2)
}
)
} }
} }
} }

@ -50,6 +50,9 @@ class RssSourceActivity : VMBaseActivity<ActivityRssSourceBinding, RssSourceView
override val viewModel by viewModels<RssSourceViewModel>() override val viewModel by viewModels<RssSourceViewModel>()
private val importRecordKey = "rssSourceRecordKey" private val importRecordKey = "rssSourceRecordKey"
private val adapter by lazy { RssSourceAdapter(this, this) } private val adapter by lazy { RssSourceAdapter(this, this) }
private val searchView: SearchView by lazy {
binding.titleBar.findViewById(R.id.search_view)
}
private var sourceFlowJob: Job? = null private var sourceFlowJob: Job? = null
private var groups = hashSetOf<String>() private var groups = hashSetOf<String>()
private var groupMenu: SubMenu? = null private var groupMenu: SubMenu? = null
@ -120,10 +123,21 @@ class RssSourceActivity : VMBaseActivity<ActivityRssSourceBinding, RssSourceView
R.id.menu_import_qr -> qrCodeResult.launch() R.id.menu_import_qr -> qrCodeResult.launch()
R.id.menu_group_manage -> showDialogFragment<GroupManageDialog>() R.id.menu_group_manage -> showDialogFragment<GroupManageDialog>()
R.id.menu_import_default -> viewModel.importDefault() R.id.menu_import_default -> viewModel.importDefault()
R.id.menu_enabled_group -> {
searchView.setQuery(getString(R.string.enabled), true)
}
R.id.menu_disabled_group -> {
searchView.setQuery(getString(R.string.disabled), true)
}
R.id.menu_group_login -> {
searchView.setQuery(getString(R.string.need_login), true)
}
R.id.menu_group_null -> {
searchView.setQuery(getString(R.string.no_group), true)
}
R.id.menu_help -> showHelp() R.id.menu_help -> showHelp()
else -> if (item.groupId == R.id.source_group) { else -> if (item.groupId == R.id.source_group) {
binding.titleBar.findViewById<SearchView>(R.id.search_view) searchView.setQuery("group:${item.title}", true)
.setQuery("group:${item.title}", true)
} }
} }
return super.onCompatOptionsItemSelected(item) return super.onCompatOptionsItemSelected(item)
@ -144,6 +158,7 @@ class RssSourceActivity : VMBaseActivity<ActivityRssSourceBinding, RssSourceView
R.id.menu_share_source -> viewModel.saveToFile(adapter.selection) { R.id.menu_share_source -> viewModel.saveToFile(adapter.selection) {
share(it) share(it)
} }
R.id.menu_check_selected_interval -> adapter.checkSelectedInterval()
} }
return true return true
} }
@ -240,6 +255,18 @@ class RssSourceActivity : VMBaseActivity<ActivityRssSourceBinding, RssSourceView
searchKey.isNullOrBlank() -> { searchKey.isNullOrBlank() -> {
appDb.rssSourceDao.flowAll() appDb.rssSourceDao.flowAll()
} }
searchKey == getString(R.string.enabled) -> {
appDb.rssSourceDao.flowEnabled()
}
searchKey == getString(R.string.disabled) -> {
appDb.rssSourceDao.flowDisabled()
}
searchKey == getString(R.string.need_login) -> {
appDb.rssSourceDao.flowLogin()
}
searchKey == getString(R.string.no_group) -> {
appDb.rssSourceDao.flowNoGroup()
}
searchKey.startsWith("group:") -> { searchKey.startsWith("group:") -> {
val key = searchKey.substringAfter("group:") val key = searchKey.substringAfter("group:")
appDb.rssSourceDao.flowGroupSearch(key) appDb.rssSourceDao.flowGroupSearch(key)

@ -17,6 +17,7 @@ import io.legado.app.lib.theme.backgroundColor
import io.legado.app.ui.widget.recycler.DragSelectTouchHelper import io.legado.app.ui.widget.recycler.DragSelectTouchHelper
import io.legado.app.ui.widget.recycler.ItemTouchCallback import io.legado.app.ui.widget.recycler.ItemTouchCallback
import io.legado.app.utils.ColorUtils import io.legado.app.utils.ColorUtils
import java.util.*
class RssSourceAdapter(context: Context, val callBack: CallBack) : class RssSourceAdapter(context: Context, val callBack: CallBack) :
@ -151,6 +152,25 @@ class RssSourceAdapter(context: Context, val callBack: CallBack) :
callBack.upCountView() callBack.upCountView()
} }
fun checkSelectedInterval() {
val selectedPosition = linkedSetOf<Int>()
getItems().forEachIndexed { index, it ->
if (selected.contains(it)) {
selectedPosition.add(index)
}
}
val minPosition = Collections.min(selectedPosition)
val maxPosition = Collections.max(selectedPosition)
val itemCount = maxPosition - minPosition + 1
for (i in minPosition..maxPosition) {
getItem(i)?.let {
selected.add(it)
}
}
notifyItemRangeChanged(minPosition, itemCount, bundleOf(Pair("selected", null)))
callBack.upCountView()
}
private fun showMenu(view: View, position: Int) { private fun showMenu(view: View, position: Int) {
val source = getItem(position) ?: return val source = getItem(position) ?: return
val popupMenu = PopupMenu(context, view) val popupMenu = PopupMenu(context, view)

@ -133,4 +133,11 @@ class RssSourceViewModel(application: Application) : BaseViewModel(application)
} }
} }
fun disable(rssSource: RssSource) {
execute {
rssSource.enabled = false
appDb.rssSourceDao.update(rssSource)
}
}
} }

@ -6,6 +6,7 @@ import io.legado.app.constant.AppPattern.dataUriRegex
import android.icu.text.Collator import android.icu.text.Collator
import android.icu.util.ULocale import android.icu.util.ULocale
import android.net.Uri import android.net.Uri
import android.text.Editable
import java.io.File import java.io.File
import java.util.* import java.util.*
@ -13,6 +14,8 @@ fun String?.safeTrim() = if (this.isNullOrBlank()) null else this.trim()
fun String?.isContentScheme(): Boolean = this?.startsWith("content://") == true fun String?.isContentScheme(): Boolean = this?.startsWith("content://") == true
fun String.toEditable(): Editable = Editable.Factory.getInstance().newEditable(this)
fun String.parseToUri(): Uri { fun String.parseToUri(): Uri {
return if (isContentScheme()) { return if (isContentScheme()) {
Uri.parse(this) Uri.parse(this)

@ -21,4 +21,8 @@
android:id="@+id/menu_change_source" android:id="@+id/menu_change_source"
android:title="@string/change_source_batch" /> android:title="@string/change_source_batch" />
<item
android:id="@+id/menu_check_selected_interval"
android:title="@string/check_selected_interval" />
</menu> </menu>

@ -9,6 +9,10 @@
android:id="@+id/menu_edit" android:id="@+id/menu_edit"
android:title="@string/edit" /> android:title="@string/edit" />
<item
android:id="@+id/menu_disable"
android:title="@string/disable_source" />
<item <item
android:id="@+id/menu_del" android:id="@+id/menu_del"
android:title="@string/delete" /> android:title="@string/delete" />

@ -16,6 +16,22 @@
android:id="@+id/menu_group_manage" android:id="@+id/menu_group_manage"
android:title="@string/group_manage" /> android:title="@string/group_manage" />
<item
android:id="@+id/menu_enabled_group"
android:title="@string/enabled" />
<item
android:id="@+id/menu_disabled_group"
android:title="@string/disabled" />
<item
android:id="@+id/menu_group_login"
android:title="@string/need_login" />
<item
android:id="@+id/menu_group_null"
android:title="@string/no_group" />
<group android:id="@+id/source_group"> <group android:id="@+id/source_group">
</group> </group>

@ -27,11 +27,14 @@
android:title="@string/export_selection" android:title="@string/export_selection"
app:showAsAction="never" /> app:showAsAction="never" />
<item <item
android:id="@+id/menu_share_source" android:id="@+id/menu_share_source"
android:title="@string/share_selected_source" android:title="@string/share_selected_source"
app:showAsAction="never" /> app:showAsAction="never" />
<item
android:id="@+id/menu_check_selected_interval"
android:title="@string/check_selected_interval"
app:showAsAction="never" />
</menu> </menu>

@ -1008,4 +1008,6 @@
<string name="page_touch_slop_summary">滑动多长距离才会触发滑动翻页(系统默认值 %s px)</string> <string name="page_touch_slop_summary">滑动多长距离才会触发滑动翻页(系统默认值 %s px)</string>
<string name="example">Ejemplo</string> <string name="example">Ejemplo</string>
<string name="check_selected_interval">选中所选区间</string> <string name="check_selected_interval">选中所选区间</string>
<string name="show_add_to_shelf_alert_title">返回时提示放入书架</string>
<string name="show_add_to_shelf_alert_summary">阅读未放入书架的书籍在返回时提示放入书架</string>
</resources> </resources>

@ -1011,4 +1011,6 @@
<string name="page_touch_slop_summary">滑动多长距离才会触发滑动翻页(系统默认值 %s px)</string> <string name="page_touch_slop_summary">滑动多长距离才会触发滑动翻页(系统默认值 %s px)</string>
<string name="example"></string> <string name="example"></string>
<string name="check_selected_interval">选中所选区间</string> <string name="check_selected_interval">选中所选区间</string>
<string name="show_add_to_shelf_alert_title">返回时提示放入书架</string>
<string name="show_add_to_shelf_alert_summary">阅读未放入书架的书籍在返回时提示放入书架</string>
</resources> </resources>

@ -1011,4 +1011,6 @@
<string name="page_touch_slop_summary">滑动多长距离才会触发滑动翻页(系统默认值 %s px)</string> <string name="page_touch_slop_summary">滑动多长距离才会触发滑动翻页(系统默认值 %s px)</string>
<string name="example">Exemplo</string> <string name="example">Exemplo</string>
<string name="check_selected_interval">选中所选区间</string> <string name="check_selected_interval">选中所选区间</string>
<string name="show_add_to_shelf_alert_title">返回时提示放入书架</string>
<string name="show_add_to_shelf_alert_summary">阅读未放入书架的书籍在返回时提示放入书架</string>
</resources> </resources>

@ -1008,4 +1008,6 @@
<string name="page_touch_slop_summary">滑动多长距离才会触发滑动翻页(系统默认值 %s px)</string> <string name="page_touch_slop_summary">滑动多长距离才会触发滑动翻页(系统默认值 %s px)</string>
<string name="example">示例</string> <string name="example">示例</string>
<string name="check_selected_interval">选中所选区间</string> <string name="check_selected_interval">选中所选区间</string>
<string name="show_add_to_shelf_alert_title">返回时提示放入书架</string>
<string name="show_add_to_shelf_alert_summary">阅读未放入书架的书籍在返回时提示放入书架</string>
</resources> </resources>

@ -1010,4 +1010,6 @@
<string name="page_touch_slop_summary">滑动多长距离才会触发滑动翻页(系统默认值 %s px)</string> <string name="page_touch_slop_summary">滑动多长距离才会触发滑动翻页(系统默认值 %s px)</string>
<string name="example">示例</string> <string name="example">示例</string>
<string name="check_selected_interval">选中所选区间</string> <string name="check_selected_interval">选中所选区间</string>
<string name="show_add_to_shelf_alert_title">返回时提示放入书架</string>
<string name="show_add_to_shelf_alert_summary">阅读未放入书架的书籍在返回时提示放入书架</string>
</resources> </resources>

@ -1010,4 +1010,6 @@
<string name="page_touch_slop_summary">滑动多长距离才会触发滑动翻页(系统默认值 %s px)</string> <string name="page_touch_slop_summary">滑动多长距离才会触发滑动翻页(系统默认值 %s px)</string>
<string name="example">示例</string> <string name="example">示例</string>
<string name="check_selected_interval">选中所选区间</string> <string name="check_selected_interval">选中所选区间</string>
<string name="show_add_to_shelf_alert_title">返回时提示放入书架</string>
<string name="show_add_to_shelf_alert_summary">阅读未放入书架的书籍在返回时提示放入书架</string>
</resources> </resources>

@ -1011,4 +1011,6 @@
<string name="page_touch_slop_dialog_title">滑动翻页阈值(0 = 系统默认值)</string> <string name="page_touch_slop_dialog_title">滑动翻页阈值(0 = 系统默认值)</string>
<string name="page_touch_slop_summary">滑动多长距离才会触发滑动翻页(系统默认值 %s px)</string> <string name="page_touch_slop_summary">滑动多长距离才会触发滑动翻页(系统默认值 %s px)</string>
<string name="check_selected_interval">选中所选区间</string> <string name="check_selected_interval">选中所选区间</string>
<string name="show_add_to_shelf_alert_title">返回时提示放入书架</string>
<string name="show_add_to_shelf_alert_summary">阅读未放入书架的书籍在返回时提示放入书架</string>
</resources> </resources>

@ -112,6 +112,12 @@
android:summary="@string/auto_clear_expired_summary" android:summary="@string/auto_clear_expired_summary"
android:title="@string/auto_clear_expired" /> android:title="@string/auto_clear_expired" />
<io.legado.app.lib.prefs.SwitchPreference
android:defaultValue="true"
android:key="showAddToShelfAlert"
android:summary="@string/show_add_to_shelf_alert_summary"
android:title="@string/show_add_to_shelf_alert_title" />
<io.legado.app.lib.prefs.Preference <io.legado.app.lib.prefs.Preference
android:key="webPort" android:key="webPort"
android:title="@string/web_port_title" android:title="@string/web_port_title"

Loading…
Cancel
Save