Added importing book shelf from YueDu

pull/32/head
atbest 6 years ago
parent 4b8f6e3f9b
commit 5f2991373b
  1. 4
      app/src/main/java/com/jayway/jsonpath/spi/json/GsonJsonProvider.java
  2. 11
      app/src/main/java/io/legado/app/data/dao/BookDao.kt
  3. 2
      app/src/main/java/io/legado/app/data/entities/Book.kt
  4. 78
      app/src/main/java/io/legado/app/ui/main/MainActivity.kt
  5. 14
      app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt
  6. 8
      app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleAdapter.kt
  7. 3
      app/src/main/java/io/legado/app/utils/MiscExtensions.kt
  8. 6
      app/src/main/res/drawable/ic_divider.xml
  9. 4
      app/src/main/res/layout/activity_replace_rule.xml
  10. 12
      app/src/main/res/layout/item_relace_rule.xml

@ -247,8 +247,8 @@ public class GsonJsonProvider extends AbstractJsonProvider {
} }
} }
throw new JsonPathException("length operation can not applied to " + obj != null ? obj.getClass().getName() throw new JsonPathException("length operation can not applied to " + (obj != null ? obj.getClass().getName()
: "null"); : "null"));
} }
@Override @Override

@ -3,6 +3,8 @@ package io.legado.app.data.dao
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.paging.DataSource import androidx.paging.DataSource
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query import androidx.room.Query
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
@ -19,4 +21,13 @@ interface BookDao {
@Query("SELECT * FROM books WHERE `name` in (:names)") @Query("SELECT * FROM books WHERE `name` in (:names)")
fun findByName(vararg names: String): List<Book> fun findByName(vararg names: String): List<Book>
@get:Query("SELECT descUrl FROM books")
val allBookUrls: List<String>
@get:Query("SELECT COUNT(*) FROM books")
val allBookCount: Int
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(vararg books: Book)
} }

@ -35,6 +35,8 @@ data class Book(@PrimaryKey
var durChapterPos: Int = 0, // 当前阅读的进度(首行字符的索引位置) var durChapterPos: Int = 0, // 当前阅读的进度(首行字符的索引位置)
var durChapterTime: Long = 0, // 最近一次阅读书籍的时间(打开正文的时间) var durChapterTime: Long = 0, // 最近一次阅读书籍的时间(打开正文的时间)
var canUpdate: Boolean = true, // 刷新书架时更新书籍信息 var canUpdate: Boolean = true, // 刷新书架时更新书籍信息
var order: Int = 0, // 手动排序
var useReplaceRule: Boolean = true, // 正文使用净化替换规则
var variable: String? = null // 自定义书籍变量信息(用于书源规则检索书籍信息) var variable: String? = null // 自定义书籍变量信息(用于书源规则检索书籍信息)
) : Parcelable { ) : Parcelable {

@ -16,6 +16,7 @@ import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseActivity import io.legado.app.base.BaseActivity
import io.legado.app.constant.AppConst.APP_TAG import io.legado.app.constant.AppConst.APP_TAG
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.ReplaceRule import io.legado.app.data.entities.ReplaceRule
import io.legado.app.help.permission.Permissions import io.legado.app.help.permission.Permissions
import io.legado.app.help.permission.PermissionsCompat import io.legado.app.help.permission.PermissionsCompat
@ -107,30 +108,85 @@ class MainActivity : BaseActivity<MainDataBinding, MainViewModel>(), NavigationV
.build() .build()
) )
// 导入书架
val shelfFile = File(yuedu, "myBookShelf.json")
val books = mutableListOf<Book>()
if (shelfFile.exists()) try {
doAsync {
val items: List<Map<String, Any>> = jsonPath.parse(shelfFile.readText()).read("$")
val existingBooks = App.db.bookDao().allBookUrls.toSet()
for (item in items) {
val jsonItem = jsonPath.parse(item)
val book = Book()
book.descUrl = jsonItem.readString("$.noteUrl") ?: ""
if (book.descUrl.isBlank()) continue
book.name = jsonItem.readString("$.bookInfoBean.name")
if (book.descUrl in existingBooks) {
Log.d(APP_TAG, "Found existing book: ${book.name}")
continue
}
book.author = jsonItem.readString("$.bookInfoBean.author")
book.type = if (jsonItem.readString("$.bookInfoBean.bookSourceType") == "AUDIO") 1 else 0
book.tocUrl = jsonItem.readString("$.bookInfoBean.chapterUrl") ?: book.descUrl
book.coverUrl = jsonItem.readString("$.bookInfoBean.coverUrl")
book.customCoverUrl = jsonItem.readString("$.customCoverPath")
book.lastCheckTime = jsonItem.readLong("$.bookInfoBean.finalRefreshData") ?: 0
book.canUpdate = jsonItem.readBool("$.allowUpdate") == true
book.totalChapterNum = jsonItem.readInt("$.chapterListSize") ?: 0
book.durChapterIndex = jsonItem.readInt("$.durChapter") ?: 0
book.durChapterTitle = jsonItem.readString("$.durChapterName")
book.durChapterPos = jsonItem.readInt("$.durChapterPage") ?: 0
book.durChapterTime = jsonItem.readLong("$.finalDate") ?: 0
book.group = jsonItem.readInt("$.group") ?: 0
// book. = jsonItem.readString("$.hasUpdate")
// book. = jsonItem.readString("$.isLoading")
book.latestChapterTitle = jsonItem.readString("$.lastChapterName")
book.lastCheckCount = jsonItem.readInt("$.newChapters") ?: 0
book.order = jsonItem.readInt("$.serialNumber") ?: 0
book.useReplaceRule = jsonItem.readBool("$.useReplaceRule") == true
book.variable = jsonItem.readString("$.variable")
books.add(book)
Log.d(APP_TAG, "Added ${book.name}")
}
App.db.bookDao().insert(*books.toTypedArray())
val count = books.size
uiThread {
toast(if (count > 0) "成功地导入 $count 本新书和音频" else "没有发现新书或音频")
}
}
} catch (e: Exception) {
Log.e(APP_TAG, "Failed to import book shelf.", e)
toast("Unable to import books:\n${e.localizedMessage}")
}
// Replace rules // Replace rules
val rFile = File(yuedu, "myBookReplaceRule.json") val ruleFile = File(yuedu, "myBookReplaceRule.json")
val replaceRules = mutableListOf<ReplaceRule>() val replaceRules = mutableListOf<ReplaceRule>()
if (rFile.exists()) try { if (ruleFile.exists()) try {
val items: List<Map<String, Any>> = jsonPath.parse(rFile.readText()).read("$.*") doAsync {
val items: List<Map<String, Any>> = jsonPath.parse(ruleFile.readText()).read("$")
val existingRules = App.db.replaceRuleDao().all.map { it.pattern }.toSet()
for (item in items) { for (item in items) {
val jsonItem = jsonPath.parse(item) val jsonItem = jsonPath.parse(item)
val rRule = ReplaceRule() val rRule = ReplaceRule()
rRule.name = jsonItem.readString("$.replaceSummary")
rRule.pattern = jsonItem.readString("$.regex") rRule.pattern = jsonItem.readString("$.regex")
if (rRule.pattern.isNullOrEmpty() || rRule.pattern in existingRules) continue
rRule.name = jsonItem.readString("$.replaceSummary")
rRule.replacement = jsonItem.readString("$.replacement") rRule.replacement = jsonItem.readString("$.replacement")
rRule.isRegex = jsonItem.readBool("$.isRegex") rRule.isRegex = jsonItem.readBool("$.isRegex") == true
rRule.scope = jsonItem.readString("$.useTo") rRule.scope = jsonItem.readString("$.useTo")
rRule.isEnabled = jsonItem.readBool("$.enable") rRule.isEnabled = jsonItem.readBool("$.enable") == true
rRule.order = jsonItem.readInt("$.serialNumber") rRule.order = jsonItem.readInt("$.serialNumber") ?: 0
replaceRules.add(rRule) replaceRules.add(rRule)
} }
doAsync {
App.db.replaceRuleDao().insert(*replaceRules.toTypedArray()) App.db.replaceRuleDao().insert(*replaceRules.toTypedArray())
val count = items.size val count = replaceRules.size
val maxId = App.db.replaceRuleDao().maxOrder val maxId = App.db.replaceRuleDao().maxOrder
uiThread { uiThread {
toast("成功地导入 $count 条净化替换规则") toast(if (count > 0) "成功地导入 $count净化替换规则" else "没有发现新的净化替换规则")
} }
} }

@ -1,11 +1,14 @@
package io.legado.app.ui.replacerule package io.legado.app.ui.replacerule
import android.os.Bundle import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.paging.LivePagedListBuilder import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList import androidx.paging.PagedList
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.App import io.legado.app.App
import io.legado.app.R import io.legado.app.R
@ -15,6 +18,8 @@ import org.jetbrains.anko.doAsync
import org.jetbrains.anko.toast import org.jetbrains.anko.toast
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import io.legado.app.constant.AppConst.APP_TAG
import kotlinx.android.synthetic.main.item_relace_rule.*
class ReplaceRuleActivity : AppCompatActivity() { class ReplaceRuleActivity : AppCompatActivity() {
@ -25,7 +30,6 @@ class ReplaceRuleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_replace_rule) setContentView(R.layout.activity_replace_rule)
rv_replace_rule.layoutManager = LinearLayoutManager(this)
initRecyclerView() initRecyclerView()
initDataObservers() initDataObservers()
initSwipeToDelete() initSwipeToDelete()
@ -57,6 +61,12 @@ class ReplaceRuleActivity : AppCompatActivity() {
} }
} }
rv_replace_rule.adapter = adapter rv_replace_rule.adapter = adapter
rv_replace_rule.addItemDecoration(
DividerItemDecoration(this, DividerItemDecoration.VERTICAL).apply {
ContextCompat.getDrawable(baseContext, R.drawable.ic_divider)?.let {
Log.e(APP_TAG, it.toString())
this.setDrawable(it) }
})
} }
private fun initDataObservers() { private fun initDataObservers() {
@ -90,7 +100,7 @@ class ReplaceRuleActivity : AppCompatActivity() {
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
toast("You swiped the item!") toast("You swiped the item!")
TODO() // TODO()
// remove((viewHolder as TodoViewHolder).todo) // remove((viewHolder as TodoViewHolder).todo)
} }
}).attachToRecyclerView(rv_replace_rule) }).attachToRecyclerView(rv_replace_rule)

@ -1,6 +1,7 @@
package io.legado.app.ui.replacerule package io.legado.app.ui.replacerule
import android.content.Context import android.content.Context
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -9,6 +10,7 @@ import androidx.paging.PagedListAdapter
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.AppConst.APP_TAG
import io.legado.app.data.entities.ReplaceRule import io.legado.app.data.entities.ReplaceRule
import kotlinx.android.synthetic.main.item_relace_rule.view.* import kotlinx.android.synthetic.main.item_relace_rule.view.*
import org.jetbrains.anko.sdk27.listeners.onClick import org.jetbrains.anko.sdk27.listeners.onClick
@ -45,14 +47,14 @@ class ReplaceRuleAdapter(context: Context) :
} }
override fun onBindViewHolder(holder: MyViewHolder, pos: Int) { override fun onBindViewHolder(holder: MyViewHolder, pos: Int) {
getItem(pos)?.let { holder.bind(it, onClickListener, pos == itemCount - 1) } getItem(pos)?.let { holder.bind(it, onClickListener) }
} }
class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) { class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
fun bind(rule: ReplaceRule, listener: OnClickListener?, hideDivider: Boolean) = with(itemView) { fun bind(rule: ReplaceRule, listener: OnClickListener?) = with(itemView) {
tv_name.text = rule.name tv_name.text = rule.name
swt_enabled.isChecked = rule.isEnabled swt_enabled.isChecked = rule.isEnabled
divider.isGone = hideDivider // divider.isGone = hideDivider
iv_delete.isGone = true iv_delete.isGone = true
iv_edit.isGone = true iv_edit.isGone = true
// iv_delete.onClick { listener?.delete(rule) } // iv_delete.onClick { listener?.delete(rule) }

@ -7,3 +7,6 @@ fun ReadContext.readString(path: String): String? = this.read(path, String::clas
fun ReadContext.readBool(path: String): Boolean? = this.read(path, Boolean::class.java) fun ReadContext.readBool(path: String): Boolean? = this.read(path, Boolean::class.java)
fun ReadContext.readInt(path: String): Int? = this.read(path, Int::class.java) fun ReadContext.readInt(path: String): Int? = this.read(path, Int::class.java)
fun ReadContext.readLong(path: String): Long? = this.read(path, Long::class.java)

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:height="0.5dp" />
<solid android:color="@color/divider" />
</shape>

@ -18,8 +18,8 @@
android:id="@+id/rv_replace_rule" android:id="@+id/rv_replace_rule"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:paddingStart="8dp" android:paddingStart="16dp"
android:paddingEnd="8dp" android:paddingEnd="16dp"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"

@ -4,9 +4,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content">
android:paddingStart="8dp"
android:paddingEnd="8dp">
<androidx.appcompat.widget.AppCompatImageView <androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_edit" android:id="@+id/iv_edit"
@ -42,7 +40,7 @@
android:text="" android:text=""
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingStart="16dp" android:paddingStart="8dp"
android:paddingTop="16dp" android:paddingTop="16dp"
android:paddingBottom="16dp" android:paddingBottom="16dp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
@ -54,7 +52,8 @@
android:id="@+id/tv_name" android:id="@+id/tv_name"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="16dp" android:paddingTop="16dp"
android:paddingBottom="16dp"
android:textSize="16sp" android:textSize="16sp"
android:text="Item Name" android:text="Item Name"
android:textColor="@color/text_default" android:textColor="@color/text_default"
@ -77,12 +76,13 @@
app:layout_constraintStart_toEndOf="@id/tv_name" app:layout_constraintStart_toEndOf="@id/tv_name"
app:layout_constraintHorizontal_bias="1"/> app:layout_constraintHorizontal_bias="1"/>
<!--
<View <View
android:id="@+id/divider" android:id="@+id/divider"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0.5dp" android:layout_height="0.5dp"
android:background="@color/divider" android:background="@color/divider"
app:layout_constraintBottom_toBottomOf="parent"/> app:layout_constraintBottom_toBottomOf="parent"/>
-->
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

Loading…
Cancel
Save