diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c1e691938..1cf753157 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -46,7 +46,7 @@
android:name=".help.permission.PermissionActivity"
android:theme="@style/Activity.Permission" />
@@ -78,7 +78,8 @@
-
+
+
diff --git a/app/src/main/java/io/legado/app/base/BaseViewModel.kt b/app/src/main/java/io/legado/app/base/BaseViewModel.kt
index 1aa8452c9..3610a00a5 100644
--- a/app/src/main/java/io/legado/app/base/BaseViewModel.kt
+++ b/app/src/main/java/io/legado/app/base/BaseViewModel.kt
@@ -2,26 +2,38 @@ package io.legado.app.base
import android.app.Application
import android.content.Context
+import androidx.annotation.CallSuper
import androidx.lifecycle.AndroidViewModel
import io.legado.app.App
import io.legado.app.help.coroutine.Coroutine
import kotlinx.coroutines.*
import org.jetbrains.anko.AnkoLogger
import org.jetbrains.anko.toast
+import kotlin.coroutines.CoroutineContext
-open class BaseViewModel(application: Application) : AndroidViewModel(application), CoroutineScope by MainScope(),
+open class BaseViewModel(application: Application) : AndroidViewModel(application),
+ CoroutineScope by MainScope(),
AnkoLogger {
val context: Context by lazy { this.getApplication() }
- fun execute(scope: CoroutineScope = this, block: suspend CoroutineScope.() -> T): Coroutine {
- return Coroutine.async(scope) { block() }
+ fun execute(
+ scope: CoroutineScope = this,
+ context: CoroutineContext = Dispatchers.IO,
+ block: suspend CoroutineScope.() -> T
+ ): Coroutine {
+ return Coroutine.async(scope, context) { block() }
}
- fun submit(scope: CoroutineScope = this, block: suspend CoroutineScope.() -> Deferred): Coroutine {
- return Coroutine.async(scope) { block().await() }
+ fun submit(
+ scope: CoroutineScope = this,
+ context: CoroutineContext = Dispatchers.IO,
+ block: suspend CoroutineScope.() -> Deferred
+ ): Coroutine {
+ return Coroutine.async(scope, context) { block().await() }
}
+ @CallSuper
override fun onCleared() {
super.onCleared()
cancel()
diff --git a/app/src/main/java/io/legado/app/constant/AppConst.kt b/app/src/main/java/io/legado/app/constant/AppConst.kt
index f9fe5b942..99dcb17cb 100644
--- a/app/src/main/java/io/legado/app/constant/AppConst.kt
+++ b/app/src/main/java/io/legado/app/constant/AppConst.kt
@@ -38,11 +38,7 @@ object AppConst {
)
}
- val defaultBookGroups by lazy {
- listOf(
- BookGroup(-1, "全部"),
- BookGroup(-2, "本地"),
- BookGroup(-3, "音频")
- )
- }
+ val bookGroupAll = BookGroup(-1, App.INSTANCE.getString(R.string.all))
+ val bookGroupLocal = BookGroup(-2, App.INSTANCE.getString(R.string.local))
+ val bookGroupAudio = BookGroup(-3, App.INSTANCE.getString(R.string.audio))
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/constant/RSSKeywords.kt b/app/src/main/java/io/legado/app/constant/RSSKeywords.kt
index 5a4119e05..bb8d4207a 100644
--- a/app/src/main/java/io/legado/app/constant/RSSKeywords.kt
+++ b/app/src/main/java/io/legado/app/constant/RSSKeywords.kt
@@ -5,7 +5,7 @@ object RSSKeywords {
const val RSS_ITEM = "item"
const val RSS_ITEM_TITLE = "title"
const val RSS_ITEM_LINK = "link"
- const val RSS_ITEM_AUTHOR = "dc:creator"
+ const val RSS_ITEM_AUTHOR = "author"
const val RSS_ITEM_CATEGORY = "category"
const val RSS_ITEM_THUMBNAIL = "media:thumbnail"
const val RSS_ITEM_ENCLOSURE = "enclosure"
diff --git a/app/src/main/java/io/legado/app/data/AppDatabase.kt b/app/src/main/java/io/legado/app/data/AppDatabase.kt
index d58a5ccb7..3b17435d5 100644
--- a/app/src/main/java/io/legado/app/data/AppDatabase.kt
+++ b/app/src/main/java/io/legado/app/data/AppDatabase.kt
@@ -12,7 +12,7 @@ import io.legado.app.data.entities.*
@Database(
entities = [Book::class, BookGroup::class, BookSource::class, BookChapter::class, ReplaceRule::class, SearchBook::class,
- SearchKeyword::class, SourceCookie::class, RssSource::class, Bookmark::class],
+ SearchKeyword::class, SourceCookie::class, RssSource::class, Bookmark::class, RssArticle::class],
version = 1,
exportSchema = true
)
@@ -58,4 +58,5 @@ abstract class AppDatabase : RoomDatabase() {
abstract fun sourceCookieDao(): SourceCookieDao
abstract fun rssSourceDao(): RssSourceDao
abstract fun bookmarkDao(): BookmarkDao
+ abstract fun rssArtivleDao(): RssArticleDao
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/data/dao/BookDao.kt b/app/src/main/java/io/legado/app/data/dao/BookDao.kt
index bae53c8f2..c74172c37 100644
--- a/app/src/main/java/io/legado/app/data/dao/BookDao.kt
+++ b/app/src/main/java/io/legado/app/data/dao/BookDao.kt
@@ -24,6 +24,9 @@ interface BookDao {
@Query("SELECT bookUrl FROM books WHERE `group` = :group")
fun observeUrlsByGroup(group: Int): LiveData>
+ @Query("SELECT * FROM books WHERE name like '%'||:key||'%' or author like '%'||:key||'%'")
+ fun liveDataSearch(key: String): LiveData>
+
@Query("SELECT * FROM books WHERE `name` in (:names)")
fun findByName(vararg names: String): List
diff --git a/app/src/main/java/io/legado/app/data/dao/BookGroupDao.kt b/app/src/main/java/io/legado/app/data/dao/BookGroupDao.kt
index 51068b149..6162ffc5a 100644
--- a/app/src/main/java/io/legado/app/data/dao/BookGroupDao.kt
+++ b/app/src/main/java/io/legado/app/data/dao/BookGroupDao.kt
@@ -1,10 +1,7 @@
package io.legado.app.data.dao
import androidx.lifecycle.LiveData
-import androidx.room.Dao
-import androidx.room.Insert
-import androidx.room.OnConflictStrategy
-import androidx.room.Query
+import androidx.room.*
import io.legado.app.data.entities.BookGroup
@Dao
@@ -17,5 +14,11 @@ interface BookGroupDao {
val maxId: Int
@Insert(onConflict = OnConflictStrategy.REPLACE)
- fun insert(bookGroup: BookGroup)
+ fun insert(vararg bookGroup: BookGroup)
+
+ @Update
+ fun update(vararg bookGroup: BookGroup)
+
+ @Delete
+ fun delete(vararg bookGroup: BookGroup)
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt b/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt
index 39dc91264..e1864eb5b 100644
--- a/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt
+++ b/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt
@@ -11,7 +11,7 @@ interface BookSourceDao {
@Query("select * from book_sources order by customOrder asc")
fun liveDataAll(): LiveData>
- @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>
@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>
- @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
@Query("update book_sources set enabled = 1 where bookSourceUrl in (:sourceUrls)")
@@ -65,6 +65,12 @@ interface BookSourceDao {
@Delete
fun delete(vararg bookSource: BookSource)
+ @Query("delete from book_sources where bookSourceUrl = :key")
+ fun delete(key: String)
+
@get:Query("select min(customOrder) from book_sources")
val minOrder: Int
+
+ @get:Query("select max(customOrder) from book_sources")
+ val maxOrder: Int
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/data/dao/RssArticleDao.kt b/app/src/main/java/io/legado/app/data/dao/RssArticleDao.kt
new file mode 100644
index 000000000..0c6290965
--- /dev/null
+++ b/app/src/main/java/io/legado/app/data/dao/RssArticleDao.kt
@@ -0,0 +1,18 @@
+package io.legado.app.data.dao
+
+import androidx.lifecycle.LiveData
+import androidx.room.*
+import io.legado.app.data.entities.RssArticle
+
+@Dao
+interface RssArticleDao {
+
+ @Query("select * from rssArticles where origin = :origin order by time desc")
+ fun liveByOrigin(origin: String): LiveData>
+
+ @Insert(onConflict = OnConflictStrategy.IGNORE)
+ fun insert(vararg rssArticle: RssArticle)
+
+ @Update
+ fun update(vararg rssArticle: RssArticle)
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/data/dao/RssSourceDao.kt b/app/src/main/java/io/legado/app/data/dao/RssSourceDao.kt
index 999898598..f280c0fb9 100644
--- a/app/src/main/java/io/legado/app/data/dao/RssSourceDao.kt
+++ b/app/src/main/java/io/legado/app/data/dao/RssSourceDao.kt
@@ -7,21 +7,48 @@ import io.legado.app.data.entities.RssSource
@Dao
interface RssSourceDao {
+ @Query("select * from rssSources where sourceUrl = :key")
+ fun getByKey(key: String): RssSource?
+
@get:Query("SELECT * FROM rssSources")
val all: List
- @Query("SELECT * FROM rssSources")
+ @Query("SELECT * FROM rssSources order by customOrder")
fun liveAll(): LiveData>
- @Query("SELECT * FROM rssSources where enabled = 1")
+ @Query("SELECT * FROM rssSources where sourceName like :key or sourceUrl like :key or sourceGroup like :key order by customOrder")
+ fun liveSearch(key: String): LiveData>
+
+ @Query("SELECT * FROM rssSources where enabled = 1 order by customOrder")
fun liveEnabled(): LiveData>
+ @Query("select sourceGroup from rssSources where sourceGroup is not null and sourceGroup <> ''")
+ fun liveGroup(): LiveData>
+
+ @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)
+
+ @get:Query("select min(customOrder) from rssSources")
+ val minOrder: Int
+
+ @get:Query("select max(customOrder) from rssSources")
+ val maxOrder: Int
+
+ @Query("delete from rssSources where sourceUrl in (:sourceUrls)")
+ fun delSection(vararg sourceUrls: String)
+
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(vararg rssSource: RssSource)
@Update
fun update(vararg rssSource: RssSource)
+ @Delete
+ fun delete(vararg rssSource: RssSource)
+
@Query("delete from rssSources where sourceUrl = :sourceUrl")
fun delete(sourceUrl: String)
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/data/dao/SearchKeywordDao.kt b/app/src/main/java/io/legado/app/data/dao/SearchKeywordDao.kt
index b2370ceb6..be7ad75c5 100644
--- a/app/src/main/java/io/legado/app/data/dao/SearchKeywordDao.kt
+++ b/app/src/main/java/io/legado/app/data/dao/SearchKeywordDao.kt
@@ -1,6 +1,6 @@
package io.legado.app.data.dao
-import androidx.paging.DataSource
+import androidx.lifecycle.LiveData
import androidx.room.*
import io.legado.app.data.entities.SearchKeyword
@@ -9,16 +9,19 @@ import io.legado.app.data.entities.SearchKeyword
interface SearchKeywordDao {
@Query("SELECT * FROM search_keywords ORDER BY usage DESC")
- fun observeByUsage(): DataSource.Factory
+ fun liveDataByUsage(): LiveData>
@Query("SELECT * FROM search_keywords ORDER BY lastUseTime DESC")
- fun observeByTime(): DataSource.Factory
+ fun liveDataByTime(): LiveData>
- @Insert(onConflict = OnConflictStrategy.REPLACE)
- fun insert(vararg keywords: SearchKeyword)
+ @Query("SELECT * FROM search_keywords where word like '%'||:key||'%' ORDER BY usage DESC")
+ fun liveDataSearch(key: String): LiveData>
+
+ @Query("select * from search_keywords where word = :key")
+ fun get(key: String): SearchKeyword?
@Insert(onConflict = OnConflictStrategy.REPLACE)
- fun insert(keyword: SearchKeyword): Long
+ fun insert(vararg keywords: SearchKeyword)
@Update
fun update(vararg keywords: SearchKeyword)
diff --git a/app/src/main/java/io/legado/app/data/entities/EditEntity.kt b/app/src/main/java/io/legado/app/data/entities/EditEntity.kt
new file mode 100644
index 000000000..d4c1ecc1a
--- /dev/null
+++ b/app/src/main/java/io/legado/app/data/entities/EditEntity.kt
@@ -0,0 +1,3 @@
+package io.legado.app.data.entities
+
+data class EditEntity(var key: String, var value: String?, var hint: Int)
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/data/entities/RssArticle.kt b/app/src/main/java/io/legado/app/data/entities/RssArticle.kt
index 6f8773ba3..f2deefb1e 100644
--- a/app/src/main/java/io/legado/app/data/entities/RssArticle.kt
+++ b/app/src/main/java/io/legado/app/data/entities/RssArticle.kt
@@ -1,14 +1,16 @@
package io.legado.app.data.entities
import androidx.room.Entity
+import androidx.room.Ignore
import androidx.room.PrimaryKey
@Entity(tableName = "rssArticles")
data class RssArticle(
var origin: String = "",
+ var time: Long = System.currentTimeMillis(),
@PrimaryKey
- var guid: String? = null,
+ var guid: String = "",
var title: String? = null,
var author: String? = null,
var link: String? = null,
@@ -16,5 +18,12 @@ data class RssArticle(
var description: String? = null,
var content: String? = null,
var image: String? = null,
- var categories: MutableList = mutableListOf()
-)
\ No newline at end of file
+ var categories: String? = null,
+ var read: Boolean = false,
+ var star: Boolean = false
+) {
+
+ @Ignore
+ var categoryList: MutableList = mutableListOf()
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/data/entities/RssSource.kt b/app/src/main/java/io/legado/app/data/entities/RssSource.kt
index 17a7064e9..82f0d4863 100644
--- a/app/src/main/java/io/legado/app/data/entities/RssSource.kt
+++ b/app/src/main/java/io/legado/app/data/entities/RssSource.kt
@@ -2,25 +2,31 @@ package io.legado.app.data.entities
import android.os.Parcelable
import androidx.room.Entity
+import androidx.room.Index
import androidx.room.PrimaryKey
import kotlinx.android.parcel.Parcelize
@Parcelize
-@Entity(tableName = "rssSources")
+@Entity(tableName = "rssSources", indices = [(Index(value = ["sourceUrl"], unique = false))])
data class RssSource(
- var sourceName: String,
@PrimaryKey
- var sourceUrl: String,
- var iconUrl: String,
+ var sourceUrl: String = "",
+ var sourceName: String = "",
+ var sourceIcon: String = "",
+ var sourceGroup: String? = null,
var enabled: Boolean = true,
+ //列表规则
+ var ruleArticles: String? = null,
var ruleGuid: String? = null,
var ruleTitle: String? = null,
var ruleAuthor: String? = null,
- var ruleLink: String? = null,
var rulePubDate: String? = null,
+ //类别
+ var ruleCategories: String? = null,
+ //描述
var ruleDescription: String? = null,
- var ruleContent: String? = null,
var ruleImage: String? = null,
- var ruleCategories: String? = null,
+ var ruleContent: String? = null,
+ var ruleLink: String? = null,
var customOrder: Int = 0
) : Parcelable
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/data/entities/SearchKeyword.kt b/app/src/main/java/io/legado/app/data/entities/SearchKeyword.kt
index 1c1c5015b..89d1c3baf 100644
--- a/app/src/main/java/io/legado/app/data/entities/SearchKeyword.kt
+++ b/app/src/main/java/io/legado/app/data/entities/SearchKeyword.kt
@@ -13,5 +13,5 @@ data class SearchKeyword(
@PrimaryKey
var word: String = "", // 搜索关键词
var usage: Int = 1, // 使用次数
- var lastUseTime: Long = 0 // 最后一次使用时间
+ var lastUseTime: Long = System.currentTimeMillis() // 最后一次使用时间
) : Parcelable
diff --git a/app/src/main/java/io/legado/app/help/ReadBookConfig.kt b/app/src/main/java/io/legado/app/help/ReadBookConfig.kt
index 915ea9f5a..9238c1341 100644
--- a/app/src/main/java/io/legado/app/help/ReadBookConfig.kt
+++ b/app/src/main/java/io/legado/app/help/ReadBookConfig.kt
@@ -1,6 +1,7 @@
package io.legado.app.help
import android.graphics.Color
+import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import io.legado.app.App
@@ -52,7 +53,11 @@ object ReadBookConfig {
}
fun upBg() {
- bg = getConfig().bgDrawable()
+ val resources = App.INSTANCE.resources
+ val dm = resources.displayMetrics
+ val width = dm.widthPixels
+ val height = dm.heightPixels
+ bg = getConfig().bgDrawable(width, height)
}
fun save() {
@@ -162,20 +167,30 @@ object ReadBookConfig {
else bgType
}
- fun bgDrawable(): Drawable {
+ fun bgDrawable(width: Int, height: Int): Drawable {
var bgDrawable: Drawable? = null
- kotlin.runCatching {
- when (bgType()) {
- 0 -> bgDrawable = ColorDrawable(Color.parseColor(bgStr()))
- 1 -> bgDrawable =
- Drawable.createFromStream(
- App.INSTANCE.assets.open("bg" + File.separator + bgStr()),
- "bg"
+ val resources = App.INSTANCE.resources
+ try {
+ bgDrawable = when (bgType()) {
+ 0 -> ColorDrawable(Color.parseColor(bgStr()))
+ 1 -> {
+ BitmapDrawable(
+ resources,
+ BitmapUtil.decodeBitmap(
+ App.INSTANCE,
+ "bg" + File.separator + bgStr(),
+ width,
+ height
+ )
)
- else -> runCatching {
- bgDrawable = Drawable.createFromPath(bgStr())
}
+ else -> BitmapDrawable(
+ resources,
+ BitmapUtil.decodeBitmap(bgStr(), width, height)
+ )
}
+ } catch (e: Exception) {
+ e.printStackTrace()
}
return bgDrawable ?: ColorDrawable(App.INSTANCE.getCompatColor(R.color.background))
}
diff --git a/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt b/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt
index 5b8c3d2c0..e9fd0df86 100644
--- a/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt
+++ b/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt
@@ -1,18 +1,26 @@
package io.legado.app.help.coroutine
-import android.util.Log
+import io.legado.app.BuildConfig
import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext
-class Coroutine(scope: CoroutineScope, block: suspend CoroutineScope.() -> T) {
+class Coroutine(
+ scope: CoroutineScope,
+ context: CoroutineContext = Dispatchers.IO,
+ block: suspend CoroutineScope.() -> T
+) {
companion object {
val DEFAULT = MainScope()
- fun async(scope: CoroutineScope = DEFAULT, block: suspend CoroutineScope.() -> T): Coroutine {
- return Coroutine(scope, block)
+ fun async(
+ scope: CoroutineScope = DEFAULT,
+ context: CoroutineContext = Dispatchers.IO,
+ block: suspend CoroutineScope.() -> T
+ ): Coroutine {
+ return Coroutine(scope, context, block)
}
}
@@ -37,7 +45,7 @@ class Coroutine(scope: CoroutineScope, block: suspend CoroutineScope.() -> T)
get() = job.isCompleted
init {
- this.job = executeInternal(scope, block)
+ this.job = executeInternal(scope, context, block)
}
fun timeout(timeMillis: () -> Long): Coroutine {
@@ -101,13 +109,20 @@ class Coroutine(scope: CoroutineScope, block: suspend CoroutineScope.() -> T)
return job.invokeOnCompletion(handler)
}
- private fun executeInternal(scope: CoroutineScope, block: suspend CoroutineScope.() -> T): Job {
+ private fun executeInternal(
+ scope: CoroutineScope,
+ context: CoroutineContext,
+ block: suspend CoroutineScope.() -> T
+ ): Job {
return scope.plus(Dispatchers.Main).launch {
try {
start?.let { dispatchVoidCallback(this, it) }
- val value = executeBlock(scope, timeMillis ?: 0L, block)
+ val value = executeBlock(scope, context, timeMillis ?: 0L, block)
success?.let { dispatchCallback(this, value, it) }
} catch (e: Throwable) {
+ if (BuildConfig.DEBUG) {
+ e.printStackTrace()
+ }
val consume: Boolean = errorReturn?.value?.let { value ->
success?.let { dispatchCallback(this, value, it) }
true
@@ -148,10 +163,11 @@ class Coroutine(scope: CoroutineScope, block: suspend CoroutineScope.() -> T)
private suspend inline fun executeBlock(
scope: CoroutineScope,
+ context: CoroutineContext,
timeMillis: Long,
noinline block: suspend CoroutineScope.() -> T
): T? {
- return withContext(scope.coroutineContext.plus(Dispatchers.IO)) {
+ return withContext(scope.coroutineContext.plus(context)) {
if (timeMillis > 0L) withTimeout(timeMillis) {
block()
} else block()
diff --git a/app/src/main/java/io/legado/app/help/storage/Backup.kt b/app/src/main/java/io/legado/app/help/storage/Backup.kt
index 92333d1d3..fa3ad14f9 100644
--- a/app/src/main/java/io/legado/app/help/storage/Backup.kt
+++ b/app/src/main/java/io/legado/app/help/storage/Backup.kt
@@ -35,7 +35,7 @@ object Backup {
fun autoBackup() {
doAsync {
- val path = defaultPath + File.separator + "autoBackup"
+ val path = defaultPath
backupBookshelf(path)
backupBookSource(path)
backupRssSource(path)
@@ -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) {
diff --git a/app/src/main/java/io/legado/app/help/storage/WebDavHelp.kt b/app/src/main/java/io/legado/app/help/storage/WebDavHelp.kt
index 128fb6078..3d82b5320 100644
--- a/app/src/main/java/io/legado/app/help/storage/WebDavHelp.kt
+++ b/app/src/main/java/io/legado/app/help/storage/WebDavHelp.kt
@@ -39,8 +39,8 @@ object WebDavHelp {
val url = getWebDavUrl()
val names = arrayListOf()
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)
diff --git a/app/src/main/java/io/legado/app/model/Rss.kt b/app/src/main/java/io/legado/app/model/Rss.kt
new file mode 100644
index 000000000..cd76e24fe
--- /dev/null
+++ b/app/src/main/java/io/legado/app/model/Rss.kt
@@ -0,0 +1,24 @@
+package io.legado.app.model
+
+import io.legado.app.data.entities.RssArticle
+import io.legado.app.data.entities.RssSource
+import io.legado.app.help.coroutine.Coroutine
+import io.legado.app.model.analyzeRule.AnalyzeUrl
+import io.legado.app.model.rss.RssParserByRule
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlin.coroutines.CoroutineContext
+
+object Rss {
+
+ fun getArticles(
+ rssSource: RssSource,
+ scope: CoroutineScope = Coroutine.DEFAULT,
+ context: CoroutineContext = Dispatchers.IO
+ ): Coroutine> {
+ return Coroutine.async(scope, context) {
+ val response = AnalyzeUrl(rssSource.sourceUrl).getResponseAsync().await()
+ RssParserByRule.parseXML(response, rssSource)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/model/WebBook.kt b/app/src/main/java/io/legado/app/model/WebBook.kt
index a09e9f326..9201fd542 100644
--- a/app/src/main/java/io/legado/app/model/WebBook.kt
+++ b/app/src/main/java/io/legado/app/model/WebBook.kt
@@ -11,6 +11,8 @@ import io.legado.app.model.webbook.BookContent
import io.legado.app.model.webbook.BookInfo
import io.legado.app.model.webbook.BookList
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlin.coroutines.CoroutineContext
class WebBook(val bookSource: BookSource) {
@@ -20,9 +22,13 @@ class WebBook(val bookSource: BookSource) {
/**
* 搜索
*/
- fun searchBook(key: String, page: Int? = 1, scope: CoroutineScope = Coroutine.DEFAULT)
- : Coroutine> {
- return Coroutine.async(scope) {
+ fun searchBook(
+ key: String,
+ page: Int? = 1,
+ scope: CoroutineScope = Coroutine.DEFAULT,
+ context: CoroutineContext = Dispatchers.IO
+ ): Coroutine> {
+ return Coroutine.async(scope, context) {
bookSource.searchUrl?.let { searchUrl ->
val analyzeUrl = AnalyzeUrl(
ruleUrl = searchUrl,
@@ -40,9 +46,13 @@ class WebBook(val bookSource: BookSource) {
/**
* 发现
*/
- fun exploreBook(url: String, page: Int? = 1, scope: CoroutineScope = Coroutine.DEFAULT)
- : Coroutine> {
- return Coroutine.async(scope) {
+ fun exploreBook(
+ url: String,
+ page: Int? = 1,
+ scope: CoroutineScope = Coroutine.DEFAULT,
+ context: CoroutineContext = Dispatchers.IO
+ ): Coroutine> {
+ return Coroutine.async(scope, context) {
val analyzeUrl = AnalyzeUrl(
ruleUrl = url,
page = page,
@@ -57,8 +67,12 @@ class WebBook(val bookSource: BookSource) {
/**
* 书籍信息
*/
- fun getBookInfo(book: Book, scope: CoroutineScope = Coroutine.DEFAULT): Coroutine {
- return Coroutine.async(scope) {
+ fun getBookInfo(
+ book: Book,
+ scope: CoroutineScope = Coroutine.DEFAULT,
+ context: CoroutineContext = Dispatchers.IO
+ ): Coroutine {
+ return Coroutine.async(scope, context) {
val analyzeUrl = AnalyzeUrl(
book = book,
ruleUrl = book.bookUrl,
@@ -76,9 +90,10 @@ class WebBook(val bookSource: BookSource) {
*/
fun getChapterList(
book: Book,
- scope: CoroutineScope = Coroutine.DEFAULT
+ scope: CoroutineScope = Coroutine.DEFAULT,
+ context: CoroutineContext = Dispatchers.IO
): Coroutine> {
- return Coroutine.async(scope) {
+ return Coroutine.async(scope, context) {
val analyzeUrl = AnalyzeUrl(
book = book,
ruleUrl = book.tocUrl,
@@ -97,9 +112,10 @@ class WebBook(val bookSource: BookSource) {
book: Book,
bookChapter: BookChapter,
nextChapterUrl: String? = null,
- scope: CoroutineScope = Coroutine.DEFAULT
+ scope: CoroutineScope = Coroutine.DEFAULT,
+ context: CoroutineContext = Dispatchers.IO
): Coroutine {
- return Coroutine.async(scope) {
+ return Coroutine.async(scope, context) {
val analyzeUrl =
AnalyzeUrl(
book = book,
diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt
index bc38b8571..c04981cdd 100644
--- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt
+++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt
@@ -445,7 +445,7 @@ class AnalyzeRule(private var book: BaseBook? = null) {
}
//分离正则表达式
val ruleStrS =
- rule.trim { it <= ' ' }.splitNotBlank("##")
+ rule.trim { it <= ' ' }.split("##")
rule = ruleStrS[0]
if (ruleStrS.size > 1) {
replaceRegex = ruleStrS[1]
diff --git a/app/src/main/java/io/legado/app/model/rss/RssParser.kt b/app/src/main/java/io/legado/app/model/rss/RssParser.kt
index 2696fb26a..7363a349e 100644
--- a/app/src/main/java/io/legado/app/model/rss/RssParser.kt
+++ b/app/src/main/java/io/legado/app/model/rss/RssParser.kt
@@ -11,7 +11,7 @@ import java.io.StringReader
object RssParser {
@Throws(XmlPullParserException::class, IOException::class)
- fun parseXML(xml: String): MutableList {
+ fun parseXML(xml: String, sourceUrl: String): MutableList {
val articleList = mutableListOf()
var currentArticle = RssArticle()
@@ -42,7 +42,7 @@ object RssParser {
xmlPullParser.name.equals(RSSKeywords.RSS_ITEM_AUTHOR, true) ->
if (insideItem) currentArticle.author = xmlPullParser.nextText().trim()
xmlPullParser.name.equals(RSSKeywords.RSS_ITEM_CATEGORY, true) ->
- if (insideItem) currentArticle.categories.add(xmlPullParser.nextText().trim())
+ if (insideItem) currentArticle.categoryList.add(xmlPullParser.nextText().trim())
xmlPullParser.name.equals(RSSKeywords.RSS_ITEM_THUMBNAIL, true) ->
if (insideItem) currentArticle.image =
xmlPullParser.getAttributeValue(null, RSSKeywords.RSS_ITEM_URL)
@@ -92,6 +92,8 @@ object RssParser {
) {
// The item is correctly parsed
insideItem = false
+ currentArticle.categories = currentArticle.categoryList.joinToString(",")
+ currentArticle.origin = sourceUrl
articleList.add(currentArticle)
currentArticle = RssArticle()
}
diff --git a/app/src/main/java/io/legado/app/model/rss/RssParserByRule.kt b/app/src/main/java/io/legado/app/model/rss/RssParserByRule.kt
new file mode 100644
index 000000000..45bbaf637
--- /dev/null
+++ b/app/src/main/java/io/legado/app/model/rss/RssParserByRule.kt
@@ -0,0 +1,94 @@
+package io.legado.app.model.rss
+
+import io.legado.app.App
+import io.legado.app.R
+import io.legado.app.data.entities.RssArticle
+import io.legado.app.data.entities.RssSource
+import io.legado.app.model.analyzeRule.AnalyzeRule
+import retrofit2.Response
+
+object RssParserByRule {
+
+ @Throws(Exception::class)
+ fun parseXML(response: Response, rssSource: RssSource): MutableList {
+ val articleList = mutableListOf()
+
+ val xml = response.body()
+ if (xml.isNullOrBlank()) {
+ throw Exception(
+ App.INSTANCE.getString(
+ R.string.error_get_web_content,
+ rssSource.sourceUrl
+ )
+ )
+ }
+
+ rssSource.ruleArticles?.let { ruleArticles ->
+ val analyzeRule = AnalyzeRule()
+ analyzeRule.setContent(xml)
+ val collections = analyzeRule.getElements(ruleArticles)
+ val ruleGuid = analyzeRule.splitSourceRule(rssSource.ruleGuid ?: "")
+ val ruleTitle = analyzeRule.splitSourceRule(rssSource.ruleTitle ?: "")
+ val ruleAuthor = analyzeRule.splitSourceRule(rssSource.ruleAuthor ?: "")
+ val rulePubDate = analyzeRule.splitSourceRule(rssSource.rulePubDate ?: "")
+ val ruleCategories = analyzeRule.splitSourceRule(rssSource.ruleCategories ?: "")
+ val ruleDescription = analyzeRule.splitSourceRule(rssSource.ruleDescription ?: "")
+ val ruleImage = analyzeRule.splitSourceRule(rssSource.ruleImage ?: "")
+ val ruleContent = analyzeRule.splitSourceRule(rssSource.ruleContent ?: "")
+ val ruleLink = analyzeRule.splitSourceRule(rssSource.ruleLink ?: "")
+ for ((index, item) in collections.withIndex()) {
+ getItem(
+ item,
+ analyzeRule,
+ index == 0,
+ ruleGuid,
+ ruleTitle,
+ ruleAuthor,
+ rulePubDate,
+ ruleCategories,
+ ruleDescription,
+ ruleImage,
+ ruleContent,
+ ruleLink
+ )?.let {
+ it.origin = rssSource.sourceUrl
+ articleList.add(it)
+ }
+ }
+ } ?: let {
+ return RssParser.parseXML(xml, rssSource.sourceUrl)
+ }
+ return articleList
+ }
+
+ private fun getItem(
+ item: Any,
+ analyzeRule: AnalyzeRule,
+ printLog: Boolean,
+ ruleGuid: List,
+ ruleTitle: List,
+ ruleAuthor: List,
+ rulePubDate: List,
+ ruleCategories: List,
+ ruleDescription: List,
+ ruleImage: List,
+ ruleContent: List,
+ ruleLink: List
+ ): RssArticle? {
+ val rssArticle = RssArticle()
+ analyzeRule.setContent(item)
+ rssArticle.guid = analyzeRule.getString(ruleGuid)
+ rssArticle.title = analyzeRule.getString(ruleTitle)
+ rssArticle.author = analyzeRule.getString(ruleAuthor)
+ rssArticle.pubDate = analyzeRule.getString(rulePubDate)
+ rssArticle.categories = analyzeRule.getString(ruleCategories)
+ rssArticle.description = analyzeRule.getString(ruleDescription)
+ rssArticle.image = analyzeRule.getString(ruleImage)
+ rssArticle.link = analyzeRule.getString(ruleLink)
+ rssArticle.content = analyzeRule.getString(ruleContent)
+ if (rssArticle.title.isNullOrBlank()) {
+ return null
+ }
+ return rssArticle
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/receiver/MediaButtonReceiver.kt b/app/src/main/java/io/legado/app/receiver/MediaButtonReceiver.kt
index 17eba6b18..ce3908021 100644
--- a/app/src/main/java/io/legado/app/receiver/MediaButtonReceiver.kt
+++ b/app/src/main/java/io/legado/app/receiver/MediaButtonReceiver.kt
@@ -7,6 +7,7 @@ import android.view.KeyEvent
import io.legado.app.constant.Bus
import io.legado.app.help.ActivityHelp
import io.legado.app.ui.book.read.ReadBookActivity
+import io.legado.app.utils.LogUtils
import io.legado.app.utils.postEvent
@@ -22,7 +23,7 @@ class MediaButtonReceiver : BroadcastReceiver() {
fun handleIntent(context: Context, intent: Intent): Boolean {
val intentAction = intent.action
val keyEventAction = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT)?.action
-// LogUtils.log("耳机按键: $intentAction $keyEventAction")
+ LogUtils.d("耳机按键", "$intentAction $keyEventAction")
if (Intent.ACTION_MEDIA_BUTTON == intentAction) {
if (keyEventAction == KeyEvent.ACTION_DOWN) {
readAloud(context)
diff --git a/app/src/main/java/io/legado/app/ui/about/AboutFragment.kt b/app/src/main/java/io/legado/app/ui/about/AboutFragment.kt
index e98523051..093c5a9ec 100644
--- a/app/src/main/java/io/legado/app/ui/about/AboutFragment.kt
+++ b/app/src/main/java/io/legado/app/ui/about/AboutFragment.kt
@@ -24,7 +24,7 @@ class AboutFragment : PreferenceFragmentCompat() {
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
when (preference?.key) {
"mail" -> openIntent(Intent.ACTION_SENDTO, "mailto:kunfei.ge@gmail.com")
- "gitHub" -> openIntent(Intent.ACTION_VIEW, getString(R.string.this_github_url))
+ "git" -> openIntent(Intent.ACTION_VIEW, getString(R.string.this_github_url))
}
return super.onPreferenceTreeClick(preference)
}
diff --git a/app/src/main/java/io/legado/app/ui/about/DonateActivity.kt b/app/src/main/java/io/legado/app/ui/about/DonateActivity.kt
index 313a6bec6..6d0671029 100644
--- a/app/src/main/java/io/legado/app/ui/about/DonateActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/about/DonateActivity.kt
@@ -8,11 +8,10 @@ import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.widget.Toast
-import androidx.lifecycle.AndroidViewModel
import io.legado.app.R
import io.legado.app.base.BaseActivity
import io.legado.app.lib.theme.ATH
-import io.legado.app.utils.getViewModel
+import io.legado.app.utils.ACache
import kotlinx.android.synthetic.main.activity_donate.*
import kotlinx.android.synthetic.main.view_title_bar.*
import org.jetbrains.anko.toast
@@ -60,7 +59,7 @@ class DonateActivity : BaseActivity(R.layout.activity_donate) {
} catch (e: Exception) {
e.printStackTrace()
} finally {
-
+ ACache.get(this, cacheDir = false).put("proTime", System.currentTimeMillis())
}
}
diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt
index 62bc33ca9..b9bb67838 100644
--- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt
@@ -14,7 +14,7 @@ import io.legado.app.help.ImageLoader
import io.legado.app.lib.theme.ATH
import io.legado.app.ui.book.info.edit.BookInfoEditActivity
import io.legado.app.ui.book.read.ReadBookActivity
-import io.legado.app.ui.book.source.edit.SourceEditActivity
+import io.legado.app.ui.book.source.edit.BookSourceEditActivity
import io.legado.app.ui.changesource.ChangeSourceDialog
import io.legado.app.utils.getCompatDrawable
import io.legado.app.utils.getViewModel
@@ -88,8 +88,8 @@ class BookInfoActivity : VMBaseActivity(R.layout.activity_boo
book.getDisplayIntro() // getString(R.string.intro_show, book.getDisplayIntro())
book.getDisplayCover()?.let {
ImageLoader.load(this, it)
- .placeholder(R.drawable.img_cover_default)
- .error(R.drawable.img_cover_default)
+ .placeholder(R.drawable.image_cover_default)
+ .error(R.drawable.image_cover_default)
.centerCrop()
.setAsDrawable(iv_cover)
}
@@ -183,10 +183,14 @@ class BookInfoActivity : VMBaseActivity(R.layout.activity_boo
}
}
}
- tv_loading.onClick { }
+ tv_loading.onClick {
+ viewModel.bookData.value?.let {
+ viewModel.loadBookInfo(it)
+ }
+ }
tv_origin.onClick {
viewModel.bookData.value?.let {
- startActivity(Pair("data", it.origin))
+ startActivity(Pair("data", it.origin))
}
}
tv_change_source.onClick {
@@ -204,8 +208,10 @@ class BookInfoActivity : VMBaseActivity(R.layout.activity_boo
}
}
iv_chapter_top.onClick {
- adapter.reorder = !adapter.reorder
- adapter.notifyDataSetChanged()
+ rv_chapter_list.scrollToPosition(0)
+ }
+ iv_chapter_bottom.onClick {
+ rv_chapter_list.scrollToPosition(adapter.itemCount - 1)
}
}
@@ -226,13 +232,11 @@ class BookInfoActivity : VMBaseActivity(R.layout.activity_boo
}
}
- override fun curOrigin(): String? {
- return viewModel.bookData.value?.origin
- }
+ override val curOrigin: String?
+ get() = viewModel.bookData.value?.origin
- override fun oldBook(): Book? {
- return viewModel.bookData.value
- }
+ override val oldBook: Book?
+ get() = viewModel.bookData.value
override fun changeTo(book: Book) {
upLoading(true)
diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt
index cb54f4df1..dc96b4c0c 100644
--- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt
@@ -99,7 +99,6 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
}
}.onError {
toast(R.string.error_get_chapter_list)
- it.printStackTrace()
}
} ?: toast(R.string.error_no_source)
}
@@ -130,7 +129,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
chapters
)
book.durChapterTitle = chapters[book.durChapterIndex].title
- App.db.bookDao().update(book)
+ App.db.bookDao().insert(book)
App.db.bookChapterDao().insert(*chapters.toTypedArray())
bookData.postValue(book)
chapterListData.postValue(chapters)
diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
index dbcc05184..c763ae746 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
@@ -32,16 +32,17 @@ import io.legado.app.service.BaseReadAloudService
import io.legado.app.ui.book.read.config.*
import io.legado.app.ui.book.read.config.BgTextConfigDialog.Companion.BG_COLOR
import io.legado.app.ui.book.read.config.BgTextConfigDialog.Companion.TEXT_COLOR
-import io.legado.app.ui.book.source.edit.SourceEditActivity
+import io.legado.app.ui.book.source.edit.BookSourceEditActivity
import io.legado.app.ui.changesource.ChangeSourceDialog
import io.legado.app.ui.chapterlist.ChapterListActivity
import io.legado.app.ui.replacerule.ReplaceRuleActivity
+import io.legado.app.ui.replacerule.edit.ReplaceEditDialog
import io.legado.app.ui.widget.page.ChapterProvider
import io.legado.app.ui.widget.page.PageView
import io.legado.app.ui.widget.page.TextChapter
import io.legado.app.ui.widget.page.delegate.PageDelegate
import io.legado.app.utils.*
-import kotlinx.android.synthetic.main.activity_read_book.*
+import kotlinx.android.synthetic.main.activity_book_read.*
import kotlinx.android.synthetic.main.view_book_page.*
import kotlinx.android.synthetic.main.view_read_menu.*
import kotlinx.android.synthetic.main.view_title_bar.*
@@ -54,7 +55,7 @@ import org.jetbrains.anko.startActivity
import org.jetbrains.anko.startActivityForResult
import org.jetbrains.anko.toast
-class ReadBookActivity : VMBaseActivity(R.layout.activity_read_book),
+class ReadBookActivity : VMBaseActivity(R.layout.activity_book_read),
PageView.CallBack,
ReadMenu.CallBack,
ReadAloudDialog.CallBack,
@@ -113,7 +114,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_rea
private fun initView() {
tv_chapter_name.onClick {
viewModel.webBook?.let {
- startActivityForResult(
+ startActivityForResult(
requestCodeEditSource,
Pair("data", it.bookSource.bookSourceUrl)
)
@@ -304,7 +305,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_rea
tv_chapter_url.text = it.url
tv_chapter_url.visible()
}
- read_menu.upReadProgress(it.pageSize().minus(1), viewModel.durPageIndex)
+ seek_read_page.max = it.pageSize().minus(1)
tv_pre.isEnabled = viewModel.durChapterIndex != 0
tv_next.isEnabled = viewModel.durChapterIndex != viewModel.chapterSize - 1
curPageChanged()
@@ -312,6 +313,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_rea
}
private fun curPageChanged() {
+ seek_read_page.progress = viewModel.durPageIndex
when (readAloudStatus) {
Status.PLAY -> readAloud()
Status.PAUSE -> {
@@ -328,13 +330,11 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_rea
return viewModel.chapterSize
}
- override fun curOrigin(): String? {
- return viewModel.bookData.value?.origin
- }
+ override val curOrigin: String?
+ get() = viewModel.bookData.value?.origin
- override fun oldBook(): Book? {
- return viewModel.bookData.value
- }
+ override val oldBook: Book?
+ get() = viewModel.bookData.value
override fun changeTo(book: Book) {
viewModel.changeTo(book)
@@ -608,7 +608,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_rea
}
}
observeEvent(Bus.REPLACE) {
- toast(it)
+ ReplaceEditDialog().show(supportFragmentManager, "replaceEditDialog")
}
}
diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
index a0b02a402..d6d6ae004 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
@@ -68,7 +68,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
}
}
saveRead(book)
- }.onError { it.printStackTrace() }
+ }
}
private fun loadBookInfo(
@@ -103,7 +103,6 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
}
}?.onError {
toast(R.string.error_load_toc)
- it.printStackTrace()
} ?: autoChangeSource()
}
}
diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt
index bb321fd65..0aa08497c 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt
@@ -95,11 +95,6 @@ class ReadMenu : FrameLayout {
}
}
- fun upReadProgress(max: Int, dur: Int) {
- seek_read_page.max = max
- seek_read_page.progress = dur
- }
-
private fun brightnessAuto(): Boolean {
return context.getPrefBoolean("brightnessAuto", true)
}
diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt
index ea80392c8..d64fc4db9 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt
@@ -20,7 +20,7 @@ import io.legado.app.ui.book.read.Help
import io.legado.app.ui.book.read.ReadBookActivity
import io.legado.app.ui.widget.font.FontSelectDialog
import io.legado.app.utils.*
-import kotlinx.android.synthetic.main.activity_read_book.*
+import kotlinx.android.synthetic.main.activity_book_read.*
import kotlinx.android.synthetic.main.dialog_read_book_style.*
import org.jetbrains.anko.sdk27.listeners.onCheckedChange
import org.jetbrains.anko.sdk27.listeners.onClick
@@ -249,7 +249,7 @@ class ReadStyleDialog : DialogFragment() {
ReadBookConfig.getConfig(i).apply {
when (bgType()) {
2 -> ImageLoader.load(requireContext(), bgStr()).centerCrop().setAsDrawable(iv)
- else -> iv.setImageDrawable(bgDrawable())
+ else -> iv.setImageDrawable(bgDrawable(100, 150))
}
}
}
diff --git a/app/src/main/java/io/legado/app/ui/book/search/BookAdapter.kt b/app/src/main/java/io/legado/app/ui/book/search/BookAdapter.kt
new file mode 100644
index 000000000..3a08418e1
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/book/search/BookAdapter.kt
@@ -0,0 +1,24 @@
+package io.legado.app.ui.book.search
+
+import android.content.Context
+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.Book
+import kotlinx.android.synthetic.main.item_text.view.*
+import org.jetbrains.anko.sdk27.listeners.onClick
+
+class BookAdapter(context: Context, val callBack: CallBack) :
+ SimpleRecyclerAdapter(context, R.layout.item_text) {
+
+ override fun convert(holder: ItemViewHolder, item: Book, payloads: MutableList) {
+ with(holder.itemView) {
+ text_view.text = item.name
+ onClick { callBack.showBookInfo(item.bookUrl) }
+ }
+ }
+
+ interface CallBack {
+ fun showBookInfo(url: String)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/book/search/HistoryKeyAdapter.kt b/app/src/main/java/io/legado/app/ui/book/search/HistoryKeyAdapter.kt
new file mode 100644
index 000000000..213157e90
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/book/search/HistoryKeyAdapter.kt
@@ -0,0 +1,42 @@
+package io.legado.app.ui.book.search
+
+import android.content.Context
+import io.legado.app.App
+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.SearchKeyword
+import io.legado.app.ui.widget.anima.explosion_field.ExplosionField
+import kotlinx.android.synthetic.main.item_text.view.*
+import kotlinx.coroutines.Dispatchers.IO
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
+import org.jetbrains.anko.sdk27.listeners.onClick
+import org.jetbrains.anko.sdk27.listeners.onLongClick
+
+
+class HistoryKeyAdapter(context: Context, val callBack: CallBack) :
+ SimpleRecyclerAdapter(context, R.layout.item_text) {
+
+ override fun convert(holder: ItemViewHolder, item: SearchKeyword, payloads: MutableList) {
+ with(holder.itemView) {
+ text_view.text = item.word
+ onClick {
+ callBack.searchHistory(item.word)
+ }
+ onLongClick {
+ it?.let {
+ ExplosionField(context).explode(it, true)
+ }
+ GlobalScope.launch(IO) {
+ App.db.searchKeywordDao().delete(item)
+ }
+ true
+ }
+ }
+ }
+
+ interface CallBack {
+ fun searchHistory(key: String)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt
index 64d5d76f8..bc25fa64f 100644
--- a/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt
@@ -7,32 +7,44 @@ import androidx.lifecycle.Observer
import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList
import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.VMBaseActivity
+import io.legado.app.data.entities.Book
+import io.legado.app.data.entities.SearchKeyword
import io.legado.app.data.entities.SearchShow
import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.primaryTextColor
import io.legado.app.ui.book.info.BookInfoActivity
import io.legado.app.utils.getViewModel
+import io.legado.app.utils.gone
import io.legado.app.utils.invisible
import io.legado.app.utils.visible
-import kotlinx.android.synthetic.main.activity_search.*
+import kotlinx.android.synthetic.main.activity_book_search.*
import kotlinx.android.synthetic.main.view_search.*
import org.jetbrains.anko.sdk27.listeners.onClick
import org.jetbrains.anko.startActivity
-class SearchActivity : VMBaseActivity(R.layout.activity_search), SearchAdapter.CallBack {
+class SearchActivity : VMBaseActivity(R.layout.activity_book_search),
+ BookAdapter.CallBack,
+ HistoryKeyAdapter.CallBack,
+ SearchAdapter.CallBack {
override val viewModel: SearchViewModel
get() = getViewModel(SearchViewModel::class.java)
private lateinit var adapter: SearchAdapter
+ private lateinit var bookAdapter: BookAdapter
+ private lateinit var historyKeyAdapter: HistoryKeyAdapter
private var searchBookData: LiveData>? = null
+ private var historyData: LiveData>? = null
+ private var bookData: LiveData>? = null
override fun onActivityCreated(savedInstanceState: Bundle?) {
initRecyclerView()
initSearchView()
+ initOtherView()
initData()
intent.getStringExtra("key")?.let {
search_view.setQuery(it, true)
@@ -49,6 +61,7 @@ class SearchActivity : VMBaseActivity(R.layout.activity_search)
override fun onQueryTextSubmit(query: String?): Boolean {
search_view.clearFocus()
query?.let {
+ viewModel.saveSearchKey(query)
viewModel.search(it, {
refresh_progress_bar.isAutoLoading = true
initData()
@@ -63,19 +76,43 @@ class SearchActivity : VMBaseActivity(R.layout.activity_search)
override fun onQueryTextChange(newText: String?): Boolean {
if (newText.isNullOrBlank()) viewModel.stop()
+ upHistory(newText)
return false
}
})
- fb_stop.onClick { viewModel.stop() }
+ search_view.setOnQueryTextFocusChangeListener { _, hasFocus ->
+ if (hasFocus) {
+ ll_history.visible()
+ } else {
+ ll_history.invisible()
+ }
+ }
}
private fun initRecyclerView() {
ATH.applyEdgeEffectColor(recycler_view)
+ ATH.applyEdgeEffectColor(rv_bookshelf_search)
+ ATH.applyEdgeEffectColor(rv_history_key)
+ bookAdapter = BookAdapter(this, this)
+ rv_bookshelf_search.layoutManager =
+ LinearLayoutManager(this, RecyclerView.HORIZONTAL, false)
+ rv_bookshelf_search.adapter = bookAdapter
+ historyKeyAdapter = HistoryKeyAdapter(this, this)
+ rv_history_key.layoutManager = LinearLayoutManager(this, RecyclerView.HORIZONTAL, false)
+ rv_history_key.adapter = historyKeyAdapter
adapter = SearchAdapter(this)
recycler_view.layoutManager = LinearLayoutManager(this)
recycler_view.adapter = adapter
}
+ private fun initOtherView() {
+ tv_clear_history.onClick { viewModel.clearHistory() }
+ fb_stop.onClick {
+ viewModel.stop()
+ refresh_progress_bar.isAutoLoading = false
+ }
+ }
+
private fun initData() {
searchBookData?.removeObservers(this)
searchBookData = LivePagedListBuilder(
@@ -85,6 +122,35 @@ class SearchActivity : VMBaseActivity(R.layout.activity_search)
), 30
).build()
searchBookData?.observe(this, Observer { adapter.submitList(it) })
+ upHistory()
+ }
+
+ private fun upHistory(key: String? = null) {
+ bookData?.removeObservers(this)
+ if (key.isNullOrBlank()) {
+ tv_book_show.gone()
+ rv_bookshelf_search.gone()
+ } else {
+ bookData = App.db.bookDao().liveDataSearch(key)
+ bookData?.observe(this, Observer {
+ if (it.isEmpty()) {
+ tv_book_show.gone()
+ rv_bookshelf_search.gone()
+ } else {
+ tv_book_show.visible()
+ rv_bookshelf_search.visible()
+ }
+ bookAdapter.setItems(it)
+ })
+ }
+ historyData?.removeObservers(this)
+ historyData =
+ if (key.isNullOrBlank()) {
+ App.db.searchKeywordDao().liveDataByUsage()
+ } else {
+ App.db.searchKeywordDao().liveDataSearch(key)
+ }
+ historyData?.observe(this, Observer { historyKeyAdapter.setItems(it) })
}
override fun showBookInfo(name: String, author: String) {
@@ -94,4 +160,12 @@ class SearchActivity : VMBaseActivity(R.layout.activity_search)
}
}
}
+
+ override fun showBookInfo(url: String) {
+ startActivity(Pair("bookUrl", url))
+ }
+
+ override fun searchHistory(key: String) {
+ search_view.setQuery(key, false)
+ }
}
diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchAdapter.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchAdapter.kt
index a2cb9ad08..38974157d 100644
--- a/app/src/main/java/io/legado/app/ui/book/search/SearchAdapter.kt
+++ b/app/src/main/java/io/legado/app/ui/book/search/SearchAdapter.kt
@@ -84,8 +84,8 @@ class SearchAdapter(val callBack: CallBack) :
}
searchBook.coverUrl.let {
ImageLoader.load(context, it)//Glide自动识别http://和file://
- .placeholder(R.drawable.img_cover_default)
- .error(R.drawable.img_cover_default)
+ .placeholder(R.drawable.image_cover_default)
+ .error(R.drawable.image_cover_default)
.centerCrop()
.setAsDrawable(iv_cover)
}
@@ -100,8 +100,8 @@ class SearchAdapter(val callBack: CallBack) :
1 -> bv_originCount.setBadgeCount(searchBook.originCount)
2 -> searchBook.coverUrl.let {
ImageLoader.load(context, it)//Glide自动识别http://和file://
- .placeholder(R.drawable.img_cover_default)
- .error(R.drawable.img_cover_default)
+ .placeholder(R.drawable.image_cover_default)
+ .error(R.drawable.image_cover_default)
.centerCrop()
.setAsDrawable(iv_cover)
}
diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt
index f8550229f..0c21d1820 100644
--- a/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt
@@ -4,12 +4,15 @@ import android.app.Application
import io.legado.app.App
import io.legado.app.base.BaseViewModel
import io.legado.app.data.entities.SearchBook
+import io.legado.app.data.entities.SearchKeyword
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.WebBook
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.delay
+import kotlinx.coroutines.asCoroutineDispatcher
+import java.util.concurrent.Executors
class SearchViewModel(application: Application) : BaseViewModel(application) {
+ private var searchPool = Executors.newFixedThreadPool(16).asCoroutineDispatcher()
private var task: Coroutine<*>? = null
var searchKey: String = ""
var startTime: Long = 0
@@ -30,7 +33,12 @@ class SearchViewModel(application: Application) : BaseViewModel(application) {
val bookSourceList = App.db.bookSourceDao().allEnabled
for (item in bookSourceList) {
//task取消时自动取消 by (scope = this@execute)
- WebBook(item).searchBook(key, searchPage, scope = this@execute)
+ WebBook(item).searchBook(
+ key,
+ searchPage,
+ scope = this@execute,
+ context = searchPool
+ )
.timeout(30000L)
.onSuccess(Dispatchers.IO) {
it?.let { list ->
@@ -40,10 +48,7 @@ class SearchViewModel(application: Application) : BaseViewModel(application) {
}
}
}
- delay(100)//每隔100毫秒搜索一个书源
}
- }.onError {
- it.printStackTrace()
}
task?.invokeOnCompletion {
@@ -61,4 +66,24 @@ class SearchViewModel(application: Application) : BaseViewModel(application) {
success?.invoke(searchBook)
}
}
+
+ fun saveSearchKey(key: String) {
+ execute {
+ App.db.searchKeywordDao().get(key)?.let {
+ it.usage = it.usage + 1
+ App.db.searchKeywordDao().update(it)
+ } ?: App.db.searchKeywordDao().insert(SearchKeyword(key, 1))
+ }
+ }
+
+ fun clearHistory() {
+ execute {
+ App.db.searchKeywordDao().deleteAll()
+ }
+ }
+
+ override fun onCleared() {
+ super.onCleared()
+ searchPool.close()
+ }
}
diff --git a/app/src/main/java/io/legado/app/ui/book/source/debug/SourceDebugActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugActivity.kt
similarity index 88%
rename from app/src/main/java/io/legado/app/ui/book/source/debug/SourceDebugActivity.kt
rename to app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugActivity.kt
index b180f6b17..dafeaa218 100644
--- a/app/src/main/java/io/legado/app/ui/book/source/debug/SourceDebugActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugActivity.kt
@@ -12,18 +12,19 @@ import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.accentColor
import io.legado.app.ui.qrcode.QrCodeActivity
import io.legado.app.utils.getViewModel
-import kotlinx.android.synthetic.main.activity_book_source_debug.*
+import kotlinx.android.synthetic.main.activity_source_debug.*
import kotlinx.android.synthetic.main.view_search.*
import kotlinx.coroutines.launch
import org.jetbrains.anko.startActivityForResult
import org.jetbrains.anko.toast
-class SourceDebugActivity : VMBaseActivity(R.layout.activity_book_source_debug) {
+class BookSourceDebugActivity :
+ VMBaseActivity(R.layout.activity_source_debug) {
- override val viewModel: SourceDebugModel
- get() = getViewModel(SourceDebugModel::class.java)
+ override val viewModel: BookSourceDebugModel
+ get() = getViewModel(BookSourceDebugModel::class.java)
- private lateinit var adapter: SourceDebugAdapter
+ private lateinit var adapter: BookSourceDebugAdapter
private val qrRequestCode = 101
override fun onActivityCreated(savedInstanceState: Bundle?) {
@@ -42,7 +43,7 @@ class SourceDebugActivity : VMBaseActivity(R.layout.activity_b
private fun initRecyclerView() {
ATH.applyEdgeEffectColor(recycler_view)
- adapter = SourceDebugAdapter(this)
+ adapter = BookSourceDebugAdapter(this)
recycler_view.layoutManager = LinearLayoutManager(this)
recycler_view.adapter = adapter
rotate_loading.loadingColor = accentColor
diff --git a/app/src/main/java/io/legado/app/ui/book/source/debug/SourceDebugAdapter.kt b/app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugAdapter.kt
similarity index 90%
rename from app/src/main/java/io/legado/app/ui/book/source/debug/SourceDebugAdapter.kt
rename to app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugAdapter.kt
index 94147a5f1..2ec4e9518 100644
--- a/app/src/main/java/io/legado/app/ui/book/source/debug/SourceDebugAdapter.kt
+++ b/app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugAdapter.kt
@@ -6,7 +6,7 @@ import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import kotlinx.android.synthetic.main.item_log.view.*
-class SourceDebugAdapter(context: Context) :
+class BookSourceDebugAdapter(context: Context) :
SimpleRecyclerAdapter(context, R.layout.item_log) {
override fun convert(holder: ItemViewHolder, item: String, payloads: MutableList) {
holder.itemView.apply {
diff --git a/app/src/main/java/io/legado/app/ui/book/source/debug/SourceDebugModel.kt b/app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugModel.kt
similarity index 91%
rename from app/src/main/java/io/legado/app/ui/book/source/debug/SourceDebugModel.kt
rename to app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugModel.kt
index a17565820..ced985aa9 100644
--- a/app/src/main/java/io/legado/app/ui/book/source/debug/SourceDebugModel.kt
+++ b/app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugModel.kt
@@ -6,7 +6,8 @@ import io.legado.app.base.BaseViewModel
import io.legado.app.model.WebBook
import io.legado.app.model.webbook.SourceDebug
-class SourceDebugModel(application: Application) : BaseViewModel(application), SourceDebug.Callback {
+class BookSourceDebugModel(application: Application) : BaseViewModel(application),
+ SourceDebug.Callback {
private var webBook: WebBook? = null
diff --git a/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt
new file mode 100644
index 000000000..267890628
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt
@@ -0,0 +1,369 @@
+package io.legado.app.ui.book.source.edit
+
+import android.app.Activity
+import android.content.ClipData
+import android.content.ClipboardManager
+import android.content.Context
+import android.graphics.Rect
+import android.os.Bundle
+import android.view.Gravity
+import android.view.Menu
+import android.view.MenuItem
+import android.view.ViewTreeObserver
+import android.widget.EditText
+import android.widget.PopupWindow
+import androidx.lifecycle.Observer
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.google.android.material.tabs.TabLayout
+import io.legado.app.R
+import io.legado.app.base.VMBaseActivity
+import io.legado.app.constant.AppConst
+import io.legado.app.data.entities.BookSource
+import io.legado.app.data.entities.EditEntity
+import io.legado.app.data.entities.rule.*
+import io.legado.app.lib.theme.ATH
+import io.legado.app.ui.book.source.debug.BookSourceDebugActivity
+import io.legado.app.ui.widget.KeyboardToolPop
+import io.legado.app.utils.GSON
+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 BookSourceEditActivity :
+ VMBaseActivity(R.layout.activity_book_source_edit, false),
+ KeyboardToolPop.CallBack {
+ override val viewModel: BookSourceEditViewModel
+ get() = getViewModel(BookSourceEditViewModel::class.java)
+
+ private val adapter = BookSourceEditAdapter()
+ private val sourceEntities: ArrayList = ArrayList()
+ private val searchEntities: ArrayList = ArrayList()
+ private val findEntities: ArrayList = ArrayList()
+ private val infoEntities: ArrayList = ArrayList()
+ private val tocEntities: ArrayList = ArrayList()
+ private val contentEntities: ArrayList = ArrayList()
+
+ private var mSoftKeyboardTool: PopupWindow? = null
+ private var mIsSoftKeyBoardShowing = false
+
+ override fun onActivityCreated(savedInstanceState: Bundle?) {
+ initView()
+ viewModel.sourceLiveData.observe(this, Observer {
+ upRecyclerView(it)
+ })
+ viewModel.initData(intent)
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ mSoftKeyboardTool?.dismiss()
+ }
+
+ override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
+ menuInflater.inflate(R.menu.source_edit, menu)
+ return super.onCompatCreateOptionsMenu(menu)
+ }
+
+ override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
+ when (item.itemId) {
+ R.id.menu_save -> {
+ getSource()?.let {
+ viewModel.save(it) { setResult(Activity.RESULT_OK); finish() }
+ }
+ }
+ R.id.menu_debug_source -> {
+ getSource()?.let {
+ viewModel.save(it) {
+ startActivity(Pair("key", it.bookSourceUrl))
+ }
+ }
+ }
+ R.id.menu_copy_source -> {
+ GSON.toJson(getSource())?.let { sourceStr ->
+ val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager?
+ clipboard?.primaryClip = ClipData.newPlainText(null, sourceStr)
+ }
+ }
+ R.id.menu_paste_source -> viewModel.pasteSource()
+ }
+ return super.onCompatOptionsItemSelected(item)
+ }
+
+ private fun initView() {
+ ATH.applyEdgeEffectColor(recycler_view)
+ mSoftKeyboardTool = KeyboardToolPop(this, AppConst.keyboardToolChars, this)
+ window.decorView.viewTreeObserver.addOnGlobalLayoutListener(KeyboardOnGlobalChangeListener())
+ recycler_view.layoutManager = LinearLayoutManager(this)
+ recycler_view.adapter = adapter
+ tab_layout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
+ override fun onTabReselected(tab: TabLayout.Tab?) {
+
+ }
+
+ override fun onTabUnselected(tab: TabLayout.Tab?) {
+
+ }
+
+ override fun onTabSelected(tab: TabLayout.Tab?) {
+ setEditEntities(tab?.position)
+ }
+ })
+ }
+
+ private fun setEditEntities(tabPosition: Int?) {
+ when (tabPosition) {
+ 1 -> adapter.editEntities = searchEntities
+ 2 -> adapter.editEntities = findEntities
+ 3 -> adapter.editEntities = infoEntities
+ 4 -> adapter.editEntities = tocEntities
+ 5 -> adapter.editEntities = contentEntities
+ else -> adapter.editEntities = sourceEntities
+ }
+ recycler_view.scrollToPosition(0)
+ }
+
+ private fun upRecyclerView(bookSource: BookSource?) {
+ bookSource?.let {
+ cb_is_enable.isChecked = it.enabled
+ cb_is_enable_find.isChecked = it.enabledExplore
+ }
+ //基本信息
+ sourceEntities.clear()
+ sourceEntities.apply {
+ add(EditEntity("bookSourceUrl", bookSource?.bookSourceUrl, R.string.book_source_url))
+ add(EditEntity("bookSourceName", bookSource?.bookSourceName, R.string.book_source_name))
+ add(
+ EditEntity(
+ "bookSourceGroup",
+ bookSource?.bookSourceGroup,
+ R.string.book_source_group
+ )
+ )
+ add(EditEntity("loginUrl", bookSource?.loginUrl, R.string.book_source_login_url))
+ add(EditEntity("bookUrlPattern", bookSource?.bookUrlPattern, R.string.book_url_pattern))
+ add(EditEntity("header", bookSource?.header, R.string.source_http_header))
+ }
+ //搜索
+ (bookSource?.getSearchRule()).let { searchRule ->
+ searchEntities.clear()
+ searchEntities.apply {
+ add(EditEntity("searchUrl", bookSource?.searchUrl, R.string.rule_search_url))
+ add(EditEntity("bookList", searchRule?.bookList, R.string.rule_book_list))
+ add(EditEntity("name", searchRule?.name, R.string.rule_book_name))
+ add(EditEntity("author", searchRule?.author, R.string.rule_book_author))
+ add(EditEntity("kind", searchRule?.kind, R.string.rule_book_kind))
+ add(EditEntity("wordCount", searchRule?.wordCount, R.string.rule_word_count))
+ add(EditEntity("lastChapter", searchRule?.lastChapter, R.string.rule_last_chapter))
+ add(EditEntity("intro", searchRule?.intro, R.string.rule_book_intro))
+ add(EditEntity("coverUrl", searchRule?.coverUrl, R.string.rule_cover_url))
+ add(EditEntity("bookUrl", searchRule?.bookUrl, R.string.rule_book_url))
+ }
+ }
+ //详情页
+ (bookSource?.getBookInfoRule()).let { infoRule ->
+ infoEntities.clear()
+ infoEntities.apply {
+ add(EditEntity("init", infoRule?.init, R.string.rule_book_info_init))
+ add(EditEntity("name", infoRule?.name, R.string.rule_book_name))
+ add(EditEntity("author", infoRule?.author, R.string.rule_book_author))
+ add(EditEntity("coverUrl", infoRule?.coverUrl, R.string.rule_cover_url))
+ add(EditEntity("intro", infoRule?.intro, R.string.rule_book_intro))
+ add(EditEntity("kind", infoRule?.kind, R.string.rule_book_kind))
+ add(EditEntity("wordCount", infoRule?.wordCount, R.string.rule_word_count))
+ add(EditEntity("lastChapter", infoRule?.lastChapter, R.string.rule_last_chapter))
+ add(EditEntity("tocUrl", infoRule?.tocUrl, R.string.rule_toc_url))
+ }
+ }
+ //目录页
+ (bookSource?.getTocRule()).let { tocRule ->
+ tocEntities.clear()
+ tocEntities.apply {
+ add(EditEntity("chapterList", tocRule?.chapterList, R.string.rule_chapter_list))
+ add(EditEntity("chapterName", tocRule?.chapterName, R.string.rule_chapter_name))
+ add(EditEntity("chapterUrl", tocRule?.chapterUrl, R.string.rule_chapter_url))
+ add(EditEntity("nextTocUrl", tocRule?.nextTocUrl, R.string.rule_next_toc_url))
+ }
+ }
+ //正文页
+ (bookSource?.getContentRule()).let { contentRule ->
+ contentEntities.clear()
+ contentEntities.apply {
+ add(EditEntity("content", contentRule?.content, R.string.rule_book_content))
+ add(
+ EditEntity(
+ "nextContentUrl",
+ contentRule?.nextContentUrl,
+ R.string.rule_content_url_next
+ )
+ )
+ }
+ }
+
+ //发现
+ (bookSource?.getExploreRule()).let { exploreRule ->
+ findEntities.clear()
+ findEntities.apply {
+ add(EditEntity("exploreUrl", bookSource?.exploreUrl, R.string.rule_find_url))
+ add(EditEntity("bookList", exploreRule?.bookList, R.string.rule_book_list))
+ add(EditEntity("name", exploreRule?.name, R.string.rule_book_name))
+ add(EditEntity("author", exploreRule?.author, R.string.rule_book_author))
+ add(EditEntity("kind", exploreRule?.kind, R.string.rule_book_kind))
+ add(EditEntity("wordCount", exploreRule?.wordCount, R.string.rule_word_count))
+ add(EditEntity("intro", exploreRule?.intro, R.string.rule_book_intro))
+ add(EditEntity("lastChapter", exploreRule?.lastChapter, R.string.rule_last_chapter))
+ add(EditEntity("coverUrl", exploreRule?.coverUrl, R.string.rule_cover_url))
+ add(EditEntity("bookUrl", exploreRule?.bookUrl, R.string.rule_book_url))
+ }
+ }
+ setEditEntities(0)
+ }
+
+ private fun getSource(): BookSource? {
+ val source = viewModel.sourceLiveData.value ?: BookSource()
+ source.enabled = cb_is_enable.isChecked
+ source.enabledExplore = cb_is_enable_find.isChecked
+ viewModel.sourceLiveData.value?.let {
+ source.customOrder = it.customOrder
+ source.weight = it.weight
+ }
+ val searchRule = SearchRule()
+ val exploreRule = ExploreRule()
+ val bookInfoRule = BookInfoRule()
+ val tocRule = TocRule()
+ val contentRule = ContentRule()
+ sourceEntities.forEach {
+ when (it.key) {
+ "bookSourceUrl" -> source.bookSourceUrl = it.value ?: ""
+ "bookSourceName" -> source.bookSourceName = it.value ?: ""
+ "bookSourceGroup" -> source.bookSourceGroup = it.value
+ "loginUrl" -> source.loginUrl = it.value
+ "bookUrlPattern" -> source.bookUrlPattern = it.value
+ "header" -> source.header = it.value
+ }
+ }
+ if (source.bookSourceUrl.isBlank() || source.bookSourceName.isBlank()) {
+ toast("书源名称和URL不能为空")
+ return null
+ }
+ searchEntities.forEach {
+ when (it.key) {
+ "searchUrl" -> source.searchUrl = it.value
+ "bookList" -> searchRule.bookList = it.value
+ "name" -> searchRule.name = it.value
+ "author" -> searchRule.author = it.value
+ "kind" -> searchRule.kind = it.value
+ "intro" -> searchRule.intro = it.value
+ "updateTime" -> searchRule.updateTime = it.value
+ "wordCount" -> searchRule.wordCount = it.value
+ "lastChapter" -> searchRule.lastChapter = it.value
+ "coverUrl" -> searchRule.coverUrl = it.value
+ "bookUrl" -> searchRule.bookUrl = it.value
+ }
+ }
+ findEntities.forEach {
+ when (it.key) {
+ "exploreUrl" -> source.exploreUrl = it.value
+ "bookList" -> exploreRule.bookList = it.value
+ "name" -> exploreRule.name = it.value
+ "author" -> exploreRule.author = it.value
+ "kind" -> exploreRule.kind = it.value
+ "intro" -> exploreRule.intro = it.value
+ "updateTime" -> exploreRule.updateTime = it.value
+ "wordCount" -> exploreRule.wordCount = it.value
+ "lastChapter" -> exploreRule.lastChapter = it.value
+ "coverUrl" -> exploreRule.coverUrl = it.value
+ "bookUrl" -> exploreRule.bookUrl = it.value
+ }
+ }
+ infoEntities.forEach {
+ when (it.key) {
+ "init" -> bookInfoRule.init = it.value
+ "name" -> bookInfoRule.name = it.value
+ "author" -> bookInfoRule.author = it.value
+ "kind" -> bookInfoRule.kind = it.value
+ "intro" -> bookInfoRule.intro = it.value
+ "updateTime" -> bookInfoRule.updateTime = it.value
+ "wordCount" -> bookInfoRule.wordCount = it.value
+ "lastChapter" -> bookInfoRule.lastChapter = it.value
+ "coverUrl" -> bookInfoRule.coverUrl = it.value
+ "tocUrl" -> bookInfoRule.tocUrl = it.value
+ }
+ }
+ tocEntities.forEach {
+ when (it.key) {
+ "chapterList" -> tocRule.chapterList = it.value
+ "chapterName" -> tocRule.chapterName = it.value
+ "chapterUrl" -> tocRule.chapterUrl = it.value
+ "nextTocUrl" -> tocRule.nextTocUrl = it.value
+ }
+ }
+ contentEntities.forEach {
+ when (it.key) {
+ "content" -> contentRule.content = it.value
+ "nextContentUrl" -> contentRule.nextContentUrl = it.value
+ }
+ }
+ source.ruleSearch = GSON.toJson(searchRule)
+ source.ruleExplore = GSON.toJson(exploreRule)
+ source.ruleBookInfo = GSON.toJson(bookInfoRule)
+ source.ruleToc = GSON.toJson(tocRule)
+ source.ruleContent = GSON.toJson(contentRule)
+ return source
+ }
+
+ override fun sendText(text: String) {
+ if (text.isBlank()) return
+ val view = window.decorView.findFocus()
+ if (view is EditText) {
+ val start = view.selectionStart
+ val end = view.selectionEnd
+ val edit = view.editableText//获取EditText的文字
+ if (start < 0 || start >= edit.length) {
+ edit.append(text)
+ } else {
+ edit.replace(start, end, text)//光标所在位置插入文字
+ }
+ }
+ }
+
+ private fun showKeyboardTopPopupWindow() {
+ mSoftKeyboardTool?.isShowing?.let { if (it) return }
+ if (!isFinishing) {
+ mSoftKeyboardTool?.showAtLocation(ll_content, Gravity.BOTTOM, 0, 0)
+ }
+ }
+
+ private fun closePopupWindow() {
+ mSoftKeyboardTool?.let {
+ if (it.isShowing) {
+ it.dismiss()
+ }
+ }
+ }
+
+ private inner class KeyboardOnGlobalChangeListener : ViewTreeObserver.OnGlobalLayoutListener {
+ override fun onGlobalLayout() {
+ val rect = Rect()
+ // 获取当前页面窗口的显示范围
+ window.decorView.getWindowVisibleDisplayFrame(rect)
+ val screenHeight = this@BookSourceEditActivity.displayMetrics.heightPixels
+ val keyboardHeight = screenHeight - rect.bottom // 输入法的高度
+ val preShowing = mIsSoftKeyBoardShowing
+ if (abs(keyboardHeight) > screenHeight / 5) {
+ mIsSoftKeyBoardShowing = true // 超过屏幕五分之一则表示弹出了输入法
+ recycler_view.setPadding(0, 0, 0, 100)
+ showKeyboardTopPopupWindow()
+ } else {
+ mIsSoftKeyBoardShowing = false
+ recycler_view.setPadding(0, 0, 0, 0)
+ if (preShowing) {
+ closePopupWindow()
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/book/source/edit/SourceEditAdapter.kt b/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditAdapter.kt
similarity index 90%
rename from app/src/main/java/io/legado/app/ui/book/source/edit/SourceEditAdapter.kt
rename to app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditAdapter.kt
index 3ac5fc3d1..37939e803 100644
--- a/app/src/main/java/io/legado/app/ui/book/source/edit/SourceEditAdapter.kt
+++ b/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditAdapter.kt
@@ -7,11 +7,12 @@ import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import io.legado.app.R
+import io.legado.app.data.entities.EditEntity
import kotlinx.android.synthetic.main.item_source_edit.view.*
-class SourceEditAdapter : RecyclerView.Adapter() {
+class BookSourceEditAdapter : RecyclerView.Adapter() {
- var editEntities: ArrayList = ArrayList()
+ var editEntities: ArrayList = ArrayList()
set(value) {
field = value
notifyDataSetChanged()
@@ -34,7 +35,7 @@ class SourceEditAdapter : RecyclerView.Adapter()
}
class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
- fun bind(editEntity: SourceEditActivity.EditEntity) = with(itemView) {
+ fun bind(editEntity: EditEntity) = with(itemView) {
if (editText.getTag(R.id.tag1) == null) {
val listener = object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View) {
diff --git a/app/src/main/java/io/legado/app/ui/book/source/edit/SourceEditViewModel.kt b/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditViewModel.kt
similarity index 58%
rename from app/src/main/java/io/legado/app/ui/book/source/edit/SourceEditViewModel.kt
rename to app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditViewModel.kt
index c3af29a6a..61cc49858 100644
--- a/app/src/main/java/io/legado/app/ui/book/source/edit/SourceEditViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditViewModel.kt
@@ -3,29 +3,44 @@ package io.legado.app.ui.book.source.edit
import android.app.Application
import android.content.ClipboardManager
import android.content.Context
+import android.content.Intent
import androidx.lifecycle.MutableLiveData
import io.legado.app.App
import io.legado.app.base.BaseViewModel
import io.legado.app.data.entities.BookSource
import io.legado.app.help.storage.OldRule
-class SourceEditViewModel(application: Application) : BaseViewModel(application) {
+class BookSourceEditViewModel(application: Application) : BaseViewModel(application) {
val sourceLiveData: MutableLiveData = MutableLiveData()
+ var oldSourceUrl: String? = null
- fun setBookSource(key: String) {
+ fun initData(intent: Intent) {
execute {
- App.db.bookSourceDao().getBookSource(key)?.let {
+ val key = intent.getStringExtra("data")
+ var source: BookSource? = null
+ if (key != null) {
+ source = App.db.bookSourceDao().getBookSource(key)
+ }
+ source?.let {
+ oldSourceUrl = it.bookSourceUrl
sourceLiveData.postValue(it)
- } ?: sourceLiveData.postValue(BookSource())
+ } ?: let {
+ sourceLiveData.postValue(BookSource().apply {
+ customOrder = App.db.bookSourceDao().maxOrder + 1
+ })
+ }
}
}
fun save(bookSource: BookSource, finally: (() -> Unit)? = null) {
execute {
- if (bookSource.customOrder == 0) {
- bookSource.customOrder = App.db.bookSourceDao().allCount()
+ oldSourceUrl?.let {
+ if (oldSourceUrl != bookSource.bookSourceUrl) {
+ App.db.bookSourceDao().delete(it)
+ }
}
+ oldSourceUrl = bookSource.bookSourceUrl
App.db.bookSourceDao().insert(bookSource)
}.onFinally {
finally?.let { it() }
diff --git a/app/src/main/java/io/legado/app/ui/book/source/edit/SourceEditActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/edit/SourceEditActivity.kt
deleted file mode 100644
index 8a2db83d6..000000000
--- a/app/src/main/java/io/legado/app/ui/book/source/edit/SourceEditActivity.kt
+++ /dev/null
@@ -1,622 +0,0 @@
-package io.legado.app.ui.book.source.edit
-
-import android.app.Activity
-import android.content.ClipData
-import android.content.ClipboardManager
-import android.content.Context
-import android.graphics.Rect
-import android.os.Bundle
-import android.view.Gravity
-import android.view.Menu
-import android.view.MenuItem
-import android.view.ViewTreeObserver
-import android.widget.EditText
-import android.widget.PopupWindow
-import androidx.lifecycle.Observer
-import androidx.recyclerview.widget.LinearLayoutManager
-import com.google.android.material.tabs.TabLayout
-import io.legado.app.R
-import io.legado.app.base.VMBaseActivity
-import io.legado.app.constant.AppConst
-import io.legado.app.data.entities.BookSource
-import io.legado.app.data.entities.rule.*
-import io.legado.app.lib.theme.ATH
-import io.legado.app.ui.book.source.debug.SourceDebugActivity
-import io.legado.app.ui.widget.KeyboardToolPop
-import io.legado.app.utils.GSON
-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 SourceEditActivity :
- VMBaseActivity(R.layout.activity_book_source_edit, false),
- KeyboardToolPop.OnClickListener {
- override val viewModel: SourceEditViewModel
- get() = getViewModel(SourceEditViewModel::class.java)
-
- private val adapter = SourceEditAdapter()
- private val sourceEntities: ArrayList = ArrayList()
- private val searchEntities: ArrayList = ArrayList()
- private val findEntities: ArrayList = ArrayList()
- private val infoEntities: ArrayList = ArrayList()
- private val tocEntities: ArrayList = ArrayList()
- private val contentEntities: ArrayList = ArrayList()
-
- private var mSoftKeyboardTool: PopupWindow? = null
- private var mIsSoftKeyBoardShowing = false
-
- override fun onActivityCreated(savedInstanceState: Bundle?) {
- initView()
- viewModel.sourceLiveData.observe(this, Observer {
- upRecyclerView(it)
- })
- if (viewModel.sourceLiveData.value == null) {
- val sourceID = intent.getStringExtra("data")
- if (sourceID == null) {
- upRecyclerView(null)
- } else {
- sourceID.let { viewModel.setBookSource(sourceID) }
- }
- } else {
- upRecyclerView(viewModel.sourceLiveData.value)
- }
- }
-
- override fun onDestroy() {
- super.onDestroy()
- mSoftKeyboardTool?.dismiss()
- }
-
- override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
- menuInflater.inflate(R.menu.source_edit, menu)
- return super.onCompatCreateOptionsMenu(menu)
- }
-
- override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
- when (item.itemId) {
- R.id.menu_save -> {
- val bookSource = getSource()
- if (bookSource == null) {
- toast("书源名称和URL不能为空")
- } else {
- viewModel.save(bookSource) { setResult(Activity.RESULT_OK); finish() }
- }
- }
- R.id.menu_debug_source -> {
- val bookSource = getSource()
- if (bookSource == null) {
- toast("书源名称和URL不能为空")
- } else {
- viewModel.save(bookSource) {
- startActivity("key" to bookSource.bookSourceUrl)
- }
- }
- }
- R.id.menu_copy_source -> {
- GSON.toJson(getSource())?.let { sourceStr ->
- val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager?
- clipboard?.primaryClip = ClipData.newPlainText(null, sourceStr)
- }
- }
- R.id.menu_paste_source -> viewModel.pasteSource()
- }
- return super.onCompatOptionsItemSelected(item)
- }
-
- private fun initView() {
- ATH.applyEdgeEffectColor(recycler_view)
- mSoftKeyboardTool = KeyboardToolPop(this, AppConst.keyboardToolChars, this)
- window.decorView.viewTreeObserver.addOnGlobalLayoutListener(KeyboardOnGlobalChangeListener())
- recycler_view.layoutManager = LinearLayoutManager(this)
- recycler_view.adapter = adapter
- tab_layout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
- override fun onTabReselected(tab: TabLayout.Tab?) {
-
- }
-
- override fun onTabUnselected(tab: TabLayout.Tab?) {
-
- }
-
- override fun onTabSelected(tab: TabLayout.Tab?) {
- setEditEntities(tab?.position)
- }
- })
- }
-
- private fun setEditEntities(tabPosition: Int?) {
- when (tabPosition) {
- 1 -> adapter.editEntities = searchEntities
- 2 -> adapter.editEntities = findEntities
- 3 -> adapter.editEntities = infoEntities
- 4 -> adapter.editEntities = tocEntities
- 5 -> adapter.editEntities = contentEntities
- else -> adapter.editEntities = sourceEntities
- }
- recycler_view.scrollToPosition(0)
- }
-
- private fun upRecyclerView(bookSource: BookSource?) {
- bookSource?.let {
- cb_is_enable.isChecked = it.enabled
- cb_is_enable_find.isChecked = it.enabledExplore
- }
- //基本信息
- with(bookSource) {
- sourceEntities.clear()
- sourceEntities
- .add(
- EditEntity(
- "bookSourceUrl",
- this?.bookSourceUrl,
- R.string.book_source_url
- )
- )
- sourceEntities
- .add(
- EditEntity(
- "bookSourceName",
- this?.bookSourceName,
- R.string.book_source_name
- )
- )
- sourceEntities.add(
- EditEntity(
- "bookSourceGroup",
- this?.bookSourceGroup,
- R.string.book_source_group
- )
- )
- sourceEntities
- .add(
- EditEntity(
- "loginUrl",
- this?.loginUrl,
- R.string.book_source_login_url
- )
- )
- sourceEntities
- .add(
- EditEntity(
- "bookUrlPattern",
- this?.bookUrlPattern,
- R.string.book_url_pattern
- )
- )
- sourceEntities.add(
- EditEntity(
- "header",
- this?.header,
- R.string.source_http_header
- )
- )
- }
- //搜索
- with(bookSource?.getSearchRule()) {
- searchEntities.clear()
- searchEntities
- .add(
- EditEntity(
- "searchUrl",
- bookSource?.searchUrl,
- R.string.rule_search_url
- )
- )
- searchEntities.add(
- EditEntity(
- "bookList",
- this?.bookList,
- R.string.rule_book_list
- )
- )
- searchEntities.add(
- EditEntity(
- "name",
- this?.name,
- R.string.rule_book_name
- )
- )
- searchEntities.add(
- EditEntity(
- "author",
- this?.author,
- R.string.rule_book_author
- )
- )
- searchEntities.add(
- EditEntity(
- "kind",
- this?.kind,
- R.string.rule_book_kind
- )
- )
- searchEntities.add(
- EditEntity(
- "wordCount",
- this?.wordCount,
- R.string.rule_word_count
- )
- )
- searchEntities
- .add(
- EditEntity(
- "lastChapter",
- this?.lastChapter,
- R.string.rule_last_chapter
- )
- )
- searchEntities.add(
- EditEntity(
- "intro",
- this?.intro,
- R.string.rule_book_intro
- )
- )
- searchEntities.add(
- EditEntity(
- "coverUrl",
- this?.coverUrl,
- R.string.rule_cover_url
- )
- )
- searchEntities.add(
- EditEntity(
- "bookUrl",
- this?.bookUrl,
- R.string.rule_book_url
- )
- )
- }
- //详情页
- with(bookSource?.getBookInfoRule()) {
- infoEntities.clear()
- infoEntities.add(
- EditEntity(
- "init",
- this?.init,
- R.string.rule_book_info_init
- )
- )
- infoEntities.add(
- EditEntity(
- "name",
- this?.name,
- R.string.rule_book_name
- )
- )
- infoEntities.add(
- EditEntity(
- "author",
- this?.author,
- R.string.rule_book_author
- )
- )
- infoEntities.add(
- EditEntity(
- "coverUrl",
- this?.coverUrl,
- R.string.rule_cover_url
- )
- )
- infoEntities.add(
- EditEntity(
- "intro",
- this?.intro,
- R.string.rule_book_intro
- )
- )
- infoEntities.add(
- EditEntity(
- "kind",
- this?.kind,
- R.string.rule_book_kind
- )
- )
- infoEntities.add(
- EditEntity(
- "wordCount",
- this?.wordCount,
- R.string.rule_word_count
- )
- )
- infoEntities.add(
- EditEntity(
- "lastChapter",
- this?.lastChapter,
- R.string.rule_last_chapter
- )
- )
- infoEntities.add(
- EditEntity(
- "tocUrl",
- this?.tocUrl,
- R.string.rule_toc_url
- )
- )
- }
- //目录页
- with(bookSource?.getTocRule()) {
- tocEntities.clear()
- tocEntities.add(
- EditEntity(
- "chapterList",
- this?.chapterList,
- R.string.rule_chapter_list
- )
- )
- tocEntities.add(
- EditEntity(
- "chapterName",
- this?.chapterName,
- R.string.rule_chapter_name
- )
- )
- tocEntities.add(
- EditEntity(
- "chapterUrl",
- this?.chapterUrl,
- R.string.rule_chapter_url
- )
- )
- tocEntities.add(
- EditEntity(
- "nextTocUrl",
- this?.nextTocUrl,
- R.string.rule_next_toc_url
- )
- )
- }
- //正文页
- with(bookSource?.getContentRule()) {
- contentEntities.clear()
- contentEntities.add(
- EditEntity(
- "content",
- this?.content,
- R.string.rule_book_content
- )
- )
- contentEntities.add(
- EditEntity(
- "nextContentUrl",
- this?.nextContentUrl,
- R.string.rule_content_url_next
- )
- )
- }
-
- //发现
- with(bookSource?.getExploreRule()) {
- findEntities.clear()
- findEntities.add(
- EditEntity(
- "exploreUrl",
- bookSource?.exploreUrl,
- R.string.rule_find_url
- )
- )
- findEntities.add(
- EditEntity(
- "bookList",
- this?.bookList,
- R.string.rule_book_list
- )
- )
- findEntities.add(
- EditEntity(
- "name",
- this?.name,
- R.string.rule_book_name
- )
- )
- findEntities.add(
- EditEntity(
- "author",
- this?.author,
- R.string.rule_book_author
- )
- )
- findEntities.add(
- EditEntity(
- "kind",
- this?.kind,
- R.string.rule_book_kind
- )
- )
- findEntities.add(
- EditEntity(
- "wordCount",
- this?.wordCount,
- R.string.rule_word_count
- )
- )
- findEntities.add(
- EditEntity(
- "intro",
- this?.intro,
- R.string.rule_book_intro
- )
- )
- findEntities.add(
- EditEntity(
- "lastChapter",
- this?.lastChapter,
- R.string.rule_last_chapter
- )
- )
- findEntities.add(
- EditEntity(
- "coverUrl",
- this?.coverUrl,
- R.string.rule_cover_url
- )
- )
- findEntities.add(
- EditEntity(
- "bookUrl",
- this?.bookUrl,
- R.string.rule_book_url
- )
- )
- }
- setEditEntities(0)
- }
-
- private fun getSource(): BookSource? {
- val source = viewModel.sourceLiveData.value ?: BookSource()
- source.enabled = cb_is_enable.isChecked
- source.enabledExplore = cb_is_enable_find.isChecked
- viewModel.sourceLiveData.value?.let {
- source.customOrder = it.customOrder
- source.weight = it.weight
- }
- val searchRule = SearchRule()
- val exploreRule = ExploreRule()
- val bookInfoRule = BookInfoRule()
- val tocRule = TocRule()
- val contentRule = ContentRule()
- for (entity in sourceEntities) {
- with(entity) {
- when (key) {
- "bookSourceUrl" -> value?.let { source.bookSourceUrl = it } ?: return null
- "bookSourceName" -> value?.let { source.bookSourceName = it } ?: return null
- "bookSourceGroup" -> source.bookSourceGroup = value
- "loginUrl" -> source.loginUrl = value
- "bookUrlPattern" -> source.bookUrlPattern = value
- "header" -> source.header = value
- }
- }
- }
- for (entity in searchEntities) {
- with(entity) {
- when (key) {
- "searchUrl" -> source.searchUrl = value
- "bookList" -> searchRule.bookList = value
- "name" -> searchRule.name = value
- "author" -> searchRule.author = value
- "kind" -> searchRule.kind = value
- "intro" -> searchRule.intro = value
- "updateTime" -> searchRule.updateTime = value
- "wordCount" -> searchRule.wordCount = value
- "lastChapter" -> searchRule.lastChapter = value
- "coverUrl" -> searchRule.coverUrl = value
- "bookUrl" -> searchRule.bookUrl = value
- }
- }
- }
- for (entity in findEntities) {
- with(entity) {
- when (key) {
- "exploreUrl" -> source.exploreUrl = value
- "bookList" -> exploreRule.bookList = value
- "name" -> exploreRule.name = value
- "author" -> exploreRule.author = value
- "kind" -> exploreRule.kind = value
- "intro" -> exploreRule.intro = value
- "updateTime" -> exploreRule.updateTime = value
- "wordCount" -> exploreRule.wordCount = value
- "lastChapter" -> exploreRule.lastChapter = value
- "coverUrl" -> exploreRule.coverUrl = value
- "bookUrl" -> exploreRule.bookUrl = value
- }
- }
- }
- for (entity in infoEntities) {
- with(entity) {
- when (key) {
- "init" -> bookInfoRule.init = value
- "name" -> bookInfoRule.name = value
- "author" -> bookInfoRule.author = value
- "kind" -> bookInfoRule.kind = value
- "intro" -> bookInfoRule.intro = value
- "updateTime" -> bookInfoRule.updateTime = value
- "wordCount" -> bookInfoRule.wordCount = value
- "lastChapter" -> bookInfoRule.lastChapter = value
- "coverUrl" -> bookInfoRule.coverUrl = value
- "tocUrl" -> bookInfoRule.tocUrl = value
- }
- }
- }
- for (entity in tocEntities) {
- with(entity) {
- when (key) {
- "chapterList" -> tocRule.chapterList = value
- "chapterName" -> tocRule.chapterName = value
- "chapterUrl" -> tocRule.chapterUrl = value
- "nextTocUrl" -> tocRule.nextTocUrl = value
- }
- }
- }
- for (entity in contentEntities) {
- with(entity) {
- when (key) {
- "content" -> contentRule.content = value
- "nextContentUrl" -> contentRule.nextContentUrl = value
- }
- }
- }
- source.ruleSearch = GSON.toJson(searchRule)
- source.ruleExplore = GSON.toJson(exploreRule)
- source.ruleBookInfo = GSON.toJson(bookInfoRule)
- source.ruleToc = GSON.toJson(tocRule)
- source.ruleContent = GSON.toJson(contentRule)
- return source
- }
-
- override fun click(text: String) {
- if (text.isBlank()) return
- val view = window.decorView.findFocus()
- if (view is EditText) {
- val start = view.selectionStart
- val end = view.selectionEnd
- val edit = view.editableText//获取EditText的文字
- if (start < 0 || start >= edit.length) {
- edit.append(text)
- } else {
- edit.replace(start, end, text)//光标所在位置插入文字
- }
- }
- }
-
- private fun showKeyboardTopPopupWindow() {
- mSoftKeyboardTool?.isShowing?.let { if (it) return }
- if (!isFinishing) {
- mSoftKeyboardTool?.showAtLocation(ll_content, Gravity.BOTTOM, 0, 0)
- }
- }
-
- private fun closePopupWindow() {
- mSoftKeyboardTool?.let {
- if (it.isShowing) {
- it.dismiss()
- }
- }
- }
-
- private inner class KeyboardOnGlobalChangeListener : ViewTreeObserver.OnGlobalLayoutListener {
- override fun onGlobalLayout() {
- val rect = Rect()
- // 获取当前页面窗口的显示范围
- window.decorView.getWindowVisibleDisplayFrame(rect)
- val screenHeight = this@SourceEditActivity.displayMetrics.heightPixels
- val keyboardHeight = screenHeight - rect.bottom // 输入法的高度
- val preShowing = mIsSoftKeyBoardShowing
- if (abs(keyboardHeight) > screenHeight / 5) {
- mIsSoftKeyBoardShowing = true // 超过屏幕五分之一则表示弹出了输入法
- recycler_view.setPadding(0, 0, 0, 100)
- showKeyboardTopPopupWindow()
- } else {
- mIsSoftKeyBoardShowing = false
- recycler_view.setPadding(0, 0, 0, 0)
- if (preShowing) {
- closePopupWindow()
- }
- }
- }
- }
-
- class EditEntity(var key: String, var value: String?, var hint: Int)
-}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
index bf357319f..bc9da05fb 100644
--- a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
@@ -28,7 +28,7 @@ import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.primaryTextColor
import io.legado.app.lib.theme.view.ATEAutoCompleteTextView
import io.legado.app.service.CheckSourceService
-import io.legado.app.ui.book.source.edit.SourceEditActivity
+import io.legado.app.ui.book.source.edit.BookSourceEditActivity
import io.legado.app.ui.qrcode.QrCodeActivity
import io.legado.app.utils.ACache
import io.legado.app.utils.applyTint
@@ -78,7 +78,7 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity
override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_add_book_source -> {
- this.startActivity()
+ this.startActivity()
}
R.id.menu_import_book_source_qr -> {
this.startActivityForResult(qrRequestCode)
@@ -151,12 +151,8 @@ class BookSourceActivity : VMBaseActivity(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)
})
@@ -232,7 +228,7 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity
}
override fun edit(bookSource: BookSource) {
- startActivity(Pair("data", bookSource.bookSourceUrl))
+ startActivity(Pair("data", bookSource.bookSourceUrl))
}
override fun upOrder() {
diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt
index 31accc240..a51b589b3 100644
--- a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt
@@ -16,8 +16,7 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
fun topSource(bookSource: BookSource) {
execute {
- val minXh = App.db.bookSourceDao().minOrder
- bookSource.customOrder = minXh - 1
+ bookSource.customOrder = App.db.bookSourceDao().minOrder - 1
App.db.bookSourceDao().insert(bookSource)
}
}
diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/GroupManageDialog.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/GroupManageDialog.kt
index 1cdcccd45..59f5e73af 100644
--- a/app/src/main/java/io/legado/app/ui/book/source/manage/GroupManageDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/book/source/manage/GroupManageDialog.kt
@@ -29,7 +29,6 @@ import io.legado.app.utils.requestInputMethod
import io.legado.app.utils.splitNotBlank
import kotlinx.android.synthetic.main.dialog_edit_text.view.*
import kotlinx.android.synthetic.main.dialog_recycler_view.*
-import kotlinx.android.synthetic.main.item_book_group.view.tv_group
import kotlinx.android.synthetic.main.item_group_manage.view.*
import org.jetbrains.anko.sdk27.listeners.onClick
diff --git a/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceDialog.kt b/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceDialog.kt
index 8f02698b9..674db46c1 100644
--- a/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceDialog.kt
@@ -35,6 +35,7 @@ class ChangeSourceDialog : DialogFragment(),
}
}
+ private var callBack: CallBack? = null
private lateinit var viewModel: ChangeSourceViewModel
private lateinit var changeSourceAdapter: ChangeSourceAdapter
@@ -49,6 +50,7 @@ class ChangeSourceDialog : DialogFragment(),
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
+ callBack = activity as? CallBack
viewModel.searchStateData.observe(viewLifecycleOwner, Observer {
refresh_progress_bar.isAutoLoading = it
})
@@ -114,31 +116,24 @@ class ChangeSourceDialog : DialogFragment(),
}
override fun changeTo(searchBook: SearchBook) {
- val activity = activity
- if (activity is CallBack) {
- val book = searchBook.toBook()
- activity.oldBook()?.let { oldBook ->
- book.durChapterIndex = oldBook.durChapterIndex
- book.durChapterPos = oldBook.durChapterPos
- book.durChapterTitle = oldBook.durChapterTitle
- book.customCoverUrl = oldBook.customCoverUrl
- book.customIntro = oldBook.customIntro
- book.order = oldBook.order
- if (book.coverUrl.isNullOrEmpty()) {
- book.coverUrl = oldBook.getDisplayCover()
- }
- activity.changeTo(book)
+ val book = searchBook.toBook()
+ callBack?.oldBook?.let { oldBook ->
+ book.durChapterIndex = oldBook.durChapterIndex
+ book.durChapterPos = oldBook.durChapterPos
+ book.durChapterTitle = oldBook.durChapterTitle
+ book.customCoverUrl = oldBook.customCoverUrl
+ book.customIntro = oldBook.customIntro
+ book.order = oldBook.order
+ if (book.coverUrl.isNullOrEmpty()) {
+ book.coverUrl = oldBook.getDisplayCover()
}
+ callBack?.changeTo(book)
}
dismiss()
}
override fun curOrigin(): String {
- val activity = activity
- if (activity is CallBack) {
- return activity.curOrigin() ?: ""
- }
- return ""
+ return callBack?.curOrigin ?: ""
}
override fun adapter(): ChangeSourceAdapter {
@@ -146,8 +141,8 @@ class ChangeSourceDialog : DialogFragment(),
}
interface CallBack {
- fun curOrigin(): String?
- fun oldBook(): Book?
+ val curOrigin: String?
+ val oldBook: Book?
fun changeTo(book: Book)
}
diff --git a/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceViewModel.kt
index db4a5e889..44483aa07 100644
--- a/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceViewModel.kt
@@ -12,11 +12,13 @@ import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.WebBook
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
-import kotlinx.coroutines.delay
+import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.withContext
import org.jetbrains.anko.debug
+import java.util.concurrent.Executors
class ChangeSourceViewModel(application: Application) : BaseViewModel(application) {
+ private var searchPool = Executors.newFixedThreadPool(16).asCoroutineDispatcher()
var callBack: CallBack? = null
val searchStateData = MutableLiveData()
var name: String = ""
@@ -56,7 +58,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
val bookSourceList = App.db.bookSourceDao().allEnabled
for (item in bookSourceList) {
//task取消时自动取消 by (scope = this@execute)
- WebBook(item).searchBook(name, scope = this@execute)
+ WebBook(item).searchBook(name, scope = this@execute, context = searchPool)
.timeout(30000L)
.onSuccess(IO) {
it?.forEach { searchBook ->
@@ -70,7 +72,6 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
}
}
}
- delay(100)
}
}
@@ -130,4 +131,9 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
interface CallBack {
fun adapter(): ChangeSourceAdapter
}
+
+ override fun onCleared() {
+ super.onCleared()
+ searchPool.close()
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/config/ConfigFragment.kt b/app/src/main/java/io/legado/app/ui/config/ConfigFragment.kt
index c3b0ae799..fd957e433 100644
--- a/app/src/main/java/io/legado/app/ui/config/ConfigFragment.kt
+++ b/app/src/main/java/io/legado/app/ui/config/ConfigFragment.kt
@@ -10,6 +10,7 @@ import io.legado.app.App
import io.legado.app.R
import io.legado.app.help.BookHelp
import io.legado.app.lib.theme.ATH
+import io.legado.app.utils.LogUtils
import io.legado.app.utils.getPrefString
@@ -39,6 +40,7 @@ class ConfigFragment : PreferenceFragmentCompat(), Preference.OnPreferenceChange
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
when (key) {
"downloadPath" -> BookHelp.upDownloadPath()
+ "recordLog" -> LogUtils.upLevel()
}
}
diff --git a/app/src/main/java/io/legado/app/ui/explore/ExploreShowAdapter.kt b/app/src/main/java/io/legado/app/ui/explore/ExploreShowAdapter.kt
index 6510735f5..1a248e736 100644
--- a/app/src/main/java/io/legado/app/ui/explore/ExploreShowAdapter.kt
+++ b/app/src/main/java/io/legado/app/ui/explore/ExploreShowAdapter.kt
@@ -59,8 +59,8 @@ class ExploreShowAdapter(context: Context, val callBack: CallBack) :
}
item.coverUrl.let {
ImageLoader.load(context, it)//Glide自动识别http://和file://
- .placeholder(R.drawable.img_cover_default)
- .error(R.drawable.img_cover_default)
+ .placeholder(R.drawable.image_cover_default)
+ .error(R.drawable.image_cover_default)
.centerCrop()
.setAsDrawable(iv_cover)
}
diff --git a/app/src/main/java/io/legado/app/ui/explore/ExploreShowViewModel.kt b/app/src/main/java/io/legado/app/ui/explore/ExploreShowViewModel.kt
index f36a5fc69..a9514c8e4 100644
--- a/app/src/main/java/io/legado/app/ui/explore/ExploreShowViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/explore/ExploreShowViewModel.kt
@@ -39,8 +39,6 @@ class ExploreShowViewModel(application: Application) : BaseViewModel(application
App.db.searchBookDao().insert(*searchBooks.toTypedArray())
page++
}
- }.onError {
- it.printStackTrace()
}
}
}
diff --git a/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt b/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt
index 3a9c1db32..992ed6a16 100644
--- a/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt
@@ -38,9 +38,6 @@ class MainViewModel(application: Application) : BaseViewModel(application) {
App.db.bookChapterDao().insert(*it.toTypedArray())
}
}
- .onError {
- it.printStackTrace()
- }
.onFinally {
synchronized(this) {
updateList.remove(book.bookUrl)
diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BooksAdapter.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BooksAdapter.kt
index 603684163..5152278ca 100644
--- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BooksAdapter.kt
+++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BooksAdapter.kt
@@ -35,7 +35,7 @@ class BooksAdapter(private val callBack: CallBack) :
}
fun notification(bookUrl: String) {
- for (i in 0..itemCount) {
+ for (i in 0 until itemCount) {
getItem(i)?.let {
if (it.bookUrl == bookUrl) {
notifyItemChanged(i)
@@ -72,8 +72,8 @@ class BooksAdapter(private val callBack: CallBack) :
tv_last.text = book.latestChapterTitle
book.getDisplayCover()?.let {
ImageLoader.load(context, it)//Glide自动识别http://和file://
- .placeholder(R.drawable.img_cover_default)
- .error(R.drawable.img_cover_default)
+ .placeholder(R.drawable.image_cover_default)
+ .error(R.drawable.image_cover_default)
.centerCrop()
.setAsDrawable(iv_cover)
}
diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BooksFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BooksFragment.kt
index 4819d288f..47d168562 100644
--- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BooksFragment.kt
+++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BooksFragment.kt
@@ -12,6 +12,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.VMBaseFragment
+import io.legado.app.constant.Bus
import io.legado.app.data.entities.Book
import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.accentColor
@@ -20,6 +21,7 @@ import io.legado.app.ui.book.read.ReadBookActivity
import io.legado.app.ui.main.MainViewModel
import io.legado.app.utils.getViewModel
import io.legado.app.utils.getViewModelOfActivity
+import io.legado.app.utils.observeEvent
import kotlinx.android.synthetic.main.fragment_books.*
import org.jetbrains.anko.startActivity
@@ -52,6 +54,9 @@ class BooksFragment : VMBaseFragment(R.layout.fragment_books),
}
initRecyclerView()
upRecyclerData()
+ observeEvent(Bus.UP_BOOK) {
+ booksAdapter.notification(it)
+ }
}
private fun initRecyclerView() {
@@ -70,7 +75,6 @@ class BooksFragment : VMBaseFragment(R.layout.fragment_books),
})
booksAdapter = BooksAdapter(this)
rv_bookshelf.adapter = booksAdapter
-
}
private fun upRecyclerData() {
diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt
index b7f91a0af..797ef096d 100644
--- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt
+++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt
@@ -1,11 +1,9 @@
package io.legado.app.ui.main.bookshelf
-import android.annotation.SuppressLint
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
-import android.widget.EditText
import androidx.appcompat.widget.SearchView
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
@@ -16,11 +14,13 @@ import io.legado.app.R
import io.legado.app.base.VMBaseFragment
import io.legado.app.constant.AppConst
import io.legado.app.data.entities.BookGroup
-import io.legado.app.lib.dialogs.*
+import io.legado.app.lib.dialogs.selector
import io.legado.app.lib.theme.ATH
import io.legado.app.ui.book.search.SearchActivity
-import io.legado.app.utils.*
-import kotlinx.android.synthetic.main.dialog_edit_text.view.*
+import io.legado.app.utils.getPrefBoolean
+import io.legado.app.utils.getViewModel
+import io.legado.app.utils.putPrefInt
+import io.legado.app.utils.startActivity
import kotlinx.android.synthetic.main.fragment_bookshelf.*
import kotlinx.android.synthetic.main.view_tab_layout.*
import kotlinx.android.synthetic.main.view_title_bar.*
@@ -28,13 +28,14 @@ import org.jetbrains.anko.startActivity
class BookshelfFragment : VMBaseFragment(R.layout.fragment_bookshelf),
SearchView.OnQueryTextListener,
+ GroupManageDialog.CallBack,
BookshelfAdapter.CallBack {
override val viewModel: BookshelfViewModel
get() = getViewModel(BookshelfViewModel::class.java)
private var bookGroupLiveData: LiveData>? = null
- private val bookGroups = mutableListOf().apply { addAll(AppConst.defaultBookGroups) }
+ private val bookGroups = mutableListOf()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
setSupportToolbar(toolbar)
@@ -51,6 +52,14 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b
when (item.itemId) {
R.id.menu_search -> startActivity()
R.id.menu_bookshelf_layout -> selectBookshelfLayout()
+ R.id.menu_group_manage -> GroupManageDialog()
+ .show(childFragmentManager, "groupManageDialog")
+ R.id.menu_add_local -> {
+ }
+ R.id.menu_add_url -> {
+ }
+ R.id.menu_arrange_bookshelf -> {
+ }
}
}
@@ -75,11 +84,18 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b
bookGroupLiveData?.removeObservers(viewLifecycleOwner)
bookGroupLiveData = App.db.bookGroupDao().liveDataAll()
bookGroupLiveData?.observe(viewLifecycleOwner, Observer {
- for (index in AppConst.defaultBookGroups.size until bookGroups.size) {
- bookGroups.removeAt(AppConst.defaultBookGroups.size)
+ synchronized(this) {
+ bookGroups.clear()
+ bookGroups.add(AppConst.bookGroupAll)
+ if (getPrefBoolean("bookGroupLocal", true)) {
+ bookGroups.add(AppConst.bookGroupLocal)
+ }
+ if (getPrefBoolean("bookGroupAudio", true)) {
+ bookGroups.add(AppConst.bookGroupAudio)
+ }
+ bookGroups.addAll(it)
+ view_pager_bookshelf.adapter?.notifyDataSetChanged()
}
- bookGroups.addAll(it)
- view_pager_bookshelf.adapter?.notifyDataSetChanged()
})
}
@@ -92,22 +108,18 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b
return false
}
- @SuppressLint("InflateParams")
- private fun showGroupInputDialog() {
- alert(title = "新建分组") {
- var editText: EditText? = null
- customView {
- layoutInflater.inflate(R.layout.dialog_edit_text, null).apply {
- editText = edit_view.apply {
- hint = "分组名称"
- }
- }
+ override fun upGroup() {
+ synchronized(this) {
+ bookGroups.remove(AppConst.bookGroupLocal)
+ bookGroups.remove(AppConst.bookGroupAudio)
+ if (getPrefBoolean("bookGroupAudio", true)) {
+ bookGroups.add(1, AppConst.bookGroupAudio)
}
- yesButton {
- viewModel.saveBookGroup(editText?.text?.toString())
+ if (getPrefBoolean("bookGroupLocal", true)) {
+ bookGroups.add(1, AppConst.bookGroupLocal)
}
- noButton()
- }.show().applyTint().requestInputMethod()
+ view_pager_bookshelf.adapter?.notifyDataSetChanged()
+ }
}
private fun selectBookshelfLayout() {
diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt
index 1bb9a3b64..0499e5e5e 100644
--- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt
@@ -7,17 +7,28 @@ import io.legado.app.data.entities.BookGroup
class BookshelfViewModel(application: Application) : BaseViewModel(application) {
- fun saveBookGroup(group: String?) {
- if (!group.isNullOrBlank()) {
- execute {
- App.db.bookGroupDao().insert(
- BookGroup(
- App.db.bookGroupDao().maxId + 1,
- group
- )
- )
- }
+
+ fun addGroup(groupName: String) {
+ execute {
+ val maxId = App.db.bookGroupDao().maxId
+ val bookGroup = BookGroup(
+ groupId = maxId.plus(1),
+ groupName = groupName,
+ order = maxId.plus(1)
+ )
+ App.db.bookGroupDao().insert(bookGroup)
+ }
+ }
+
+ fun upGroup(vararg bookGroup: BookGroup) {
+ execute {
+ App.db.bookGroupDao().update(*bookGroup)
}
}
+ fun delGroup(vararg bookGroup: BookGroup) {
+ execute {
+ App.db.bookGroupDao().delete(*bookGroup)
+ }
+ }
}
diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/GroupManageDialog.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/GroupManageDialog.kt
new file mode 100644
index 000000000..f6151edaf
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/GroupManageDialog.kt
@@ -0,0 +1,176 @@
+package io.legado.app.ui.main.bookshelf
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.Bundle
+import android.util.DisplayMetrics
+import android.view.LayoutInflater
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
+import android.widget.EditText
+import androidx.appcompat.widget.Toolbar
+import androidx.fragment.app.DialogFragment
+import androidx.lifecycle.Observer
+import androidx.recyclerview.widget.DividerItemDecoration
+import androidx.recyclerview.widget.ItemTouchHelper
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import io.legado.app.App
+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.BookGroup
+import io.legado.app.help.ItemTouchCallback
+import io.legado.app.lib.dialogs.alert
+import io.legado.app.lib.dialogs.customView
+import io.legado.app.lib.dialogs.noButton
+import io.legado.app.lib.dialogs.yesButton
+import io.legado.app.utils.*
+import kotlinx.android.synthetic.main.dialog_edit_text.view.*
+import kotlinx.android.synthetic.main.dialog_recycler_view.*
+import kotlinx.android.synthetic.main.item_group_manage.view.*
+import org.jetbrains.anko.sdk27.listeners.onClick
+
+class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
+ private lateinit var viewModel: BookshelfViewModel
+ private lateinit var adapter: GroupAdapter
+ private var callBack: CallBack? = null
+
+ override fun onStart() {
+ super.onStart()
+ val dm = DisplayMetrics()
+ activity?.windowManager?.defaultDisplay?.getMetrics(dm)
+ dialog?.window?.setLayout((dm.widthPixels * 0.9).toInt(), (dm.heightPixels * 0.9).toInt())
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ viewModel = getViewModel(BookshelfViewModel::class.java)
+ return inflater.inflate(R.layout.dialog_recycler_view, container)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ callBack = parentFragment as? CallBack
+ initData()
+ }
+
+ private fun initData() {
+ tool_bar.title = getString(R.string.group_manage)
+ tool_bar.inflateMenu(R.menu.book_group_manage)
+ tool_bar.menu.applyTint(requireContext(), false)
+ tool_bar.setOnMenuItemClickListener(this)
+ tool_bar.menu.findItem(R.id.menu_group_local)
+ .isChecked = getPrefBoolean("bookGroupLocal", true)
+ tool_bar.menu.findItem(R.id.menu_group_audio)
+ .isChecked = getPrefBoolean("bookGroupAudio", true)
+ adapter = GroupAdapter(requireContext())
+ recycler_view.layoutManager = LinearLayoutManager(requireContext())
+ recycler_view.addItemDecoration(
+ DividerItemDecoration(requireContext(), RecyclerView.VERTICAL)
+ )
+ recycler_view.adapter = adapter
+ App.db.bookGroupDao().liveDataAll().observe(viewLifecycleOwner, Observer {
+ adapter.setItems(it)
+ })
+ val itemTouchCallback = ItemTouchCallback()
+ itemTouchCallback.onItemTouchCallbackListener = adapter
+ itemTouchCallback.isCanDrag = true
+ ItemTouchHelper(itemTouchCallback).attachToRecyclerView(recycler_view)
+ }
+
+ override fun onMenuItemClick(item: MenuItem?): Boolean {
+ when (item?.itemId) {
+ R.id.menu_add -> addGroup()
+ R.id.menu_group_local -> {
+ item.isChecked = !item.isChecked
+ putPrefBoolean("bookGroupLocal", item.isChecked)
+ callBack?.upGroup()
+ }
+ R.id.menu_group_audio -> {
+ item.isChecked = !item.isChecked
+ putPrefBoolean("bookGroupAudio", item.isChecked)
+ callBack?.upGroup()
+ }
+ }
+ return true
+ }
+
+ @SuppressLint("InflateParams")
+ private fun addGroup() {
+ alert(title = getString(R.string.add_group)) {
+ var editText: EditText? = null
+ customView {
+ layoutInflater.inflate(R.layout.dialog_edit_text, null).apply {
+ editText = edit_view.apply {
+ hint = "分组名称"
+ }
+ }
+ }
+ yesButton {
+ editText?.text?.toString()?.let {
+ if (it.isNotBlank()) {
+ viewModel.addGroup(it)
+ }
+ }
+ }
+ noButton()
+ }.show().applyTint().requestInputMethod()
+ }
+
+ @SuppressLint("InflateParams")
+ private fun editGroup(bookGroup: BookGroup) {
+ alert(title = getString(R.string.group_edit)) {
+ var editText: EditText? = null
+ customView {
+ layoutInflater.inflate(R.layout.dialog_edit_text, null).apply {
+ editText = edit_view.apply {
+ hint = "分组名称"
+ setText(bookGroup.groupName)
+ }
+ }
+ }
+ yesButton {
+ viewModel.upGroup(bookGroup.copy(groupName = editText?.text?.toString() ?: ""))
+ }
+ noButton()
+ }.show().applyTint().requestInputMethod()
+ }
+
+ private inner class GroupAdapter(context: Context) :
+ SimpleRecyclerAdapter(context, R.layout.item_group_manage),
+ ItemTouchCallback.OnItemTouchCallbackListener {
+
+ override fun convert(holder: ItemViewHolder, item: BookGroup, payloads: MutableList) {
+ with(holder.itemView) {
+ tv_group.text = item.groupName
+ tv_edit.onClick { editGroup(item) }
+ tv_del.onClick { viewModel.delGroup(item) }
+ }
+ }
+
+ override fun onMove(srcPosition: Int, targetPosition: Int): Boolean {
+ val srcItem = getItem(srcPosition)
+ val targetItem = getItem(targetPosition)
+ if (srcItem != null && targetItem != null) {
+ val order = srcItem.order
+ srcItem.order = targetItem.order
+ targetItem.order = order
+ viewModel.upGroup(srcItem, targetItem)
+ }
+ return true
+ }
+
+ override fun onSwiped(adapterPosition: Int) {
+
+ }
+ }
+
+ interface CallBack {
+ fun upGroup()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt b/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt
index 6889124f3..ea23c0909 100644
--- a/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt
+++ b/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt
@@ -12,7 +12,7 @@ import io.legado.app.base.VMBaseFragment
import io.legado.app.data.entities.BookSource
import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.primaryTextColor
-import io.legado.app.ui.book.source.edit.SourceEditActivity
+import io.legado.app.ui.book.source.edit.BookSourceEditActivity
import io.legado.app.ui.explore.ExploreShowActivity
import io.legado.app.utils.getViewModel
import io.legado.app.utils.startActivity
@@ -88,7 +88,7 @@ class ExploreFragment : VMBaseFragment(R.layout.fragment_find_
}
override fun editSource(sourceUrl: String) {
- startActivity(Pair("data", sourceUrl))
+ startActivity(Pair("data", sourceUrl))
}
override fun toTop(source: BookSource) {
diff --git a/app/src/main/java/io/legado/app/ui/main/my/MyFragment.kt b/app/src/main/java/io/legado/app/ui/main/my/MyFragment.kt
index 36f2db63b..af839204e 100644
--- a/app/src/main/java/io/legado/app/ui/main/my/MyFragment.kt
+++ b/app/src/main/java/io/legado/app/ui/main/my/MyFragment.kt
@@ -10,6 +10,7 @@ import androidx.preference.PreferenceFragmentCompat
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseFragment
+import io.legado.app.help.BookHelp
import io.legado.app.help.permission.Permissions
import io.legado.app.help.permission.PermissionsCompat
import io.legado.app.help.storage.Backup
@@ -21,6 +22,7 @@ import io.legado.app.ui.book.source.manage.BookSourceActivity
import io.legado.app.ui.config.ConfigActivity
import io.legado.app.ui.config.ConfigViewModel
import io.legado.app.ui.replacerule.ReplaceRuleActivity
+import io.legado.app.utils.LogUtils
import io.legado.app.utils.startActivity
import kotlinx.android.synthetic.main.view_title_bar.*
import org.jetbrains.anko.startActivity
@@ -82,9 +84,9 @@ class MyFragment : BaseFragment(R.layout.fragment_my_config) {
key: String?
) {
when (key) {
- "isNightTheme" -> {
- App.INSTANCE.applyDayNight()
- }
+ "isNightTheme" -> App.INSTANCE.applyDayNight()
+ "recordLog" -> LogUtils.upLevel()
+ "downloadPath" -> BookHelp.upDownloadPath()
}
}
diff --git a/app/src/main/java/io/legado/app/ui/main/rss/RssAdapter.kt b/app/src/main/java/io/legado/app/ui/main/rss/RssAdapter.kt
index 6bef664c6..b490c8ec8 100644
--- a/app/src/main/java/io/legado/app/ui/main/rss/RssAdapter.kt
+++ b/app/src/main/java/io/legado/app/ui/main/rss/RssAdapter.kt
@@ -5,15 +5,26 @@ 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.ImageLoader
+import kotlinx.android.synthetic.main.item_rss.view.*
+import org.jetbrains.anko.sdk27.listeners.onClick
class RssAdapter(context: Context, val callBack: CallBack) :
SimpleRecyclerAdapter(context, R.layout.item_rss) {
override fun convert(holder: ItemViewHolder, item: RssSource, payloads: MutableList) {
-
+ with(holder.itemView) {
+ tv_name.text = item.sourceName
+ ImageLoader.load(context, item.sourceIcon)
+ .centerCrop()
+ .placeholder(R.drawable.image_rss)
+ .error(R.drawable.image_rss)
+ .setAsBitmap(iv_icon)
+ onClick { callBack.openRss(item) }
+ }
}
interface CallBack {
- fun openRss()
+ fun openRss(rssSource: RssSource)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt b/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt
index 79c82bf1d..0dddd4426 100644
--- a/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt
+++ b/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt
@@ -1,15 +1,24 @@
package io.legado.app.ui.main.rss
import android.os.Bundle
+import android.util.Log
+import android.view.Gravity
import android.view.Menu
import android.view.MenuItem
import android.view.View
+import android.widget.EditText
+import android.widget.LinearLayout
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.GridLayoutManager
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseFragment
+import io.legado.app.data.entities.RssSource
+import io.legado.app.lib.dialogs.alert
+import io.legado.app.lib.dialogs.cancelButton
+import io.legado.app.lib.dialogs.yesButton
import io.legado.app.lib.theme.ATH
+import io.legado.app.ui.rss.article.RssArticlesActivity
import io.legado.app.ui.rss.source.manage.RssSourceActivity
import io.legado.app.utils.startActivity
import kotlinx.android.synthetic.main.fragment_rss.*
@@ -34,6 +43,25 @@ class RssFragment : BaseFragment(R.layout.fragment_rss),
super.onCompatOptionsItemSelected(item)
when (item.itemId) {
R.id.menu_rss_config -> startActivity()
+ R.id.menu_rss_add -> {
+ alert {
+ title = "快速添加并预览"
+ val layout = LinearLayout(activity)
+ val urlEdit = EditText(activity)
+ urlEdit.hint = "输入RSS地址"
+ urlEdit.width = 800
+ layout.gravity = Gravity.CENTER
+ layout.addView(urlEdit)
+ customView = layout
+ cancelButton{
+ Log.i("RSS","Quick Add URL cancel")
+ }
+ yesButton{
+ Log.i("RSS","Quick Add URL: ${urlEdit.text}")
+ startActivity("url" to urlEdit.text.toString().trim())
+ }
+ }.show()
+ }
}
}
@@ -50,7 +78,7 @@ class RssFragment : BaseFragment(R.layout.fragment_rss),
})
}
- override fun openRss() {
-
+ override fun openRss(rssSource: RssSource) {
+ startActivity(Pair("url", rssSource.sourceUrl))
}
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/replacerule/GroupManageDialog.kt b/app/src/main/java/io/legado/app/ui/replacerule/GroupManageDialog.kt
index 49fb8c3e6..2b2a84835 100644
--- a/app/src/main/java/io/legado/app/ui/replacerule/GroupManageDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/replacerule/GroupManageDialog.kt
@@ -29,7 +29,6 @@ import io.legado.app.utils.requestInputMethod
import io.legado.app.utils.splitNotBlank
import kotlinx.android.synthetic.main.dialog_edit_text.view.*
import kotlinx.android.synthetic.main.dialog_recycler_view.*
-import kotlinx.android.synthetic.main.item_book_group.view.tv_group
import kotlinx.android.synthetic.main.item_group_manage.view.*
import org.jetbrains.anko.sdk27.listeners.onClick
diff --git a/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt b/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt
index 3fa28cd69..4b11ab39c 100644
--- a/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt
@@ -25,7 +25,7 @@ class ReplaceEditDialog : DialogFragment(),
val dialog = ReplaceEditDialog()
id?.let {
val bundle = Bundle()
- bundle.putLong("data", id)
+ bundle.putLong("id", id)
dialog.arguments = bundle
}
return dialog
diff --git a/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditViewModel.kt b/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditViewModel.kt
index ae8ba8025..281a0ac2d 100644
--- a/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditViewModel.kt
@@ -14,7 +14,7 @@ class ReplaceEditViewModel(application: Application) : BaseViewModel(application
fun initData(bundle: Bundle) {
execute {
replaceRuleData.value ?: let {
- val id = bundle.getLong("data")
+ val id = bundle.getLong("id")
if (id > 0) {
App.db.replaceRuleDao().findById(id)?.let {
replaceRuleData.postValue(it)
diff --git a/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesActivity.kt b/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesActivity.kt
index a69c70fb5..8ec14de5b 100644
--- a/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesActivity.kt
@@ -1,3 +1,70 @@
package io.legado.app.ui.rss.article
-class RssArticlesActivity
\ No newline at end of file
+import android.os.Bundle
+import androidx.core.content.ContextCompat
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.Observer
+import androidx.recyclerview.widget.DividerItemDecoration
+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.RssArticle
+import io.legado.app.lib.theme.ATH
+import io.legado.app.ui.rss.read.ReadRssActivity
+import io.legado.app.utils.getViewModel
+import kotlinx.android.synthetic.main.activity_rss_artivles.*
+import org.jetbrains.anko.startActivity
+
+class RssArticlesActivity : VMBaseActivity(R.layout.activity_rss_artivles),
+ RssArticlesAdapter.CallBack {
+
+ override val viewModel: RssArticlesViewModel
+ get() = getViewModel(RssArticlesViewModel::class.java)
+
+ private var adapter: RssArticlesAdapter? = null
+ private var rssArticlesData: LiveData>? = null
+
+ override fun onActivityCreated(savedInstanceState: Bundle?) {
+ initView()
+ viewModel.titleLiveData.observe(this, Observer {
+ title_bar.title = it
+ })
+ intent.getStringExtra("url")?.let {
+ initData(it)
+ viewModel.loadContent(it) {
+ refresh_progress_bar.isAutoLoading = false
+ }
+ }
+ }
+
+ private fun initView() {
+ ATH.applyEdgeEffectColor(recycler_view)
+ recycler_view.layoutManager = LinearLayoutManager(this)
+ recycler_view.addItemDecoration(
+ DividerItemDecoration(this, DividerItemDecoration.VERTICAL).apply {
+ ContextCompat.getDrawable(baseContext, R.drawable.ic_divider)?.let {
+ this.setDrawable(it)
+ }
+ })
+ adapter = RssArticlesAdapter(this, this)
+ recycler_view.adapter = adapter
+ refresh_progress_bar.isAutoLoading = true
+ }
+
+ private fun initData(origin: String) {
+ rssArticlesData?.removeObservers(this)
+ rssArticlesData = App.db.rssArtivleDao().liveByOrigin(origin)
+ rssArticlesData?.observe(this, Observer {
+ adapter?.setItems(it)
+ })
+ }
+
+ override fun readRss(rssArticle: RssArticle) {
+ viewModel.read(rssArticle)
+ startActivity(
+ Pair("description", rssArticle.description),
+ Pair("url", rssArticle.link)
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesAdapter.kt b/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesAdapter.kt
new file mode 100644
index 000000000..03546d957
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesAdapter.kt
@@ -0,0 +1,44 @@
+package io.legado.app.ui.rss.article
+
+import android.content.Context
+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.RssArticle
+import io.legado.app.help.ImageLoader
+import io.legado.app.utils.gone
+import io.legado.app.utils.visible
+import kotlinx.android.synthetic.main.item_rss_article.view.*
+import org.jetbrains.anko.sdk27.listeners.onClick
+import org.jetbrains.anko.textColorResource
+
+
+class RssArticlesAdapter(context: Context, val callBack: CallBack) :
+ SimpleRecyclerAdapter(context, R.layout.item_rss_article) {
+
+ override fun convert(holder: ItemViewHolder, item: RssArticle, payloads: MutableList) {
+ with(holder.itemView) {
+ tv_title.text = item.title
+ tv_pub_date.text = item.pubDate
+ onClick {
+ callBack.readRss(item)
+ }
+ if (item.image.isNullOrBlank()) {
+ image_view.gone()
+ } else {
+ image_view.visible()
+ ImageLoader.load(context, item.image)
+ .setAsBitmap(image_view)
+ }
+ if (item.read) {
+ tv_title.textColorResource = R.color.tv_text_summary
+ } else {
+ tv_title.textColorResource = R.color.tv_text_default
+ }
+ }
+ }
+
+ interface CallBack {
+ fun readRss(rssArticle: RssArticle)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesViewModel.kt b/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesViewModel.kt
new file mode 100644
index 000000000..a75e7ae07
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesViewModel.kt
@@ -0,0 +1,45 @@
+package io.legado.app.ui.rss.article
+
+import android.app.Application
+import androidx.lifecycle.MutableLiveData
+import io.legado.app.App
+import io.legado.app.base.BaseViewModel
+import io.legado.app.data.entities.RssArticle
+import io.legado.app.data.entities.RssSource
+import io.legado.app.model.Rss
+import kotlinx.coroutines.Dispatchers.IO
+
+
+class RssArticlesViewModel(application: Application) : BaseViewModel(application) {
+
+ val titleLiveData = MutableLiveData()
+
+ fun loadContent(url: String, onFinally: () -> Unit) {
+ execute {
+ var rssSource = App.db.rssSourceDao().getByKey(url)
+ if (rssSource == null) {
+ rssSource = RssSource(sourceUrl = url)
+ } else {
+ titleLiveData.postValue(rssSource.sourceName)
+ }
+ Rss.getArticles(rssSource, this)
+ .onSuccess(IO) {
+ it?.let {
+ App.db.rssArtivleDao().insert(*it.toTypedArray())
+ }
+ }.onError {
+ toast(it.localizedMessage)
+ }.onFinally {
+ onFinally()
+ }
+ }
+ }
+
+ fun read(rssArticle: RssArticle) {
+ execute {
+ rssArticle.read = true
+ App.db.rssArtivleDao().update(rssArticle)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt
index 5e7972c6d..eb8fa6451 100644
--- a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt
@@ -3,13 +3,18 @@ package io.legado.app.ui.rss.read
import android.os.Bundle
import io.legado.app.R
import io.legado.app.base.BaseActivity
+import kotlinx.android.synthetic.main.activity_rss_read.*
-
-class ReadRssActivity : BaseActivity(R.layout.activity_read_rss) {
+class ReadRssActivity : BaseActivity(R.layout.activity_rss_read) {
override fun onActivityCreated(savedInstanceState: Bundle?) {
-
+ val description = intent.getStringExtra("description")
+ val url = intent.getStringExtra("url")
+ if (description.isNullOrBlank()) {
+ webView.loadUrl(url)
+ } else {
+ webView.loadData("$description", "text/html", "utf-8")
+ }
}
-
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt
new file mode 100644
index 000000000..6d43baf9a
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt
@@ -0,0 +1,2 @@
+package io.legado.app.ui.rss.read
+
diff --git a/app/src/main/java/io/legado/app/ui/rss/source/debug/RssSourceDebugActivity.kt b/app/src/main/java/io/legado/app/ui/rss/source/debug/RssSourceDebugActivity.kt
index de8bf27dc..3c476c921 100644
--- a/app/src/main/java/io/legado/app/ui/rss/source/debug/RssSourceDebugActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/source/debug/RssSourceDebugActivity.kt
@@ -1,2 +1,16 @@
package io.legado.app.ui.rss.source.debug
+import android.os.Bundle
+import io.legado.app.R
+import io.legado.app.base.BaseActivity
+
+
+class RssSourceDebugActivity : BaseActivity(R.layout.activity_source_debug) {
+
+
+ override fun onActivityCreated(savedInstanceState: Bundle?) {
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditActivity.kt b/app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditActivity.kt
index e58ba568c..10e783e15 100644
--- a/app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditActivity.kt
@@ -1,19 +1,212 @@
package io.legado.app.ui.rss.source.edit
+import android.app.Activity
+import android.content.ClipData
+import android.content.ClipboardManager
+import android.content.Context
+import android.graphics.Rect
import android.os.Bundle
+import android.view.Gravity
+import android.view.Menu
+import android.view.MenuItem
+import android.view.ViewTreeObserver
+import android.widget.EditText
+import android.widget.PopupWindow
+import androidx.lifecycle.Observer
+import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.R
import io.legado.app.base.VMBaseActivity
+import io.legado.app.constant.AppConst
+import io.legado.app.data.entities.EditEntity
+import io.legado.app.data.entities.RssSource
+import io.legado.app.lib.theme.ATH
+import io.legado.app.ui.rss.source.debug.RssSourceDebugActivity
+import io.legado.app.ui.widget.KeyboardToolPop
+import io.legado.app.utils.GSON
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 :
- VMBaseActivity(R.layout.activity_rss_source_edit, false) {
+ VMBaseActivity(R.layout.activity_rss_source_edit, false),
+ KeyboardToolPop.CallBack {
+
+ private var mSoftKeyboardTool: PopupWindow? = null
+ private var mIsSoftKeyBoardShowing = false
+
+ private val adapter = RssSourceEditAdapter()
+ private val sourceEntities: ArrayList = ArrayList()
override val viewModel: RssSourceEditViewModel
get() = getViewModel(RssSourceEditViewModel::class.java)
override fun onActivityCreated(savedInstanceState: Bundle?) {
+ initView()
+ viewModel.sourceLiveData.observe(this, Observer {
+ upRecyclerView(it)
+ })
+ viewModel.initData(intent)
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ mSoftKeyboardTool?.dismiss()
+ }
+
+ override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
+ menuInflater.inflate(R.menu.source_edit, menu)
+ return super.onCompatCreateOptionsMenu(menu)
+ }
+
+ override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
+ when (item.itemId) {
+ R.id.menu_save -> {
+ getRssSource()?.let {
+ viewModel.save(it) {
+ setResult(Activity.RESULT_OK)
+ finish()
+ }
+ }
+ }
+ R.id.menu_debug_source -> {
+ getRssSource()?.let {
+ viewModel.save(it) {
+ startActivity(Pair("key", it.sourceUrl))
+ }
+ }
+ }
+ R.id.menu_copy_source -> {
+ GSON.toJson(getRssSource())?.let { sourceStr ->
+ val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager?
+ clipboard?.primaryClip = ClipData.newPlainText(null, sourceStr)
+ }
+ }
+ R.id.menu_paste_source -> viewModel.pasteSource()
+ }
+ return super.onCompatOptionsItemSelected(item)
+ }
+
+ private fun initView() {
+ ATH.applyEdgeEffectColor(recycler_view)
+ mSoftKeyboardTool = KeyboardToolPop(this, AppConst.keyboardToolChars, this)
+ window.decorView.viewTreeObserver.addOnGlobalLayoutListener(KeyboardOnGlobalChangeListener())
+ recycler_view.layoutManager = LinearLayoutManager(this)
+ recycler_view.adapter = adapter
+ }
+
+ private fun upRecyclerView(rssSource: RssSource?) {
+ sourceEntities.clear()
+ sourceEntities.apply {
+ add(EditEntity("sourceName", rssSource?.sourceName, R.string.rss_source_name))
+ add(EditEntity("sourceUrl", rssSource?.sourceUrl, R.string.rss_source_url))
+ add(EditEntity("sourceIcon", rssSource?.sourceIcon, R.string.rss_source_icon))
+ add(EditEntity("sourceGroup", rssSource?.sourceGroup, R.string.rss_source_group))
+ add(EditEntity("ruleArticles", rssSource?.ruleArticles, R.string.rss_rule_articles))
+ 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))
+ add(EditEntity("rulePubDate", rssSource?.rulePubDate, R.string.rss_rule_date))
+ add(
+ EditEntity(
+ "ruleCategories",
+ rssSource?.ruleCategories,
+ R.string.rss_rule_categories
+ )
+ )
+ add(
+ EditEntity(
+ "ruleDescription",
+ rssSource?.ruleDescription,
+ R.string.rss_rule_description
+ )
+ )
+ add(EditEntity("ruleImage", rssSource?.ruleImage, R.string.rss_rule_image))
+ add(EditEntity("ruleLink", rssSource?.ruleLink, R.string.rss_rule_link))
+ add(EditEntity("ruleContent", rssSource?.ruleContent, R.string.rss_rule_content))
+ }
+ adapter.editEntities = sourceEntities
+ }
+
+ private fun getRssSource(): RssSource? {
+ val source = viewModel.sourceLiveData.value ?: RssSource()
+ sourceEntities.forEach {
+ when (it.key) {
+ "sourceName" -> source.sourceName = it.value ?: ""
+ "sourceUrl" -> source.sourceUrl = it.value ?: ""
+ "sourceIcon" -> source.sourceIcon = it.value ?: ""
+ "sourceGroup" -> source.sourceGroup = it.value
+ "ruleArticles" -> source.ruleArticles = 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
+ "ruleLink" -> source.ruleLink = it.value
+ "ruleContent" -> source.ruleContent = it.value
+ }
+ }
+ if (source.sourceName.isBlank() || source.sourceName.isBlank()) {
+ toast("名称或url不能为空")
+ return null
+ }
+ return source
+ }
+ override fun sendText(text: String) {
+ if (text.isBlank()) return
+ val view = window.decorView.findFocus()
+ if (view is EditText) {
+ val start = view.selectionStart
+ val end = view.selectionEnd
+ val edit = view.editableText//获取EditText的文字
+ if (start < 0 || start >= edit.length) {
+ edit.append(text)
+ } else {
+ edit.replace(start, end, text)//光标所在位置插入文字
+ }
+ }
}
+ private fun showKeyboardTopPopupWindow() {
+ mSoftKeyboardTool?.isShowing?.let { if (it) return }
+ if (!isFinishing) {
+ mSoftKeyboardTool?.showAtLocation(ll_content, Gravity.BOTTOM, 0, 0)
+ }
+ }
+
+ private fun closePopupWindow() {
+ mSoftKeyboardTool?.let {
+ if (it.isShowing) {
+ it.dismiss()
+ }
+ }
+ }
+
+ private inner class KeyboardOnGlobalChangeListener : ViewTreeObserver.OnGlobalLayoutListener {
+ override fun onGlobalLayout() {
+ val rect = Rect()
+ // 获取当前页面窗口的显示范围
+ window.decorView.getWindowVisibleDisplayFrame(rect)
+ val screenHeight = this@RssSourceEditActivity.displayMetrics.heightPixels
+ val keyboardHeight = screenHeight - rect.bottom // 输入法的高度
+ val preShowing = mIsSoftKeyBoardShowing
+ if (abs(keyboardHeight) > screenHeight / 5) {
+ mIsSoftKeyBoardShowing = true // 超过屏幕五分之一则表示弹出了输入法
+ recycler_view.setPadding(0, 0, 0, 100)
+ showKeyboardTopPopupWindow()
+ } else {
+ mIsSoftKeyBoardShowing = false
+ recycler_view.setPadding(0, 0, 0, 0)
+ if (preShowing) {
+ closePopupWindow()
+ }
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditAdapter.kt b/app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditAdapter.kt
new file mode 100644
index 000000000..78e0740ef
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditAdapter.kt
@@ -0,0 +1,85 @@
+package io.legado.app.ui.rss.source.edit
+
+import android.text.Editable
+import android.text.TextWatcher
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import io.legado.app.R
+import io.legado.app.data.entities.EditEntity
+import kotlinx.android.synthetic.main.item_source_edit.view.*
+
+class RssSourceEditAdapter : RecyclerView.Adapter() {
+
+ var editEntities: ArrayList = ArrayList()
+ set(value) {
+ field = value
+ notifyDataSetChanged()
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
+ return MyViewHolder(
+ LayoutInflater.from(
+ parent.context
+ ).inflate(R.layout.item_source_edit, parent, false)
+ )
+ }
+
+ override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
+ holder.bind(editEntities[position])
+ }
+
+ override fun getItemCount(): Int {
+ return editEntities.size
+ }
+
+ class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+ fun bind(editEntity: EditEntity) = with(itemView) {
+ if (editText.getTag(R.id.tag1) == null) {
+ val listener = object : View.OnAttachStateChangeListener {
+ override fun onViewAttachedToWindow(v: View) {
+ editText.isCursorVisible = false
+ editText.isCursorVisible = true
+ editText.isFocusable = true
+ editText.isFocusableInTouchMode = true
+ }
+
+ override fun onViewDetachedFromWindow(v: View) {
+
+ }
+ }
+ editText.addOnAttachStateChangeListener(listener)
+ editText.setTag(R.id.tag1, listener)
+ }
+ editText.getTag(R.id.tag2)?.let {
+ if (it is TextWatcher) {
+ editText.removeTextChangedListener(it)
+ }
+ }
+ editText.setText(editEntity.value)
+ textInputLayout.hint = context.getString(editEntity.hint)
+ val textWatcher = object : TextWatcher {
+ override fun beforeTextChanged(
+ s: CharSequence,
+ start: Int,
+ count: Int,
+ after: Int
+ ) {
+
+ }
+
+ override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
+
+ }
+
+ override fun afterTextChanged(s: Editable?) {
+ editEntity.value = (s?.toString())
+ }
+ }
+ editText.addTextChangedListener(textWatcher)
+ editText.setTag(R.id.tag2, textWatcher)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditViewModel.kt b/app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditViewModel.kt
index fdb353c69..8b0f2cd11 100644
--- a/app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditViewModel.kt
@@ -1,9 +1,66 @@
package io.legado.app.ui.rss.source.edit
import android.app.Application
+import android.content.ClipboardManager
+import android.content.Context
+import android.content.Intent
+import androidx.lifecycle.MutableLiveData
+import io.legado.app.App
import io.legado.app.base.BaseViewModel
+import io.legado.app.data.entities.RssSource
+import io.legado.app.utils.GSON
+import io.legado.app.utils.fromJsonObject
class RssSourceEditViewModel(application: Application) : BaseViewModel(application) {
+ val sourceLiveData: MutableLiveData = MutableLiveData()
+ var oldSourceUrl: String? = null
+ fun initData(intent: Intent) {
+ execute {
+ val key = intent.getStringExtra("data")
+ var source: RssSource? = null
+ if (key != null) {
+ source = App.db.rssSourceDao().getByKey(key)
+ }
+ source?.let {
+ oldSourceUrl = it.sourceUrl
+ sourceLiveData.postValue(it)
+ } ?: let {
+ sourceLiveData.postValue(RssSource().apply {
+ customOrder = App.db.rssSourceDao().maxOrder + 1
+ })
+ }
+ }
+ }
+
+ fun save(rssSource: RssSource, success: (() -> Unit)) {
+ execute {
+ oldSourceUrl?.let {
+ if (oldSourceUrl != rssSource.sourceUrl) {
+ App.db.rssSourceDao().delete(it)
+ }
+ }
+ oldSourceUrl = rssSource.sourceUrl
+ App.db.rssSourceDao().insert(rssSource)
+ }.onSuccess {
+ success()
+ }.onError {
+ toast(it.localizedMessage)
+ }
+ }
+
+ fun pasteSource() {
+ execute {
+ val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager?
+ clipboard?.primaryClip?.let {
+ if (it.itemCount > 0) {
+ val json = it.getItemAt(0).text.toString()
+ GSON.fromJsonObject(json)?.let { source ->
+ sourceLiveData.postValue(source)
+ } ?: toast("格式不对")
+ }
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/DiffCallBack.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/DiffCallBack.kt
new file mode 100644
index 000000000..4260febe4
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/DiffCallBack.kt
@@ -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,
+ private val newItems: List
+) : 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
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt
index 047a342d4..5515e4cf1 100644
--- a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt
@@ -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(R.layout.activity_r
get() = getViewModel(RssSourceViewModel::class.java)
private lateinit var adapter: RssSourceAdapter
+ private var sourceLiveData: LiveData>? = null
+ private var groups = hashSetOf()
+ 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(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()
+ R.id.menu_add -> startActivity()
+ 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,24 +103,57 @@ class RssSourceActivity : VMBaseActivity(R.layout.activity_r
})
}
- override fun del(source: RssSource) {
+ 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)
+ }
}
- override fun edit(source: RssSource) {
+ 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) {
+ viewModel.del(source)
}
- override fun update(vararg source: RssSource) {
+ override fun edit(source: RssSource) {
+ startActivity(Pair("data", source.sourceUrl))
+ }
+ override fun update(vararg source: RssSource) {
+ viewModel.update(*source)
}
override fun toTop(source: RssSource) {
-
+ viewModel.topSource(source)
}
override fun upOrder() {
-
+ viewModel.upOrder()
}
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceAdapter.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceAdapter.kt
index 85aebdb18..8dee3c485 100644
--- a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceAdapter.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceAdapter.kt
@@ -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(context, R.layout.item_rss_source),
ItemTouchCallback.OnItemTouchCallbackListener {
- override fun convert(holder: ItemViewHolder, item: RssSource, payloads: MutableList) {
+ private val selectedIds = linkedSetOf()
+
+ 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 {
+ val selection = linkedSetOf()
+ getItems().map {
+ if (selectedIds.contains(it.sourceUrl)) {
+ selection.add(it.sourceUrl)
+ }
+ }
+ return selection
+ }
+
+ override fun convert(holder: ItemViewHolder, item: RssSource, payloads: MutableList) {
+ 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
+ }
+ }
+ }
}
diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt
index c91f73ef7..b0ad309d6 100644
--- a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt
@@ -1,8 +1,52 @@
package io.legado.app.ui.rss.source.manage
import android.app.Application
+import io.legado.app.App
import io.legado.app.base.BaseViewModel
+import io.legado.app.data.entities.RssSource
class RssSourceViewModel(application: Application) : BaseViewModel(application) {
+ fun topSource(rssSource: RssSource) {
+ execute {
+ rssSource.customOrder = App.db.rssSourceDao().minOrder - 1
+ App.db.rssSourceDao().insert(rssSource)
+ }
+ }
+
+ fun del(rssSource: RssSource) {
+ execute { App.db.rssSourceDao().delete(rssSource) }
+ }
+
+ fun update(vararg rssSource: RssSource) {
+ execute { App.db.rssSourceDao().update(*rssSource) }
+ }
+
+ fun upOrder() {
+ execute {
+ val sources = App.db.rssSourceDao().all
+ for ((index: Int, source: RssSource) in sources.withIndex()) {
+ source.customOrder = index + 1
+ }
+ App.db.rssSourceDao().update(*sources.toTypedArray())
+ }
+ }
+
+ fun enableSelection(ids: LinkedHashSet) {
+ execute {
+ App.db.rssSourceDao().enableSection(*ids.toTypedArray())
+ }
+ }
+
+ fun disableSelection(ids: LinkedHashSet) {
+ execute {
+ App.db.rssSourceDao().disableSection(*ids.toTypedArray())
+ }
+ }
+
+ fun delSelection(ids: LinkedHashSet) {
+ execute {
+ App.db.rssSourceDao().delSection(*ids.toTypedArray())
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/widget/KeyboardToolPop.kt b/app/src/main/java/io/legado/app/ui/widget/KeyboardToolPop.kt
index 57f40c039..40e321b87 100644
--- a/app/src/main/java/io/legado/app/ui/widget/KeyboardToolPop.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/KeyboardToolPop.kt
@@ -18,7 +18,7 @@ import org.jetbrains.anko.sdk27.listeners.onClick
class KeyboardToolPop(
context: Context,
private val chars: List,
- val onClickListener: OnClickListener?
+ val callBack: CallBack?
) : PopupWindow(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) {
init {
@@ -45,13 +45,13 @@ class KeyboardToolPop(
override fun convert(holder: ItemViewHolder, item: String, payloads: MutableList) {
with(holder.itemView) {
text_view.text = item
- onClick { onClickListener?.click(item) }
+ onClick { callBack?.sendText(item) }
}
}
}
- interface OnClickListener {
- fun click(text: String)
+ interface CallBack {
+ fun sendText(text: String)
}
}
diff --git a/app/src/main/java/io/legado/app/ui/widget/anima/RefreshProgressBar.kt b/app/src/main/java/io/legado/app/ui/widget/anima/RefreshProgressBar.kt
index 3eee366be..1095d5889 100644
--- a/app/src/main/java/io/legado/app/ui/widget/anima/RefreshProgressBar.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/anima/RefreshProgressBar.kt
@@ -16,15 +16,15 @@ class RefreshProgressBar @JvmOverloads constructor(
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
- internal var a = 1
- var maxProgress = 100
+ private var a = 1
private var durProgress = 0
- var secondMaxProgress = 100
private var secondDurProgress = 0
+ var maxProgress = 100
+ var secondMaxProgress = 100
var bgColor = 0x00000000
var secondColor = -0x3e3e3f
var fontColor = -0xc9c9ca
- var speed = 1
+ var speed = 2
var secondFinalProgress = 0
private set
private var paint: Paint = Paint()
diff --git a/app/src/main/java/io/legado/app/ui/widget/anima/explosion_field/Utils.kt b/app/src/main/java/io/legado/app/ui/widget/anima/explosion_field/Utils.kt
index 2f99191d2..6e33941fb 100644
--- a/app/src/main/java/io/legado/app/ui/widget/anima/explosion_field/Utils.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/anima/explosion_field/Utils.kt
@@ -22,6 +22,7 @@ import android.graphics.Canvas
import android.graphics.drawable.BitmapDrawable
import android.view.View
import android.widget.ImageView
+import kotlin.math.roundToInt
object Utils {
@@ -29,7 +30,7 @@ object Utils {
private val sCanvas = Canvas()
fun dp2Px(dp: Int): Int {
- return Math.round(dp * DENSITY)
+ return (dp * DENSITY).roundToInt()
}
fun createBitmapFromView(view: View): Bitmap? {
@@ -55,7 +56,7 @@ object Utils {
return bitmap
}
- fun createBitmapSafely(
+ private fun createBitmapSafely(
width: Int,
height: Int,
config: Bitmap.Config,
diff --git a/app/src/main/java/io/legado/app/ui/widget/image/FilletImageView.kt b/app/src/main/java/io/legado/app/ui/widget/image/FilletImageView.kt
index bb6a4461e..bcb449329 100644
--- a/app/src/main/java/io/legado/app/ui/widget/image/FilletImageView.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/image/FilletImageView.kt
@@ -7,6 +7,8 @@ import android.graphics.Path
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageView
import io.legado.app.R
+import io.legado.app.utils.dp
+import kotlin.math.max
class FilletImageView : AppCompatImageView {
internal var width: Float = 0.toFloat()
@@ -29,7 +31,7 @@ class FilletImageView : AppCompatImageView {
private fun init(context: Context, attrs: AttributeSet) {
// 读取配置
val array = context.obtainStyledAttributes(attrs, R.styleable.FilletImageView)
- val defaultRadius = 5
+ val defaultRadius = 5.dp
val radius = array.getDimensionPixelOffset(R.styleable.FilletImageView_radius, defaultRadius)
leftTopRadius = array.getDimensionPixelOffset(R.styleable.FilletImageView_left_top_radius, defaultRadius)
rightTopRadius = array.getDimensionPixelOffset(R.styleable.FilletImageView_right_top_radius, defaultRadius)
@@ -62,11 +64,11 @@ class FilletImageView : AppCompatImageView {
override fun onDraw(canvas: Canvas) {
//这里做下判断,只有图片的宽高大于设置的圆角距离的时候才进行裁剪
- val maxLeft = Math.max(leftTopRadius, leftBottomRadius)
- val maxRight = Math.max(rightTopRadius, rightBottomRadius)
+ val maxLeft = max(leftTopRadius, leftBottomRadius)
+ val maxRight = max(rightTopRadius, rightBottomRadius)
val minWidth = maxLeft + maxRight
- val maxTop = Math.max(leftTopRadius, rightTopRadius)
- val maxBottom = Math.max(leftBottomRadius, rightBottomRadius)
+ val maxTop = max(leftTopRadius, rightTopRadius)
+ val maxBottom = max(leftBottomRadius, rightBottomRadius)
val minHeight = maxTop + maxBottom
if (width >= minWidth && height > minHeight) {
@SuppressLint("DrawAllocation") val path = Path()
diff --git a/app/src/main/java/io/legado/app/ui/widget/page/ContentView.kt b/app/src/main/java/io/legado/app/ui/widget/page/ContentView.kt
index 719129a03..228e6f2e9 100644
--- a/app/src/main/java/io/legado/app/ui/widget/page/ContentView.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/page/ContentView.kt
@@ -10,7 +10,6 @@ import android.widget.ImageView
import androidx.appcompat.widget.AppCompatImageView
import io.legado.app.R
import io.legado.app.constant.AppConst.TIME_FORMAT
-import io.legado.app.help.ImageLoader
import io.legado.app.help.ReadBookConfig
import io.legado.app.utils.*
import kotlinx.android.synthetic.main.view_book_page.view.*
@@ -90,10 +89,7 @@ class ContentView : FrameLayout {
}
fun setBg(bg: Drawable?) {
- //all supported
- ImageLoader.load(context, bg)
- .centerCrop()
- .setAsDrawable(bgImage)
+ bgImage.background = bg
}
fun upTime() {
diff --git a/app/src/main/java/io/legado/app/utils/ACache.kt b/app/src/main/java/io/legado/app/utils/ACache.kt
index 5ac75f875..7549813e8 100644
--- a/app/src/main/java/io/legado/app/utils/ACache.kt
+++ b/app/src/main/java/io/legado/app/utils/ACache.kt
@@ -23,7 +23,49 @@ import kotlin.math.min
* 本地缓存
*/
class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int) {
- private lateinit var mCache: ACacheManager
+
+
+ companion object {
+ const val TIME_HOUR = 60 * 60
+ const val TIME_DAY = TIME_HOUR * 24
+ private const val MAX_SIZE = 1000 * 1000 * 50 // 50 mb
+ private const val MAX_COUNT = Integer.MAX_VALUE // 不限制存放数据的数量
+ private val mInstanceMap = HashMap()
+
+ @JvmOverloads
+ fun get(
+ ctx: Context,
+ cacheName: String = "ACache",
+ maxSize: Long = MAX_SIZE.toLong(),
+ maxCount: Int = MAX_COUNT,
+ cacheDir: Boolean = true
+ ): ACache {
+ val f = if (cacheDir) File(ctx.cacheDir, cacheName) else File(ctx.filesDir, cacheName)
+ return get(f, maxSize, maxCount)
+ }
+
+ @JvmOverloads
+ fun get(
+ cacheDir: File,
+ maxSize: Long = MAX_SIZE.toLong(),
+ maxCount: Int = MAX_COUNT
+ ): ACache {
+ synchronized(this) {
+ var manager = mInstanceMap[cacheDir.absoluteFile.toString() + myPid()]
+ if (manager == null) {
+ manager = ACache(cacheDir, maxSize, maxCount)
+ mInstanceMap[cacheDir.absolutePath + myPid()] = manager
+ }
+ return manager
+ }
+ }
+
+ private fun myPid(): String {
+ return "_" + android.os.Process.myPid()
+ }
+ }
+
+ private var mCache: ACacheManager? = null
init {
try {
@@ -48,12 +90,14 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int)
* @param value 保存的String数据
*/
fun put(key: String, value: String) {
- try {
- val file = mCache.newFile(key)
- file.writeText(value)
- mCache.put(file)
- } catch (e: Exception) {
- e.printStackTrace()
+ mCache?.let { mCache ->
+ try {
+ val file = mCache.newFile(key)
+ file.writeText(value)
+ mCache.put(file)
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
}
}
@@ -74,24 +118,26 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int)
* @return String 数据
*/
fun getAsString(key: String): String? {
- val file = mCache[key]
- if (!file.exists())
- return null
- var removeFile = false
- return try {
- val text = file.readText()
- if (!Utils.isDue(text)) {
- Utils.clearDateInfo(text)
- } else {
- removeFile = true
- null
+ mCache?.let { mCache ->
+ val file = mCache[key]
+ if (!file.exists())
+ return null
+ var removeFile = false
+ try {
+ val text = file.readText()
+ if (!Utils.isDue(text)) {
+ return Utils.clearDateInfo(text)
+ } else {
+ removeFile = true
+ }
+ } catch (e: IOException) {
+ e.printStackTrace()
+ } finally {
+ if (removeFile)
+ remove(key)
}
- } catch (e: IOException) {
- null
- } finally {
- if (removeFile)
- remove(key)
}
+ return null
}
// =======================================
@@ -184,9 +230,11 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int)
* @param value 保存的数据
*/
fun put(key: String, value: ByteArray) {
- val file = mCache.newFile(key)
- file.writeBytes(value)
- mCache.put(file)
+ mCache?.let { mCache ->
+ val file = mCache.newFile(key)
+ file.writeBytes(value)
+ mCache.put(file)
+ }
}
/**
@@ -206,26 +254,28 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int)
* @return byte 数据
*/
fun getAsBinary(key: String): ByteArray? {
- var removeFile = false
- try {
- val file = mCache[key]
- if (!file.exists())
- return null
+ mCache?.let { mCache ->
+ var removeFile = false
+ try {
+ val file = mCache[key]
+ if (!file.exists())
+ return null
- val byteArray = file.readBytes()
- return if (!Utils.isDue(byteArray)) {
- Utils.clearDateInfo(byteArray)
- } else {
- removeFile = true
- null
+ val byteArray = file.readBytes()
+ return if (!Utils.isDue(byteArray)) {
+ Utils.clearDateInfo(byteArray)
+ } else {
+ removeFile = true
+ null
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ } finally {
+ if (removeFile)
+ remove(key)
}
- } catch (e: Exception) {
- e.printStackTrace()
- return null
- } finally {
- if (removeFile)
- remove(key)
}
+ return null
}
/**
@@ -269,7 +319,6 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int)
return ois.readObject()
} catch (e: Exception) {
e.printStackTrace()
- return null
} finally {
try {
bais?.close()
@@ -371,15 +420,16 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int)
* @return value 缓存的文件
*/
fun file(key: String): File? {
- try {
- val f = mCache.newFile(key)
- if (f.exists()) {
- return f
+ mCache?.let { mCache ->
+ try {
+ val f = mCache.newFile(key)
+ if (f.exists()) {
+ return f
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
}
- } catch (e: Exception) {
- e.printStackTrace()
}
-
return null
}
@@ -389,14 +439,14 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int)
* @return 是否移除成功
*/
fun remove(key: String): Boolean {
- return mCache.remove(key)
+ return mCache?.remove(key) == true
}
/**
* 清除所有数据
*/
fun clear() {
- mCache.clear()
+ mCache?.clear()
}
/**
@@ -726,44 +776,4 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int)
}
}
- companion object {
- const val TIME_HOUR = 60 * 60
- const val TIME_DAY = TIME_HOUR * 24
- private const val MAX_SIZE = 1000 * 1000 * 50 // 50 mb
- private const val MAX_COUNT = Integer.MAX_VALUE // 不限制存放数据的数量
- private val mInstanceMap = HashMap()
-
- @JvmOverloads
- fun get(
- ctx: Context,
- cacheName: String = "ACache",
- maxSize: Long = MAX_SIZE.toLong(),
- maxCount: Int = MAX_COUNT,
- cacheDir: Boolean = true
- ): ACache {
- val f = if (cacheDir) File(ctx.cacheDir, cacheName) else File(ctx.filesDir, cacheName)
- return get(f, maxSize, maxCount)
- }
-
- @JvmOverloads
- fun get(
- cacheDir: File,
- maxSize: Long = MAX_SIZE.toLong(),
- maxCount: Int = MAX_COUNT
- ): ACache {
- synchronized(this) {
- var manager = mInstanceMap[cacheDir.absoluteFile.toString() + myPid()]
- if (manager == null) {
- manager = ACache(cacheDir, maxSize, maxCount)
- mInstanceMap[cacheDir.absolutePath + myPid()] = manager
- }
- return manager
- }
- }
-
- private fun myPid(): String {
- return "_" + android.os.Process.myPid()
- }
- }
-
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/utils/BitmapUtil.kt b/app/src/main/java/io/legado/app/utils/BitmapUtil.kt
new file mode 100644
index 000000000..2b72e17db
--- /dev/null
+++ b/app/src/main/java/io/legado/app/utils/BitmapUtil.kt
@@ -0,0 +1,255 @@
+package io.legado.app.utils
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Bitmap.Config
+import android.graphics.BitmapFactory
+import android.graphics.Canvas
+import android.renderscript.Allocation
+import android.renderscript.Element
+import android.renderscript.RenderScript
+import android.renderscript.ScriptIntrinsicBlur
+import android.view.View
+import io.legado.app.App
+import java.io.IOException
+import kotlin.math.ceil
+import kotlin.math.floor
+import kotlin.math.min
+import kotlin.math.sqrt
+
+
+@Suppress("unused", "WeakerAccess")
+object BitmapUtil {
+ /**
+ * 从path中获取图片信息,在通过BitmapFactory.decodeFile(String path)方法将突破转成Bitmap时,
+ * 遇到大一些的图片,我们经常会遇到OOM(Out Of Memory)的问题。所以用到了我们上面提到的BitmapFactory.Options这个类。
+ *
+ * @param path 文件路径
+ * @param width 想要显示的图片的宽度
+ * @param height 想要显示的图片的高度
+ * @return
+ */
+ fun decodeBitmap(path: String, width: Int, height: Int): Bitmap {
+ val op = BitmapFactory.Options()
+ // inJustDecodeBounds如果设置为true,仅仅返回图片实际的宽和高,宽和高是赋值给opts.outWidth,opts.outHeight;
+ op.inJustDecodeBounds = true
+ BitmapFactory.decodeFile(path, op) //获取尺寸信息
+ //获取比例大小
+ val wRatio = ceil((op.outWidth / width).toDouble()).toInt()
+ val hRatio = ceil((op.outHeight / height).toDouble()).toInt()
+ //如果超出指定大小,则缩小相应的比例
+ if (wRatio > 1 && hRatio > 1) {
+ if (wRatio > hRatio) {
+ op.inSampleSize = wRatio
+ } else {
+ op.inSampleSize = hRatio
+ }
+ }
+ op.inJustDecodeBounds = false
+ return BitmapFactory.decodeFile(path, op)
+ }
+
+ /** 从path中获取Bitmap图片
+ * @param path 图片路径
+ * @return
+ */
+
+ fun decodeBitmap(path: String): Bitmap {
+ val opts = BitmapFactory.Options()
+
+ opts.inJustDecodeBounds = true
+ BitmapFactory.decodeFile(path, opts)
+
+ opts.inSampleSize = computeSampleSize(opts, -1, 128 * 128)
+
+ opts.inJustDecodeBounds = false
+
+ return BitmapFactory.decodeFile(path, opts)
+ }
+
+ /**
+ * 以最省内存的方式读取本地资源的图片
+ * @param context 设备上下文
+ * @param resId 资源ID
+ * @return
+ */
+ fun decodeBitmap(context: Context, resId: Int): Bitmap? {
+ val opt = BitmapFactory.Options()
+ opt.inPreferredConfig = Config.RGB_565
+ //获取资源图片
+ val `is` = context.resources.openRawResource(resId)
+ return BitmapFactory.decodeStream(`is`, null, opt)
+ }
+
+ /**
+ * @param context 设备上下文
+ * @param resId 资源ID
+ * @param width
+ * @param height
+ * @return
+ */
+ fun decodeBitmap(context: Context, resId: Int, width: Int, height: Int): Bitmap? {
+
+ var inputStream = context.resources.openRawResource(resId)
+
+ val op = BitmapFactory.Options()
+ // inJustDecodeBounds如果设置为true,仅仅返回图片实际的宽和高,宽和高是赋值给opts.outWidth,opts.outHeight;
+ op.inJustDecodeBounds = true
+ BitmapFactory.decodeStream(inputStream, null, op) //获取尺寸信息
+ //获取比例大小
+ val wRatio = ceil((op.outWidth / width).toDouble()).toInt()
+ val hRatio = ceil((op.outHeight / height).toDouble()).toInt()
+ //如果超出指定大小,则缩小相应的比例
+ if (wRatio > 1 && hRatio > 1) {
+ if (wRatio > hRatio) {
+ op.inSampleSize = wRatio
+ } else {
+ op.inSampleSize = hRatio
+ }
+ }
+ inputStream = context.resources.openRawResource(resId)
+ op.inJustDecodeBounds = false
+ return BitmapFactory.decodeStream(inputStream, null, op)
+ }
+
+ /**
+ * @param context 设备上下文
+ * @param fileNameInAssets Assets里面文件的名称
+ * @param width 图片的宽度
+ * @param height 图片的高度
+ * @return Bitmap
+ * @throws IOException
+ */
+ @Throws(IOException::class)
+ fun decodeBitmap(context: Context, fileNameInAssets: String, width: Int, height: Int): Bitmap? {
+
+ var inputStream = context.assets.open(fileNameInAssets)
+ val op = BitmapFactory.Options()
+ // inJustDecodeBounds如果设置为true,仅仅返回图片实际的宽和高,宽和高是赋值给opts.outWidth,opts.outHeight;
+ op.inJustDecodeBounds = true
+ BitmapFactory.decodeStream(inputStream, null, op) //获取尺寸信息
+ //获取比例大小
+ val wRatio = ceil((op.outWidth / width).toDouble()).toInt()
+ val hRatio = ceil((op.outHeight / height).toDouble()).toInt()
+ //如果超出指定大小,则缩小相应的比例
+ if (wRatio > 1 && hRatio > 1) {
+ if (wRatio > hRatio) {
+ op.inSampleSize = wRatio
+ } else {
+ op.inSampleSize = hRatio
+ }
+ }
+ inputStream = context.assets.open(fileNameInAssets)
+ op.inJustDecodeBounds = false
+ return BitmapFactory.decodeStream(inputStream, null, op)
+ }
+
+
+ //图片不被压缩
+ fun convertViewToBitmap(view: View, bitmapWidth: Int, bitmapHeight: Int): Bitmap {
+ val bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888)
+ view.draw(Canvas(bitmap))
+ return bitmap
+ }
+
+
+ /**
+ * @param options
+ * @param minSideLength
+ * @param maxNumOfPixels
+ * @return
+ * 设置恰当的inSampleSize是解决该问题的关键之一。BitmapFactory.Options提供了另一个成员inJustDecodeBounds。
+ * 设置inJustDecodeBounds为true后,decodeFile并不分配空间,但可计算出原始图片的长度和宽度,即opts.width和opts.height。
+ * 有了这两个参数,再通过一定的算法,即可得到一个恰当的inSampleSize。
+ * 查看Android源码,Android提供了下面这种动态计算的方法。
+ */
+ fun computeSampleSize(
+ options: BitmapFactory.Options,
+ minSideLength: Int,
+ maxNumOfPixels: Int
+ ): Int {
+
+ val initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels)
+
+ var roundedSize: Int
+
+ if (initialSize <= 8) {
+ roundedSize = 1
+ while (roundedSize < initialSize) {
+ roundedSize = roundedSize shl 1
+ }
+ } else {
+ roundedSize = (initialSize + 7) / 8 * 8
+ }
+
+ return roundedSize
+ }
+
+
+ private fun computeInitialSampleSize(
+ options: BitmapFactory.Options,
+ minSideLength: Int,
+ maxNumOfPixels: Int
+ ): Int {
+
+ val w = options.outWidth.toDouble()
+ val h = options.outHeight.toDouble()
+
+ val lowerBound = if (maxNumOfPixels == -1)
+ 1
+ else
+ ceil(sqrt(w * h / maxNumOfPixels)).toInt()
+
+ val upperBound = if (minSideLength == -1) 128 else min(
+ floor(w / minSideLength),
+ floor(h / minSideLength)
+ ).toInt()
+
+ if (upperBound < lowerBound) {
+ // return the larger one when there is no overlapping zone.
+ return lowerBound
+ }
+
+ return if (maxNumOfPixels == -1 && minSideLength == -1) {
+ 1
+ } else if (minSideLength == -1) {
+ lowerBound
+ } else {
+ upperBound
+ }
+ }
+
+ /**
+ * 高斯模糊
+ */
+ fun stackBlur(srcBitmap: Bitmap?): Bitmap? {
+ if (srcBitmap == null) return null
+ val rs = RenderScript.create(App.INSTANCE)
+ val blurredBitmap = srcBitmap.copy(Config.ARGB_8888, true)
+
+ //分配用于渲染脚本的内存
+ val input = Allocation.createFromBitmap(
+ rs,
+ blurredBitmap,
+ Allocation.MipmapControl.MIPMAP_FULL,
+ Allocation.USAGE_SHARED
+ )
+ val output = Allocation.createTyped(rs, input.type)
+
+ //加载我们想要使用的特定脚本的实例。
+ val script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs))
+ script.setInput(input)
+
+ //设置模糊半径
+ script.setRadius(8f)
+
+ //启动 ScriptIntrinsicBlur
+ script.forEach(output)
+
+ //将输出复制到模糊的位图
+ output.copyTo(blurredBitmap)
+
+ return blurredBitmap
+ }
+
+}
diff --git a/app/src/main/java/io/legado/app/utils/LogUtils.kt b/app/src/main/java/io/legado/app/utils/LogUtils.kt
index 571c4e4cf..3dd8f3dfe 100644
--- a/app/src/main/java/io/legado/app/utils/LogUtils.kt
+++ b/app/src/main/java/io/legado/app/utils/LogUtils.kt
@@ -11,66 +11,52 @@ import java.util.logging.Formatter
object LogUtils {
+ const val TIME_PATTERN = "yyyy-MM-dd HH:mm:ss"
- private val logger: Logger by lazy {
- Logger.getGlobal().apply {
- addFileHandler(
- this,
- Level.INFO,
- FileHelp.getCachePath()
- )
- }
+ @JvmStatic
+ fun d(tag: String, msg: String) {
+ logger.log(Level.INFO, "$tag $msg")
}
- fun log(msg: String) {
- if (App.INSTANCE.getPrefBoolean("recordLog")) {
- logger.log(Level.INFO, msg)
- }
+ @JvmStatic
+ fun e(tag: String, msg: String) {
+ logger.log(Level.WARNING, "$tag $msg")
}
- private const val DATE_PATTERN = "yyyy-MM-dd"
- const val TIME_PATTERN = "HH:mm:ss"
-
- /**
- * 为log添加控制台handler
- *
- * @param log 要添加handler的log
- * @param level 控制台的输出等级
- */
- fun addConsoleHandler(log: Logger, level: Level) {
- // 控制台输出的handler
- val consoleHandler = ConsoleHandler()
- // 设置控制台输出的等级(如果ConsoleHandler的等级高于或者等于log的level,则按照FileHandler的level输出到控制台,如果低于,则按照Log等级输出)
- consoleHandler.level = level
- // 添加控制台的handler
- log.addHandler(consoleHandler)
+ private val logger: Logger by lazy {
+ Logger.getGlobal().apply {
+ addHandler(fileHandler)
+ }
}
- /**
- * 为log添加文件输出Handler
- *
- * @param log 要添加文件输出handler的log
- * @param level log输出等级
- * @param filePath 指定文件全路径
- */
- fun addFileHandler(log: Logger, level: Level, filePath: String) {
- var fileHandler: FileHandler? = null
- try {
- fileHandler =
- FileHandler(filePath + File.separator + getCurrentDateStr(DATE_PATTERN) + ".log")
- // 设置输出文件的等级(如果FileHandler的等级高于或者等于log的level,则按照FileHandler的level输出到文件,如果低于,则按照Log等级输出)
- fileHandler.level = level
- fileHandler.formatter = object : Formatter() {
+ private val fileHandler by lazy {
+ val logFolder = FileHelp.getCachePath() + File.separator + "logs"
+ FileHelp.getFolder(logFolder)
+ FileHandler(
+ logFolder + File.separator + "app.log",
+ 2048,
+ 10
+ ).apply {
+ formatter = object : Formatter() {
override fun format(record: LogRecord): String {
// 设置文件输出格式
- return (getCurrentDateStr(TIME_PATTERN) + ":" + record.message + "\n")
+ return (getCurrentDateStr(TIME_PATTERN) + ": " + record.message + "\n")
}
}
- } catch (e: Exception) {
- e.printStackTrace()
+ level = if (App.INSTANCE.getPrefBoolean("recordLog")) {
+ Level.INFO
+ } else {
+ Level.OFF
+ }
+ }
+ }
+
+ fun upLevel() {
+ fileHandler.level = if (App.INSTANCE.getPrefBoolean("recordLog")) {
+ Level.INFO
+ } else {
+ Level.OFF
}
- // 添加输出文件handler
- log.addHandler(fileHandler)
}
/**
diff --git a/app/src/main/res/drawable/img_cover_default.jpg b/app/src/main/res/drawable/image_cover_default.jpg
similarity index 100%
rename from app/src/main/res/drawable/img_cover_default.jpg
rename to app/src/main/res/drawable/image_cover_default.jpg
diff --git a/app/src/main/res/drawable/img_cover_gs.jpg b/app/src/main/res/drawable/image_cover_gs.jpg
similarity index 100%
rename from app/src/main/res/drawable/img_cover_gs.jpg
rename to app/src/main/res/drawable/image_cover_gs.jpg
diff --git a/app/src/main/res/drawable/image_rss.jpg b/app/src/main/res/drawable/image_rss.jpg
new file mode 100644
index 000000000..a152df506
Binary files /dev/null and b/app/src/main/res/drawable/image_rss.jpg differ
diff --git a/app/src/main/res/layout-land/activity_book_info.xml b/app/src/main/res/layout-land/activity_book_info.xml
index a5419f1e6..a284835eb 100644
--- a/app/src/main/res/layout-land/activity_book_info.xml
+++ b/app/src/main/res/layout-land/activity_book_info.xml
@@ -28,7 +28,7 @@
android:layout_width="86dp"
android:layout_height="120dp"
android:layout_margin="10dp"
- android:src="@drawable/img_cover_default"
+ android:src="@drawable/image_cover_default"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title_bar" />
@@ -165,7 +165,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
- android:text="简介"
+ android:text="@string/book_intro"
android:textColor="@color/tv_text_default"
android:textSize="16sp" />
@@ -192,22 +192,15 @@
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toRightOf="@+id/fg">
-
-
-
-
-
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:paddingTop="8dp"
+ android:text="@string/chapter_list"
+ android:textColor="@color/tv_text_default"
+ android:textSize="16sp" />
+ android:text="@string/dur_pos" />
+
+
diff --git a/app/src/main/res/layout/activity_audio_play.xml b/app/src/main/res/layout/activity_audio_play.xml
index 2a392be02..ec6bbd556 100644
--- a/app/src/main/res/layout/activity_audio_play.xml
+++ b/app/src/main/res/layout/activity_audio_play.xml
@@ -25,7 +25,7 @@
android:layout_height="260dp"
android:layout_gravity="center"
android:contentDescription="@string/img_cover"
- android:src="@drawable/img_cover_default"
+ android:src="@drawable/image_cover_default"
app:layout_constraintTop_toBottomOf="@+id/title_bar"
app:layout_constraintBottom_toTopOf="@+id/ll_player_progress"
app:layout_constraintLeft_toLeftOf="parent"
diff --git a/app/src/main/res/layout/activity_book_info.xml b/app/src/main/res/layout/activity_book_info.xml
index 2ccef7e17..9c2887c68 100644
--- a/app/src/main/res/layout/activity_book_info.xml
+++ b/app/src/main/res/layout/activity_book_info.xml
@@ -19,7 +19,7 @@
android:layout_height="120dp"
android:layout_margin="10dp"
android:scaleType="centerCrop"
- android:src="@drawable/img_cover_default"
+ android:src="@drawable/image_cover_default"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title_bar" />
@@ -154,7 +154,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
- android:text="简介"
+ android:text="@string/book_intro"
android:textColor="@color/tv_text_default"
android:textSize="16sp" />
@@ -184,22 +184,15 @@
android:layout_height="5dp"
android:background="@color/bg_divider_line" />
-
-
-
-
-
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:paddingTop="8dp"
+ android:text="@string/chapter_list"
+ android:textColor="@color/tv_text_default"
+ android:textSize="16sp" />
+ android:text="@string/dur_pos" />
+
+
@@ -284,8 +287,6 @@
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/selector_btn_accent_bg"
- android:clickable="true"
- android:focusable="true"
android:gravity="center"
android:includeFontPadding="false"
android:text="@string/reading"
diff --git a/app/src/main/res/layout/activity_book_info_edit.xml b/app/src/main/res/layout/activity_book_info_edit.xml
index 4244c2b89..12328a069 100644
--- a/app/src/main/res/layout/activity_book_info_edit.xml
+++ b/app/src/main/res/layout/activity_book_info_edit.xml
@@ -34,7 +34,7 @@
android:layout_height="126dp"
android:contentDescription="@string/img_cover"
android:scaleType="centerCrop"
- android:src="@drawable/img_cover_default" />
+ android:src="@drawable/image_cover_default" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_rss_artivles.xml b/app/src/main/res/layout/activity_rss_artivles.xml
new file mode 100644
index 000000000..5eceec843
--- /dev/null
+++ b/app/src/main/res/layout/activity_rss_artivles.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_rss_read.xml b/app/src/main/res/layout/activity_rss_read.xml
new file mode 100644
index 000000000..5d403f5fe
--- /dev/null
+++ b/app/src/main/res/layout/activity_rss_read.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_rss_source_edit.xml b/app/src/main/res/layout/activity_rss_source_edit.xml
index 437330f50..dd2545faf 100644
--- a/app/src/main/res/layout/activity_rss_source_edit.xml
+++ b/app/src/main/res/layout/activity_rss_source_edit.xml
@@ -1,6 +1,7 @@
@@ -11,7 +12,12 @@
android:layout_height="wrap_content"
app:contentInsetStartWithNavigation="0dp"
app:displayHomeAsUp="true"
+ app:fitStatusBar="false"
app:title="@string/rss_source_edit" />
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_book_source_debug.xml b/app/src/main/res/layout/activity_source_debug.xml
similarity index 100%
rename from app/src/main/res/layout/activity_book_source_debug.xml
rename to app/src/main/res/layout/activity_source_debug.xml
diff --git a/app/src/main/res/layout/fragment_rss.xml b/app/src/main/res/layout/fragment_rss.xml
index 3d9e9a4b5..f5a3b374c 100644
--- a/app/src/main/res/layout/fragment_rss.xml
+++ b/app/src/main/res/layout/fragment_rss.xml
@@ -11,7 +11,6 @@
android:layout_height="wrap_content"
app:attachToActivity="false"
app:title="@string/rss" />
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_bookshelf_list.xml b/app/src/main/res/layout/item_bookshelf_list.xml
index 03fd1afc1..b1b0f45a9 100644
--- a/app/src/main/res/layout/item_bookshelf_list.xml
+++ b/app/src/main/res/layout/item_bookshelf_list.xml
@@ -16,7 +16,7 @@
android:layout_margin="8dp"
android:contentDescription="@string/img_cover"
android:scaleType="centerCrop"
- android:src="@drawable/img_cover_default"
+ android:src="@drawable/image_cover_default"
android:transitionName="img_cover"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
diff --git a/app/src/main/res/layout/item_group_manage.xml b/app/src/main/res/layout/item_group_manage.xml
index 493bf7998..d7d85630a 100644
--- a/app/src/main/res/layout/item_group_manage.xml
+++ b/app/src/main/res/layout/item_group_manage.xml
@@ -2,6 +2,7 @@
diff --git a/app/src/main/res/layout/item_rss.xml b/app/src/main/res/layout/item_rss.xml
index d829e291c..8bf74003e 100644
--- a/app/src/main/res/layout/item_rss.xml
+++ b/app/src/main/res/layout/item_rss.xml
@@ -1,7 +1,26 @@
-
+ android:layout_height="wrap_content">
-
\ No newline at end of file
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_rss_article.xml b/app/src/main/res/layout/item_rss_article.xml
new file mode 100644
index 000000000..293ae0ce3
--- /dev/null
+++ b/app/src/main/res/layout/item_rss_article.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_rss_source.xml b/app/src/main/res/layout/item_rss_source.xml
index 7d9513d98..d0ecd8788 100644
--- a/app/src/main/res/layout/item_rss_source.xml
+++ b/app/src/main/res/layout/item_rss_source.xml
@@ -9,7 +9,7 @@
android:padding="16dp">
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/main_bookshelf.xml b/app/src/main/res/menu/main_bookshelf.xml
index 19bbbdb7f..45b586ca8 100644
--- a/app/src/main/res/menu/main_bookshelf.xml
+++ b/app/src/main/res/menu/main_bookshelf.xml
@@ -26,6 +26,12 @@
android:title="@string/download_all"
app:showAsAction="never" />
+
+
-
+
-
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/pref_key_value.xml b/app/src/main/res/values/pref_key_value.xml
index dd706603a..091d276ad 100644
--- a/app/src/main/res/values/pref_key_value.xml
+++ b/app/src/main/res/values/pref_key_value.xml
@@ -19,7 +19,7 @@
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.2357.134 Safari/537.36
https://gedoor.github.io/MyBookshelf/sourcerule.html
- https://github.com/gedoor/MyBookshelf
+ https://github.com/gedoor/legado
https://gedoor.github.io/MyBookshelf/disclaimer.html
https://gedoor.github.io/MyBookshelf/
https://github.com/gedoor/MyBookshelf/releases/latest
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 72667cbab..52525e7cb 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -35,6 +35,7 @@
替换净化-搜索
书架
订阅
+ 全部
最近阅读
最后阅读
让阅读成为一种习惯。
@@ -149,9 +150,9 @@
新建订阅源
添加书籍
扫描
- 拷贝书源
- 粘贴书源
- 书源规则说明
+ 拷贝源
+ 粘贴源
+ 源规则说明
检查更新
扫描二维码
扫描本地图片
@@ -374,6 +375,23 @@
正文下一页URL规则(nextContentUrl)
+
+ 名称(sourceName)
+ url(sourceUrl)
+ 图标(sourceIcon)
+ 分组(sourceGroup)
+ 列表规则(ruleArticles)
+ 标题规则(ruleTitle)
+ 作者规则(ruleAuthor)
+ guid规则(ruleGuid)
+ 时间规则(rulePubDate)
+ 类别规则(ruleCategories)
+ 描述规则(ruleDescription)
+ 图片url规则(ruleImage)
+ 内容规则(ruleContent)
+ 链接规则(ruleLink)
+
+
没有书源
书籍信息获取失败
@@ -390,7 +408,7 @@
header
- 调试书源
+ 调试源
二维码导入
扫描二维码
选中时点击可弹出菜单
@@ -508,5 +526,6 @@
编辑订阅源
筛选
筛选发现
+ 当前位置:
diff --git a/build.gradle b/build.gradle
index 3c95124c7..bfcb4cc7a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -9,7 +9,7 @@ buildscript {
maven { url 'https://plugins.gradle.org/m2/' }
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.5.0'
+ classpath 'com.android.tools.build:gradle:3.5.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'de.timfreiheit.resourceplaceholders:placeholders:0.3'
}