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. 50
      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. 111
      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. 51
      app/src/main/java/io/legado/app/ui/main/MainActivity.kt
  33. 122
      app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt
  34. 4
      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. 34
      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-lang3:3.11'
implementation 'org.apache.commons:commons-text:1.8' implementation 'org.apache.commons:commons-text:1.8'
//
implementation 'net.ricecode:string-similarity:1.0.0'
//MarkDown //MarkDown
implementation 'ru.noties.markwon:core:3.1.0' implementation 'ru.noties.markwon:core:3.1.0'
@ -205,8 +208,6 @@ dependencies {
exclude group: 'xmlpull' exclude group: 'xmlpull'
} }
//E-Ink
//implementation 'fadeapp.widgets:scrollless-recyclerView:1.0.2'
} }
apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.gms.google-services'

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

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

@ -2,6 +2,7 @@ package io.legado.app.constant
import java.util.regex.Pattern import java.util.regex.Pattern
@Suppress("RegExpRedundantEscape")
object AppPattern { object AppPattern {
val JS_PATTERN: Pattern = val JS_PATTERN: Pattern =
Pattern.compile("(<js>[\\w\\W]*?</js>|@js:[\\w\\W]*$)", Pattern.CASE_INSENSITIVE) 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.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase import androidx.sqlite.db.SupportSQLiteDatabase
import io.legado.app.App import io.legado.app.App
import io.legado.app.constant.AppConst
import io.legado.app.data.dao.* import io.legado.app.data.dao.*
import io.legado.app.data.entities.* import io.legado.app.data.entities.*
import java.util.*
@Database( @Database(
@ -16,7 +18,7 @@ import io.legado.app.data.entities.*
ReplaceRule::class, SearchBook::class, SearchKeyword::class, Cookie::class, ReplaceRule::class, SearchBook::class, SearchKeyword::class, Cookie::class,
RssSource::class, Bookmark::class, RssArticle::class, RssReadRecord::class, RssSource::class, Bookmark::class, RssArticle::class, RssReadRecord::class,
RssStar::class, TxtTocRule::class, ReadRecord::class, HttpTTS::class], RssStar::class, TxtTocRule::class, ReadRecord::class, HttpTTS::class],
version = 20, version = 21,
exportSchema = true exportSchema = true
) )
abstract class AppDatabase: RoomDatabase() { abstract class AppDatabase: RoomDatabase() {
@ -37,11 +39,49 @@ abstract class AppDatabase: RoomDatabase() {
migration_15_17, migration_15_17,
migration_17_18, migration_17_18,
migration_18_19, migration_18_19,
migration_19_20 migration_19_20,
migration_20_21
) )
.allowMainThreadQueries() .allowMainThreadQueries()
.addCallback(dbCallback)
.build() .build()
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) { private val migration_10_11 = object : Migration(10, 11) {
override fun migrate(database: SupportSQLiteDatabase) { override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE txtTocRules") database.execSQL("DROP TABLE txtTocRules")
@ -117,6 +157,12 @@ abstract class AppDatabase: RoomDatabase() {
database.execSQL("ALTER TABLE book_sources ADD bookSourceComment TEXT") 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 abstract fun bookDao(): BookDao

@ -18,18 +18,22 @@ interface BookDao {
@Query("SELECT * FROM books WHERE origin = '${BookType.local}'") @Query("SELECT * FROM books WHERE origin = '${BookType.local}'")
fun observeLocal(): LiveData<List<Book>> 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}'") @Query("SELECT bookUrl FROM books WHERE origin = '${BookType.local}'")
fun observeLocalUri(): LiveData<List<String>> fun observeLocalUri(): LiveData<List<String>>
@Query("SELECT * FROM books WHERE (`group` & :group) > 0") @Query("SELECT * FROM books WHERE (`group` & :group) > 0")
fun observeByGroup(group: Long): LiveData<List<Book>> 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||'%'") @Query("SELECT * FROM books WHERE name like '%'||:key||'%' or author like '%'||:key||'%'")
fun liveDataSearch(key: String): LiveData<List<Book>> fun liveDataSearch(key: String): LiveData<List<Book>>

@ -16,15 +16,27 @@ interface BookGroupDao {
@Query("SELECT * FROM book_groups ORDER BY `order`") @Query("SELECT * FROM book_groups ORDER BY `order`")
fun liveDataAll(): LiveData<List<BookGroup>> 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 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 val maxOrder: Int
@get:Query("SELECT * FROM book_groups ORDER BY `order`") @get:Query("SELECT * FROM book_groups ORDER BY `order`")
val all: List<BookGroup> 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) @Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(vararg bookGroup: BookGroup) fun insert(vararg bookGroup: BookGroup)

@ -10,7 +10,7 @@ interface ReadRecordDao {
@get:Query("select * from readRecord") @get:Query("select * from readRecord")
val all: List<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> val allShow: List<ReadRecordShow>
@get:Query("select sum(readTime) from readRecord") @get:Query("select sum(readTime) from readRecord")

@ -1,8 +1,11 @@
package io.legado.app.data.entities package io.legado.app.data.entities
import android.content.Context
import android.os.Parcelable import android.os.Parcelable
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import io.legado.app.R
import io.legado.app.constant.AppConst
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
@Parcelize @Parcelize
@ -11,5 +14,18 @@ data class BookGroup(
@PrimaryKey @PrimaryKey
val groupId: Long = 0b1, val groupId: Long = 0b1,
var groupName: String, var groupName: String,
var order: Int = 0 var order: Int = 0,
) : Parcelable 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) 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 var elevation: Int
@SuppressLint("PrivateResource") @SuppressLint("PrivateResource")
get() = App.INSTANCE.getPrefInt( get() = App.INSTANCE.getPrefInt(

@ -15,10 +15,15 @@ import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext 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 org.jetbrains.anko.toast
import java.io.File import java.io.File
import java.util.concurrent.CopyOnWriteArraySet 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 import kotlin.math.min
object BookHelp { object BookHelp {
@ -202,54 +207,88 @@ object BookHelp {
} }
/** /**
* 找到相似度最高的章节 * 根据目录名获取当前章节
*/ */
fun getDurChapterIndexByChapterTitle( fun getDurChapter(
title: String?, oldDurChapterIndex: Int,
index: Int, oldChapterListSize: Int,
chapters: List<BookChapter>, oldDurChapterName: String?,
newChapterList: List<BookChapter>
): Int { ): Int {
if (title.isNullOrEmpty()) { if (oldChapterListSize == 0) return 0
return min(index, chapters.lastIndex) val oldChapterNum = getChapterNum(oldDurChapterName)
} val oldName = getPureChapterName(oldDurChapterName)
if (chapters.size > index && title == chapters[index].title) { val newChapterSize = newChapterList.size
return index 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 var newIndex = 0
val jSimilarity = JaccardSimilarity() var newNum = 0
var similarity = if (chapters.size > index) { if (oldName.isNotEmpty()) {
jSimilarity.apply(title, chapters[index].title) val service = StringSimilarityServiceImpl(JaroWinklerStrategy())
} else 0.0 for (i in min..max) {
if (similarity == 1.0) { val newName = getPureChapterName(newChapterList[i].title)
return index val temp = service.score(oldName, newName)
} else { if (temp > nameSim) {
for (i in 1..50) { nameSim = temp
if (index - i in chapters.indices) { newIndex = i
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
} }
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
} }
} }
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 newIndex 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 private var bookName: String? = null

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

@ -20,7 +20,7 @@ import kotlin.collections.HashMap
* 统一解析接口 * 统一解析接口
*/ */
@Keep @Keep
@Suppress("unused") @Suppress("unused", "RegExpRedundantEscape")
class AnalyzeRule(var book: BaseBook? = null) : JsExtensions { class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
var chapter: BookChapter? = null var chapter: BookChapter? = null
private var content: Any? = 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.help.coroutine.Coroutine
import io.legado.app.model.analyzeRule.AnalyzeRule import io.legado.app.model.analyzeRule.AnalyzeRule
import io.legado.app.model.analyzeRule.AnalyzeUrl 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 io.legado.app.utils.NetworkUtils
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -21,7 +19,7 @@ object Rss {
page: Int, page: Int,
scope: CoroutineScope = Coroutine.DEFAULT, scope: CoroutineScope = Coroutine.DEFAULT,
context: CoroutineContext = Dispatchers.IO context: CoroutineContext = Dispatchers.IO
): Coroutine<Result> { ): Coroutine<RssResult> {
return Coroutine.async(scope, context) { return Coroutine.async(scope, context) {
val analyzeUrl = AnalyzeUrl( val analyzeUrl = AnalyzeUrl(
sortUrl, sortUrl,

@ -14,7 +14,12 @@ import java.util.*
object RssParserByRule { object RssParserByRule {
@Throws(Exception::class) @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 val sourceUrl = rssSource.sourceUrl
var nextUrl: String? = null var nextUrl: String? = null
if (body.isNullOrBlank()) { if (body.isNullOrBlank()) {
@ -29,7 +34,7 @@ object RssParserByRule {
var ruleArticles = rssSource.ruleArticles var ruleArticles = rssSource.ruleArticles
if (ruleArticles.isNullOrBlank()) { if (ruleArticles.isNullOrBlank()) {
Debug.log(sourceUrl, "⇒列表规则为空, 使用默认规则解析") Debug.log(sourceUrl, "⇒列表规则为空, 使用默认规则解析")
return RssParser.parseXML(sortName, body, sourceUrl) return RssParserDefault.parseXML(sortName, body, sourceUrl)
} else { } else {
val articleList = mutableListOf<RssArticle>() val articleList = mutableListOf<RssArticle>()
val analyzeRule = AnalyzeRule() val analyzeRule = AnalyzeRule()
@ -72,7 +77,7 @@ object RssParserByRule {
if (reverse) { if (reverse) {
articleList.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.IOException
import java.io.StringReader import java.io.StringReader
object RssParser { object RssParserDefault {
@Throws(XmlPullParserException::class, IOException::class) @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>() val articleList = mutableListOf<RssArticle>()
var currentArticle = RssArticle() var currentArticle = RssArticle()
@ -105,7 +105,7 @@ object RssParser {
Debug.log(sourceUrl, "┌获取文章链接") Debug.log(sourceUrl, "┌获取文章链接")
Debug.log(sourceUrl, "${it.link}") 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) { fun changeTo(book1: Book) {
execute { execute {
var oldTocSize: Int = book1.totalChapterNum
AudioPlay.book?.let { AudioPlay.book?.let {
oldTocSize = it.totalChapterNum
book1.order = it.order book1.order = it.order
App.db.bookDao().delete(it) App.db.bookDao().delete(it)
} }
@ -99,18 +101,23 @@ class AudioPlayViewModel(application: Application) : BaseViewModel(application)
AudioPlay.webBook = WebBook(it) AudioPlay.webBook = WebBook(it)
} }
if (book1.tocUrl.isEmpty()) { if (book1.tocUrl.isEmpty()) {
loadBookInfo(book1) { upChangeDurChapterIndex(book1, it) } loadBookInfo(book1) { upChangeDurChapterIndex(book1, oldTocSize, it) }
} else { } 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 { execute {
AudioPlay.durChapterIndex = BookHelp.getDurChapterIndexByChapterTitle( AudioPlay.durChapterIndex = BookHelp.getDurChapter(
book.durChapterTitle,
book.durChapterIndex, book.durChapterIndex,
oldTocSize,
book.durChapterTitle,
chapters chapters
) )
book.durChapterIndex = AudioPlay.durChapterIndex book.durChapterIndex = AudioPlay.durChapterIndex

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

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

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

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

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

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

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

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

@ -166,8 +166,7 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
recycler_view.addItemDecoration(VerticalDivider(this)) recycler_view.addItemDecoration(VerticalDivider(this))
adapter = BookSourceAdapter(this, this) adapter = BookSourceAdapter(this, this)
recycler_view.adapter = adapter recycler_view.adapter = adapter
val itemTouchCallback = ItemTouchCallback() val itemTouchCallback = ItemTouchCallback(adapter)
itemTouchCallback.onItemTouchCallbackListener = adapter
itemTouchCallback.isCanDrag = true itemTouchCallback.isCanDrag = true
val dragSelectTouchHelper: DragSelectTouchHelper = val dragSelectTouchHelper: DragSelectTouchHelper =
DragSelectTouchHelper(adapter.initDragSelectTouchHelperCallback()).setSlideArea(16, 50) 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.data.entities.BookSource
import io.legado.app.lib.theme.backgroundColor import io.legado.app.lib.theme.backgroundColor
import io.legado.app.ui.widget.recycler.DragSelectTouchHelper 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.invisible
import io.legado.app.utils.visible import io.legado.app.utils.visible
import kotlinx.android.synthetic.main.item_book_source.view.* import kotlinx.android.synthetic.main.item_book_source.view.*
@ -23,7 +23,7 @@ import java.util.*
class BookSourceAdapter(context: Context, val callBack: CallBack) : class BookSourceAdapter(context: Context, val callBack: CallBack) :
SimpleRecyclerAdapter<BookSource>(context, R.layout.item_book_source), SimpleRecyclerAdapter<BookSource>(context, R.layout.item_book_source),
OnItemTouchCallbackListener { Callback {
private val selected = linkedSetOf<BookSource>() private val selected = linkedSetOf<BookSource>()

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

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

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

@ -63,9 +63,8 @@ class BookshelfViewModel(application: Application) : BaseViewModel(application)
} }
fun checkGroup(groups: List<BookGroup>) { fun checkGroup(groups: List<BookGroup>) {
execute {
groups.forEach { group -> groups.forEach { group ->
if (group.groupId and (group.groupId - 1) != 0L) { if (group.groupId >= 0 && group.groupId and (group.groupId - 1) != 0L) {
var id = 1L var id = 1L
val idsSum = App.db.bookGroupDao().idsSum val idsSum = App.db.bookGroupDao().idsSum
while (id and idsSum != 0L) { while (id and idsSum != 0L) {
@ -77,6 +76,5 @@ class BookshelfViewModel(application: Application) : BaseViewModel(application)
} }
} }
} }
}
} }

@ -29,7 +29,6 @@ import io.legado.app.utils.getViewModelOfActivity
import io.legado.app.utils.observeEvent import io.legado.app.utils.observeEvent
import io.legado.app.utils.startActivity import io.legado.app.utils.startActivity
import kotlinx.android.synthetic.main.fragment_books.* import kotlinx.android.synthetic.main.fragment_books.*
import org.jetbrains.anko.startActivity
import kotlin.math.max import kotlin.math.max
/** /**
@ -61,7 +60,6 @@ class BooksFragment : BaseFragment(R.layout.fragment_books),
position = it.getInt("position", 0) position = it.getInt("position", 0)
groupId = it.getLong("groupId", -1) groupId = it.getLong("groupId", -1)
} }
tv_empty_msg.setText(R.string.bookshelf_empty)
initRecyclerView() initRecyclerView()
upRecyclerData() upRecyclerData()
} }
@ -104,10 +102,10 @@ class BooksFragment : BaseFragment(R.layout.fragment_books),
private fun upRecyclerData() { private fun upRecyclerData() {
bookshelfLiveData?.removeObservers(this) bookshelfLiveData?.removeObservers(this)
bookshelfLiveData = when (groupId) { bookshelfLiveData = when (groupId) {
AppConst.bookGroupAll.groupId -> App.db.bookDao().observeAll() AppConst.bookGroupAllId -> App.db.bookDao().observeAll()
AppConst.bookGroupLocal.groupId -> App.db.bookDao().observeLocal() AppConst.bookGroupLocalId -> App.db.bookDao().observeLocal()
AppConst.bookGroupAudio.groupId -> App.db.bookDao().observeAudio() AppConst.bookGroupAudioId -> App.db.bookDao().observeAudio()
AppConst.bookGroupNone.groupId -> App.db.bookDao().observeNoGroup() AppConst.bookGroupNoneId -> App.db.bookDao().observeNoGroup()
else -> App.db.bookDao().observeByGroup(groupId) else -> App.db.bookDao().observeByGroup(groupId)
} }
bookshelfLiveData?.observe(this, { list -> bookshelfLiveData?.observe(this, { list ->
@ -139,8 +137,8 @@ class BooksFragment : BaseFragment(R.layout.fragment_books),
override fun open(book: Book) { override fun open(book: Book) {
when (book.type) { when (book.type) {
BookType.audio -> BookType.audio ->
context?.startActivity<AudioPlayActivity>(Pair("bookUrl", book.bookUrl)) startActivity<AudioPlayActivity>(Pair("bookUrl", book.bookUrl))
else -> context?.startActivity<ReadBookActivity>( else -> startActivity<ReadBookActivity>(
Pair("bookUrl", book.bookUrl), Pair("bookUrl", book.bookUrl),
Pair("key", IntentDataHelp.putData(book)) 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.getViewModel
import io.legado.app.utils.splitNotBlank import io.legado.app.utils.splitNotBlank
import io.legado.app.utils.startActivity 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_search.*
import kotlinx.android.synthetic.main.view_title_bar.* import kotlinx.android.synthetic.main.view_title_bar.*
import java.text.Collator 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 { ExploreAdapter.CallBack {
override val viewModel: ExploreViewModel override val viewModel: ExploreViewModel
get() = getViewModel(ExploreViewModel::class.java) get() = getViewModel(ExploreViewModel::class.java)
@ -46,7 +46,6 @@ class ExploreFragment : VMBaseFragment<ExploreViewModel>(R.layout.fragment_find_
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
setSupportToolbar(toolbar) setSupportToolbar(toolbar)
tv_empty_msg.setText(R.string.explore_empty)
initSearchView() initSearchView()
initRecyclerView() initRecyclerView()
initGroupData() initGroupData()
@ -115,7 +114,7 @@ class ExploreFragment : VMBaseFragment<ExploreViewModel>(R.layout.fragment_find_
App.db.bookSourceDao().liveExplore("%$key%") App.db.bookSourceDao().liveExplore("%$key%")
} }
liveExplore?.observe(viewLifecycleOwner, { liveExplore?.observe(viewLifecycleOwner, {
tv_empty_msg.isGone = it.isNotEmpty() tv_empty_msg.isGone = it.isNotEmpty() || search_view.query.isNotEmpty()
val diffResult = DiffUtil val diffResult = DiffUtil
.calculateDiff(ExploreDiffCallBack(ArrayList(adapter.getItems()), it)) .calculateDiff(ExploreDiffCallBack(ArrayList(adapter.getItems()), it))
adapter.setItems(it) adapter.setItems(it)

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

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

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

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

@ -19,7 +19,7 @@ import java.util.*
class RssSourceAdapter(context: Context, val callBack: CallBack) : class RssSourceAdapter(context: Context, val callBack: CallBack) :
SimpleRecyclerAdapter<RssSource>(context, R.layout.item_rss_source), SimpleRecyclerAdapter<RssSource>(context, R.layout.item_rss_source),
ItemTouchCallback.OnItemTouchCallbackListener { ItemTouchCallback.Callback {
private val selected = linkedSetOf<RssSource>() 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.ui.widget.text.AccentBgTextView
import io.legado.app.utils.dp import io.legado.app.utils.dp
@Suppress("unused") @Suppress("unused", "MemberVisibilityCanBePrivate")
class LabelsBar(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) { class LabelsBar(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) {
private val unUsedViews = arrayListOf<TextView>() private val unUsedViews = arrayListOf<TextView>()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -48,7 +48,7 @@ class NumberPickerDialog(context: Context) {
listener?.invoke() listener?.invoke()
} }
} }
return this; return this
} }
fun show(callBack: ((value: Int) -> Unit)?) { 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.lib.theme.ATH
import io.legado.app.utils.ColorUtils import io.legado.app.utils.ColorUtils
@Suppress("MemberVisibilityCanBePrivate", "unused")
class ColorPreference(context: Context, attrs: AttributeSet) : Preference(context, attrs), class ColorPreference(context: Context, attrs: AttributeSet) : Preference(context, attrs),
ColorPickerDialogListener { ColorPickerDialogListener {

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

@ -51,7 +51,7 @@ import kotlin.math.min
* | | ----------------------------------------------> | | * | | ----------------------------------------------> | |
* +-------------------+ +-----------------------+ * +-------------------+ +-----------------------+
*/ */
@Suppress("unused") @Suppress("unused", "MemberVisibilityCanBePrivate")
class DragSelectTouchHelper( class DragSelectTouchHelper(
/** /**
* Developer callback which controls the behavior of 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. * 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 swipeRefreshLayout: SwipeRefreshLayout? = null
private var viewPager: ViewPager? = null private var viewPager: ViewPager? = null
/**
* Item操作的回调
*/
var onItemTouchCallbackListener: OnItemTouchCallbackListener? = null
/** /**
* 是否可以拖拽 * 是否可以拖拽
*/ */
var isCanDrag = false var isCanDrag = false
/** /**
* 是否可以被滑动 * 是否可以被滑动
*/ */
@ -88,13 +85,11 @@ class ItemTouchCallback : ItemTouchHelper.Callback() {
srcViewHolder: RecyclerView.ViewHolder, srcViewHolder: RecyclerView.ViewHolder,
targetViewHolder: RecyclerView.ViewHolder targetViewHolder: RecyclerView.ViewHolder
): Boolean { ): Boolean {
return onItemTouchCallbackListener return callback.onMove(srcViewHolder.adapterPosition, targetViewHolder.adapterPosition)
?.onMove(srcViewHolder.adapterPosition, targetViewHolder.adapterPosition)
?: false
} }
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
onItemTouchCallbackListener?.onSwiped(viewHolder.adapterPosition) callback.onSwiped(viewHolder.adapterPosition)
} }
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
@ -106,10 +101,10 @@ class ItemTouchCallback : ItemTouchHelper.Callback() {
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) { override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
super.clearView(recyclerView, viewHolder) super.clearView(recyclerView, viewHolder)
onItemTouchCallbackListener?.onClearView(recyclerView, viewHolder) callback.onClearView(recyclerView, viewHolder)
} }
interface OnItemTouchCallbackListener { interface Callback {
/** /**
* 当某个Item被滑动删除的时候 * 当某个Item被滑动删除的时候

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

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

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

@ -33,9 +33,11 @@ import kotlin.math.min
import kotlin.math.roundToInt import kotlin.math.roundToInt
@Suppress("SameParameterValue")
class FastScroller : LinearLayout { class FastScroller : LinearLayout {
@ColorInt @ColorInt
private var mBubbleColor: Int = 0 private var mBubbleColor: Int = 0
@ColorInt @ColorInt
private var mHandleColor: Int = 0 private var mHandleColor: Int = 0
private var mBubbleHeight: 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.InvocationTargetException
import java.lang.reflect.Method import java.lang.reflect.Method
@Suppress("SameParameterValue")
class VerticalSeekBar : AppCompatSeekBar { class VerticalSeekBar : AppCompatSeekBar {
private var mIsDragging: Boolean = false 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) { class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int) {
companion object { companion object {
@ -543,6 +543,7 @@ class ACache private constructor(cacheDir: File, max_size: Long, max_count: Int)
return null return null
} }
@Suppress("SameParameterValue")
private fun indexOf(data: ByteArray, c: Char): Int { private fun indexOf(data: ByteArray, c: Char): Int {
for (i in data.indices) { for (i in data.indices) {
if (data[i] == c.toByte()) { if (data[i] == c.toByte()) {

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

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

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

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

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

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

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

@ -1,3 +1,5 @@
@file:Suppress("unused")
package io.legado.app.utils package io.legado.app.utils
import android.app.Activity import android.app.Activity
@ -14,7 +16,8 @@ import org.jetbrains.anko.defaultSharedPreferences
import org.jetbrains.anko.internals.AnkoInternals import org.jetbrains.anko.internals.AnkoInternals
@Suppress("DEPRECATION") @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) = fun Fragment.getPrefBoolean(key: String, defValue: Boolean = false) =
requireContext().defaultSharedPreferences.getBoolean(key, defValue) requireContext().defaultSharedPreferences.getBoolean(key, defValue)
@ -40,7 +43,10 @@ fun Fragment.getPrefString(key: String, defValue: String? = null) =
fun Fragment.putPrefString(key: String, value: String) = fun Fragment.putPrefString(key: String, value: String) =
requireContext().defaultSharedPreferences.edit { putString(key, value) } 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) requireContext().defaultSharedPreferences.getStringSet(key, defValue)
fun Fragment.putPrefStringSet(key: String, value: MutableSet<String>) = 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.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?>) = inline fun <reified T : Activity> Fragment.startActivity(vararg params: Pair<String, Any?>) =
AnkoInternals.internalStartActivity(requireActivity(), T::class.java, params) AnkoInternals.internalStartActivity(requireActivity(), T::class.java, params)
inline fun <reified T : Activity> Fragment.startActivityForResult(requestCode: Int, vararg params: Pair<String, Any?>) = inline fun <reified T : Activity> Fragment.startActivityForResult(
startActivityForResult(AnkoInternals.createIntent(requireActivity(), T::class.java, params), requestCode) 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?>) = inline fun <reified T : Service> Fragment.startService(vararg params: Pair<String, Any?>) =
AnkoInternals.internalStartService(requireActivity(), T::class.java, params) AnkoInternals.internalStartService(requireActivity(), T::class.java, params)

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

@ -72,14 +72,6 @@ object LanguageUtils {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
locale = context.resources.configuration.locale 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 return locale
} }

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

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

@ -7,7 +7,6 @@ import android.graphics.Canvas
import android.os.Build import android.os.Build
import android.view.View import android.view.View
import android.view.View.* import android.view.View.*
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.RadioGroup import android.widget.RadioGroup
import android.widget.SeekBar import android.widget.SeekBar
@ -80,13 +79,6 @@ fun View.screenshot(): Bitmap? {
}.getOrNull() }.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) { fun SeekBar.progressAdd(int: Int) {
progress += int progress += int
} }

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

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

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

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

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

@ -1,13 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?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:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="8dp" android:paddingTop="8dp"
android:paddingRight="8dp"
android:paddingLeft="8dp" android:paddingLeft="8dp"
android:paddingBottom="4dp"> android:paddingRight="8dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<io.legado.app.ui.widget.image.CoverImageView <io.legado.app.ui.widget.image.CoverImageView
android:id="@+id/iv_cover" android:id="@+id/iv_cover"
@ -26,8 +30,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="right" android:layout_gravity="right"
android:includeFontPadding="false" android:includeFontPadding="false"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="RtlHardcoded" /> tools:ignore="RtlHardcoded" />
<io.legado.app.ui.widget.anima.RotateLoading <io.legado.app.ui.widget.anima.RotateLoading
@ -36,28 +40,28 @@
android:layout_height="22dp" android:layout_height="22dp"
android:layout_gravity="right" android:layout_gravity="right"
android:visibility="invisible" android:visibility="invisible"
app:layout_constraintTop_toTopOf="parent" app:hide_mode="invisible"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:loading_width="2dp" app:loading_width="2dp"
app:hide_mode="invisible"
tools:ignore="RtlHardcoded" /> tools:ignore="RtlHardcoded" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView <TextView
android:id="@+id/tv_name" android:id="@+id/tv_name"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingLeft="4dp" android:layout_marginTop="6dp"
android:paddingRight="4dp" android:ellipsize="end"
android:includeFontPadding="false"
android:gravity="top|center_horizontal" android:gravity="top|center_horizontal"
android:includeFontPadding="false"
android:lines="2" android:lines="2"
android:ellipsize="end"
android:text="@string/book_name" android:text="@string/book_name"
android:textColor="@color/primaryText" android:textColor="@color/primaryText"
android:textSize="12sp" 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" /> tools:ignore="RtlHardcoded,RtlSymmetry" />
</androidx.constraintlayout.widget.ConstraintLayout> </LinearLayout>

@ -29,4 +29,10 @@
android:padding="8dp" android:padding="8dp"
android:text="@string/delete" /> 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> </LinearLayout>

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

@ -14,22 +14,6 @@
android:id="@+id/menu_group_manage" android:id="@+id/menu_group_manage"
android:title="@string/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> </menu>
</item> </item>

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

@ -10,36 +10,4 @@
app:showAsAction="always" app:showAsAction="always"
tools:ignore="AlwaysShowAction" /> 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> </menu>

@ -45,7 +45,7 @@
<string name="recent_reading">最近閱讀</string> <string name="recent_reading">最近閱讀</string>
<string name="last_read">最後閱讀</string> <string name="last_read">最後閱讀</string>
<string name="update_log">更新日誌</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_search">搜尋</string>
<string name="action_download">下載</string> <string name="action_download">下載</string>
<string name="layout_list">列表</string> <string name="layout_list">列表</string>
@ -767,7 +767,7 @@
<string name="share_selected_source">分享選中書源</string> <string name="share_selected_source">分享選中書源</string>
<string name="sort_by_lastUpdateTime">時間排序</string> <string name="sort_by_lastUpdateTime">時間排序</string>
<string name="search_content">全文搜尋</string> <string name="search_content">全文搜尋</string>
<string name="rss_source_empty">关注公众号[开源阅读]获取订阅源!</string> <string name="rss_source_empty">關注公眾號[开源阅读]獲取訂閱源!</string>
<string name="explore_empty">当前没有发现源,关注公众号[开源阅读]添加带发现的书源!</string> <string name="explore_empty">目前沒有發現源,關注公眾號[开源阅读]添加包含發現的書源!</string>
</resources> </resources>

Loading…
Cancel
Save