@@ -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
|
||||||
@@ -77,11 +79,12 @@ object LocalBook {
|
|||||||
throw TocEmptyException(appCtx.getString(R.string.chapter_list_empty))
|
throw TocEmptyException(appCtx.getString(R.string.chapter_list_empty))
|
||||||
}
|
}
|
||||||
val lh = LinkedHashSet(chapters)
|
val lh = LinkedHashSet(chapters)
|
||||||
|
lh.forEachIndexed { index, bookChapter -> bookChapter.index = index }
|
||||||
return ArrayList(lh)
|
return ArrayList(lh)
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +98,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("<img", "< 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
|
||||||
@@ -1148,13 +1149,17 @@ class ReadBookActivity : BaseReadBookActivity(),
|
|||||||
override fun finish() {
|
override fun finish() {
|
||||||
ReadBook.book?.let {
|
ReadBook.book?.let {
|
||||||
if (!ReadBook.inBookshelf) {
|
if (!ReadBook.inBookshelf) {
|
||||||
alert(title = getString(R.string.add_to_shelf)) {
|
if (!AppConfig.showAddToShelfAlert) {
|
||||||
setMessage(getString(R.string.check_add_bookshelf, it.name))
|
viewModel.removeFromBookshelf { super.finish() }
|
||||||
okButton {
|
} else {
|
||||||
ReadBook.inBookshelf = true
|
alert(title = getString(R.string.add_to_shelf)) {
|
||||||
setResult(Activity.RESULT_OK)
|
setMessage(getString(R.string.check_add_bookshelf, it.name))
|
||||||
|
okButton {
|
||||||
|
ReadBook.inBookshelf = true
|
||||||
|
setResult(Activity.RESULT_OK)
|
||||||
|
}
|
||||||
|
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"
|
||||||
|
|||||||
Reference in New Issue
Block a user