Merge pull request #7 from gedoor/master

merge
pull/441/head
口口吕 4 years ago committed by GitHub
commit e8afb4a818
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      app/build.gradle
  2. 5
      app/src/main/assets/updateLog.md
  3. 11
      app/src/main/java/io/legado/app/constant/AppConst.kt
  4. 1
      app/src/main/java/io/legado/app/constant/AppPattern.kt
  5. 56
      app/src/main/java/io/legado/app/data/AppDatabase.kt
  6. 16
      app/src/main/java/io/legado/app/data/dao/BookDao.kt
  7. 16
      app/src/main/java/io/legado/app/data/dao/BookGroupDao.kt
  8. 2
      app/src/main/java/io/legado/app/data/dao/ReadRecordDao.kt
  9. 20
      app/src/main/java/io/legado/app/data/entities/BookGroup.kt
  10. 24
      app/src/main/java/io/legado/app/help/AppConfig.kt
  11. 123
      app/src/main/java/io/legado/app/help/BookHelp.kt
  12. 1
      app/src/main/java/io/legado/app/help/storage/Restore.kt
  13. 2
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt
  14. 5
      app/src/main/java/io/legado/app/model/rss/Result.kt
  15. 4
      app/src/main/java/io/legado/app/model/rss/Rss.kt
  16. 11
      app/src/main/java/io/legado/app/model/rss/RssParserByRule.kt
  17. 6
      app/src/main/java/io/legado/app/model/rss/RssParserDefault.kt
  18. 5
      app/src/main/java/io/legado/app/model/rss/RssResult.kt
  19. 17
      app/src/main/java/io/legado/app/ui/audio/AudioPlayViewModel.kt
  20. 31
      app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt
  21. 4
      app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookAdapter.kt
  22. 18
      app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt
  23. 73
      app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt
  24. 12
      app/src/main/java/io/legado/app/ui/book/group/GroupSelectDialog.kt
  25. 33
      app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt
  26. 17
      app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
  27. 2
      app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt
  28. 5
      app/src/main/java/io/legado/app/ui/book/read/config/TocRegexDialog.kt
  29. 3
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
  30. 4
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceAdapter.kt
  31. 1
      app/src/main/java/io/legado/app/ui/filechooser/utils/ConvertUtils.kt
  32. 53
      app/src/main/java/io/legado/app/ui/main/MainActivity.kt
  33. 128
      app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt
  34. 20
      app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt
  35. 14
      app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt
  36. 7
      app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt
  37. 1
      app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt
  38. 3
      app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt
  39. 2
      app/src/main/java/io/legado/app/ui/replace/ReplaceRuleAdapter.kt
  40. 3
      app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt
  41. 2
      app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceAdapter.kt
  42. 2
      app/src/main/java/io/legado/app/ui/widget/LabelsBar.kt
  43. 2
      app/src/main/java/io/legado/app/ui/widget/SearchView.kt
  44. 1
      app/src/main/java/io/legado/app/ui/widget/SelectActionBar.kt
  45. 1
      app/src/main/java/io/legado/app/ui/widget/ShadowLayout.kt
  46. 1
      app/src/main/java/io/legado/app/ui/widget/TitleBar.kt
  47. 1
      app/src/main/java/io/legado/app/ui/widget/anima/RefreshProgressBar.kt
  48. 1
      app/src/main/java/io/legado/app/ui/widget/anima/RotateLoading.kt
  49. 31
      app/src/main/java/io/legado/app/ui/widget/anima/explosion_field/ExplosionAnimator.kt
  50. 5
      app/src/main/java/io/legado/app/ui/widget/anima/explosion_field/ExplosionView.kt
  51. 1
      app/src/main/java/io/legado/app/ui/widget/dynamiclayout/DynamicFrameLayout.kt
  52. 1
      app/src/main/java/io/legado/app/ui/widget/image/CircleImageView.kt
  53. 6
      app/src/main/java/io/legado/app/ui/widget/image/CoverImageView.kt
  54. 6
      app/src/main/java/io/legado/app/ui/widget/image/PhotoView.kt
  55. 1
      app/src/main/java/io/legado/app/ui/widget/image/photo/Info.kt
  56. 20
      app/src/main/java/io/legado/app/ui/widget/image/photo/RotateGestureDetector.kt
  57. 2
      app/src/main/java/io/legado/app/ui/widget/number/NumberPickerDialog.kt
  58. 1
      app/src/main/java/io/legado/app/ui/widget/prefs/ColorPreference.kt
  59. 1
      app/src/main/java/io/legado/app/ui/widget/recycler/DividerNoLast.kt
  60. 2
      app/src/main/java/io/legado/app/ui/widget/recycler/DragSelectTouchHelper.kt
  61. 19
      app/src/main/java/io/legado/app/ui/widget/recycler/ItemTouchCallback.kt
  62. 1
      app/src/main/java/io/legado/app/ui/widget/recycler/LoadMoreView.kt
  63. 1
      app/src/main/java/io/legado/app/ui/widget/recycler/UpLinearLayoutManager.kt
  64. 1
      app/src/main/java/io/legado/app/ui/widget/recycler/scroller/FastScrollRecyclerView.kt
  65. 2
      app/src/main/java/io/legado/app/ui/widget/recycler/scroller/FastScroller.kt
  66. 1
      app/src/main/java/io/legado/app/ui/widget/seekbar/VerticalSeekBar.kt
  67. 3
      app/src/main/java/io/legado/app/utils/ACache.kt
  68. 2
      app/src/main/java/io/legado/app/utils/BitmapUtils.kt
  69. 2
      app/src/main/java/io/legado/app/utils/ColorUtils.kt
  70. 2
      app/src/main/java/io/legado/app/utils/ConstraintUtil.kt
  71. 1
      app/src/main/java/io/legado/app/utils/DocumentUtils.kt
  72. 2
      app/src/main/java/io/legado/app/utils/EventBusExtensions.kt
  73. 13
      app/src/main/java/io/legado/app/utils/FileUtils.kt
  74. 3
      app/src/main/java/io/legado/app/utils/FloatExtensions.kt
  75. 26
      app/src/main/java/io/legado/app/utils/FragmentExtensions.kt
  76. 31
      app/src/main/java/io/legado/app/utils/JsoupExtensions.kt
  77. 8
      app/src/main/java/io/legado/app/utils/LanguageUtils.kt
  78. 2
      app/src/main/java/io/legado/app/utils/NetworkUtils.kt
  79. 2
      app/src/main/java/io/legado/app/utils/StringUtils.kt
  80. 8
      app/src/main/java/io/legado/app/utils/ViewExtensions.kt
  81. 13
      app/src/main/res/layout/activity_main.xml
  82. 1
      app/src/main/res/layout/fragment_books.xml
  83. 11
      app/src/main/res/layout/fragment_bookshelf.xml
  84. 1
      app/src/main/res/layout/fragment_explore.xml
  85. 1
      app/src/main/res/layout/fragment_rss.xml
  86. 90
      app/src/main/res/layout/item_bookshelf_grid.xml
  87. 6
      app/src/main/res/layout/item_group_manage.xml
  88. 1
      app/src/main/res/layout/item_rss.xml
  89. 16
      app/src/main/res/menu/arrange_book.xml
  90. 12
      app/src/main/res/menu/book_cache.xml
  91. 32
      app/src/main/res/menu/book_group_manage.xml
  92. 6
      app/src/main/res/values-zh-rTW/strings.xml

@ -193,6 +193,9 @@ dependencies {
implementation 'org.apache.commons:commons-lang3:3.11'
implementation 'org.apache.commons:commons-text:1.8'
//
implementation 'net.ricecode:string-similarity:1.0.0'
//MarkDown
implementation 'ru.noties.markwon:core:3.1.0'
@ -205,8 +208,6 @@ dependencies {
exclude group: 'xmlpull'
}
//E-Ink
//implementation 'fadeapp.widgets:scrollless-recyclerView:1.0.2'
}
apply plugin: 'com.google.gms.google-services'

@ -3,6 +3,11 @@
* 关注合作公众号 **[小说拾遗]()** 获取好看的小说。
* 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。
**2020/10/18**
* 优化分组管理,默认分组可以重命名了
* 修复书架空白的bug,是constraintlayout库新版本的bug
* 修复分组和崩溃bug
**2020/10/16**
* 修复排版导入背景失败bug
* 修改默认度逍遥per为5003,需要重新导入默认

@ -1,9 +1,6 @@
package io.legado.app.constant
import android.annotation.SuppressLint
import io.legado.app.App
import io.legado.app.R
import io.legado.app.data.entities.BookGroup
import java.text.SimpleDateFormat
import javax.script.ScriptEngine
import javax.script.ScriptEngineManager
@ -47,10 +44,10 @@ object AppConst {
)
}
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))
val bookGroupNone = BookGroup(-4, App.INSTANCE.getString(R.string.no_group))
const val bookGroupAllId = -1L
const val bookGroupLocalId = -2L
const val bookGroupAudioId = -3L
const val bookGroupNoneId = -4L
const val notificationIdRead = 1144771
const val notificationIdAudio = 1144772

@ -2,6 +2,7 @@ package io.legado.app.constant
import java.util.regex.Pattern
@Suppress("RegExpRedundantEscape")
object AppPattern {
val JS_PATTERN: Pattern =
Pattern.compile("(<js>[\\w\\W]*?</js>|@js:[\\w\\W]*$)", Pattern.CASE_INSENSITIVE)

@ -7,8 +7,10 @@ import androidx.room.RoomDatabase
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import io.legado.app.App
import io.legado.app.constant.AppConst
import io.legado.app.data.dao.*
import io.legado.app.data.entities.*
import java.util.*
@Database(
@ -16,7 +18,7 @@ import io.legado.app.data.entities.*
ReplaceRule::class, SearchBook::class, SearchKeyword::class, Cookie::class,
RssSource::class, Bookmark::class, RssArticle::class, RssReadRecord::class,
RssStar::class, TxtTocRule::class, ReadRecord::class, HttpTTS::class],
version = 20,
version = 21,
exportSchema = true
)
abstract class AppDatabase: RoomDatabase() {
@ -37,12 +39,50 @@ abstract class AppDatabase: RoomDatabase() {
migration_15_17,
migration_17_18,
migration_18_19,
migration_19_20
migration_19_20,
migration_20_21
)
.allowMainThreadQueries()
.addCallback(dbCallback)
.build()
private val migration_10_11 = object: Migration(10, 11) {
private val dbCallback = object : Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
db.setLocale(Locale.CHINESE)
}
override fun onOpen(db: SupportSQLiteDatabase) {
db.setLocale(Locale.CHINESE)
db.execSQL(
"""
insert into book_groups(groupId, groupName, 'order', show) select ${AppConst.bookGroupAllId}, '全部', -10, 1
where not exists (select * from book_groups where groupId = ${AppConst.bookGroupAllId})
"""
)
db.execSQL(
"""
insert into book_groups(groupId, groupName, 'order', show) select ${AppConst.bookGroupLocalId}, '本地', -9, 1
where not exists (select * from book_groups where groupId = ${AppConst.bookGroupLocalId})
"""
)
db.execSQL(
"""
insert into book_groups(groupId, groupName, 'order', show) select ${AppConst.bookGroupAudioId}, '音频', -8, 1
where not exists (select * from book_groups where groupId = ${AppConst.bookGroupAudioId})
"""
)
db.execSQL(
"""
insert into book_groups(groupId, groupName, 'order', show) select ${AppConst.bookGroupNoneId}, '未分组', -7, 1
where not exists (select * from book_groups where groupId = ${AppConst.bookGroupNoneId})
"""
)
}
}
private val migration_10_11 = object : Migration(10, 11) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE txtTocRules")
database.execSQL(
@ -112,11 +152,17 @@ abstract class AppDatabase: RoomDatabase() {
database.execSQL("ALTER TABLE readRecordNew RENAME TO readRecord")
}
}
private val migration_19_20 = object: Migration(19, 20) {
private val migration_19_20 = object : Migration(19, 20) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE book_sources ADD bookSourceComment TEXT")
}
}
private val migration_20_21 = object : Migration(20, 21) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE book_groups ADD show INTEGER NOT NULL DEFAULT 1")
}
}
}
abstract fun bookDao(): BookDao

@ -18,18 +18,22 @@ interface BookDao {
@Query("SELECT * FROM books WHERE origin = '${BookType.local}'")
fun observeLocal(): LiveData<List<Book>>
@Query(
"""
select * from books where ((SELECT sum(groupId) FROM book_groups where groupId > 0) & `group`) = 0 and type != ${BookType.audio} and origin != '${BookType.local}'
"""
)
fun observeNoGroup(): LiveData<List<Book>>
@Query("select count(bookUrl) from books where (SELECT sum(groupId) FROM book_groups where groupId > 0) & `group` = 0")
fun observeNoGroupSize(): LiveData<Int>
@Query("SELECT bookUrl FROM books WHERE origin = '${BookType.local}'")
fun observeLocalUri(): LiveData<List<String>>
@Query("SELECT * FROM books WHERE (`group` & :group) > 0")
fun observeByGroup(group: Long): LiveData<List<Book>>
@Query("select * from books where (SELECT sum(groupId) FROM book_groups) & `group` = 0")
fun observeNoGroup(): LiveData<List<Book>>
@Query("select count(bookUrl) from books where (SELECT sum(groupId) FROM book_groups) & `group` = 0")
fun observeNoGroupSize(): LiveData<Int>
@Query("SELECT * FROM books WHERE name like '%'||:key||'%' or author like '%'||:key||'%'")
fun liveDataSearch(key: String): LiveData<List<Book>>

@ -16,15 +16,27 @@ interface BookGroupDao {
@Query("SELECT * FROM book_groups ORDER BY `order`")
fun liveDataAll(): LiveData<List<BookGroup>>
@get:Query("SELECT sum(groupId) FROM book_groups")
@Query("SELECT * FROM book_groups where show > 0 or groupId >= 0 ORDER BY `order`")
fun liveDataShow(): LiveData<List<BookGroup>>
@Query("SELECT * FROM book_groups where groupId >= 0 ORDER BY `order`")
fun liveDataSelect(): LiveData<List<BookGroup>>
@get:Query("SELECT sum(groupId) FROM book_groups where groupId >= 0")
val idsSum: Long
@get:Query("SELECT MAX(`order`) FROM book_groups")
@get:Query("SELECT MAX(`order`) FROM book_groups where groupId >= 0")
val maxOrder: Int
@get:Query("SELECT * FROM book_groups ORDER BY `order`")
val all: List<BookGroup>
@Query("update book_groups set show = 1 where groupId = :groupId")
fun enableGroup(groupId: Long)
@Query("select groupName from book_groups where groupId > 0 and (groupId & :id) > 0")
fun getGroupNames(id: Long): List<String>
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(vararg bookGroup: BookGroup)

@ -10,7 +10,7 @@ interface ReadRecordDao {
@get:Query("select * from readRecord")
val all: List<ReadRecord>
@get:Query("select bookName, sum(readTime) as readTime from readRecord group by bookName order by bookName")
@get:Query("select bookName, sum(readTime) as readTime from readRecord group by bookName order by bookName collate localized")
val allShow: List<ReadRecordShow>
@get:Query("select sum(readTime) from readRecord")

@ -1,8 +1,11 @@
package io.legado.app.data.entities
import android.content.Context
import android.os.Parcelable
import androidx.room.Entity
import androidx.room.PrimaryKey
import io.legado.app.R
import io.legado.app.constant.AppConst
import kotlinx.android.parcel.Parcelize
@Parcelize
@ -11,5 +14,18 @@ data class BookGroup(
@PrimaryKey
val groupId: Long = 0b1,
var groupName: String,
var order: Int = 0
) : Parcelable
var order: Int = 0,
var show: Boolean = true
) : Parcelable {
fun getManageName(context: Context): String {
return when (groupId) {
AppConst.bookGroupAllId -> "$groupName(${context.getString(R.string.all)})"
AppConst.bookGroupAudioId -> "$groupName(${context.getString(R.string.audio)})"
AppConst.bookGroupLocalId -> "$groupName(${context.getString(R.string.local)})"
AppConst.bookGroupNoneId -> "$groupName(${context.getString(R.string.no_group)})"
else -> groupName
}
}
}

@ -97,30 +97,6 @@ object AppConfig {
App.INSTANCE.putPrefInt(PreferKey.systemTypefaces, value)
}
var bookGroupAllShow: Boolean
get() = App.INSTANCE.getPrefBoolean("bookGroupAll", true)
set(value) {
App.INSTANCE.putPrefBoolean("bookGroupAll", value)
}
var bookGroupLocalShow: Boolean
get() = App.INSTANCE.getPrefBoolean("bookGroupLocal", false)
set(value) {
App.INSTANCE.putPrefBoolean("bookGroupLocal", value)
}
var bookGroupAudioShow: Boolean
get() = App.INSTANCE.getPrefBoolean("bookGroupAudio", false)
set(value) {
App.INSTANCE.putPrefBoolean("bookGroupAudio", value)
}
var bookGroupNoneShow: Boolean
get() = App.INSTANCE.getPrefBoolean("bookGroupNone", false)
set(value) {
App.INSTANCE.putPrefBoolean("bookGroupNone", value)
}
var elevation: Int
@SuppressLint("PrivateResource")
get() = App.INSTANCE.getPrefInt(

@ -15,10 +15,15 @@ import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import org.apache.commons.text.similarity.JaccardSimilarity
import net.ricecode.similarity.JaroWinklerStrategy
import net.ricecode.similarity.StringSimilarityServiceImpl
import org.jetbrains.anko.toast
import java.io.File
import java.util.concurrent.CopyOnWriteArraySet
import java.util.regex.Matcher
import java.util.regex.Pattern
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
object BookHelp {
@ -202,54 +207,88 @@ object BookHelp {
}
/**
* 找到相似度最高的章节
* 根据目录名获取当前章节
*/
fun getDurChapterIndexByChapterTitle(
title: String?,
index: Int,
chapters: List<BookChapter>,
fun getDurChapter(
oldDurChapterIndex: Int,
oldChapterListSize: Int,
oldDurChapterName: String?,
newChapterList: List<BookChapter>
): Int {
if (title.isNullOrEmpty()) {
return min(index, chapters.lastIndex)
}
if (chapters.size > index && title == chapters[index].title) {
return index
}
if (oldChapterListSize == 0) return 0
val oldChapterNum = getChapterNum(oldDurChapterName)
val oldName = getPureChapterName(oldDurChapterName)
val newChapterSize = newChapterList.size
val min = max(
0,
min(
oldDurChapterIndex,
oldDurChapterIndex - oldChapterListSize + newChapterSize
) - 10
)
val max = min(
newChapterSize - 1,
max(
oldDurChapterIndex,
oldDurChapterIndex - oldChapterListSize + newChapterSize
) + 10
)
var nameSim = 0.0
var newIndex = 0
val jSimilarity = JaccardSimilarity()
var similarity = if (chapters.size > index) {
jSimilarity.apply(title, chapters[index].title)
} else 0.0
if (similarity == 1.0) {
return index
} else {
for (i in 1..50) {
if (index - i in chapters.indices) {
jSimilarity.apply(title, chapters[index - i].title).let {
if (it > similarity) {
similarity = it
newIndex = index - i
if (similarity == 1.0) {
return newIndex
}
}
}
var newNum = 0
if (oldName.isNotEmpty()) {
val service = StringSimilarityServiceImpl(JaroWinklerStrategy())
for (i in min..max) {
val newName = getPureChapterName(newChapterList[i].title)
val temp = service.score(oldName, newName)
if (temp > nameSim) {
nameSim = temp
newIndex = i
}
if (index + i in chapters.indices) {
jSimilarity.apply(title, chapters[index + i].title).let {
if (it > similarity) {
similarity = it
newIndex = index + i
if (similarity == 1.0) {
return newIndex
}
}
}
}
}
if (nameSim < 0.96 && oldChapterNum > 0) {
for (i in min..max) {
val temp = getChapterNum(newChapterList[i].title)
if (temp == oldChapterNum) {
newNum = temp
newIndex = i
break
} else if (abs(temp - oldChapterNum) < abs(newNum - oldChapterNum)) {
newNum = temp
newIndex = i
}
}
}
return newIndex
return if (nameSim > 0.96 || abs(newNum - oldChapterNum) < 1) {
newIndex
} else {
min(max(0, newChapterList.size - 1), oldDurChapterIndex)
}
}
private val chapterNamePattern =
Pattern.compile("^(.*?第([\\d零〇一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟0-9\\s]+)[章节篇回集])[、,。 ::.\\s]*")
private fun getChapterNum(chapterName: String?): Int {
if (chapterName != null) {
val matcher: Matcher = chapterNamePattern.matcher(chapterName)
if (matcher.find()) {
return StringUtils.stringToInt(matcher.group(2))
}
}
return -1
}
private fun getPureChapterName(chapterName: String?): String {
// 所有非字母数字中日韩文字 CJK区+扩展A-F区
return if (chapterName == null) "" else StringUtils.fullToHalf(chapterName)
.replace("\\s".toRegex(), "")
.replace("^第.*?章|[(\\[][^()\\[\\]]{2,}[)\\]]$".toRegex(), "")
.replace(
"[^\\w\\u4E00-\\u9FEF〇\\u3400-\\u4DBF\\u20000-\\u2A6DF\\u2A700-\\u2EBEF]".toRegex(),
""
)
}
private var bookName: String? = null

@ -228,7 +228,6 @@ object Restore {
LanguageUtils.setConfiguration(App.INSTANCE)
App.INSTANCE.applyDayNight()
postEvent(EventBus.SHOW_RSS, "")
postEvent(EventBus.RECREATE, "")
}
}

@ -20,7 +20,7 @@ import kotlin.collections.HashMap
* 统一解析接口
*/
@Keep
@Suppress("unused")
@Suppress("unused", "RegExpRedundantEscape")
class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
var chapter: BookChapter? = null
private var content: Any? = null

@ -1,5 +0,0 @@
package io.legado.app.model.rss
import io.legado.app.data.entities.RssArticle
data class Result(val articles: MutableList<RssArticle>, val nextPageUrl: String?)

@ -5,8 +5,6 @@ import io.legado.app.data.entities.RssSource
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.analyzeRule.AnalyzeRule
import io.legado.app.model.analyzeRule.AnalyzeUrl
import io.legado.app.model.rss.Result
import io.legado.app.model.rss.RssParserByRule
import io.legado.app.utils.NetworkUtils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -21,7 +19,7 @@ object Rss {
page: Int,
scope: CoroutineScope = Coroutine.DEFAULT,
context: CoroutineContext = Dispatchers.IO
): Coroutine<Result> {
): Coroutine<RssResult> {
return Coroutine.async(scope, context) {
val analyzeUrl = AnalyzeUrl(
sortUrl,

@ -14,7 +14,12 @@ import java.util.*
object RssParserByRule {
@Throws(Exception::class)
fun parseXML(sortName: String, sortUrl: String, body: String?, rssSource: RssSource): Result {
fun parseXML(
sortName: String,
sortUrl: String,
body: String?,
rssSource: RssSource
): RssResult {
val sourceUrl = rssSource.sourceUrl
var nextUrl: String? = null
if (body.isNullOrBlank()) {
@ -29,7 +34,7 @@ object RssParserByRule {
var ruleArticles = rssSource.ruleArticles
if (ruleArticles.isNullOrBlank()) {
Debug.log(sourceUrl, "⇒列表规则为空, 使用默认规则解析")
return RssParser.parseXML(sortName, body, sourceUrl)
return RssParserDefault.parseXML(sortName, body, sourceUrl)
} else {
val articleList = mutableListOf<RssArticle>()
val analyzeRule = AnalyzeRule()
@ -72,7 +77,7 @@ object RssParserByRule {
if (reverse) {
articleList.reverse()
}
return Result(articleList, nextUrl)
return RssResult(articleList, nextUrl)
}
}

@ -8,10 +8,10 @@ import org.xmlpull.v1.XmlPullParserFactory
import java.io.IOException
import java.io.StringReader
object RssParser {
object RssParserDefault {
@Throws(XmlPullParserException::class, IOException::class)
fun parseXML(sortName: String, xml: String, sourceUrl: String): Result {
fun parseXML(sortName: String, xml: String, sourceUrl: String): RssResult {
val articleList = mutableListOf<RssArticle>()
var currentArticle = RssArticle()
@ -105,7 +105,7 @@ object RssParser {
Debug.log(sourceUrl, "┌获取文章链接")
Debug.log(sourceUrl, "${it.link}")
}
return Result(articleList, null)
return RssResult(articleList, null)
}
/**

@ -0,0 +1,5 @@
package io.legado.app.model.rss
import io.legado.app.data.entities.RssArticle
data class RssResult(val articles: MutableList<RssArticle>, val nextPageUrl: String?)

@ -89,7 +89,9 @@ class AudioPlayViewModel(application: Application) : BaseViewModel(application)
fun changeTo(book1: Book) {
execute {
var oldTocSize: Int = book1.totalChapterNum
AudioPlay.book?.let {
oldTocSize = it.totalChapterNum
book1.order = it.order
App.db.bookDao().delete(it)
}
@ -99,18 +101,23 @@ class AudioPlayViewModel(application: Application) : BaseViewModel(application)
AudioPlay.webBook = WebBook(it)
}
if (book1.tocUrl.isEmpty()) {
loadBookInfo(book1) { upChangeDurChapterIndex(book1, it) }
loadBookInfo(book1) { upChangeDurChapterIndex(book1, oldTocSize, it) }
} else {
loadChapterList(book1) { upChangeDurChapterIndex(book1, it) }
loadChapterList(book1) { upChangeDurChapterIndex(book1, oldTocSize, it) }
}
}
}
private fun upChangeDurChapterIndex(book: Book, chapters: List<BookChapter>) {
private fun upChangeDurChapterIndex(
book: Book,
oldTocSize: Int,
chapters: List<BookChapter>
) {
execute {
AudioPlay.durChapterIndex = BookHelp.getDurChapterIndexByChapterTitle(
book.durChapterTitle,
AudioPlay.durChapterIndex = BookHelp.getDurChapter(
book.durChapterIndex,
oldTocSize,
book.durChapterTitle,
chapters
)
book.durChapterIndex = AudioPlay.durChapterIndex

@ -69,8 +69,7 @@ class ArrangeBookActivity : VMBaseActivity<ArrangeBookViewModel>(R.layout.activi
recycler_view.addItemDecoration(VerticalDivider(this))
adapter = ArrangeBookAdapter(this, this)
recycler_view.adapter = adapter
val itemTouchCallback = ItemTouchCallback()
itemTouchCallback.onItemTouchCallbackListener = adapter
val itemTouchCallback = ItemTouchCallback(adapter)
itemTouchCallback.isCanDrag = getPrefInt(PreferKey.bookshelfSort) == 3
val dragSelectTouchHelper: DragSelectTouchHelper =
DragSelectTouchHelper(adapter.initDragSelectTouchHelperCallback()).setSlideArea(16, 50)
@ -112,10 +111,10 @@ class ArrangeBookActivity : VMBaseActivity<ArrangeBookViewModel>(R.layout.activi
booksLiveData?.removeObservers(this)
booksLiveData =
when (groupId) {
AppConst.bookGroupAll.groupId -> App.db.bookDao().observeAll()
AppConst.bookGroupLocal.groupId -> App.db.bookDao().observeLocal()
AppConst.bookGroupAudio.groupId -> App.db.bookDao().observeAudio()
AppConst.bookGroupNone.groupId -> App.db.bookDao().observeNoGroup()
AppConst.bookGroupAllId -> App.db.bookDao().observeAll()
AppConst.bookGroupLocalId -> App.db.bookDao().observeLocal()
AppConst.bookGroupAudioId -> App.db.bookDao().observeAudio()
AppConst.bookGroupNoneId -> App.db.bookDao().observeNoGroup()
else -> App.db.bookDao().observeByGroup(groupId)
}
booksLiveData?.observe(this, { list ->
@ -134,26 +133,6 @@ class ArrangeBookActivity : VMBaseActivity<ArrangeBookViewModel>(R.layout.activi
when (item.itemId) {
R.id.menu_group_manage -> GroupManageDialog()
.show(supportFragmentManager, "groupManage")
R.id.menu_no_group -> {
title_bar.subtitle = getString(R.string.no_group)
groupId = AppConst.bookGroupNone.groupId
initBookData()
}
R.id.menu_all -> {
title_bar.subtitle = item.title
groupId = AppConst.bookGroupAll.groupId
initBookData()
}
R.id.menu_local -> {
title_bar.subtitle = item.title
groupId = AppConst.bookGroupLocal.groupId
initBookData()
}
R.id.menu_audio -> {
title_bar.subtitle = item.title
groupId = AppConst.bookGroupAudio.groupId
initBookData()
}
else -> if (item.groupId == R.id.menu_group) {
title_bar.subtitle = item.title
groupId = App.db.bookGroupDao().getByName(item.title.toString())?.groupId ?: 0

@ -19,7 +19,7 @@ import java.util.*
class ArrangeBookAdapter(context: Context, val callBack: CallBack) :
SimpleRecyclerAdapter<Book>(context, R.layout.item_arrange_book),
ItemTouchCallback.OnItemTouchCallbackListener {
ItemTouchCallback.Callback {
val groupRequestCode = 12
private val selectedBooks: HashSet<Book> = hashSetOf()
var actionItem: Book? = null
@ -112,7 +112,7 @@ class ArrangeBookAdapter(context: Context, val callBack: CallBack) :
private fun getGroupList(groupId: Long): List<String> {
val groupNames = arrayListOf<String>()
callBack.groupList.forEach {
if (it.groupId and groupId > 0) {
if (it.groupId > 0 && it.groupId and groupId > 0) {
groupNames.add(it.groupName)
}
}

@ -57,7 +57,7 @@ class CacheActivity : VMBaseActivity<CacheViewModel>(R.layout.activity_download)
}
override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.download, menu)
menuInflater.inflate(R.menu.book_cache, menu)
return super.onCompatCreateOptionsMenu(menu)
}
@ -95,16 +95,6 @@ class CacheActivity : VMBaseActivity<CacheViewModel>(R.layout.activity_download)
R.id.menu_log -> {
TextListDialog.show(supportFragmentManager, getString(R.string.log), CacheBook.logs)
}
R.id.menu_no_group -> {
title_bar.subtitle = getString(R.string.no_group)
groupId = AppConst.bookGroupNone.groupId
initBookData()
}
R.id.menu_all -> {
title_bar.subtitle = item.title
groupId = AppConst.bookGroupAll.groupId
initBookData()
}
else -> if (item.groupId == R.id.menu_group) {
title_bar.subtitle = item.title
groupId = App.db.bookGroupDao().getByName(item.title.toString())?.groupId ?: 0
@ -123,8 +113,10 @@ class CacheActivity : VMBaseActivity<CacheViewModel>(R.layout.activity_download)
private fun initBookData() {
booksLiveData?.removeObservers(this)
booksLiveData = when (groupId) {
AppConst.bookGroupAll.groupId -> App.db.bookDao().observeAll()
AppConst.bookGroupNone.groupId -> App.db.bookDao().observeNoGroup()
AppConst.bookGroupAllId -> App.db.bookDao().observeAll()
AppConst.bookGroupLocalId -> App.db.bookDao().observeLocal()
AppConst.bookGroupAudioId -> App.db.bookDao().observeAudio()
AppConst.bookGroupNoneId -> App.db.bookDao().observeNoGroup()
else -> App.db.bookDao().observeByGroup(groupId)
}
booksLiveData?.observe(this, { list ->

@ -10,6 +10,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import androidx.appcompat.widget.Toolbar
import androidx.core.view.isGone
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
@ -20,7 +21,6 @@ import io.legado.app.base.BaseDialogFragment
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.AppConfig
import io.legado.app.lib.dialogs.*
import io.legado.app.lib.theme.accentColor
import io.legado.app.lib.theme.backgroundColor
@ -41,7 +41,6 @@ import kotlin.collections.ArrayList
class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
private lateinit var viewModel: GroupViewModel
private lateinit var adapter: GroupAdapter
private val callBack: CallBack? get() = parentFragment as? CallBack
override fun onStart() {
super.onStart()
@ -62,67 +61,41 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
tool_bar.setBackgroundColor(primaryColor)
tool_bar.title = getString(R.string.group_manage)
initView()
initData()
initMenu()
}
private fun initData() {
private fun initView() {
adapter = GroupAdapter(requireContext())
recycler_view.layoutManager = LinearLayoutManager(requireContext())
recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
val itemTouchCallback = ItemTouchCallback(adapter)
itemTouchCallback.isCanDrag = true
ItemTouchHelper(itemTouchCallback).attachToRecyclerView(recycler_view)
tv_ok.setTextColor(requireContext().accentColor)
tv_ok.visible()
tv_ok.onClick { dismiss() }
}
private fun initData() {
App.db.bookGroupDao().liveDataAll().observe(viewLifecycleOwner, {
val diffResult =
DiffUtil.calculateDiff(GroupDiffCallBack(ArrayList(adapter.getItems()), it))
adapter.setItems(it, diffResult)
})
val itemTouchCallback = ItemTouchCallback()
itemTouchCallback.onItemTouchCallbackListener = adapter
itemTouchCallback.isCanDrag = true
ItemTouchHelper(itemTouchCallback).attachToRecyclerView(recycler_view)
}
private fun initMenu() {
tool_bar.setOnMenuItemClickListener(this)
tool_bar.inflateMenu(R.menu.book_group_manage)
tool_bar.menu.let {
it.applyTint(requireContext())
it.findItem(R.id.menu_group_all)
.isChecked = AppConfig.bookGroupAllShow
it.findItem(R.id.menu_group_local)
.isChecked = AppConfig.bookGroupLocalShow
it.findItem(R.id.menu_group_audio)
.isChecked = AppConfig.bookGroupAudioShow
it.findItem(R.id.menu_group_none)
.isChecked = AppConfig.bookGroupNoneShow
}
tool_bar.menu.applyTint(requireContext())
}
override fun onMenuItemClick(item: MenuItem?): Boolean {
when (item?.itemId) {
R.id.menu_add -> addGroup()
R.id.menu_group_all -> {
item.isChecked = !item.isChecked
AppConfig.bookGroupAllShow = item.isChecked
callBack?.upGroup()
}
R.id.menu_group_local -> {
item.isChecked = !item.isChecked
AppConfig.bookGroupLocalShow = item.isChecked
callBack?.upGroup()
}
R.id.menu_group_audio -> {
item.isChecked = !item.isChecked
AppConfig.bookGroupAudioShow = item.isChecked
callBack?.upGroup()
}
R.id.menu_group_none -> {
item.isChecked = !item.isChecked
AppConfig.bookGroupNoneShow = item.isChecked
}
}
return true
}
@ -134,7 +107,7 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
customView {
layoutInflater.inflate(R.layout.dialog_edit_text, null).apply {
editText = edit_view.apply {
hint = "分组名称"
setHint(R.string.group_name)
}
}
}
@ -156,7 +129,7 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
customView {
layoutInflater.inflate(R.layout.dialog_edit_text, null).apply {
editText = edit_view.apply {
hint = "分组名称"
setHint(R.string.group_name)
setText(bookGroup.groupName)
}
}
@ -191,27 +164,33 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return true
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
return oldItem.groupId == newItem.groupId
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
return oldItem.groupName == newItem.groupName
&& oldItem.show == newItem.show
}
}
private inner class GroupAdapter(context: Context) :
SimpleRecyclerAdapter<BookGroup>(context, R.layout.item_group_manage),
ItemTouchCallback.OnItemTouchCallbackListener {
ItemTouchCallback.Callback {
private var isMoved = false
override fun convert(holder: ItemViewHolder, item: BookGroup, payloads: MutableList<Any>) {
holder.itemView.apply {
setBackgroundColor(context.backgroundColor)
tv_group.text = item.groupName
tv_group.text = item.getManageName(context)
sw_show.isChecked = item.show
tv_del.isGone = item.groupId < 0
sw_show.isGone = item.groupId >= 0
}
}
@ -219,6 +198,13 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
holder.itemView.apply {
tv_edit.onClick { getItem(holder.layoutPosition)?.let { editGroup(it) } }
tv_del.onClick { getItem(holder.layoutPosition)?.let { deleteGroup(it) } }
sw_show.setOnCheckedChangeListener { buttonView, isChecked ->
if (buttonView.isPressed) {
getItem(holder.layoutPosition)?.let {
viewModel.upGroup(it.copy(show = isChecked))
}
}
}
}
}
@ -240,7 +226,4 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
}
}
interface CallBack {
fun upGroup()
}
}

@ -93,13 +93,11 @@ class GroupSelectDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
tool_bar.inflateMenu(R.menu.book_group_manage)
tool_bar.menu.applyTint(requireContext())
tool_bar.setOnMenuItemClickListener(this)
tool_bar.menu.setGroupVisible(R.id.menu_groups, false)
adapter = GroupAdapter(requireContext())
recycler_view.layoutManager = LinearLayoutManager(requireContext())
recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
val itemTouchCallback = ItemTouchCallback()
itemTouchCallback.onItemTouchCallbackListener = adapter
val itemTouchCallback = ItemTouchCallback(adapter)
itemTouchCallback.isCanDrag = true
ItemTouchHelper(itemTouchCallback).attachToRecyclerView(recycler_view)
tv_cancel.onClick { dismiss() }
@ -111,7 +109,7 @@ class GroupSelectDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
}
private fun initData() {
App.db.bookGroupDao().liveDataAll().observe(viewLifecycleOwner, {
App.db.bookGroupDao().liveDataSelect().observe(viewLifecycleOwner, {
adapter.setItems(it)
})
}
@ -130,7 +128,7 @@ class GroupSelectDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
customView {
layoutInflater.inflate(R.layout.dialog_edit_text, null).apply {
editText = edit_view.apply {
hint = "分组名称"
setHint(R.string.group_name)
}
}
}
@ -152,7 +150,7 @@ class GroupSelectDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
customView {
layoutInflater.inflate(R.layout.dialog_edit_text, null).apply {
editText = edit_view.apply {
hint = "分组名称"
setHint(R.string.group_name)
setText(bookGroup.groupName)
}
}
@ -166,7 +164,7 @@ class GroupSelectDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
private inner class GroupAdapter(context: Context) :
SimpleRecyclerAdapter<BookGroup>(context, R.layout.item_group_select),
ItemTouchCallback.OnItemTouchCallbackListener {
ItemTouchCallback.Callback {
private var isMoved: Boolean = false

@ -119,13 +119,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
fun loadGroup(groupId: Long, success: ((groupNames: String?) -> Unit)) {
execute {
val groupNames = arrayListOf<String>()
App.db.bookGroupDao().all.forEach {
if (groupId and it.groupId > 0) {
groupNames.add(it.groupName)
}
}
groupNames.joinToString(",")
App.db.bookGroupDao().getGroupNames(groupId).joinToString(",")
}.onSuccess {
success.invoke(it)
}
@ -133,23 +127,36 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
fun changeTo(newBook: Book) {
execute {
var oldTocSize: Int = newBook.totalChapterNum
if (inBookshelf) {
bookData.value?.changeTo(newBook)
bookData.value?.let {
oldTocSize = it.totalChapterNum
it.changeTo(newBook)
}
}
bookData.postValue(newBook)
if (newBook.tocUrl.isEmpty()) {
loadBookInfo(newBook, false) { upChangeDurChapterIndex(newBook, it) }
loadBookInfo(newBook, false) {
upChangeDurChapterIndex(newBook, oldTocSize, it)
}
} else {
loadChapter(newBook) { upChangeDurChapterIndex(newBook, it) }
loadChapter(newBook) {
upChangeDurChapterIndex(newBook, oldTocSize, it)
}
}
}
}
private fun upChangeDurChapterIndex(book: Book, chapters: List<BookChapter>) {
private fun upChangeDurChapterIndex(
book: Book,
oldTocSize: Int,
chapters: List<BookChapter>
) {
execute {
book.durChapterIndex = BookHelp.getDurChapterIndexByChapterTitle(
book.durChapterTitle,
book.durChapterIndex = BookHelp.getDurChapter(
book.durChapterIndex,
oldTocSize,
book.durChapterTitle,
chapters
)
book.durChapterTitle = chapters[book.durChapterIndex].title

@ -157,8 +157,12 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
fun changeTo(newBook: Book) {
execute {
var oldTocSize: Int = newBook.totalChapterNum
ReadBook.upMsg(null)
ReadBook.book?.changeTo(newBook)
ReadBook.book?.let {
oldTocSize = it.totalChapterNum
it.changeTo(newBook)
}
ReadBook.book = newBook
App.db.bookSourceDao().getBookSource(newBook.origin)?.let {
ReadBook.webBook = WebBook(it)
@ -171,11 +175,11 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
}
if (newBook.tocUrl.isEmpty()) {
loadBookInfo(newBook) {
upChangeDurChapterIndex(newBook, it)
upChangeDurChapterIndex(newBook, oldTocSize, it)
}
} else {
loadChapterList(newBook) {
upChangeDurChapterIndex(newBook, it)
upChangeDurChapterIndex(newBook, oldTocSize, it)
}
}
}
@ -208,11 +212,12 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
}
}
private fun upChangeDurChapterIndex(book: Book, chapters: List<BookChapter>) {
private fun upChangeDurChapterIndex(book: Book, oldTocSize: Int, chapters: List<BookChapter>) {
execute {
ReadBook.durChapterIndex = BookHelp.getDurChapterIndexByChapterTitle(
book.durChapterTitle,
ReadBook.durChapterIndex = BookHelp.getDurChapter(
book.durChapterIndex,
oldTocSize,
book.durChapterTitle,
chapters
)
book.durChapterIndex = ReadBook.durChapterIndex

@ -11,7 +11,6 @@ import android.widget.SeekBar
import androidx.core.view.isVisible
import io.legado.app.App
import io.legado.app.R
import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.help.AppConfig
import io.legado.app.help.ReadBookConfig
@ -193,7 +192,6 @@ class ReadMenu : FrameLayout {
fabNightTheme.onClick {
AppConfig.isNightTheme = !AppConfig.isNightTheme
App.INSTANCE.applyDayNight()
postEvent(EventBus.RECREATE, "")
}
//上一章

@ -81,8 +81,7 @@ class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
recycler_view.layoutManager = LinearLayoutManager(requireContext())
recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
val itemTouchCallback = ItemTouchCallback()
itemTouchCallback.onItemTouchCallbackListener = adapter
val itemTouchCallback = ItemTouchCallback(adapter)
itemTouchCallback.isCanDrag = true
ItemTouchHelper(itemTouchCallback).attachToRecyclerView(recycler_view)
tv_cancel.onClick {
@ -199,7 +198,7 @@ class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
inner class TocRegexAdapter(context: Context) :
SimpleRecyclerAdapter<TxtTocRule>(context, R.layout.item_toc_regex),
ItemTouchCallback.OnItemTouchCallbackListener {
ItemTouchCallback.Callback {
override fun convert(holder: ItemViewHolder, item: TxtTocRule, payloads: MutableList<Any>) {
holder.itemView.apply {

@ -166,8 +166,7 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
recycler_view.addItemDecoration(VerticalDivider(this))
adapter = BookSourceAdapter(this, this)
recycler_view.adapter = adapter
val itemTouchCallback = ItemTouchCallback()
itemTouchCallback.onItemTouchCallbackListener = adapter
val itemTouchCallback = ItemTouchCallback(adapter)
itemTouchCallback.isCanDrag = true
val dragSelectTouchHelper: DragSelectTouchHelper =
DragSelectTouchHelper(adapter.initDragSelectTouchHelperCallback()).setSlideArea(16, 50)

@ -14,7 +14,7 @@ import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.data.entities.BookSource
import io.legado.app.lib.theme.backgroundColor
import io.legado.app.ui.widget.recycler.DragSelectTouchHelper
import io.legado.app.ui.widget.recycler.ItemTouchCallback.OnItemTouchCallbackListener
import io.legado.app.ui.widget.recycler.ItemTouchCallback.Callback
import io.legado.app.utils.invisible
import io.legado.app.utils.visible
import kotlinx.android.synthetic.main.item_book_source.view.*
@ -23,7 +23,7 @@ import java.util.*
class BookSourceAdapter(context: Context, val callBack: CallBack) :
SimpleRecyclerAdapter<BookSource>(context, R.layout.item_book_source),
OnItemTouchCallbackListener {
Callback {
private val selected = linkedSetOf<BookSource>()

@ -17,6 +17,7 @@ import java.text.DecimalFormat
* @author 李玉江[QQ:1023694760]
* @since 2014-4-18
*/
@Suppress("MemberVisibilityCanBePrivate")
object ConvertUtils {
const val GB: Long = 1073741824
const val MB: Long = 1048576

@ -178,48 +178,23 @@ class MainActivity : VMBaseActivity<MainViewModel>(R.layout.activity_main),
private inner class TabFragmentPageAdapter(fm: FragmentManager) :
FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
val bookshelfFragment: Fragment
get() {
if (!fragmentMap.containsKey(0)) {
fragmentMap[0] = BookshelfFragment()
}
return fragmentMap.getValue(0)
}
val exploreFragment: Fragment
get() {
if (!fragmentMap.containsKey(1)) {
fragmentMap[1] = ExploreFragment()
}
return fragmentMap.getValue(1)
}
val rssFragment: Fragment
get() {
if (!fragmentMap.containsKey(2)) {
fragmentMap[2] = RssFragment()
}
return fragmentMap.getValue(2)
}
val myFragment: Fragment
get() {
if (!fragmentMap.containsKey(3)) {
fragmentMap[3] = MyFragment()
}
return fragmentMap.getValue(3)
private fun getId(position: Int): Int {
return when (position) {
2 -> if (AppConfig.isShowRSS) 2 else 3
else -> position
}
}
override fun getItemPosition(`object`: Any): Int {
return POSITION_NONE
}
override fun getItem(position: Int): Fragment {
return when (position) {
0 -> bookshelfFragment
1 -> exploreFragment
2 -> if (AppConfig.isShowRSS) rssFragment else myFragment
else -> myFragment
return when (getId(position)) {
0 -> BookshelfFragment()
1 -> ExploreFragment()
2 -> RssFragment()
else -> MyFragment()
}
}
@ -229,13 +204,7 @@ class MainActivity : VMBaseActivity<MainViewModel>(R.layout.activity_main),
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val fragment = super.instantiateItem(container, position) as Fragment
val id = when (position) {
2 -> if (AppConfig.isShowRSS) 2 else 3
else -> position
}
if (!fragmentMap.containsKey(id)) {
fragmentMap[id] = fragment
}
fragmentMap[getId(position)] = fragment
return fragment
}

@ -15,7 +15,6 @@ import io.legado.app.base.VMBaseFragment
import io.legado.app.constant.AppConst
import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.BookGroup
import io.legado.app.help.AppConfig
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.noButton
@ -36,26 +35,22 @@ import kotlinx.android.synthetic.main.dialog_edit_text.view.*
import kotlinx.android.synthetic.main.fragment_bookshelf.*
import kotlinx.android.synthetic.main.view_tab_layout.*
import kotlinx.android.synthetic.main.view_title_bar.*
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.jetbrains.anko.startActivity
/**
* 书架界面
*/
class BookshelfFragment : VMBaseFragment<BookshelfViewModel>(R.layout.fragment_bookshelf),
TabLayout.OnTabSelectedListener,
SearchView.OnQueryTextListener,
GroupManageDialog.CallBack {
SearchView.OnQueryTextListener {
override val viewModel: BookshelfViewModel
get() = getViewModel(BookshelfViewModel::class.java)
private val activityViewModel: MainViewModel
get() = getViewModelOfActivity(MainViewModel::class.java)
private lateinit var adapter: FragmentStatePagerAdapter
private var bookGroupLiveData: LiveData<List<BookGroup>>? = null
private var noGroupLiveData: LiveData<Int>? = null
private val bookGroups = mutableListOf<BookGroup>()
private val fragmentMap = hashMapOf<Long, BooksFragment>()
private var showGroupNone = false
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
setSupportToolbar(toolbar)
@ -104,63 +99,21 @@ class BookshelfFragment : VMBaseFragment<BookshelfViewModel>(R.layout.fragment_b
tab_layout.setSelectedTabIndicatorColor(requireContext().accentColor)
tab_layout.setupWithViewPager(view_pager_bookshelf)
view_pager_bookshelf.offscreenPageLimit = 1
view_pager_bookshelf.adapter = TabFragmentPageAdapter(childFragmentManager)
adapter = TabFragmentPageAdapter(childFragmentManager)
view_pager_bookshelf.adapter = adapter
}
private fun initBookGroupData() {
bookGroupLiveData?.removeObservers(viewLifecycleOwner)
bookGroupLiveData = App.db.bookGroupDao().liveDataAll()
bookGroupLiveData = App.db.bookGroupDao().liveDataShow()
bookGroupLiveData?.observe(viewLifecycleOwner, {
viewModel.checkGroup(it)
launch {
synchronized(this) {
tab_layout.removeOnTabSelectedListener(this@BookshelfFragment)
}
var noGroupSize = 0
withContext(IO) {
if (AppConfig.bookGroupNoneShow) {
noGroupSize = App.db.bookDao().noGroupSize
}
}
synchronized(this@BookshelfFragment) {
bookGroups.clear()
if (AppConfig.bookGroupAllShow) {
bookGroups.add(AppConst.bookGroupAll)
}
if (AppConfig.bookGroupLocalShow) {
bookGroups.add(AppConst.bookGroupLocal)
}
if (AppConfig.bookGroupAudioShow) {
bookGroups.add(AppConst.bookGroupAudio)
}
showGroupNone = if (noGroupSize > 0 && it.isNotEmpty()) {
bookGroups.add(AppConst.bookGroupNone)
true
} else {
false
}
bookGroups.addAll(it)
view_pager_bookshelf.adapter?.notifyDataSetChanged()
tab_layout.getTabAt(getPrefInt(PreferKey.saveTabPosition, 0))?.select()
tab_layout.addOnTabSelectedListener(this@BookshelfFragment)
}
}
})
noGroupLiveData?.removeObservers(viewLifecycleOwner)
noGroupLiveData = App.db.bookDao().observeNoGroupSize()
noGroupLiveData?.observe(viewLifecycleOwner, {
if (it > 0 && !showGroupNone && AppConfig.bookGroupNoneShow) {
showGroupNone = true
upGroup()
} else if (it == 0 && showGroupNone) {
showGroupNone = false
upGroup()
}
upGroup(it)
})
}
override fun onQueryTextSubmit(query: String?): Boolean {
context?.startActivity<SearchActivity>(Pair("key", query))
startActivity<SearchActivity>(Pair("key", query))
return false
}
@ -168,40 +121,25 @@ class BookshelfFragment : VMBaseFragment<BookshelfViewModel>(R.layout.fragment_b
return false
}
override fun upGroup() {
launch {
var noGroupSize = 0
withContext(IO) {
if (AppConfig.bookGroupNoneShow) {
noGroupSize = App.db.bookDao().noGroupSize
}
}
synchronized(this@BookshelfFragment) {
bookGroups.remove(AppConst.bookGroupAll)
bookGroups.remove(AppConst.bookGroupLocal)
bookGroups.remove(AppConst.bookGroupAudio)
bookGroups.remove(AppConst.bookGroupNone)
showGroupNone =
if (noGroupSize > 0 && bookGroups.isNotEmpty()) {
bookGroups.add(0, AppConst.bookGroupNone)
true
} else {
false
}
if (AppConfig.bookGroupAudioShow) {
bookGroups.add(0, AppConst.bookGroupAudio)
}
if (AppConfig.bookGroupLocalShow) {
bookGroups.add(0, AppConst.bookGroupLocal)
}
if (AppConfig.bookGroupAllShow) {
bookGroups.add(0, AppConst.bookGroupAll)
}
view_pager_bookshelf.adapter?.notifyDataSetChanged()
}
@Synchronized
private fun upGroup(data: List<BookGroup>) {
if (data.isEmpty()) {
App.db.bookGroupDao().enableGroup(AppConst.bookGroupAllId)
} else {
bookGroups.clear()
bookGroups.addAll(data)
adapter.notifyDataSetChanged()
selectLastTab()
}
}
@Synchronized
private fun selectLastTab() {
tab_layout.removeOnTabSelectedListener(this)
tab_layout.getTabAt(getPrefInt(PreferKey.saveTabPosition, 0))?.select()
tab_layout.addOnTabSelectedListener(this)
}
@SuppressLint("InflateParams")
private fun configBookshelf() {
requireContext().alert(titleResource = R.string.bookshelf_layout) {
@ -252,14 +190,12 @@ class BookshelfFragment : VMBaseFragment<BookshelfViewModel>(R.layout.fragment_b
}.show().applyTint()
}
override fun onTabReselected(tab: TabLayout.Tab?) = Unit
override fun onTabReselected(tab: TabLayout.Tab) = Unit
override fun onTabUnselected(tab: TabLayout.Tab?) = Unit
override fun onTabUnselected(tab: TabLayout.Tab) = Unit
override fun onTabSelected(tab: TabLayout.Tab?) {
tab?.position?.let {
putPrefInt(PreferKey.saveTabPosition, it)
}
override fun onTabSelected(tab: TabLayout.Tab) {
putPrefInt(PreferKey.saveTabPosition, tab.position)
}
fun gotoTop() {
@ -294,9 +230,7 @@ class BookshelfFragment : VMBaseFragment<BookshelfViewModel>(R.layout.fragment_b
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val fragment = super.instantiateItem(container, position) as BooksFragment
val group = bookGroups[position]
if (!fragmentMap.containsKey(group.groupId)) {
fragmentMap[group.groupId] = fragment
}
fragmentMap[group.groupId] = fragment
return fragment
}

@ -63,18 +63,16 @@ class BookshelfViewModel(application: Application) : BaseViewModel(application)
}
fun checkGroup(groups: List<BookGroup>) {
execute {
groups.forEach { group ->
if (group.groupId and (group.groupId - 1) != 0L) {
var id = 1L
val idsSum = App.db.bookGroupDao().idsSum
while (id and idsSum != 0L) {
id = id.shl(1)
}
App.db.bookGroupDao().delete(group)
App.db.bookGroupDao().insert(group.copy(groupId = id))
App.db.bookDao().upGroup(group.groupId, id)
groups.forEach { group ->
if (group.groupId >= 0 && group.groupId and (group.groupId - 1) != 0L) {
var id = 1L
val idsSum = App.db.bookGroupDao().idsSum
while (id and idsSum != 0L) {
id = id.shl(1)
}
App.db.bookGroupDao().delete(group)
App.db.bookGroupDao().insert(group.copy(groupId = id))
App.db.bookDao().upGroup(group.groupId, id)
}
}
}

@ -29,7 +29,6 @@ import io.legado.app.utils.getViewModelOfActivity
import io.legado.app.utils.observeEvent
import io.legado.app.utils.startActivity
import kotlinx.android.synthetic.main.fragment_books.*
import org.jetbrains.anko.startActivity
import kotlin.math.max
/**
@ -61,7 +60,6 @@ class BooksFragment : BaseFragment(R.layout.fragment_books),
position = it.getInt("position", 0)
groupId = it.getLong("groupId", -1)
}
tv_empty_msg.setText(R.string.bookshelf_empty)
initRecyclerView()
upRecyclerData()
}
@ -104,10 +102,10 @@ class BooksFragment : BaseFragment(R.layout.fragment_books),
private fun upRecyclerData() {
bookshelfLiveData?.removeObservers(this)
bookshelfLiveData = when (groupId) {
AppConst.bookGroupAll.groupId -> App.db.bookDao().observeAll()
AppConst.bookGroupLocal.groupId -> App.db.bookDao().observeLocal()
AppConst.bookGroupAudio.groupId -> App.db.bookDao().observeAudio()
AppConst.bookGroupNone.groupId -> App.db.bookDao().observeNoGroup()
AppConst.bookGroupAllId -> App.db.bookDao().observeAll()
AppConst.bookGroupLocalId -> App.db.bookDao().observeLocal()
AppConst.bookGroupAudioId -> App.db.bookDao().observeAudio()
AppConst.bookGroupNoneId -> App.db.bookDao().observeNoGroup()
else -> App.db.bookDao().observeByGroup(groupId)
}
bookshelfLiveData?.observe(this, { list ->
@ -139,8 +137,8 @@ class BooksFragment : BaseFragment(R.layout.fragment_books),
override fun open(book: Book) {
when (book.type) {
BookType.audio ->
context?.startActivity<AudioPlayActivity>(Pair("bookUrl", book.bookUrl))
else -> context?.startActivity<ReadBookActivity>(
startActivity<AudioPlayActivity>(Pair("bookUrl", book.bookUrl))
else -> startActivity<ReadBookActivity>(
Pair("bookUrl", book.bookUrl),
Pair("key", IntentDataHelp.putData(book))
)

@ -24,7 +24,7 @@ import io.legado.app.ui.book.source.edit.BookSourceEditActivity
import io.legado.app.utils.getViewModel
import io.legado.app.utils.splitNotBlank
import io.legado.app.utils.startActivity
import kotlinx.android.synthetic.main.fragment_find_book.*
import kotlinx.android.synthetic.main.fragment_explore.*
import kotlinx.android.synthetic.main.view_search.*
import kotlinx.android.synthetic.main.view_title_bar.*
import java.text.Collator
@ -32,7 +32,7 @@ import java.text.Collator
/**
* 发现界面
*/
class ExploreFragment : VMBaseFragment<ExploreViewModel>(R.layout.fragment_find_book),
class ExploreFragment : VMBaseFragment<ExploreViewModel>(R.layout.fragment_explore),
ExploreAdapter.CallBack {
override val viewModel: ExploreViewModel
get() = getViewModel(ExploreViewModel::class.java)
@ -46,7 +46,6 @@ class ExploreFragment : VMBaseFragment<ExploreViewModel>(R.layout.fragment_find_
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
setSupportToolbar(toolbar)
tv_empty_msg.setText(R.string.explore_empty)
initSearchView()
initRecyclerView()
initGroupData()
@ -115,7 +114,7 @@ class ExploreFragment : VMBaseFragment<ExploreViewModel>(R.layout.fragment_find_
App.db.bookSourceDao().liveExplore("%$key%")
}
liveExplore?.observe(viewLifecycleOwner, {
tv_empty_msg.isGone = it.isNotEmpty()
tv_empty_msg.isGone = it.isNotEmpty() || search_view.query.isNotEmpty()
val diffResult = DiffUtil
.calculateDiff(ExploreDiffCallBack(ArrayList(adapter.getItems()), it))
adapter.setItems(it)

@ -34,7 +34,6 @@ class RssFragment : VMBaseFragment<RssSourceViewModel>(R.layout.fragment_rss),
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
setSupportToolbar(toolbar)
tv_empty_msg.setText(R.string.rss_source_empty)
initRecyclerView()
initData()
}

@ -87,8 +87,7 @@ class ReplaceRuleActivity :
adapter = ReplaceRuleAdapter(this, this)
recycler_view.adapter = adapter
recycler_view.addItemDecoration(VerticalDivider(this))
val itemTouchCallback = ItemTouchCallback()
itemTouchCallback.onItemTouchCallbackListener = adapter
val itemTouchCallback = ItemTouchCallback(adapter)
itemTouchCallback.isCanDrag = true
val dragSelectTouchHelper: DragSelectTouchHelper =
DragSelectTouchHelper(adapter.initDragSelectTouchHelperCallback()).setSlideArea(16, 50)

@ -20,7 +20,7 @@ import java.util.*
class ReplaceRuleAdapter(context: Context, var callBack: CallBack) :
SimpleRecyclerAdapter<ReplaceRule>(context, R.layout.item_replace_rule),
ItemTouchCallback.OnItemTouchCallbackListener {
ItemTouchCallback.Callback {
private val selected = linkedSetOf<ReplaceRule>()

@ -114,8 +114,7 @@ class RssSourceActivity : VMBaseActivity<RssSourceViewModel>(R.layout.activity_r
recycler_view.addItemDecoration(VerticalDivider(this))
adapter = RssSourceAdapter(this, this)
recycler_view.adapter = adapter
val itemTouchCallback = ItemTouchCallback()
itemTouchCallback.onItemTouchCallbackListener = adapter
val itemTouchCallback = ItemTouchCallback(adapter)
itemTouchCallback.isCanDrag = true
val dragSelectTouchHelper: DragSelectTouchHelper =
DragSelectTouchHelper(adapter.initDragSelectTouchHelperCallback()).setSlideArea(16, 50)

@ -19,7 +19,7 @@ import java.util.*
class RssSourceAdapter(context: Context, val callBack: CallBack) :
SimpleRecyclerAdapter<RssSource>(context, R.layout.item_rss_source),
ItemTouchCallback.OnItemTouchCallbackListener {
ItemTouchCallback.Callback {
private val selected = linkedSetOf<RssSource>()

@ -7,7 +7,7 @@ import android.widget.TextView
import io.legado.app.ui.widget.text.AccentBgTextView
import io.legado.app.utils.dp
@Suppress("unused")
@Suppress("unused", "MemberVisibilityCanBePrivate")
class LabelsBar(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) {
private val unUsedViews = arrayListOf<TextView>()

@ -1,5 +1,6 @@
package io.legado.app.ui.widget
import android.annotation.SuppressLint
import android.app.SearchableInfo
import android.content.Context
import android.graphics.Canvas
@ -30,6 +31,7 @@ class SearchView : SearchView {
defStyleAttr: Int
) : super(context, attrs, defStyleAttr)
@SuppressLint("UseCompatLoadingForDrawables")
override fun onLayout(
changed: Boolean,
left: Int,

@ -16,6 +16,7 @@ import io.legado.app.utils.visible
import kotlinx.android.synthetic.main.view_select_action_bar.view.*
import org.jetbrains.anko.sdk27.listeners.onClick
@Suppress("unused")
class SelectActionBar(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) {
private var callBack: CallBack? = null
private var selMenu: PopupMenu? = null

@ -17,6 +17,7 @@ import io.legado.app.utils.getCompatColor
*
* Created by lijiankun on 17/8/11.
*/
@Suppress("unused")
class ShadowLayout(
context: Context,
attrs: AttributeSet? = null

@ -21,6 +21,7 @@ import org.jetbrains.anko.backgroundColor
import org.jetbrains.anko.bottomPadding
import org.jetbrains.anko.topPadding
@Suppress("unused")
class TitleBar(context: Context, attrs: AttributeSet?) : AppBarLayout(context, attrs) {
val toolbar: Toolbar

@ -11,6 +11,7 @@ import android.view.View
import io.legado.app.R
@Suppress("unused", "MemberVisibilityCanBePrivate")
class RefreshProgressBar @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,

@ -17,6 +17,7 @@ import io.legado.app.utils.dp
* RotateLoading
* Created by Victor on 2015/4/28.
*/
@Suppress("MemberVisibilityCanBePrivate")
class RotateLoading : View {
private lateinit var mPaint: Paint

@ -20,6 +20,7 @@ import android.graphics.*
import android.view.View
import android.view.animation.AccelerateInterpolator
import java.util.*
import kotlin.math.pow
class ExplosionAnimator(private val mContainer: View, bitmap: Bitmap, bound: Rect) :
ValueAnimator() {
@ -99,20 +100,20 @@ class ExplosionAnimator(private val mContainer: View, bitmap: Bitmap, bound: Rec
}
private inner class Particle {
internal var alpha: Float = 0.toFloat()
internal var color: Int = 0
internal var cx: Float = 0.toFloat()
internal var cy: Float = 0.toFloat()
internal var radius: Float = 0.toFloat()
internal var baseCx: Float = 0.toFloat()
internal var baseCy: Float = 0.toFloat()
internal var baseRadius: Float = 0.toFloat()
internal var top: Float = 0.toFloat()
internal var bottom: Float = 0.toFloat()
internal var mag: Float = 0.toFloat()
internal var neg: Float = 0.toFloat()
internal var life: Float = 0.toFloat()
internal var overflow: Float = 0.toFloat()
var alpha: Float = 0.toFloat()
var color: Int = 0
var cx: Float = 0.toFloat()
var cy: Float = 0.toFloat()
var radius: Float = 0.toFloat()
var baseCx: Float = 0.toFloat()
var baseCy: Float = 0.toFloat()
var baseRadius: Float = 0.toFloat()
var top: Float = 0.toFloat()
var bottom: Float = 0.toFloat()
var mag: Float = 0.toFloat()
var neg: Float = 0.toFloat()
var life: Float = 0.toFloat()
var overflow: Float = 0.toFloat()
fun advance(factor: Float) {
@ -130,7 +131,7 @@ class ExplosionAnimator(private val mContainer: View, bitmap: Bitmap, bound: Rec
alpha = 1f - f
f = bottom * f2
cx = baseCx + f
cy = (baseCy - this.neg * Math.pow(f.toDouble(), 2.0)).toFloat() - f * mag
cy = (baseCy - this.neg * f.toDouble().pow(2.0)).toFloat() - f * mag
radius = V + (baseRadius - V) * f2
}
}

@ -29,6 +29,7 @@ import android.view.View
import java.util.*
@Suppress("unused")
class ExplosionView : View {
private var customDuration = ExplosionAnimator.DEFAULT_DURATION
@ -75,8 +76,8 @@ class ExplosionView : View {
this.customDuration = customDuration
}
fun addActionEvent(ievents: OnAnimatorListener) {
this.mZAnimatorListener = ievents
fun addActionEvent(iEvents: OnAnimatorListener) {
this.mZAnimatorListener = iEvents
}

@ -12,6 +12,7 @@ import androidx.appcompat.widget.AppCompatTextView
import io.legado.app.R
import kotlinx.android.synthetic.main.view_dynamic.view.*
@Suppress("unused")
class DynamicFrameLayout(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs), ViewSwitcher {
private var errorView: View? = null

@ -24,6 +24,7 @@ import io.legado.app.utils.sp
import kotlin.math.min
import kotlin.math.pow
@Suppress("unused", "MemberVisibilityCanBePrivate")
class CircleImageView(context: Context, attrs: AttributeSet) :
AppCompatImageView(
context,

@ -16,7 +16,10 @@ import io.legado.app.constant.PreferKey
import io.legado.app.help.ImageLoader
import io.legado.app.utils.getPrefString
/**
* 封面
*/
@Suppress("unused")
class CoverImageView : androidx.appcompat.widget.AppCompatImageView {
internal var width: Float = 0.toFloat()
internal var height: Float = 0.toFloat()
@ -171,6 +174,7 @@ class CoverImageView : androidx.appcompat.widget.AppCompatImageView {
upDefaultCover()
}
@SuppressLint("UseCompatLoadingForDrawables")
fun upDefaultCover() {
val path = App.INSTANCE.getPrefString(PreferKey.defaultCover)
var dw = Drawable.createFromPath(path)

@ -24,6 +24,7 @@ import kotlin.math.abs
import kotlin.math.roundToInt
@Suppress("UNUSED_PARAMETER", "unused", "MemberVisibilityCanBePrivate")
@SuppressLint("AppCompatCustomView")
class PhotoView : ImageView {
val MIN_ROTATE = 35
@ -197,6 +198,7 @@ class PhotoView : ImageView {
MAX_ANIM_FROM_WAITE = wait
}
@SuppressLint("UseCompatLoadingForDrawables")
override fun setImageResource(resId: Int) {
var drawable: Drawable? = null
try {
@ -832,7 +834,7 @@ class PhotoView : ImageView {
}
private inner class Transform internal constructor() : Runnable {
private inner class Transform : Runnable {
var isRunning = false
var mTranslateScroller: OverScroller
var mFlingScroller: OverScroller
@ -1230,7 +1232,7 @@ class PhotoView : ImageView {
val scale = if (scaleX > scaleY) scaleX else scaleY
mAnimMatrix.postRotate(mDegrees, mScaleCenter.x, mScaleCenter.y)
mAnimMatrix.mapRect(mImgRect, mBaseRect)
mDegrees = mDegrees % 360
mDegrees %= 360
mTranslate.withTranslate(
0,
0,

@ -6,6 +6,7 @@ import android.graphics.RectF
import android.widget.ImageView
@Suppress("MemberVisibilityCanBePrivate")
class Info(
rect: RectF,
img: RectF,

@ -26,26 +26,26 @@ class RotateGestureDetector(private val mListener: OnRotateListener) {
MotionEvent.ACTION_MOVE -> if (event.pointerCount > 1) {
mCurrSlope = calculateSlope(event)
val currDegrees = Math.toDegrees(atan(mCurrSlope.toDouble()));
val prevDegrees = Math.toDegrees(atan(mPrevSlope.toDouble()));
val currDegrees = Math.toDegrees(atan(mCurrSlope.toDouble()))
val prevDegrees = Math.toDegrees(atan(mPrevSlope.toDouble()))
val deltaSlope = currDegrees - prevDegrees;
val deltaSlope = currDegrees - prevDegrees
if (abs(deltaSlope) <= MAX_DEGREES_STEP) {
mListener.onRotate(deltaSlope.toFloat(), (x2 + x1) / 2, (y2 + y1) / 2);
mListener.onRotate(deltaSlope.toFloat(), (x2 + x1) / 2, (y2 + y1) / 2)
}
mPrevSlope = mCurrSlope;
mPrevSlope = mCurrSlope
}
}
}
private fun calculateSlope(event: MotionEvent): Float {
val x1 = event.getX(0);
val y1 = event.getY(0);
val x2 = event.getX(1);
val y2 = event.getY(1);
return (y2 - y1) / (x2 - x1);
val x1 = event.getX(0)
val y1 = event.getY(0)
val x2 = event.getX(1)
val y2 = event.getY(1)
return (y2 - y1) / (x2 - x1)
}
}

@ -48,7 +48,7 @@ class NumberPickerDialog(context: Context) {
listener?.invoke()
}
}
return this;
return this
}
fun show(callBack: ((value: Int) -> Unit)?) {

@ -16,6 +16,7 @@ import com.jaredrummler.android.colorpicker.*
import io.legado.app.lib.theme.ATH
import io.legado.app.utils.ColorUtils
@Suppress("MemberVisibilityCanBePrivate", "unused")
class ColorPreference(context: Context, attrs: AttributeSet) : Preference(context, attrs),
ColorPickerDialogListener {

@ -14,6 +14,7 @@ import kotlin.math.roundToInt
/**
* 不画最后一条分隔线
*/
@Suppress("MemberVisibilityCanBePrivate", "RedundantRequireNotNullCall", "unused")
class DividerNoLast(context: Context, orientation: Int) :
RecyclerView.ItemDecoration() {

@ -51,7 +51,7 @@ import kotlin.math.min
* | | ----------------------------------------------> | |
* +-------------------+ +-----------------------+
*/
@Suppress("unused")
@Suppress("unused", "MemberVisibilityCanBePrivate")
class DragSelectTouchHelper(
/**
* Developer callback which controls the behavior of DragSelectTouchHelper.

@ -12,20 +12,17 @@ import androidx.viewpager.widget.ViewPager
* Created by GKF on 2018/3/16.
*/
class ItemTouchCallback : ItemTouchHelper.Callback() {
@Suppress("MemberVisibilityCanBePrivate")
class ItemTouchCallback(private val callback: Callback) : ItemTouchHelper.Callback() {
private var swipeRefreshLayout: SwipeRefreshLayout? = null
private var viewPager: ViewPager? = null
/**
* Item操作的回调
*/
var onItemTouchCallbackListener: OnItemTouchCallbackListener? = null
/**
* 是否可以拖拽
*/
var isCanDrag = false
/**
* 是否可以被滑动
*/
@ -88,13 +85,11 @@ class ItemTouchCallback : ItemTouchHelper.Callback() {
srcViewHolder: RecyclerView.ViewHolder,
targetViewHolder: RecyclerView.ViewHolder
): Boolean {
return onItemTouchCallbackListener
?.onMove(srcViewHolder.adapterPosition, targetViewHolder.adapterPosition)
?: false
return callback.onMove(srcViewHolder.adapterPosition, targetViewHolder.adapterPosition)
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
onItemTouchCallbackListener?.onSwiped(viewHolder.adapterPosition)
callback.onSwiped(viewHolder.adapterPosition)
}
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
@ -106,10 +101,10 @@ class ItemTouchCallback : ItemTouchHelper.Callback() {
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
super.clearView(recyclerView, viewHolder)
onItemTouchCallbackListener?.onClearView(recyclerView, viewHolder)
callback.onClearView(recyclerView, viewHolder)
}
interface OnItemTouchCallbackListener {
interface Callback {
/**
* 当某个Item被滑动删除的时候

@ -9,6 +9,7 @@ import io.legado.app.utils.invisible
import io.legado.app.utils.visible
import kotlinx.android.synthetic.main.view_load_more.view.*
@Suppress("unused")
class LoadMoreView(context: Context) : FrameLayout(context) {
var hasMore = true

@ -4,6 +4,7 @@ import android.content.Context
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearSmoothScroller
@Suppress("MemberVisibilityCanBePrivate", "unused")
class UpLinearLayoutManager(val context: Context) : LinearLayoutManager(context) {
fun smoothScrollToPosition(position: Int) {

@ -7,6 +7,7 @@ import androidx.annotation.ColorInt
import androidx.recyclerview.widget.RecyclerView
import io.legado.app.R
@Suppress("MemberVisibilityCanBePrivate", "unused")
class FastScrollRecyclerView : RecyclerView {
private lateinit var mFastScroller: FastScroller

@ -33,9 +33,11 @@ import kotlin.math.min
import kotlin.math.roundToInt
@Suppress("SameParameterValue")
class FastScroller : LinearLayout {
@ColorInt
private var mBubbleColor: Int = 0
@ColorInt
private var mHandleColor: Int = 0
private var mBubbleHeight: Int = 0

@ -16,6 +16,7 @@ import io.legado.app.lib.theme.ThemeStore
import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Method
@Suppress("SameParameterValue")
class VerticalSeekBar : AppCompatSeekBar {
private var mIsDragging: Boolean = false

@ -22,7 +22,7 @@ import kotlin.math.min
/**
* 本地缓存
*/
@Suppress("unused")
@Suppress("unused", "MemberVisibilityCanBePrivate")
class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int) {
companion object {
@ -543,6 +543,7 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int)
return null
}
@Suppress("SameParameterValue")
private fun indexOf(data: ByteArray, c: Char): Int {
for (i in data.indices) {
if (data[i] == c.toByte()) {

@ -16,7 +16,7 @@ import java.io.IOException
import kotlin.math.*
@Suppress("unused", "WeakerAccess")
@Suppress("unused", "WeakerAccess", "MemberVisibilityCanBePrivate")
object BitmapUtils {
/**

@ -7,7 +7,7 @@ import androidx.annotation.FloatRange
import java.util.*
import kotlin.math.*
@Suppress("unused")
@Suppress("unused", "MemberVisibilityCanBePrivate")
object ColorUtils {
fun intToString(intColor: Int): String {

@ -6,6 +6,7 @@ import androidx.constraintlayout.widget.ConstraintSet
import androidx.transition.TransitionManager
@Suppress("MemberVisibilityCanBePrivate", "unused")
class ConstraintUtil(private val constraintLayout: ConstraintLayout) {
private var begin: ConstraintBegin? = null
@ -55,6 +56,7 @@ class ConstraintUtil(private val constraintLayout: ConstraintLayout) {
}
@Suppress("unused", "MemberVisibilityCanBePrivate")
class ConstraintBegin(
private val constraintLayout: ConstraintLayout,
private val applyConstraintSet: ConstraintSet

@ -8,6 +8,7 @@ import androidx.documentfile.provider.DocumentFile
import java.util.*
@Suppress("MemberVisibilityCanBePrivate")
object DocumentUtils {
fun exists(root: DocumentFile, fileName: String, vararg subDirs: String): Boolean {

@ -1,3 +1,5 @@
@file:Suppress("unused")
package io.legado.app.utils
import androidx.appcompat.app.AppCompatActivity

@ -11,7 +11,7 @@ import java.text.SimpleDateFormat
import java.util.*
import java.util.regex.Pattern
@Suppress("unused")
@Suppress("unused", "MemberVisibilityCanBePrivate")
object FileUtils {
fun exists(root: File, vararg subDirFiles: String): Boolean {
@ -489,11 +489,10 @@ object FileUtils {
*/
@JvmOverloads
fun writeText(filepath: String, content: String, charset: String = "utf-8"): Boolean {
try {
return try {
writeBytes(filepath, content.toByteArray(charset(charset)))
return true
} catch (e: UnsupportedEncodingException) {
return false
false
}
}
@ -569,15 +568,15 @@ object FileUtils {
* 获取文件名不包括扩展名
*/
fun getNameExcludeExtension(path: String): String {
try {
return try {
var fileName = File(path).name
val lastIndexOf = fileName.lastIndexOf(".")
if (lastIndexOf != -1) {
fileName = fileName.substring(0, lastIndexOf)
}
return fileName
fileName
} catch (e: Exception) {
return ""
""
}
}

@ -1,3 +1,5 @@
@file:Suppress("unused")
package io.legado.app.utils
import android.content.res.Resources
@ -7,7 +9,6 @@ val Float.dp: Float
android.util.TypedValue.COMPLEX_UNIT_DIP, this, Resources.getSystem().displayMetrics
)
val Float.sp: Float
get() = android.util.TypedValue.applyDimension(
android.util.TypedValue.COMPLEX_UNIT_SP, this, Resources.getSystem().displayMetrics

@ -1,3 +1,5 @@
@file:Suppress("unused")
package io.legado.app.utils
import android.app.Activity
@ -14,7 +16,8 @@ import org.jetbrains.anko.defaultSharedPreferences
import org.jetbrains.anko.internals.AnkoInternals
@Suppress("DEPRECATION")
fun Fragment.isOnline() = requireContext().connectivityManager.activeNetworkInfo?.isConnected == true
fun Fragment.isOnline() =
requireContext().connectivityManager.activeNetworkInfo?.isConnected == true
fun Fragment.getPrefBoolean(key: String, defValue: Boolean = false) =
requireContext().defaultSharedPreferences.getBoolean(key, defValue)
@ -40,7 +43,10 @@ fun Fragment.getPrefString(key: String, defValue: String? = null) =
fun Fragment.putPrefString(key: String, value: String) =
requireContext().defaultSharedPreferences.edit { putString(key, value) }
fun Fragment.getPrefStringSet(key: String, defValue: MutableSet<String>? = null) =
fun Fragment.getPrefStringSet(
key: String,
defValue: MutableSet<String>? = null
): MutableSet<String>? =
requireContext().defaultSharedPreferences.getStringSet(key, defValue)
fun Fragment.putPrefStringSet(key: String, value: MutableSet<String>) =
@ -51,15 +57,23 @@ fun Fragment.removePref(key: String) =
fun Fragment.getCompatColor(@ColorRes id: Int): Int = requireContext().getCompatColor(id)
fun Fragment.getCompatDrawable(@DrawableRes id: Int): Drawable? = requireContext().getCompatDrawable(id)
fun Fragment.getCompatDrawable(@DrawableRes id: Int): Drawable? =
requireContext().getCompatDrawable(id)
fun Fragment.getCompatColorStateList(@ColorRes id: Int): ColorStateList? = requireContext().getCompatColorStateList(id)
fun Fragment.getCompatColorStateList(@ColorRes id: Int): ColorStateList? =
requireContext().getCompatColorStateList(id)
inline fun <reified T : Activity> Fragment.startActivity(vararg params: Pair<String, Any?>) =
AnkoInternals.internalStartActivity(requireActivity(), T::class.java, params)
inline fun <reified T : Activity> Fragment.startActivityForResult(requestCode: Int, vararg params: Pair<String, Any?>) =
startActivityForResult(AnkoInternals.createIntent(requireActivity(), T::class.java, params), requestCode)
inline fun <reified T : Activity> Fragment.startActivityForResult(
requestCode: Int,
vararg params: Pair<String, Any?>
) =
startActivityForResult(
AnkoInternals.createIntent(requireActivity(), T::class.java, params),
requestCode
)
inline fun <reified T : Service> Fragment.startService(vararg params: Pair<String, Any?>) =
AnkoInternals.internalStartService(requireActivity(), T::class.java, params)

@ -10,41 +10,38 @@ import org.jsoup.select.NodeVisitor
fun Element.textArray(): Array<String> {
val accum = StringUtil.borrowBuilder()
val sb = StringUtil.borrowBuilder()
NodeTraversor.traverse(object : NodeVisitor {
override fun head(node: Node, depth: Int) {
if (node is TextNode) {
appendNormalisedText(accum, node)
appendNormalisedText(sb, node)
} else if (node is Element) {
if (accum.isNotEmpty() &&
if (sb.isNotEmpty() &&
(node.isBlock || node.tag().name == "br") &&
!lastCharIsWhitespace(accum)
) accum.append("\n")
!lastCharIsWhitespace(sb)
) sb.append("\n")
}
}
override fun tail(node: Node, depth: Int) {
if (node is Element) {
if (node.isBlock && node.nextSibling() is TextNode && !lastCharIsWhitespace(
accum
)
) accum.append("\n")
if (node.isBlock && node.nextSibling() is TextNode
&& !lastCharIsWhitespace(sb)
) {
sb.append("\n")
}
}
}
}, this)
val text = StringUtil.releaseBuilder(accum).trim { it <= ' ' }
val text = StringUtil.releaseBuilder(sb).trim { it <= ' ' }
return text.splitNotBlank("\n")
}
private fun appendNormalisedText(accum: StringBuilder, textNode: TextNode) {
private fun appendNormalisedText(sb: StringBuilder, textNode: TextNode) {
val text = textNode.wholeText
if (preserveWhitespace(textNode.parentNode()) || textNode is CDataNode)
accum.append(text)
else StringUtil.appendNormalisedWhitespace(
accum,
text,
lastCharIsWhitespace(accum)
)
sb.append(text)
else StringUtil.appendNormalisedWhitespace(sb, text, lastCharIsWhitespace(sb))
}
private fun preserveWhitespace(node: Node?): Boolean {

@ -72,14 +72,6 @@ object LanguageUtils {
@Suppress("DEPRECATION")
locale = context.resources.configuration.locale
}
/*
Log.d("h11128", "displayName " + locale.displayName)
Log.d("h11128", "displayCountry " + locale.displayCountry)
Log.d("h11128", "displayLanguage " + locale.displayLanguage)
Log.d("h11128", "Language " + locale.language)
Log.d("h11128", "Country " + locale.country)
*/
return locale
}

@ -8,7 +8,7 @@ import java.net.URL
import java.util.*
import java.util.regex.Pattern
@Suppress("unused")
@Suppress("unused", "MemberVisibilityCanBePrivate")
object NetworkUtils {
fun getUrl(response: Response<*>): String {
val networkResponse = response.raw().networkResponse()

@ -12,7 +12,7 @@ import kotlin.math.abs
import kotlin.math.log10
import kotlin.math.pow
@Suppress("unused")
@Suppress("unused", "MemberVisibilityCanBePrivate")
object StringUtils {
private const val HOUR_OF_DAY = 24
private const val DAY_OF_YESTERDAY = 2

@ -7,7 +7,6 @@ import android.graphics.Canvas
import android.os.Build
import android.view.View
import android.view.View.*
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.RadioGroup
import android.widget.SeekBar
@ -80,13 +79,6 @@ fun View.screenshot(): Bitmap? {
}.getOrNull()
}
fun View.setMargin(left: Int, top: Int, right: Int, bottom: Int) {
if (layoutParams is ViewGroup.MarginLayoutParams) {
(layoutParams as ViewGroup.MarginLayoutParams).setMargins(left, top, right, bottom)
requestLayout()
}
}
fun SeekBar.progressAdd(int: Int) {
progress += int
}

@ -1,15 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.viewpager.widget.ViewPager
android:id="@+id/view_pager_main"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/bottom_navigation_view"
app:layout_constraintTop_toTopOf="parent" />
android:layout_weight="1" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation_view"
@ -17,7 +17,6 @@
android:layout_height="50dp"
android:background="@color/background"
app:labelVisibilityMode="unlabeled"
app:menu="@menu/main_bnv"
app:layout_constraintBottom_toBottomOf="parent" />
app:menu="@menu/main_bnv" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>

@ -25,6 +25,7 @@
android:gravity="center"
android:layout_gravity="center"
android:visibility="gone"
android:text="@string/bookshelf_empty"
tools:text="TextView" />
</FrameLayout>

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -11,14 +11,11 @@
android:layout_height="wrap_content"
app:attachToActivity="false"
app:title="@string/bookshelf"
app:contentLayout="@layout/view_tab_layout_min"
app:layout_constraintTop_toTopOf="parent" />
app:contentLayout="@layout/view_tab_layout_min" />
<androidx.viewpager.widget.ViewPager
android:id="@+id/view_pager_bookshelf"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@+id/title_bar"
app:layout_constraintBottom_toBottomOf="parent" />
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>

@ -30,6 +30,7 @@
android:layout_margin="16dp"
android:gravity="center"
android:visibility="gone"
android:text="@string/explore_empty"
app:layout_constraintTop_toBottomOf="@+id/title_bar"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"

@ -31,6 +31,7 @@
android:layout_margin="16dp"
android:gravity="center"
android:visibility="gone"
android:text="@string/rss_source_empty"
app:layout_constraintTop_toBottomOf="@+id/title_bar"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"

@ -1,63 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="8dp"
android:paddingRight="8dp"
android:paddingLeft="8dp"
android:paddingBottom="4dp">
android:paddingRight="8dp">
<io.legado.app.ui.widget.image.CoverImageView
android:id="@+id/iv_cover"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:scaleType="centerCrop"
android:src="@drawable/image_cover_default"
android:transitionName="img_cover"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="UnusedAttribute" />
<io.legado.app.ui.widget.text.BadgeView
android:id="@+id/bv_unread"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:includeFontPadding="false"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent"
tools:ignore="RtlHardcoded" />
<io.legado.app.ui.widget.anima.RotateLoading
android:id="@+id/rl_loading"
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_gravity="right"
android:visibility="invisible"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:loading_width="2dp"
app:hide_mode="invisible"
tools:ignore="RtlHardcoded" />
android:layout_height="wrap_content">
<io.legado.app.ui.widget.image.CoverImageView
android:id="@+id/iv_cover"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:scaleType="centerCrop"
android:src="@drawable/image_cover_default"
android:transitionName="img_cover"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="UnusedAttribute" />
<io.legado.app.ui.widget.text.BadgeView
android:id="@+id/bv_unread"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:includeFontPadding="false"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="RtlHardcoded" />
<io.legado.app.ui.widget.anima.RotateLoading
android:id="@+id/rl_loading"
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_gravity="right"
android:visibility="invisible"
app:hide_mode="invisible"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:loading_width="2dp"
tools:ignore="RtlHardcoded" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/tv_name"
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:includeFontPadding="false"
android:layout_marginTop="6dp"
android:ellipsize="end"
android:gravity="top|center_horizontal"
android:includeFontPadding="false"
android:lines="2"
android:ellipsize="end"
android:text="@string/book_name"
android:textColor="@color/primaryText"
android:textSize="12sp"
app:layout_constraintLeft_toLeftOf="@+id/iv_cover"
app:layout_constraintRight_toRightOf="@+id/iv_cover"
app:layout_constraintTop_toBottomOf="@+id/iv_cover"
tools:ignore="RtlHardcoded,RtlSymmetry" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>

@ -29,4 +29,10 @@
android:padding="8dp"
android:text="@string/delete" />
<io.legado.app.lib.theme.view.ATESwitch
android:id="@+id/sw_show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone" />
</LinearLayout>

@ -25,6 +25,7 @@
android:gravity="top|center_horizontal"
android:lines="2"
android:ellipsize="end"
android:textColor="@color/secondaryText"
tools:text="RSS"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"

@ -14,22 +14,6 @@
android:id="@+id/menu_group_manage"
android:title="@string/group_manage" />
<item
android:id="@+id/menu_no_group"
android:title="@string/no_group" />
<item
android:id="@+id/menu_all"
android:title="@string/all" />
<item
android:id="@+id/menu_local"
android:title="@string/local" />
<item
android:id="@+id/menu_audio"
android:title="@string/audio" />
</menu>
</item>

@ -16,17 +16,7 @@
android:icon="@drawable/ic_groups"
app:showAsAction="always">
<menu>
<item
android:id="@+id/menu_no_group"
android:title="@string/no_group" />
<item
android:id="@+id/menu_all"
android:title="@string/all" />
</menu>
<menu />
</item>

@ -10,36 +10,4 @@
app:showAsAction="always"
tools:ignore="AlwaysShowAction" />
<group android:id="@+id/menu_groups">
<item
android:id="@+id/menu_group_all"
android:title="@string/all"
android:checkable="true"
android:checked="true"
app:showAsAction="never" />
<item
android:id="@+id/menu_group_local"
android:title="@string/local"
android:checkable="true"
android:checked="false"
app:showAsAction="never" />
<item
android:id="@+id/menu_group_audio"
android:title="@string/audio"
android:checkable="true"
android:checked="false"
app:showAsAction="never" />
<item
android:id="@+id/menu_group_none"
android:title="@string/no_group"
android:checkable="true"
android:checked="false"
app:showAsAction="never" />
</group>
</menu>

@ -45,7 +45,7 @@
<string name="recent_reading">最近閱讀</string>
<string name="last_read">最後閱讀</string>
<string name="update_log">更新日誌</string>
<string name="bookshelf_empty">書架還空著,先去搜索書籍或從發現裏添加吧!\n如果初次使用請先關註公眾號[開源閱讀]獲取書源!</string>
<string name="bookshelf_empty">書架還空著,先去搜尋書籍或從發現裡添加吧!\n如果初次使用請先關注公眾號[开源阅读]獲取書源!</string>
<string name="action_search">搜尋</string>
<string name="action_download">下載</string>
<string name="layout_list">列表</string>
@ -767,7 +767,7 @@
<string name="share_selected_source">分享選中書源</string>
<string name="sort_by_lastUpdateTime">時間排序</string>
<string name="search_content">全文搜尋</string>
<string name="rss_source_empty">关注公众号[开源阅读]获取订阅源!</string>
<string name="explore_empty">当前没有发现源,关注公众号[开源阅读]添加带发现的书源!</string>
<string name="rss_source_empty">關注公眾號[开源阅读]獲取訂閱源!</string>
<string name="explore_empty">目前沒有發現源,關注公眾號[开源阅读]添加包含發現的書源!</string>
</resources>

Loading…
Cancel
Save