Merge branches 'mabdc' and 'master' of https://github.com/gedoor/legado into mabdc

# Conflicts:
#	app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt
pull/36/head
mabdc 5 years ago
commit 0d68701842
  1. 4
      app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt
  2. 15
      app/src/main/java/io/legado/app/data/dao/RssSourceDao.kt
  3. 5
      app/src/main/java/io/legado/app/data/entities/RssSource.kt
  4. 40
      app/src/main/java/io/legado/app/help/storage/Backup.kt
  5. 4
      app/src/main/java/io/legado/app/help/storage/WebDavHelp.kt
  6. 8
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
  7. 28
      app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditActivity.kt
  8. 2
      app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditViewModel.kt
  9. 41
      app/src/main/java/io/legado/app/ui/rss/source/manage/DiffCallBack.kt
  10. 59
      app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt
  11. 79
      app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceAdapter.kt
  12. 18
      app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt
  13. 2
      app/src/main/res/layout/item_rss_source.xml
  14. 9
      app/src/main/res/values/strings.xml

@ -11,7 +11,7 @@ interface BookSourceDao {
@Query("select * from book_sources order by customOrder asc")
fun liveDataAll(): LiveData<List<BookSource>>
@Query("select * from book_sources where bookSourceName like :searchKey or `bookSourceGroup` like :searchKey or bookSourceUrl like :searchKey order by customOrder asc")
@Query("select * from book_sources where bookSourceName like :searchKey or bookSourceGroup like :searchKey or bookSourceUrl like :searchKey order by customOrder asc")
fun liveDataSearch(searchKey: String = ""): LiveData<List<BookSource>>
@Query("select * from book_sources where enabledExplore = 1 and exploreUrl is not null and exploreUrl <> '' order by customOrder asc")
@ -23,7 +23,7 @@ interface BookSourceDao {
@Query("select bookSourceGroup from book_sources where bookSourceGroup is not null and bookSourceGroup <> ''")
fun liveGroup(): LiveData<List<String>>
@Query("select distinct enabled from book_sources where bookSourceName like :searchKey or `bookSourceGroup` like :searchKey or bookSourceUrl like :searchKey")
@Query("select distinct enabled from book_sources where bookSourceName like :searchKey or bookSourceGroup like :searchKey or bookSourceUrl like :searchKey")
fun searchIsEnable(searchKey: String = ""): List<Boolean>
@Query("update book_sources set enabled = 1 where bookSourceUrl in (:sourceUrls)")

@ -16,9 +16,24 @@ interface RssSourceDao {
@Query("SELECT * FROM rssSources")
fun liveAll(): LiveData<List<RssSource>>
@Query("SELECT * FROM rssSources where sourceName like :key or sourceUrl like :key or sourceGroup like :key")
fun liveSearch(key: String): LiveData<List<RssSource>>
@Query("SELECT * FROM rssSources where enabled = 1")
fun liveEnabled(): LiveData<List<RssSource>>
@Query("select sourceGroup from rssSources where sourceGroup is not null and sourceGroup <> ''")
fun liveGroup(): LiveData<List<String>>
@Query("update rssSources set enabled = 1 where sourceUrl in (:sourceUrls)")
fun enableSection(vararg sourceUrls: String)
@Query("update rssSources set enabled = 0 where sourceUrl in (:sourceUrls)")
fun disableSection(vararg sourceUrls: String)
@Query("delete from rssSources where sourceUrl in (:sourceUrls)")
fun delSection(vararg sourceUrls: String)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(vararg rssSource: RssSource)

@ -8,10 +8,11 @@ import kotlinx.android.parcel.Parcelize
@Parcelize
@Entity(tableName = "rssSources")
data class RssSource(
var sourceName: String = "",
@PrimaryKey
var sourceUrl: String = "",
var iconUrl: String = "",
var sourceName: String = "",
var sourceIcon: String = "",
var sourceGroup: String? = null,
var enabled: Boolean = true,
var ruleGuid: String? = null,
var ruleTitle: String? = null,

@ -45,27 +45,43 @@ object Backup {
}
private fun backupBookshelf(path: String) {
val json = GSON.toJson(App.db.bookDao().allBooks)
val file = FileHelp.getFile(path + File.separator + "bookshelf.json")
file.writeText(json)
App.db.bookDao().allBooks.let {
if (it.isNotEmpty()) {
val json = GSON.toJson(it)
val file = FileHelp.getFile(path + File.separator + "bookshelf.json")
file.writeText(json)
}
}
}
private fun backupBookSource(path: String) {
val json = GSON.toJson(App.db.bookSourceDao().all)
val file = FileHelp.getFile(path + File.separator + "bookSource.json")
file.writeText(json)
App.db.bookSourceDao().all.let {
if (it.isNotEmpty()) {
val json = GSON.toJson(it)
val file = FileHelp.getFile(path + File.separator + "bookSource.json")
file.writeText(json)
}
}
}
private fun backupRssSource(path: String) {
val json = GSON.toJson(App.db.rssSourceDao().all)
val file = FileHelp.getFile(path + File.separator + "rssSource.json")
file.writeText(json)
App.db.rssSourceDao().all.let {
if (it.isNotEmpty()) {
val json = GSON.toJson(it)
val file = FileHelp.getFile(path + File.separator + "rssSource.json")
file.writeText(json)
}
}
}
private fun backupReplaceRule(path: String) {
val json = GSON.toJson(App.db.replaceRuleDao().all)
val file = FileHelp.getFile(path + File.separator + "replaceRule.json")
file.writeText(json)
App.db.replaceRuleDao().all.let {
if (it.isNotEmpty()) {
val json = GSON.toJson(it)
val file = FileHelp.getFile(path + File.separator + "replaceRule.json")
file.writeText(json)
}
}
}
private fun backupPreference(path: String) {

@ -39,8 +39,8 @@ object WebDavHelp {
val url = getWebDavUrl()
val names = arrayListOf<String>()
if (!url.isNullOrBlank() && initWebDav()) {
val files = WebDav(url + "legado/").listFiles()
files.reversed()
var files = WebDav(url + "legado/").listFiles()
files = files.reversed()
for (index: Int in 0 until min(10, files.size)) {
files[index].displayName?.let {
names.add(it)

@ -135,12 +135,8 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
}
bookSourceLiveDate?.observe(this, Observer {
search_view.queryHint = getString(R.string.search_book_source_num, it.size)
val diffResult = DiffUtil.calculateDiff(
DiffCallBack(
adapter.getItems(),
it
)
)
val diffResult = DiffUtil
.calculateDiff(DiffCallBack(adapter.getItems(), it))
adapter.setItemsNoNotify(it)
diffResult.dispatchUpdatesTo(adapter)
})

@ -27,6 +27,7 @@ import io.legado.app.utils.getViewModel
import kotlinx.android.synthetic.main.activity_book_source_edit.*
import org.jetbrains.anko.displayMetrics
import org.jetbrains.anko.startActivity
import org.jetbrains.anko.toast
import kotlin.math.abs
class RssSourceEditActivity :
@ -111,7 +112,8 @@ class RssSourceEditActivity :
sourceEntities.apply {
add(EditEntity("sourceName", rssSource?.sourceName, R.string.rss_source_name))
add(EditEntity("sourceUrl", rssSource?.sourceUrl, R.string.rss_source_url))
add(EditEntity("iconUrl", rssSource?.iconUrl, R.string.rss_source_icon))
add(EditEntity("sourceIcon", rssSource?.sourceIcon, R.string.rss_source_icon))
add(EditEntity("sourceGroup", rssSource?.sourceGroup, R.string.rss_source_group))
add(EditEntity("ruleTitle", rssSource?.ruleTitle, R.string.rss_rule_title))
add(EditEntity("ruleAuthor", rssSource?.ruleAuthor, R.string.rss_rule_author))
add(EditEntity("ruleGuid", rssSource?.ruleGuid, R.string.rss_rule_guid))
@ -142,20 +144,22 @@ class RssSourceEditActivity :
sourceEntities.forEach {
when (it.key) {
"sourceName" -> source.sourceName = it.value ?: ""
"sourceUrl" -> source.sourceName = it.value ?: ""
"iconUrl" -> source.sourceName = it.value ?: ""
"ruleTitle" -> source.sourceName = it.value ?: ""
"ruleAuthor" -> source.sourceName = it.value ?: ""
"ruleGuid" -> source.sourceName = it.value ?: ""
"rulePubDate" -> source.sourceName = it.value ?: ""
"ruleCategories" -> source.sourceName = it.value ?: ""
"ruleDescription" -> source.sourceName = it.value ?: ""
"ruleImage" -> source.sourceName = it.value ?: ""
"ruleContent" -> source.sourceName = it.value ?: ""
"ruleLink" -> source.sourceName = it.value ?: ""
"sourceUrl" -> source.sourceUrl = it.value ?: ""
"sourceIcon" -> source.sourceIcon = it.value ?: ""
"sourceGroup" -> source.sourceGroup = it.value
"ruleTitle" -> source.ruleTitle = it.value
"ruleAuthor" -> source.ruleAuthor = it.value
"ruleGuid" -> source.ruleGuid = it.value
"rulePubDate" -> source.rulePubDate = it.value
"ruleCategories" -> source.ruleCategories = it.value
"ruleDescription" -> source.ruleDescription = it.value
"ruleImage" -> source.ruleImage = it.value
"ruleContent" -> source.ruleContent = it.value
"ruleLink" -> source.ruleLink = it.value
}
}
if (source.sourceName.isBlank() || source.sourceName.isBlank()) {
toast("名称或url不能为空")
return null
}
return source

@ -27,6 +27,8 @@ class RssSourceEditViewModel(application: Application) : BaseViewModel(applicati
App.db.rssSourceDao().insert(rssSource)
}.onSuccess {
success()
}.onError {
toast(it.localizedMessage)
}
}

@ -0,0 +1,41 @@
package io.legado.app.ui.rss.source.manage
import androidx.recyclerview.widget.DiffUtil
import io.legado.app.data.entities.RssSource
class DiffCallBack(
private val oldItems: List<RssSource>,
private val newItems: List<RssSource>
) : DiffUtil.Callback() {
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
return oldItem.sourceUrl == newItem.sourceUrl
}
override fun getOldListSize(): Int {
return oldItems.size
}
override fun getNewListSize(): Int {
return newItems.size
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
return oldItem.sourceName == newItem.sourceName
&& oldItem.enabled == newItem.enabled
}
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
return when {
oldItem.sourceName == newItem.sourceName
&& oldItem.enabled != newItem.enabled -> 2
else -> null
}
}
}

@ -3,11 +3,16 @@ package io.legado.app.ui.rss.source.manage
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.SubMenu
import androidx.appcompat.widget.SearchView
import androidx.core.content.ContextCompat
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.VMBaseActivity
import io.legado.app.data.entities.RssSource
@ -16,6 +21,7 @@ import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.primaryTextColor
import io.legado.app.ui.rss.source.edit.RssSourceEditActivity
import io.legado.app.utils.getViewModel
import io.legado.app.utils.splitNotBlank
import kotlinx.android.synthetic.main.activity_rss_source.*
import kotlinx.android.synthetic.main.view_search.*
import kotlinx.android.synthetic.main.view_title_bar.*
@ -29,11 +35,16 @@ class RssSourceActivity : VMBaseActivity<RssSourceViewModel>(R.layout.activity_r
get() = getViewModel(RssSourceViewModel::class.java)
private lateinit var adapter: RssSourceAdapter
private var sourceLiveData: LiveData<List<RssSource>>? = null
private var groups = hashSetOf<String>()
private var groupMenu: SubMenu? = null
override fun onActivityCreated(savedInstanceState: Bundle?) {
setSupportActionBar(toolbar)
initRecyclerView()
initSearchView()
initLiveDataGroup()
initLiveDataSource()
}
override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
@ -41,9 +52,20 @@ class RssSourceActivity : VMBaseActivity<RssSourceViewModel>(R.layout.activity_r
return super.onCompatCreateOptionsMenu(menu)
}
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
groupMenu = menu?.findItem(R.id.menu_group)?.subMenu
upGroupMenu()
return super.onPrepareOptionsMenu(menu)
}
override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_add_book_source -> startActivity<RssSourceEditActivity>()
R.id.menu_add -> startActivity<RssSourceEditActivity>()
R.id.menu_select_all -> adapter.selectAll()
R.id.menu_revert_selection -> adapter.revertSelection()
R.id.menu_enable_selection -> viewModel.enableSelection(adapter.getSelectionIds())
R.id.menu_disable_selection -> viewModel.disableSelection(adapter.getSelectionIds())
R.id.menu_del_selection -> viewModel.delSelection(adapter.getSelectionIds())
}
return super.onCompatOptionsItemSelected(item)
}
@ -81,12 +103,45 @@ class RssSourceActivity : VMBaseActivity<RssSourceViewModel>(R.layout.activity_r
})
}
private fun initLiveDataGroup() {
App.db.rssSourceDao().liveGroup().observe(this, Observer {
groups.clear()
it.map { group ->
groups.addAll(group.splitNotBlank(",", ";"))
}
upGroupMenu()
})
}
private fun upGroupMenu() {
groupMenu?.removeGroup(R.id.source_group)
groups.map {
groupMenu?.add(R.id.source_group, Menu.NONE, Menu.NONE, it)
}
}
private fun initLiveDataSource(key: String? = null) {
sourceLiveData?.removeObservers(this)
sourceLiveData =
if (key.isNullOrBlank()) {
App.db.rssSourceDao().liveAll()
} else {
App.db.rssSourceDao().liveSearch("%$key%")
}
sourceLiveData?.observe(this, Observer {
val diffResult = DiffUtil
.calculateDiff(DiffCallBack(adapter.getItems(), it))
adapter.setItemsNoNotify(it)
diffResult.dispatchUpdatesTo(adapter)
})
}
override fun del(source: RssSource) {
}
override fun edit(source: RssSource) {
startActivity<RssSourceEditActivity>(Pair("data", source.sourceUrl))
}
override fun update(vararg source: RssSource) {

@ -1,18 +1,95 @@
package io.legado.app.ui.rss.source.manage
import android.content.Context
import android.view.Menu
import android.widget.PopupMenu
import io.legado.app.R
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.data.entities.RssSource
import io.legado.app.help.ItemTouchCallback
import io.legado.app.lib.theme.backgroundColor
import kotlinx.android.synthetic.main.item_rss_source.view.*
import org.jetbrains.anko.sdk27.listeners.onClick
class RssSourceAdapter(context: Context, val callBack: CallBack) :
SimpleRecyclerAdapter<RssSource>(context, R.layout.item_rss_source),
ItemTouchCallback.OnItemTouchCallbackListener {
override fun convert(holder: ItemViewHolder, item: RssSource, payloads: MutableList<Any>) {
private val selectedIds = linkedSetOf<String>()
fun selectAll() {
getItems().forEach {
selectedIds.add(it.sourceUrl)
}
notifyItemRangeChanged(0, itemCount, 1)
}
fun revertSelection() {
getItems().forEach {
if (selectedIds.contains(it.sourceUrl)) {
selectedIds.remove(it.sourceUrl)
} else {
selectedIds.add(it.sourceUrl)
}
}
notifyItemRangeChanged(0, itemCount, 1)
}
fun getSelectionIds(): LinkedHashSet<String> {
val selection = linkedSetOf<String>()
getItems().map {
if (selectedIds.contains(it.sourceUrl)) {
selection.add(it.sourceUrl)
}
}
return selection
}
override fun convert(holder: ItemViewHolder, item: RssSource, payloads: MutableList<Any>) {
with(holder.itemView) {
if (payloads.isEmpty()) {
this.setBackgroundColor(context.backgroundColor)
if (item.sourceGroup.isNullOrEmpty()) {
cb_source.text = item.sourceName
} else {
cb_source.text =
String.format("%s (%s)", item.sourceName, item.sourceGroup)
}
swt_enabled.isChecked = item.enabled
swt_enabled.onClick {
item.enabled = swt_enabled.isChecked
callBack.update(item)
}
cb_source.isChecked = selectedIds.contains(item.sourceUrl)
cb_source.setOnClickListener {
if (cb_source.isChecked) {
selectedIds.add(item.sourceUrl)
} else {
selectedIds.remove(item.sourceUrl)
}
}
iv_edit.onClick { callBack.edit(item) }
iv_menu_more.onClick {
val popupMenu = PopupMenu(context, it)
popupMenu.menu.add(Menu.NONE, R.id.menu_top, Menu.NONE, R.string.to_top)
popupMenu.menu.add(Menu.NONE, R.id.menu_del, Menu.NONE, R.string.delete)
popupMenu.setOnMenuItemClickListener { menuItem ->
when (menuItem.itemId) {
R.id.menu_top -> callBack.toTop(item)
R.id.menu_del -> callBack.del(item)
}
true
}
popupMenu.show()
}
} else {
when (payloads[0]) {
1 -> cb_source.isChecked = selectedIds.contains(item.sourceUrl)
2 -> swt_enabled.isChecked = item.enabled
}
}
}
}

@ -1,8 +1,26 @@
package io.legado.app.ui.rss.source.manage
import android.app.Application
import io.legado.app.App
import io.legado.app.base.BaseViewModel
class RssSourceViewModel(application: Application) : BaseViewModel(application) {
fun enableSelection(ids: LinkedHashSet<String>) {
execute {
App.db.rssSourceDao().enableSection(*ids.toTypedArray())
}
}
fun disableSelection(ids: LinkedHashSet<String>) {
execute {
App.db.rssSourceDao().disableSection(*ids.toTypedArray())
}
}
fun delSelection(ids: LinkedHashSet<String>) {
execute {
App.db.rssSourceDao().delSection(*ids.toTypedArray())
}
}
}

@ -9,7 +9,7 @@
android:padding="16dp">
<io.legado.app.lib.theme.view.ATECheckBox
android:id="@+id/cb_book_source"
android:id="@+id/cb_source"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"

@ -150,9 +150,9 @@
<string name="add_rss_source">新建订阅源</string>
<string name="book_file_selector">添加书籍</string>
<string name="scan_book_source">扫描</string>
<string name="copy_source">拷贝</string>
<string name="paste_source">粘贴</string>
<string name="source_rule_s">源规则说明</string>
<string name="copy_source">拷贝源</string>
<string name="paste_source">粘贴源</string>
<string name="source_rule_s">源规则说明</string>
<string name="check_update">检查更新</string>
<string name="camera_scan">扫描二维码</string>
<string name="scan_image">扫描本地图片</string>
@ -379,6 +379,7 @@
<string name="rss_source_name">名称</string>
<string name="rss_source_url">url</string>
<string name="rss_source_icon">图标</string>
<string name="rss_source_group">分组</string>
<string name="rss_rule_title">标题</string>
<string name="rss_rule_author">作者</string>
<string name="rss_rule_guid">guid</string>
@ -406,7 +407,7 @@
<!--error string end-->
<string name="source_http_header">header</string>
<string name="debug_source">调试</string>
<string name="debug_source">调试源</string>
<string name="import_by_qr_code">二维码导入</string>
<string name="scan_qr_code">扫描二维码</string>
<string name="click_on_selected_show_menu">选中时点击可弹出菜单</string>

Loading…
Cancel
Save