添加规则订阅功能

pull/517/head
gedoor 4 years ago
parent d3b7bf7b2e
commit 7dd28530f7
  1. 4
      app/src/main/AndroidManifest.xml
  2. 25
      app/src/main/java/io/legado/app/data/AppDatabase.kt
  3. 27
      app/src/main/java/io/legado/app/data/dao/RuleSubDao.kt
  4. 27
      app/src/main/java/io/legado/app/data/dao/SourceSubDao.kt
  5. 10
      app/src/main/java/io/legado/app/data/entities/RuleSub.kt
  6. 2
      app/src/main/java/io/legado/app/help/storage/Backup.kt
  7. 4
      app/src/main/java/io/legado/app/help/storage/Restore.kt
  8. 6
      app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt
  9. 72
      app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubActivity.kt
  10. 20
      app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubAdapter.kt
  11. 4
      app/src/main/res/layout/activity_rule_sub.xml
  12. 4
      app/src/main/res/values-zh-rHK/strings.xml
  13. 4
      app/src/main/res/values-zh-rTW/strings.xml
  14. 4
      app/src/main/res/values-zh/strings.xml
  15. 4
      app/src/main/res/values/strings.xml

@ -171,9 +171,9 @@
<activity
android:name=".ui.qrcode.QrCodeActivity"
android:launchMode="singleTask" />
<!-- 订阅 -->
<!-- 规则订阅 -->
<activity
android:name=".ui.rss.subscription.SourceSubActivity"
android:name=".ui.rss.subscription.RuleSubActivity"
android:launchMode="singleTask" />
<!-- 书源编辑 -->
<activity

@ -18,8 +18,8 @@ import java.util.*
ReplaceRule::class, SearchBook::class, SearchKeyword::class, Cookie::class,
RssSource::class, Bookmark::class, RssArticle::class, RssReadRecord::class,
RssStar::class, TxtTocRule::class, ReadRecord::class, HttpTTS::class, Cache::class,
SourceSub::class],
version = 25,
RuleSub::class],
version = 26,
exportSchema = true
)
abstract class AppDatabase : RoomDatabase() {
@ -40,7 +40,7 @@ abstract class AppDatabase : RoomDatabase() {
abstract fun readRecordDao(): ReadRecordDao
abstract fun httpTTSDao(): HttpTTSDao
abstract fun cacheDao(): CacheDao
abstract fun sourceSubDao(): SourceSubDao
abstract fun ruleSubDao(): RuleSubDao
companion object {
@ -53,7 +53,7 @@ abstract class AppDatabase : RoomDatabase() {
migration_10_11, migration_11_12, migration_12_13, migration_13_14,
migration_14_15, migration_15_17, migration_17_18, migration_18_19,
migration_19_20, migration_20_21, migration_21_22, migration_22_23,
migration_23_24, migration_24_25
migration_23_24, migration_24_25, migration_25_26
)
.allowMainThreadQueries()
.addCallback(dbCallback)
@ -215,6 +215,23 @@ abstract class AppDatabase : RoomDatabase() {
)
}
}
private val migration_25_26 = object : Migration(25, 26) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
"""
CREATE TABLE IF NOT EXISTS `ruleSubs` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, `type` INTEGER NOT NULL,
`customOrder` INTEGER NOT NULL, `autoUpdate` INTEGER NOT NULL, `update` INTEGER NOT NULL, PRIMARY KEY(`id`))
"""
)
database.execSQL(
"""
insert into `ruleSubs` select *, 0, 0 from `sourceSubs`
"""
)
database.execSQL("DROP TABLE `sourceSubs`")
}
}
}
}

@ -0,0 +1,27 @@
package io.legado.app.data.dao
import androidx.lifecycle.LiveData
import androidx.room.*
import io.legado.app.data.entities.RuleSub
@Dao
interface RuleSubDao {
@get:Query("select * from ruleSubs order by customOrder")
val all: List<RuleSub>
@Query("select * from ruleSubs order by customOrder")
fun observeAll(): LiveData<List<RuleSub>>
@get:Query("select customOrder from ruleSubs order by customOrder limit 0,1")
val maxOrder: Int
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(vararg ruleSub: RuleSub)
@Delete
fun delete(vararg ruleSub: RuleSub)
@Update
fun update(vararg ruleSub: RuleSub)
}

@ -1,27 +0,0 @@
package io.legado.app.data.dao
import androidx.lifecycle.LiveData
import androidx.room.*
import io.legado.app.data.entities.SourceSub
@Dao
interface SourceSubDao {
@get:Query("select * from sourceSubs order by customOrder")
val all: List<SourceSub>
@Query("select * from sourceSubs order by customOrder")
fun observeAll(): LiveData<List<SourceSub>>
@get:Query("select customOrder from sourceSubs order by customOrder limit 0,1")
val maxOrder: Int
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(vararg sourceSub: SourceSub)
@Delete
fun delete(vararg sourceSub: SourceSub)
@Update
fun update(vararg sourceSub: SourceSub)
}

@ -3,14 +3,16 @@ package io.legado.app.data.entities
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "sourceSubs")
data class SourceSub(
@Entity(tableName = "ruleSubs")
data class RuleSub(
@PrimaryKey
val id: Long = System.currentTimeMillis(),
var name: String = "",
var url: String = "",
var type: Int = 0,
var customOrder: Int = 0
var customOrder: Int = 0,
var autoUpdate: Boolean = false,
var update: Long = System.currentTimeMillis()
) {
fun setType(type: Type) {
@ -18,6 +20,6 @@ data class SourceSub(
}
enum class Type {
BookSource, RssSource
BookSource, RssSource, ReplaceRule
}
}

@ -67,7 +67,7 @@ object Backup {
writeListToJson(App.db.replaceRuleDao().all, "replaceRule.json", backupPath)
writeListToJson(App.db.readRecordDao().all, "readRecord.json", backupPath)
writeListToJson(App.db.searchKeywordDao().all, "searchHistory.json", backupPath)
writeListToJson(App.db.sourceSubDao().all, "sourceSub.json", backupPath)
writeListToJson(App.db.ruleSubDao().all, "sourceSub.json", backupPath)
writeListToJson(App.db.txtTocRule().all, DefaultData.txtTocRuleFileName, backupPath)
writeListToJson(App.db.httpTTSDao().all, DefaultData.httpTtsFileName, backupPath)
GSON.toJson(ReadBookConfig.configList).let {

@ -134,8 +134,8 @@ object Restore {
fileToListT<SearchKeyword>(path, "searchHistory.json")?.let {
App.db.searchKeywordDao().insert(*it.toTypedArray())
}
fileToListT<SourceSub>(path, "sourceSub.json")?.let {
App.db.sourceSubDao().insert(*it.toTypedArray())
fileToListT<RuleSub>(path, "sourceSub.json")?.let {
App.db.ruleSubDao().insert(*it.toTypedArray())
}
fileToListT<TxtTocRule>(path, DefaultData.txtTocRuleFileName)?.let {
App.db.txtTocRule().insert(*it.toTypedArray())

@ -16,7 +16,7 @@ import io.legado.app.ui.rss.favorites.RssFavoritesActivity
import io.legado.app.ui.rss.source.edit.RssSourceEditActivity
import io.legado.app.ui.rss.source.manage.RssSourceActivity
import io.legado.app.ui.rss.source.manage.RssSourceViewModel
import io.legado.app.ui.rss.subscription.SourceSubActivity
import io.legado.app.ui.rss.subscription.RuleSubActivity
import io.legado.app.utils.getViewModel
import io.legado.app.utils.startActivity
import io.legado.app.utils.viewbindingdelegate.viewBinding
@ -56,10 +56,10 @@ class RssFragment : VMBaseFragment<RssSourceViewModel>(R.layout.fragment_rss),
binding.recyclerView.adapter = adapter
adapter.addHeaderView {
ItemRssBinding.inflate(layoutInflater, it, false).apply {
tvName.setText(R.string.source_subscription)
tvName.setText(R.string.rule_subscription)
ivIcon.setImageResource(R.mipmap.ic_launcher)
root.onClick {
startActivity<SourceSubActivity>()
startActivity<RuleSubActivity>()
}
}
}

@ -9,8 +9,8 @@ import androidx.recyclerview.widget.ItemTouchHelper
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseActivity
import io.legado.app.data.entities.SourceSub
import io.legado.app.databinding.ActivitySourceSubBinding
import io.legado.app.data.entities.RuleSub
import io.legado.app.databinding.ActivityRuleSubBinding
import io.legado.app.databinding.DialogSourceSubEditBinding
import io.legado.app.lib.dialogs.alert
import io.legado.app.ui.association.ImportBookSourceActivity
@ -20,14 +20,14 @@ import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.launch
import org.jetbrains.anko.startActivity
class SourceSubActivity : BaseActivity<ActivitySourceSubBinding>(),
SourceSubAdapter.Callback {
class RuleSubActivity : BaseActivity<ActivityRuleSubBinding>(),
RuleSubAdapter.Callback {
private lateinit var adapter: SourceSubAdapter
private var liveData: LiveData<List<SourceSub>>? = null
private lateinit var adapter: RuleSubAdapter
private var liveData: LiveData<List<RuleSub>>? = null
override fun getViewBinding(): ActivitySourceSubBinding {
return ActivitySourceSubBinding.inflate(layoutInflater)
override fun getViewBinding(): ActivityRuleSubBinding {
return ActivityRuleSubBinding.inflate(layoutInflater)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
@ -43,15 +43,15 @@ class SourceSubActivity : BaseActivity<ActivitySourceSubBinding>(),
override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_add -> {
val order = App.db.sourceSubDao().maxOrder + 1
editSubscription(SourceSub(customOrder = order))
val order = App.db.ruleSubDao().maxOrder + 1
editSubscription(RuleSub(customOrder = order))
}
}
return super.onCompatOptionsItemSelected(item)
}
private fun initView() {
adapter = SourceSubAdapter(this, this)
adapter = RuleSubAdapter(this, this)
binding.recyclerView.adapter = adapter
val itemTouchCallback = ItemTouchCallback(adapter)
itemTouchCallback.isCanDrag = true
@ -60,69 +60,69 @@ class SourceSubActivity : BaseActivity<ActivitySourceSubBinding>(),
private fun initData() {
liveData?.removeObservers(this)
liveData = App.db.sourceSubDao().observeAll()
liveData = App.db.ruleSubDao().observeAll()
liveData?.observe(this) {
binding.tvEmptyMsg.isGone = it.isNotEmpty()
adapter.setItems(it)
}
}
override fun openSubscription(sourceSub: SourceSub) {
when (sourceSub.type) {
SourceSub.Type.RssSource.ordinal -> {
startActivity<ImportRssSourceActivity>("source" to sourceSub.url)
override fun openSubscription(ruleSub: RuleSub) {
when (ruleSub.type) {
RuleSub.Type.RssSource.ordinal -> {
startActivity<ImportRssSourceActivity>("source" to ruleSub.url)
}
else -> {
startActivity<ImportBookSourceActivity>("source" to sourceSub.url)
startActivity<ImportBookSourceActivity>("source" to ruleSub.url)
}
}
}
override fun editSubscription(sourceSub: SourceSub) {
alert(R.string.source_subscription) {
override fun editSubscription(ruleSub: RuleSub) {
alert(R.string.rule_subscription) {
val alertBinding = DialogSourceSubEditBinding.inflate(layoutInflater).apply {
when (sourceSub.type) {
SourceSub.Type.RssSource.ordinal -> rgType.check(R.id.rb_rss_source)
when (ruleSub.type) {
RuleSub.Type.RssSource.ordinal -> rgType.check(R.id.rb_rss_source)
else -> rgType.check(R.id.rb_book_source)
}
etName.setText(sourceSub.name)
etUrl.setText(sourceSub.url)
etName.setText(ruleSub.name)
etUrl.setText(ruleSub.url)
}
customView = alertBinding.root
okButton {
when (alertBinding.rgType.checkedRadioButtonId) {
R.id.rb_rss_source -> sourceSub.setType(SourceSub.Type.RssSource)
else -> sourceSub.setType(SourceSub.Type.BookSource)
R.id.rb_rss_source -> ruleSub.setType(RuleSub.Type.RssSource)
else -> ruleSub.setType(RuleSub.Type.BookSource)
}
sourceSub.name = alertBinding.etName.text?.toString() ?: ""
sourceSub.url = alertBinding.etUrl.text?.toString() ?: ""
ruleSub.name = alertBinding.etName.text?.toString() ?: ""
ruleSub.url = alertBinding.etUrl.text?.toString() ?: ""
launch(IO) {
App.db.sourceSubDao().insert(sourceSub)
App.db.ruleSubDao().insert(ruleSub)
}
}
cancelButton()
}.show()
}
override fun delSubscription(sourceSub: SourceSub) {
override fun delSubscription(ruleSub: RuleSub) {
launch(IO) {
App.db.sourceSubDao().delete(sourceSub)
App.db.ruleSubDao().delete(ruleSub)
}
}
override fun updateSourceSub(vararg sourceSub: SourceSub) {
override fun updateSourceSub(vararg ruleSub: RuleSub) {
launch(IO) {
App.db.sourceSubDao().update(*sourceSub)
App.db.ruleSubDao().update(*ruleSub)
}
}
override fun upOrder() {
launch(IO) {
val sourceSubs = App.db.sourceSubDao().all
for ((index: Int, sourceSub: SourceSub) in sourceSubs.withIndex()) {
sourceSub.customOrder = index + 1
val sourceSubs = App.db.ruleSubDao().all
for ((index: Int, ruleSub: RuleSub) in sourceSubs.withIndex()) {
ruleSub.customOrder = index + 1
}
App.db.sourceSubDao().update(*sourceSubs.toTypedArray())
App.db.ruleSubDao().update(*sourceSubs.toTypedArray())
}
}

@ -8,23 +8,23 @@ import androidx.recyclerview.widget.RecyclerView
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.SourceSub
import io.legado.app.data.entities.RuleSub
import io.legado.app.databinding.ItemSourceSubBinding
import io.legado.app.ui.widget.recycler.ItemTouchCallback
import org.jetbrains.anko.sdk27.listeners.onClick
import java.util.*
class SourceSubAdapter(context: Context, val callBack: Callback) :
SimpleRecyclerAdapter<SourceSub, ItemSourceSubBinding>(context),
class RuleSubAdapter(context: Context, val callBack: Callback) :
SimpleRecyclerAdapter<RuleSub, ItemSourceSubBinding>(context),
ItemTouchCallback.Callback {
override fun convert(
holder: ItemViewHolder,
binding: ItemSourceSubBinding,
item: SourceSub,
item: RuleSub,
payloads: MutableList<Any>
) {
binding.tvType.text = SourceSub.Type.values()[item.type].name
binding.tvType.text = RuleSub.Type.values()[item.type].name
binding.tvName.text = item.name
binding.tvUrl.text = item.url
}
@ -77,7 +77,7 @@ class SourceSubAdapter(context: Context, val callBack: Callback) :
return true
}
private val movedItems = hashSetOf<SourceSub>()
private val movedItems = hashSetOf<RuleSub>()
override fun onClearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
if (movedItems.isNotEmpty()) {
@ -87,10 +87,10 @@ class SourceSubAdapter(context: Context, val callBack: Callback) :
}
interface Callback {
fun openSubscription(sourceSub: SourceSub)
fun editSubscription(sourceSub: SourceSub)
fun delSubscription(sourceSub: SourceSub)
fun updateSourceSub(vararg sourceSub: SourceSub)
fun openSubscription(ruleSub: RuleSub)
fun editSubscription(ruleSub: RuleSub)
fun delSubscription(ruleSub: RuleSub)
fun updateSourceSub(vararg ruleSub: RuleSub)
fun upOrder()
}

@ -10,7 +10,7 @@
android:id="@+id/title_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/source_subscription" />
app:title="@string/rule_subscription" />
<FrameLayout
android:layout_width="match_parent"
@ -31,7 +31,7 @@
android:layout_gravity="center"
android:gravity="center"
android:visibility="gone"
android:text="@string/source_sub_empty_msg"
android:text="@string/rule_sub_empty_msg"
app:layout_constraintTop_toBottomOf="@+id/title_bar"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"

@ -787,7 +787,7 @@
<string name="body_title">正文标题</string>
<string name="show_hide">显示/隐藏</string>
<string name="header_footer">页眉<![CDATA[&]]>页脚</string>
<string name="source_subscription">订阅</string>
<string name="source_sub_empty_msg">添加大佬们提供的源导入地址,添加后点击可导入源</string>
<string name="rule_subscription">规则订阅</string>
<string name="rule_sub_empty_msg">添加大佬们提供的源导入地址,添加后点击可导入源</string>
</resources>

@ -788,7 +788,7 @@
<string name="body_title">正文标题</string>
<string name="show_hide">显示/隐藏</string>
<string name="header_footer">页眉<![CDATA[&]]>页脚</string>
<string name="source_subscription">订阅</string>
<string name="source_sub_empty_msg">添加大佬们提供的源导入地址,添加后点击可导入源</string>
<string name="rule_subscription">规则订阅</string>
<string name="rule_sub_empty_msg">添加大佬们提供的源导入地址,添加后点击可导入源</string>
</resources>

@ -791,7 +791,7 @@
<string name="body_title">正文标题</string>
<string name="show_hide">显示/隐藏</string>
<string name="header_footer">页眉<![CDATA[&]]>页脚</string>
<string name="source_subscription">订阅</string>
<string name="source_sub_empty_msg">添加大佬们提供的源导入地址,添加后点击可导入源</string>
<string name="rule_subscription">规则订阅</string>
<string name="rule_sub_empty_msg">添加大佬们提供的源导入地址,添加后点击可导入源</string>
</resources>

@ -794,7 +794,7 @@
<string name="body_title">正文标题</string>
<string name="show_hide">显示/隐藏</string>
<string name="header_footer">footer <![CDATA[&]]> header</string>
<string name="source_subscription">订阅</string>
<string name="source_sub_empty_msg">添加大佬们提供的源导入地址,添加后点击可导入源</string>
<string name="rule_subscription">规则订阅</string>
<string name="rule_sub_empty_msg">添加大佬们提供的源导入地址,添加后点击可导入源</string>
</resources>

Loading…
Cancel
Save