HapeLee 1 год назад
Родитель
Сommit
9f5a0eb483
36 измененных файлов с 1331 добавлено и 947 удалено
  1. 3 0
      app/src/main/java/io/legato/kazusa/data/dao/BookGroupDao.kt
  2. 5 6
      app/src/main/java/io/legato/kazusa/lib/prefs/NameListPreference.kt
  3. 10 1
      app/src/main/java/io/legato/kazusa/ui/book/group/GroupEditDialog.kt
  4. 7 0
      app/src/main/java/io/legato/kazusa/ui/book/group/GroupViewModel.kt
  5. 23 36
      app/src/main/java/io/legato/kazusa/ui/book/read/ReadMenu.kt
  6. 20 16
      app/src/main/java/io/legato/kazusa/ui/main/MainActivity.kt
  7. 47 13
      app/src/main/java/io/legato/kazusa/ui/main/bookshelf/BaseBookshelfFragment.kt
  8. 1 9
      app/src/main/java/io/legato/kazusa/ui/main/bookshelf/style1/BookshelfFragment1.kt
  9. 144 0
      app/src/main/java/io/legato/kazusa/ui/main/bookshelf/style1/BookshelfFragment3.kt
  10. 10 9
      app/src/main/java/io/legato/kazusa/ui/main/bookshelf/style1/books/BooksAdapterList.kt
  11. 7 4
      app/src/main/java/io/legato/kazusa/ui/main/bookshelf/style2/BaseBooksAdapter.kt
  12. 47 12
      app/src/main/java/io/legato/kazusa/ui/main/bookshelf/style2/BooksAdapterGrid.kt
  13. 71 29
      app/src/main/java/io/legato/kazusa/ui/main/bookshelf/style2/BooksAdapterList.kt
  14. 41 10
      app/src/main/java/io/legato/kazusa/ui/main/bookshelf/style2/BookshelfFragment2.kt
  15. 2 4
      app/src/main/java/io/legato/kazusa/ui/widget/LabelsBar.kt
  16. 1 1
      app/src/main/res/anim/anim_readbook_bottom_in.xml
  17. 1 1
      app/src/main/res/anim/anim_readbook_top_in.xml
  18. 1 1
      app/src/main/res/drawable/bg_book_mask.xml
  19. 6 0
      app/src/main/res/drawable/bg_book_mask_land.xml
  20. 0 8
      app/src/main/res/drawable/bg_gradient.xml
  21. 1 1
      app/src/main/res/drawable/bg_gradient_mask_land.xml
  22. 4 0
      app/src/main/res/drawable/bg_surface_variant.xml
  23. 248 237
      app/src/main/res/layout-land/activity_book_info.xml
  24. 3 4
      app/src/main/res/layout/activity_book_info.xml
  25. 14 5
      app/src/main/res/layout/dialog_book_group_edit.xml
  26. 62 107
      app/src/main/res/layout/dialog_bookshelf_config.xml
  27. 2 2
      app/src/main/res/layout/fragment_bookshelf1.xml
  28. 2 1
      app/src/main/res/layout/fragment_bookshelf2.xml
  29. 45 0
      app/src/main/res/layout/fragment_bookshelf3.xml
  30. 128 66
      app/src/main/res/layout/item_bookshelf_grid_group.xml
  31. 189 186
      app/src/main/res/layout/item_bookshelf_list.xml
  32. 134 147
      app/src/main/res/layout/item_bookshelf_list_group.xml
  33. 1 1
      app/src/main/res/layout/view_preference.xml
  34. 11 0
      app/src/main/res/values-zh/arrays.xml
  35. 30 30
      app/src/main/res/values-zh/strings.xml
  36. 10 0
      app/src/main/res/values/arrays.xml

+ 3 - 0
app/src/main/java/io/legato/kazusa/data/dao/BookGroupDao.kt

@@ -85,6 +85,9 @@ interface BookGroupDao {
     @Delete
     fun delete(vararg bookGroup: BookGroup)
 
+    @Query("UPDATE book_groups SET cover = NULL WHERE groupId = :groupId")
+    fun clearCover(groupId: Long)
+
     fun isInRules(id: Long): Boolean {
         if (id < 0) {
             return true

+ 5 - 6
app/src/main/java/io/legato/kazusa/lib/prefs/NameListPreference.kt

@@ -18,7 +18,6 @@ class NameListPreference(context: Context, attrs: AttributeSet) : ListPreference
 
     init {
         layoutResource = R.layout.view_preference
-        widgetLayoutResource = R.layout.item_fillet_text
         context.withStyledAttributes(attrs, R.styleable.Preference) {
             isBottomBackground = getBoolean(R.styleable.Preference_isBottomBackground, false)
         }
@@ -26,16 +25,16 @@ class NameListPreference(context: Context, attrs: AttributeSet) : ListPreference
 
     override fun onBindViewHolder(holder: PreferenceViewHolder) {
         val v = Preference.bindView<TextView>(
-            context, holder, icon, title, summary, widgetLayoutResource,
+            context, holder, icon, title, entry,
             R.id.text_view, isBottomBackground = isBottomBackground
         )
         if (v is TextView) {
             v.text = entry
-            if (isBottomBackground) {
+//            if (isBottomBackground) {
 //                val bgColor = context.bottomBackground
-//                val pTextColor = context.getPrimaryTextColor(ColorUtils.isColorLight(bgColor))
-                //v.setTextColor(pTextColor)
-            }
+//               val pTextColor = context.getPrimaryTextColor(ColorUtils.isColorLight(bgColor))
+//                //v.setTextColor(pTextColor)
+//            }
         }
         super.onBindViewHolder(holder)
     }

+ 10 - 1
app/src/main/java/io/legato/kazusa/ui/book/group/GroupEditDialog.kt

@@ -67,7 +67,8 @@ class GroupEditDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_book_gro
         bookGroup?.let {
             binding.btnDelete.visible(it.groupId > 0 || it.groupId == Long.MIN_VALUE)
             binding.tieGroupName.setText(it.groupName)
-            binding.ivCover.load(it.cover)
+            if (bookGroup?.cover != null)
+                binding.ivCover.load(it.cover)
             binding.spSort.setSelection(it.bookSort + 1)
             binding.cbEnableRefresh.isChecked = it.enableRefresh
         } ?: let {
@@ -79,6 +80,14 @@ class GroupEditDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_book_gro
             ivCover.onClick {
                 selectImage.launch()
             }
+            btnReset.onClick {
+                bookGroup?.let {
+                    viewModel.clearCover(it) {
+                        toastOnUi("封面已重置")
+                        dismiss()
+                    }
+                }
+            }
             btnCancel.onClick {
                 dismiss()
             }

+ 7 - 0
app/src/main/java/io/legato/kazusa/ui/book/group/GroupViewModel.kt

@@ -48,5 +48,12 @@ class GroupViewModel(application: Application) : BaseViewModel(application) {
         }
     }
 
+    fun clearCover(bookGroup: BookGroup, finally: () -> Unit) {
+        execute {
+            appDb.bookGroupDao.clearCover(bookGroup.groupId)
+        }.onFinally {
+            finally()
+        }
+    }
 
 }

+ 23 - 36
app/src/main/java/io/legato/kazusa/ui/book/read/ReadMenu.kt

@@ -4,7 +4,6 @@ import android.annotation.SuppressLint
 import android.content.Context
 import android.graphics.drawable.GradientDrawable
 import android.util.AttributeSet
-import android.util.Log
 import android.view.LayoutInflater
 import android.view.WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
 import android.view.animation.Animation
@@ -19,19 +18,10 @@ import io.legato.kazusa.constant.PreferKey
 import io.legato.kazusa.databinding.ViewReadMenuBinding
 import io.legato.kazusa.help.config.AppConfig
 import io.legato.kazusa.help.config.LocalConfig
-import io.legato.kazusa.help.config.ReadBookConfig
-import io.legato.kazusa.help.config.ThemeConfig
 import io.legato.kazusa.help.coroutine.Coroutine
 import io.legato.kazusa.help.source.getSourceType
 import io.legato.kazusa.lib.dialogs.alert
-//import io.legado.app.lib.theme.accentColor
-//import io.legado.app.lib.theme.bottomBackground
-//import io.legado.app.lib.theme.buttonDisabledColor
-//import io.legado.app.lib.theme.getPrimaryTextColor
-//import io.legado.app.lib.theme.primaryColor
-//import io.legado.app.lib.theme.primaryTextColor
 import io.legato.kazusa.model.ReadBook
-import io.legato.kazusa.ui.book.read.config.MoreConfigDialog
 import io.legato.kazusa.ui.browser.WebViewActivity
 import io.legato.kazusa.ui.widget.seekbar.SeekBarChangeListener
 import io.legato.kazusa.utils.ConstraintModify
@@ -45,7 +35,6 @@ import io.legato.kazusa.utils.loadAnimation
 import io.legato.kazusa.utils.modifyBegin
 import io.legato.kazusa.utils.openUrl
 import io.legato.kazusa.utils.putPrefBoolean
-import io.legato.kazusa.utils.showDialogFragment
 import io.legato.kazusa.utils.startActivity
 import io.legato.kazusa.utils.visible
 import splitties.views.onClick
@@ -75,8 +64,8 @@ class ReadMenu @JvmOverloads constructor(
     private val menuBottomOut: Animation by lazy {
         loadAnimation(context, R.anim.anim_readbook_bottom_out)
     }
-    private val immersiveMenu: Boolean
-        get() = AppConfig.readBarStyleFollowPage && ReadBookConfig.durConfig.curBgType() == 0
+//    private val immersiveMenu: Boolean
+//        get() = AppConfig.readBarStyleFollowPage && ReadBookConfig.durConfig.curBgType() == 0
 //    private var bgColor: Int = if (immersiveMenu) {
 //        kotlin.runCatching {
 //            ReadBookConfig.durConfig.curBgStr().toColorInt()
@@ -160,29 +149,29 @@ class ReadMenu @JvmOverloads constructor(
         bindEvent()
     }
 
-    private fun initView(reset: Boolean = false) = binding.run {
+    private fun initView() = binding.run {
 //        if (AppConfig.isNightTheme) {
 //            fabNightTheme.setIconResource(R.drawable.ic_daytime)
 //        } else {
 //            fabNightTheme.setIconResource(R.drawable.ic_brightness)
 //        }
         initAnimation()
-        if (immersiveMenu) {
-//            val lightTextColor = ColorUtils.withAlpha(ColorUtils.lightenColor(textColor), 0.75f)
-//            titleBar.setTextColor(textColor)
-//            titleBar.setBackgroundColor(bgColor)
-//            titleBar.setColorFilter(textColor)
-//            tvChapterName.setTextColor(lightTextColor)
-//            tvChapterUrl.setTextColor(lightTextColor)
-        } else if (reset) {
-//            val bgColor = context.primaryColor
-//            val textColor = context.primaryTextColor
-//            titleBar.setTextColor(textColor)
-//            titleBar.setBackgroundColor(bgColor)
-//            titleBar.setColorFilter(textColor)
-//            tvChapterName.setTextColor(textColor)
-//            tvChapterUrl.setTextColor(textColor)
-        }
+//        if (immersiveMenu) {
+////            val lightTextColor = ColorUtils.withAlpha(ColorUtils.lightenColor(textColor), 0.75f)
+////            titleBar.setTextColor(textColor)
+////            titleBar.setBackgroundColor(bgColor)
+////            titleBar.setColorFilter(textColor)
+////            tvChapterName.setTextColor(lightTextColor)
+////            tvChapterUrl.setTextColor(lightTextColor)
+//        } else if (reset) {
+////            val bgColor = context.primaryColor
+////            val textColor = context.primaryTextColor
+////            titleBar.setTextColor(textColor)
+////            titleBar.setBackgroundColor(bgColor)
+////            titleBar.setColorFilter(textColor)
+////            tvChapterName.setTextColor(textColor)
+////            tvChapterUrl.setTextColor(textColor)
+//        }
         val brightnessBackground = GradientDrawable()
         brightnessBackground.cornerRadius = 5F.dpToPx()
        //brightnessBackground.setColor(ColorUtils.adjustAlpha(bgColor, 0.5f))
@@ -230,13 +219,13 @@ class ReadMenu @JvmOverloads constructor(
 
     fun reset() {
         upColorConfig()
-        initView(true)
+        initView()
     }
 
     fun refreshMenuColorFilter() {
-        if (immersiveMenu) {
-            //binding.titleBar.setColorFilter(textColor)
-        }
+//        if (immersiveMenu) {
+//            //binding.titleBar.setColorFilter(textColor)
+//        }
     }
 
     private fun upColorConfig() {
@@ -514,8 +503,6 @@ class ReadMenu @JvmOverloads constructor(
 
     fun upSeekBar() {
         binding.seekReadPage.apply {
-            Log.d("SliderDebug", "durPageIndex: ${ReadBook.durPageIndex}, pageSize: ${ReadBook.curTextChapter?.pageSize}")
-            Log.d("SliderDebug", "durChapterIndex: ${ReadBook.durChapterIndex}, simulatedChapterSize: ${ReadBook.simulatedChapterSize}")
             when (AppConfig.progressBarBehavior) {
                 "page" -> {
                     ReadBook.curTextChapter?.let { chapter ->

+ 20 - 16
app/src/main/java/io/legato/kazusa/ui/main/MainActivity.kt

@@ -9,7 +9,6 @@ import android.view.MenuItem
 import android.view.MotionEvent
 import android.view.ViewGroup
 import androidx.activity.addCallback
-import androidx.activity.enableEdgeToEdge
 import androidx.activity.viewModels
 import androidx.core.view.postDelayed
 import androidx.fragment.app.Fragment
@@ -33,7 +32,6 @@ import io.legato.kazusa.help.config.LocalConfig
 import io.legato.kazusa.help.coroutine.Coroutine
 import io.legato.kazusa.help.storage.Backup
 import io.legato.kazusa.lib.dialogs.alert
-//import io.legado.app.lib.theme.primaryColor
 import io.legato.kazusa.service.BaseReadAloudService
 import io.legato.kazusa.ui.about.CrashLogsDialog
 import io.legato.kazusa.ui.main.bookshelf.BaseBookshelfFragment
@@ -45,9 +43,7 @@ import io.legato.kazusa.ui.main.rss.RssFragment
 import io.legato.kazusa.ui.widget.dialog.TextDialog
 import io.legato.kazusa.utils.hideSoftInput
 import io.legato.kazusa.utils.isCreated
-import io.legato.kazusa.utils.navigationBarHeight
 import io.legato.kazusa.utils.observeEvent
-import io.legato.kazusa.utils.setOnApplyWindowInsetsListenerCompat
 import io.legato.kazusa.utils.shouldHideSoftInput
 import io.legato.kazusa.utils.showDialogFragment
 import io.legato.kazusa.utils.toastOnUi
@@ -55,14 +51,11 @@ import io.legato.kazusa.utils.viewbindingdelegate.viewBinding
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
-import splitties.views.bottomPadding
 import kotlin.coroutines.resume
 import kotlin.coroutines.suspendCoroutine
 import androidx.core.view.get
 import com.google.android.material.navigation.NavigationBarView
-import io.legato.kazusa.ui.welcome.WelcomeActivity
-import io.legato.kazusa.utils.applyNavigationBarPadding
-import io.legato.kazusa.utils.applyStatusBarPadding
+import io.legato.kazusa.ui.main.bookshelf.style1.BookshelfFragment3
 
 /**
  * 主界面
@@ -76,6 +69,7 @@ class MainActivity : VMBaseActivity<ActivityMainBinding, MainViewModel>(),
     private val idBookshelf = 0
     private val idBookshelf1 = 11
     private val idBookshelf2 = 12
+    private val idBookshelf3 = 13
     private val idExplore = 1
     private val idRss = 2
     private val idMy = 3
@@ -424,11 +418,17 @@ class MainActivity : VMBaseActivity<ActivityMainBinding, MainViewModel>(),
     private fun getFragmentId(position: Int): Int {
         val id = realPositions[position]
         if (id == idBookshelf) {
-            return if (AppConfig.bookGroupStyle == 1) idBookshelf2 else idBookshelf1
+            return when (AppConfig.bookGroupStyle) {
+                0 -> idBookshelf1
+                1 -> idBookshelf2
+                2 -> idBookshelf3
+                else -> idBookshelf1
+            }
         }
         return id
     }
 
+
     private inner class PageChangeCallback : ViewPager.SimpleOnPageChangeListener() {
 
         override fun onPageSelected(position: Int) {
@@ -447,24 +447,28 @@ class MainActivity : VMBaseActivity<ActivityMainBinding, MainViewModel>(),
         }
 
         override fun getItemPosition(any: Any): Int {
-            val position = (any as MainFragmentInterface).position
-                ?: return POSITION_NONE
+            val position = (any as? MainFragmentInterface)?.position ?: return POSITION_NONE
             val fragmentId = getId(position)
-            if ((fragmentId == idBookshelf1 && any is BookshelfFragment1)
-                || (fragmentId == idBookshelf2 && any is BookshelfFragment2)
-                || (fragmentId == idExplore && any is ExploreFragment)
-                || (fragmentId == idRss && any is RssFragment)
-                || (fragmentId == idMy && any is MyFragment)
+
+            if (
+                (fragmentId == idBookshelf1 && any is BookshelfFragment1) ||
+                (fragmentId == idBookshelf2 && any is BookshelfFragment2) ||
+                (fragmentId == idBookshelf3 && any is BookshelfFragment3) ||
+                (fragmentId == idExplore && any is ExploreFragment) ||
+                (fragmentId == idRss && any is RssFragment) ||
+                (fragmentId == idMy && any is MyFragment)
             ) {
                 return POSITION_UNCHANGED
             }
             return POSITION_NONE
         }
 
+
         override fun getItem(position: Int): Fragment {
             return when (getId(position)) {
                 idBookshelf1 -> BookshelfFragment1(position)
                 idBookshelf2 -> BookshelfFragment2(position)
+                idBookshelf3 -> BookshelfFragment3(position)
                 idExplore -> ExploreFragment(position)
                 idRss -> RssFragment(position)
                 else -> MyFragment(position)

+ 47 - 13
app/src/main/java/io/legato/kazusa/ui/main/bookshelf/BaseBookshelfFragment.kt

@@ -4,10 +4,12 @@ import android.annotation.SuppressLint
 import android.content.res.Configuration
 import android.view.Menu
 import android.view.MenuItem
+import android.view.View
 import androidx.core.view.isVisible
 import androidx.fragment.app.activityViewModels
 import androidx.fragment.app.viewModels
 import androidx.lifecycle.LiveData
+import com.google.android.material.chip.Chip
 import io.legato.kazusa.R
 import io.legato.kazusa.base.VMBaseFragment
 import io.legato.kazusa.constant.EventBus
@@ -30,9 +32,6 @@ import io.legato.kazusa.ui.file.HandleFileContract
 import io.legato.kazusa.ui.main.MainFragmentInterface
 import io.legato.kazusa.ui.main.MainViewModel
 import io.legato.kazusa.ui.widget.dialog.WaitDialog
-import io.legato.kazusa.utils.bookshelfLayout
-import io.legato.kazusa.utils.checkByIndex
-import io.legato.kazusa.utils.getCheckedIndex
 import io.legato.kazusa.utils.isAbsUrl
 import io.legato.kazusa.utils.postEvent
 import io.legato.kazusa.utils.readText
@@ -166,7 +165,7 @@ abstract class BaseBookshelfFragment(layoutId: Int) : VMBaseFragment<BookshelfVi
 
     @SuppressLint("InflateParams")
     fun configBookshelf() {
-        alert(titleResource = R.string.bookshelf_layout) {
+        alert {
 
             val orientation = requireContext().resources.configuration.orientation
             val bookshelfLayout = if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
@@ -182,20 +181,53 @@ abstract class BaseBookshelfFragment(layoutId: Int) : VMBaseFragment<BookshelfVi
             val alertBinding =
                 DialogBookshelfConfigBinding.inflate(layoutInflater)
                     .apply {
-                        spGroupStyle.setSelection(AppConfig.bookGroupStyle)
+
+                        val groupStyleArray = resources.getStringArray(R.array.group_style)
+                        val chipGroupStyle = chipGroupStyle
+
+                        groupStyleArray.forEachIndexed { index, label ->
+                            val chip = Chip(context).apply {
+                                text = label
+                                isCheckable = true
+                                isClickable = true
+                                id = View.generateViewId()
+                            }
+                            chipGroupStyle.addView(chip)
+                            if (index == AppConfig.bookGroupStyle) {
+                                chipGroupStyle.check(chip.id)
+                            }
+                        }
+
+                        val sortArray = resources.getStringArray(R.array.bookshelf_px_array)
+                        val chipGroupSort = chipGroupSort
+
+                        sortArray.forEachIndexed { index, label ->
+                            val chip = Chip(context).apply {
+                                text = label
+                                isCheckable = true
+                                isClickable = true
+                                id = View.generateViewId()
+                            }
+                            chipGroupSort.addView(chip)
+                            if (index == AppConfig.bookshelfSort) {
+                                chipGroupSort.check(chip.id)
+                            }
+                        }
+
                         swShowUnread.isChecked = AppConfig.showUnread
                         swShowLastUpdateTime.isChecked = AppConfig.showLastUpdateTime
                         swShowWaitUpBooks.isChecked = AppConfig.showWaitUpCount
                         swShowBookshelfFastScroller.isChecked = AppConfig.showBookshelfFastScroller
-                        rgSort.checkByIndex(bookshelfSort)
 
                         chipList.isChecked = !isGrid
                         chipGrid.isChecked = isGrid
-                        llGridSlider.isVisible = isGrid
+                        sliderText.isVisible = isGrid
+                        sliderGridCount.isVisible = isGrid
                         sliderGridCount.value = columnCount.toFloat()
 
-                        chipGroupLayout.setOnCheckedChangeListener { _, checkedId ->
-                            llGridSlider.isVisible = checkedId == R.id.chip_grid
+                        chipGroupLayout.setOnCheckedStateChangeListener { group, checkedIds ->
+                            sliderText.isVisible = checkedIds.firstOrNull() == R.id.chip_grid
+                            sliderGridCount.isVisible = checkedIds.firstOrNull() == R.id.chip_grid
                         }
 
                     }
@@ -205,10 +237,12 @@ abstract class BaseBookshelfFragment(layoutId: Int) : VMBaseFragment<BookshelfVi
                 alertBinding.apply {
                     var notifyMain = false
                     var recreate = false
-                    if (AppConfig.bookGroupStyle != spGroupStyle.selectedItemPosition) {
-                        AppConfig.bookGroupStyle = spGroupStyle.selectedItemPosition
+
+                    if (AppConfig.bookGroupStyle != chipGroupStyle.checkedChipId) {
+                        AppConfig.bookGroupStyle = chipGroupStyle.indexOfChild(chipGroupStyle.findViewById(chipGroupStyle.checkedChipId))
                         notifyMain = true
                     }
+
                     if (AppConfig.showUnread != swShowUnread.isChecked) {
                         AppConfig.showUnread = swShowUnread.isChecked
                         postEvent(EventBus.BOOKSHELF_REFRESH, "")
@@ -225,8 +259,8 @@ abstract class BaseBookshelfFragment(layoutId: Int) : VMBaseFragment<BookshelfVi
                         AppConfig.showBookshelfFastScroller = swShowBookshelfFastScroller.isChecked
                         postEvent(EventBus.BOOKSHELF_REFRESH, "")
                     }
-                    if (bookshelfSort != rgSort.getCheckedIndex()) {
-                        AppConfig.bookshelfSort = rgSort.getCheckedIndex()
+                    if (bookshelfSort != chipGroupSort.indexOfChild(chipGroupSort.findViewById(chipGroupSort.checkedChipId))) {
+                        AppConfig.bookshelfSort = chipGroupSort.indexOfChild(chipGroupSort.findViewById(chipGroupSort.checkedChipId))
                         upSort()
                     }
 

+ 1 - 9
app/src/main/java/io/legato/kazusa/ui/main/bookshelf/style1/BookshelfFragment1.kt

@@ -5,11 +5,8 @@ package io.legato.kazusa.ui.main.bookshelf.style1
 import android.annotation.SuppressLint
 import android.os.Bundle
 import android.view.View
-import android.view.ViewGroup
 import androidx.appcompat.widget.SearchView
 import androidx.fragment.app.Fragment
-import androidx.fragment.app.FragmentManager
-import androidx.fragment.app.FragmentStatePagerAdapter
 import androidx.viewpager2.adapter.FragmentStateAdapter
 import com.google.android.material.tabs.TabLayout
 import com.google.android.material.tabs.TabLayoutMediator
@@ -19,13 +16,10 @@ import io.legato.kazusa.data.entities.Book
 import io.legato.kazusa.data.entities.BookGroup
 import io.legato.kazusa.databinding.FragmentBookshelf1Binding
 import io.legato.kazusa.help.config.AppConfig
-//import io.legado.app.lib.theme.accentColor
-//import io.legado.app.lib.theme.primaryColor
 import io.legato.kazusa.ui.book.group.GroupEditDialog
 import io.legato.kazusa.ui.book.search.SearchActivity
 import io.legato.kazusa.ui.main.bookshelf.BaseBookshelfFragment
 import io.legato.kazusa.ui.main.bookshelf.style1.books.BooksFragment
-import io.legato.kazusa.utils.isCreated
 import io.legato.kazusa.utils.showDialogFragment
 import io.legato.kazusa.utils.toastOnUi
 import io.legato.kazusa.utils.viewbindingdelegate.viewBinding
@@ -45,9 +39,7 @@ class BookshelfFragment1() : BaseBookshelfFragment(R.layout.fragment_bookshelf1)
     }
 
     private val binding by viewBinding(FragmentBookshelf1Binding::bind)
-    private val tabLayout: TabLayout by lazy {
-        binding.tabLayout.findViewById(R.id.tab_layout)
-    }
+
     private val bookGroups = mutableListOf<BookGroup>()
     private val fragmentMap = hashMapOf<Long, BooksFragment>()
     override val groupId: Long get() = selectedGroup?.groupId ?: 0

+ 144 - 0
app/src/main/java/io/legato/kazusa/ui/main/bookshelf/style1/BookshelfFragment3.kt

@@ -0,0 +1,144 @@
+@file:Suppress("DEPRECATION")
+
+package io.legato.kazusa.ui.main.bookshelf.style1
+
+import android.annotation.SuppressLint
+import android.os.Bundle
+import android.view.View
+import androidx.appcompat.widget.SearchView
+import androidx.fragment.app.Fragment
+import androidx.viewpager2.adapter.FragmentStateAdapter
+import androidx.viewpager2.widget.ViewPager2
+import com.google.android.material.tabs.TabLayout
+import io.legato.kazusa.R
+import io.legato.kazusa.data.appDb
+import io.legato.kazusa.data.entities.Book
+import io.legato.kazusa.data.entities.BookGroup
+import io.legato.kazusa.databinding.FragmentBookshelf3Binding
+import io.legato.kazusa.help.config.AppConfig
+import io.legato.kazusa.ui.book.search.SearchActivity
+import io.legato.kazusa.ui.main.bookshelf.BaseBookshelfFragment
+import io.legato.kazusa.ui.main.bookshelf.style1.books.BooksFragment
+import io.legato.kazusa.utils.viewbindingdelegate.viewBinding
+import kotlin.collections.set
+
+/**
+ * 书架界面
+ */
+class BookshelfFragment3() : BaseBookshelfFragment(R.layout.fragment_bookshelf3),
+    TabLayout.OnTabSelectedListener,
+    SearchView.OnQueryTextListener {
+
+    constructor(position: Int) : this() {
+        val bundle = Bundle()
+        bundle.putInt("position", position)
+        arguments = bundle
+    }
+
+    private val binding by viewBinding(FragmentBookshelf3Binding::bind)
+
+    private val bookGroups = mutableListOf<BookGroup>()
+    private val fragmentMap = hashMapOf<Long, BooksFragment>()
+    override val groupId: Long get() = selectedGroup?.groupId ?: 0
+
+    private lateinit var adapter: TabFragmentPageAdapter
+
+    override val books: List<Book>
+        get() {
+            val fragment = fragmentMap[groupId]
+            return fragment?.getBooks() ?: emptyList()
+        }
+
+    override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
+        setSupportToolbar(binding.topBar)
+        initView()
+        initBookGroupData()
+    }
+
+    private val selectedGroup: BookGroup?
+        get() = bookGroups.getOrNull(binding.viewPagerBookshelf.currentItem)
+
+    private fun initView() {
+        adapter = TabFragmentPageAdapter(this)
+        binding.viewPagerBookshelf.adapter = adapter
+        binding.viewPagerBookshelf.offscreenPageLimit = 1
+
+        binding.viewPagerBookshelf.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
+            override fun onPageSelected(position: Int) {
+                super.onPageSelected(position)
+                updateToolbarTitle(position)
+            }
+        })
+    }
+
+    private fun updateToolbarTitle(position: Int) {
+        if (bookGroups.isNotEmpty() && position in 0 until bookGroups.size) {
+            binding.collTopBar.title = bookGroups[position].groupName
+        }
+    }
+
+    override fun onQueryTextSubmit(query: String?): Boolean {
+        SearchActivity.start(requireContext(), query)
+        return false
+    }
+
+    override fun onQueryTextChange(newText: String?): Boolean {
+        return false
+    }
+
+    @SuppressLint("NotifyDataSetChanged")
+    @Synchronized
+    override fun upGroup(data: List<BookGroup>) {
+        if (data.isEmpty()) {
+            appDb.bookGroupDao.enableGroup(BookGroup.IdAll)
+        } else {
+            if (data != bookGroups) {
+                bookGroups.clear()
+                bookGroups.addAll(data)
+                adapter.notifyDataSetChanged()
+                adapter = TabFragmentPageAdapter(this)
+                binding.viewPagerBookshelf.adapter = adapter
+
+                updateToolbarTitle(binding.viewPagerBookshelf.currentItem)
+            }
+        }
+    }
+
+    override fun upSort() {
+        childFragmentManager.fragments.forEach {
+            if (it is BooksFragment) {
+                val position = it.position
+                val group = bookGroups.getOrNull(position) ?: return@forEach
+                val newSort = group.getRealBookSort()
+                it.setEnableRefresh(group.enableRefresh)
+                if (it.bookSort != newSort) {
+                    it.upBookSort(newSort)
+                }
+            }
+        }
+    }
+
+    override fun onTabReselected(tab: TabLayout.Tab) {
+    }
+
+    override fun onTabUnselected(tab: TabLayout.Tab) = Unit
+
+    override fun onTabSelected(tab: TabLayout.Tab) {
+        AppConfig.saveTabPosition = tab.position
+    }
+
+    override fun gotoTop() {
+        fragmentMap[groupId]?.gotoTop()
+    }
+
+    private inner class TabFragmentPageAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
+        override fun getItemCount(): Int = bookGroups.size
+
+        override fun createFragment(position: Int): Fragment {
+            val group = bookGroups[position]
+            val fragment = BooksFragment(position, group)
+            fragmentMap[group.groupId] = fragment
+            return fragment
+        }
+    }
+}

+ 10 - 9
app/src/main/java/io/legato/kazusa/ui/main/bookshelf/style1/books/BooksAdapterList.kt

@@ -3,6 +3,7 @@ package io.legato.kazusa.ui.main.bookshelf.style1.books
 import android.content.Context
 import android.os.Bundle
 import android.view.ViewGroup
+import androidx.core.view.isVisible
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.Lifecycle
 import io.legato.kazusa.base.adapter.ItemViewHolder
@@ -10,7 +11,6 @@ import io.legato.kazusa.data.entities.Book
 import io.legato.kazusa.databinding.ItemBookshelfListBinding
 import io.legato.kazusa.help.book.isLocal
 import io.legato.kazusa.help.config.AppConfig
-import io.legato.kazusa.utils.invisible
 import io.legato.kazusa.utils.toTimeAgo
 import splitties.views.onLongClick
 
@@ -68,15 +68,16 @@ class BooksAdapterList(
 
     private fun upRefresh(binding: ItemBookshelfListBinding, item: Book) {
         if (!item.isLocal && callBack.isUpdate(item.bookUrl)) {
-            binding.bvUnread.invisible()
-            binding.rlLoading.visible()
+            binding.tvUnread.isVisible = false
+            binding.rlLoading.isVisible = true
         } else {
-            binding.rlLoading.gone()
+            binding.rlLoading.isVisible = false
             if (AppConfig.showUnread) {
-                binding.bvUnread.setHighlight(item.lastCheckCount > 0)
-                binding.bvUnread.setBadgeCount(item.getUnreadChapterNum())
+                //binding.tvUnread.setHighlight(item.lastCheckCount > 0)
+                binding.tvUnread.isVisible = true
+                binding.tvUnread.text = item.getUnreadChapterNum().toString()
             } else {
-                binding.bvUnread.invisible()
+                binding.tvUnread.isVisible = false
             }
         }
     }
@@ -94,13 +95,13 @@ class BooksAdapterList(
 
     override fun registerListener(holder: ItemViewHolder, binding: ItemBookshelfListBinding) {
         holder.itemView.apply {
-            setOnClickListener {
+            binding.cvContent.setOnClickListener {
                 getItem(holder.layoutPosition)?.let {
                     callBack.open(it)
                 }
             }
 
-            onLongClick {
+            binding.cvContent.onLongClick {
                 getItem(holder.layoutPosition)?.let {
                     callBack.openBookInfo(it)
                 }

+ 7 - 4
app/src/main/java/io/legato/kazusa/ui/main/bookshelf/style2/BaseBooksAdapter.kt

@@ -46,10 +46,7 @@ abstract class BaseBooksAdapter<VH : RecyclerView.ViewHolder>(
                             oldItem.getUnreadChapterNum() == newItem.getUnreadChapterNum()
                 }
 
-                oldItem is BookGroup && newItem is BookGroup -> {
-                    oldItem.groupName == newItem.groupName &&
-                            oldItem.cover == newItem.cover
-                }
+                oldItem is BookGroup && newItem is BookGroup -> false
 
                 else -> false
             }
@@ -120,6 +117,12 @@ abstract class BaseBooksAdapter<VH : RecyclerView.ViewHolder>(
 
     fun getItem(position: Int) = getItems().getOrNull(position)
 
+    internal var allBooks: List<Book> = emptyList()
+
+    fun setAllBooks(all: List<Book>) {
+        this.allBooks = all
+    }
+
     override fun getItemCount(): Int {
         return getItems().size
     }

+ 47 - 12
app/src/main/java/io/legato/kazusa/ui/main/bookshelf/style2/BooksAdapterGrid.kt

@@ -4,13 +4,16 @@ import android.content.Context
 import android.os.Bundle
 import android.view.View
 import android.view.ViewGroup
+import androidx.core.content.ContextCompat
 import androidx.recyclerview.widget.RecyclerView
+import io.legato.kazusa.R
 import io.legato.kazusa.data.entities.Book
 import io.legato.kazusa.data.entities.BookGroup
 import io.legato.kazusa.databinding.ItemBookshelfGridBinding
 import io.legato.kazusa.databinding.ItemBookshelfGridGroupBinding
 import io.legato.kazusa.help.book.isLocal
 import io.legato.kazusa.help.config.AppConfig
+import io.legato.kazusa.utils.gone
 import splitties.views.onLongClick
 
 @Suppress("UNUSED_PARAMETER")
@@ -32,31 +35,40 @@ class BooksAdapterGrid(context: Context, callBack: CallBack) :
         position: Int,
         payloads: MutableList<Any>
     ) {
+
         when (holder) {
             is BookViewHolder -> (getItem(position) as? Book)?.let {
                 holder.registerListener(it)
-                holder.onBind(it, position, payloads)
+                holder.onBind(it, payloads)
             }
 
             is GroupViewHolder -> (getItem(position) as? BookGroup)?.let {
                 holder.registerListener(it)
-                holder.onBind(it, position, payloads)
+                holder.onBind(it, payloads)
             }
         }
     }
 
+    private fun getBooksForGroup(group: BookGroup): List<Book> {
+        return if (group.groupId == -1L) {
+            allBooks.take(4)
+        } else {
+            allBooks.filter { (it.group and group.groupId) != 0L }.take(4)
+        }
+    }
+
     inner class BookViewHolder(val binding: ItemBookshelfGridBinding) :
         RecyclerView.ViewHolder(binding.root) {
 
-        fun onBind(item: Book, position: Int) = binding.run {
+        fun onBind(item: Book) = binding.run {
             tvName.text = item.name
             ivCover.load(item.getDisplayCover(), item.name, item.author, false, item.origin)
             upRefresh(this, item)
         }
 
-        fun onBind(item: Book, position: Int, payloads: MutableList<Any>) = binding.run {
+        fun onBind(item: Book, payloads: MutableList<Any>) = binding.run {
             if (payloads.isEmpty()) {
-                onBind(item, position)
+                onBind(item)
             } else {
                 for (i in payloads.indices) {
                     val bundle = payloads[i] as Bundle
@@ -79,10 +91,10 @@ class BooksAdapterGrid(context: Context, callBack: CallBack) :
         }
 
         fun registerListener(item: Any) {
-            binding.root.setOnClickListener {
+            binding.cvContent.setOnClickListener {
                 callBack.onItemClick(item)
             }
-            binding.root.onLongClick {
+            binding.cvContent.onLongClick {
                 callBack.onItemLongClick(item)
             }
         }
@@ -113,21 +125,22 @@ class BooksAdapterGrid(context: Context, callBack: CallBack) :
     inner class GroupViewHolder(val binding: ItemBookshelfGridGroupBinding) :
         RecyclerView.ViewHolder(binding.root) {
 
-        fun onBind(item: BookGroup, position: Int) = binding.run {
+        fun onBind(item: BookGroup) = binding.run {
             tvName.text = item.groupName
-            ivCover.load(item.cover)
+            loadGroupCover(item)
+            cdUnread.gone()
         }
 
-        fun onBind(item: BookGroup, position: Int, payloads: MutableList<Any>) = binding.run {
+        fun onBind(item: BookGroup, payloads: MutableList<Any>) = binding.run {
             if (payloads.isEmpty()) {
-                onBind(item, position)
+                onBind(item)
             } else {
                 for (i in payloads.indices) {
                     val bundle = payloads[i] as Bundle
                     bundle.keySet().forEach {
                         when (it) {
                             "groupName" -> tvName.text = item.groupName
-                            "cover" -> ivCover.load(item.cover)
+                            "cover" -> loadGroupCover(item)
                         }
                     }
                 }
@@ -143,6 +156,28 @@ class BooksAdapterGrid(context: Context, callBack: CallBack) :
             }
         }
 
+        private fun loadGroupCover(item: BookGroup) = binding.run {
+
+            binding.ivCover.setImageDrawable(
+                ContextCompat.getDrawable(binding.root.context, R.drawable.bg_surface_variant)
+            )
+            binding.ivCover1.setImageDrawable(null)
+            binding.ivCover2.setImageDrawable(null)
+            binding.ivCover3.setImageDrawable(null)
+            binding.ivCover4.setImageDrawable(null)
+            if (!item.cover.isNullOrEmpty()) {
+                ivCover.load(item.cover)
+            } else {
+
+                val books = getBooksForGroup(item)
+
+                if (books.size > 0) binding.ivCover1.load(books[0].getDisplayCover())
+                if (books.size > 1) binding.ivCover2.load(books[1].getDisplayCover())
+                if (books.size > 2) binding.ivCover3.load(books[2].getDisplayCover())
+                if (books.size > 3) binding.ivCover4.load(books[3].getDisplayCover())
+            }
+        }
+
     }
 
 }

+ 71 - 29
app/src/main/java/io/legato/kazusa/ui/main/bookshelf/style2/BooksAdapterList.kt

@@ -3,7 +3,10 @@ package io.legato.kazusa.ui.main.bookshelf.style2
 import android.content.Context
 import android.os.Bundle
 import android.view.ViewGroup
+import androidx.core.content.ContextCompat
+import androidx.core.view.isVisible
 import androidx.recyclerview.widget.RecyclerView
+import io.legato.kazusa.R
 import io.legato.kazusa.data.entities.Book
 import io.legato.kazusa.data.entities.BookGroup
 import io.legato.kazusa.databinding.ItemBookshelfListBinding
@@ -11,7 +14,7 @@ import io.legato.kazusa.databinding.ItemBookshelfListGroupBinding
 import io.legato.kazusa.help.book.isLocal
 import io.legato.kazusa.help.config.AppConfig
 import io.legato.kazusa.utils.gone
-import io.legato.kazusa.utils.invisible
+import io.legato.kazusa.utils.toTimeAgo
 import io.legato.kazusa.utils.visible
 import splitties.views.onLongClick
 
@@ -34,35 +37,44 @@ class BooksAdapterList(context: Context, callBack: CallBack) :
         when (holder) {
             is BookViewHolder -> (getItem(position) as? Book)?.let {
                 holder.registerListener(it)
-                holder.onBind(it, position, payloads)
+                holder.onBind(it, payloads)
             }
 
             is GroupViewHolder -> (getItem(position) as? BookGroup)?.let {
                 holder.registerListener(it)
-                holder.onBind(it, position, payloads)
+                holder.onBind(it, payloads)
             }
         }
     }
 
+    private fun getBooksForGroup(group: BookGroup): List<Book> {
+        return if (group.groupId == -1L) {
+            allBooks.take(4)
+        } else {
+            allBooks.filter { (it.group and group.groupId) != 0L }.take(4)
+        }
+    }
+
     inner class BookViewHolder(val binding: ItemBookshelfListBinding) :
         RecyclerView.ViewHolder(binding.root) {
 
-        fun onBind(item: Book, position: Int) = binding.run {
+        fun onBind(item: Book) = binding.run {
             tvName.text = item.name
             tvAuthor.text = item.author
             tvRead.text = item.durChapterTitle
             tvLast.text = item.latestChapterTitle
             ivCover.load(item.getDisplayCover(), item.name, item.author, false, item.origin)
-            flHasNew.visible()
+            cdUnread.visible()
             ivAuthor.visible()
             ivLast.visible()
             ivRead.visible()
             upRefresh(this, item)
+            upLastUpdateTime(binding, item)
         }
 
-        fun onBind(item: Book, position: Int, payloads: MutableList<Any>) = binding.run {
+        fun onBind(item: Book, payloads: MutableList<Any>) = binding.run {
             if (payloads.isEmpty()) {
-                onBind(item, position)
+                onBind(item)
             } else {
                 for (i in payloads.indices) {
                     val bundle = payloads[i] as Bundle
@@ -81,32 +93,45 @@ class BooksAdapterList(context: Context, callBack: CallBack) :
                             )
 
                             "refresh" -> upRefresh(this, item)
+                            "lastUpdateTime" -> upLastUpdateTime(binding, item)
                         }
                     }
                 }
             }
         }
 
+        private fun upLastUpdateTime(binding: ItemBookshelfListBinding, item: Book) {
+            if (AppConfig.showLastUpdateTime && !item.isLocal) {
+                val time = item.latestChapterTime.toTimeAgo()
+                if (binding.tvLastUpdateTime.text != time) {
+                    binding.tvLastUpdateTime.text = time
+                }
+            } else {
+                binding.tvLastUpdateTime.text = ""
+            }
+        }
+
         fun registerListener(item: Any) {
-            binding.root.setOnClickListener {
+            binding.cvContent.setOnClickListener {
                 callBack.onItemClick(item)
             }
-            binding.root.onLongClick {
+            binding.cvContent.onLongClick {
                 callBack.onItemLongClick(item)
             }
         }
 
         private fun upRefresh(binding: ItemBookshelfListBinding, item: Book) {
             if (!item.isLocal && callBack.isUpdate(item.bookUrl)) {
-                binding.bvUnread.invisible()
-                binding.rlLoading.visible()
+                binding.tvUnread.isVisible = false
+                binding.rlLoading.isVisible = true
             } else {
-                binding.rlLoading.gone()
+                binding.rlLoading.isVisible = false
                 if (AppConfig.showUnread) {
-                    binding.bvUnread.setHighlight(item.lastCheckCount > 0)
-                    binding.bvUnread.setBadgeCount(item.getUnreadChapterNum())
+                    //binding.tvUnread.setHighlight(item.lastCheckCount > 0)
+                    binding.tvUnread.isVisible = true
+                    binding.tvUnread.text = item.getUnreadChapterNum().toString()
                 } else {
-                    binding.bvUnread.invisible()
+                    binding.tvUnread.isVisible = false
                 }
             }
         }
@@ -116,28 +141,23 @@ class BooksAdapterList(context: Context, callBack: CallBack) :
     inner class GroupViewHolder(val binding: ItemBookshelfListGroupBinding) :
         RecyclerView.ViewHolder(binding.root) {
 
-        fun onBind(item: BookGroup, position: Int) = binding.run {
+        fun onBind(item: BookGroup) = binding.run {
             tvName.text = item.groupName
-            ivCover.load(item.cover)
-            flHasNew.gone()
-            ivAuthor.gone()
-            ivLast.gone()
-            ivRead.gone()
-            tvAuthor.gone()
-            tvLast.gone()
-            tvRead.gone()
+            loadGroupCover(item)
+            cdUnread.gone()
         }
 
-        fun onBind(item: BookGroup, position: Int, payloads: MutableList<Any>) = binding.run {
+
+        fun onBind(item: BookGroup, payloads: MutableList<Any>) = binding.run {
             if (payloads.isEmpty()) {
-                onBind(item, position)
+                onBind(item)
             } else {
                 for (i in payloads.indices) {
                     val bundle = payloads[i] as Bundle
                     bundle.keySet().forEach {
                         when (it) {
                             "groupName" -> tvName.text = item.groupName
-                            "cover" -> ivCover.load(item.cover)
+                            "cover" -> loadGroupCover(item)
                         }
                     }
                 }
@@ -145,14 +165,36 @@ class BooksAdapterList(context: Context, callBack: CallBack) :
         }
 
         fun registerListener(item: Any) {
-            binding.root.setOnClickListener {
+            binding.cvContent.setOnClickListener {
                 callBack.onItemClick(item)
             }
-            binding.root.onLongClick {
+            binding.cvContent.onLongClick {
                 callBack.onItemLongClick(item)
             }
         }
 
+        private fun loadGroupCover(item: BookGroup) = binding.run {
+
+            binding.ivCover.setImageDrawable(
+                ContextCompat.getDrawable(binding.root.context, R.drawable.bg_surface_variant)
+            )
+            binding.ivCover1.setImageDrawable(null)
+            binding.ivCover2.setImageDrawable(null)
+            binding.ivCover3.setImageDrawable(null)
+            binding.ivCover4.setImageDrawable(null)
+            if (!item.cover.isNullOrEmpty()) {
+                ivCover.load(item.cover)
+            } else {
+
+                val books = getBooksForGroup(item)
+
+                if (books.size > 0) binding.ivCover1.load(books[0].getDisplayCover())
+                if (books.size > 1) binding.ivCover2.load(books[1].getDisplayCover())
+                if (books.size > 2) binding.ivCover3.load(books[2].getDisplayCover())
+                if (books.size > 3) binding.ivCover4.load(books[3].getDisplayCover())
+            }
+        }
+
     }
 
 }

+ 41 - 10
app/src/main/java/io/legato/kazusa/ui/main/bookshelf/style2/BookshelfFragment2.kt

@@ -7,6 +7,7 @@ import androidx.appcompat.widget.SearchView
 import androidx.core.view.isGone
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
+import androidx.recyclerview.widget.DefaultItemAnimator
 import androidx.recyclerview.widget.GridLayoutManager
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
@@ -19,8 +20,6 @@ import io.legato.kazusa.data.entities.Book
 import io.legato.kazusa.data.entities.BookGroup
 import io.legato.kazusa.databinding.FragmentBookshelf2Binding
 import io.legato.kazusa.help.config.AppConfig
-//import io.legado.app.lib.theme.accentColor
-//import io.legado.app.lib.theme.primaryColor
 import io.legato.kazusa.ui.book.group.GroupEditDialog
 import io.legato.kazusa.ui.book.info.BookInfoActivity
 import io.legato.kazusa.ui.book.search.SearchActivity
@@ -35,7 +34,6 @@ import io.legato.kazusa.utils.startActivityForBook
 import io.legato.kazusa.utils.viewbindingdelegate.viewBinding
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Job
-import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.catch
 import kotlinx.coroutines.flow.conflate
 import kotlinx.coroutines.flow.flowOn
@@ -67,9 +65,11 @@ class BookshelfFragment2() : BaseBookshelfFragment(R.layout.fragment_bookshelf2)
             BooksAdapterGrid(requireContext(), this)
         }
     }
+
     private var bookGroups: List<BookGroup> = emptyList()
     private var booksFlowJob: Job? = null
     override var groupId = BookGroup.IdRoot
+    private var allBooks: List<Book> = emptyList()
     override var books: List<Book> = emptyList()
     private var enableRefresh = true
 
@@ -77,12 +77,11 @@ class BookshelfFragment2() : BaseBookshelfFragment(R.layout.fragment_bookshelf2)
         setSupportToolbar(binding.titleBar.toolbar)
         initRecyclerView()
         initBookGroupData()
+        initAllBooksData()
         initBooksData()
     }
 
     private fun initRecyclerView() {
-//        binding.rvBookshelf.setEdgeEffectColor(primaryColor)
-//        binding.refreshLayout.setColorSchemeColors(accentColor)
         binding.refreshLayout.setOnRefreshListener {
             binding.refreshLayout.isRefreshing = false
             activityViewModel.upToc(books)
@@ -111,6 +110,7 @@ class BookshelfFragment2() : BaseBookshelfFragment(R.layout.fragment_bookshelf2)
                 }
             }
         })
+        binding.rvBookshelf.itemAnimator = DefaultItemAnimator()
     }
 
     override fun upGroup(data: List<BookGroup>) {
@@ -123,9 +123,37 @@ class BookshelfFragment2() : BaseBookshelfFragment(R.layout.fragment_bookshelf2)
     }
 
     override fun upSort() {
+        initAllBooksData()
         initBooksData()
     }
 
+    private var allBooksFlowJob: Job? = null
+
+    private fun initAllBooksData() {
+        allBooksFlowJob?.cancel()
+        allBooksFlowJob = viewLifecycleOwner.lifecycleScope.launch {
+            appDb.bookDao.flowAll().map { list ->
+                when (AppConfig.getBookSortByGroupId(BookGroup.IdRoot)) {
+                    1 -> list.sortedByDescending { it.latestChapterTime }
+                    2 -> list.sortedWith { o1, o2 -> o1.name.cnCompare(o2.name) }
+                    3 -> list.sortedBy { it.order }
+                    4 -> list.sortedByDescending { max(it.latestChapterTime, it.durChapterTime) }
+                    else -> list.sortedByDescending { it.durChapterTime }
+                }
+            }.flowWithLifecycleAndDatabaseChangeFirst(
+                viewLifecycleOwner.lifecycle,
+                Lifecycle.State.RESUMED,
+                AppDatabase.BOOK_TABLE_NAME
+            ).catch {
+                AppLog.put("所有书籍更新出错", it)
+            }.conflate().flowOn(Dispatchers.Default).collect { list ->
+                allBooks = list
+                booksAdapter.setAllBooks(allBooks)
+                booksAdapter.updateItems()
+            }
+        }
+    }
+
     private fun initBooksData() {
         if (groupId == BookGroup.IdRoot) {
             if (isAdded) {
@@ -137,14 +165,16 @@ class BookshelfFragment2() : BaseBookshelfFragment(R.layout.fragment_bookshelf2)
             bookGroups.firstOrNull {
                 groupId == it.groupId
             }?.let {
-                binding.titleBar.title = "${getString(R.string.bookshelf)}(${it.groupName})"
+                binding.titleBar.title = it.groupName
                 binding.refreshLayout.isEnabled = it.enableRefresh
                 enableRefresh = it.enableRefresh
             }
         }
         booksFlowJob?.cancel()
         booksFlowJob = viewLifecycleOwner.lifecycleScope.launch {
+
             appDb.bookDao.flowByGroup(groupId).map { list ->
+
                 //排序
                 when (AppConfig.getBookSortByGroupId(groupId)) {
                     1 -> list.sortedByDescending {
@@ -178,7 +208,6 @@ class BookshelfFragment2() : BaseBookshelfFragment(R.layout.fragment_bookshelf2)
                 booksAdapter.updateItems()
                 binding.tvEmptyMsg.isGone = getItemCount() > 0
                 binding.refreshLayout.isEnabled = enableRefresh && getItemCount() > 0
-                delay(100)
             }
         }
     }
@@ -244,12 +273,14 @@ class BookshelfFragment2() : BaseBookshelfFragment(R.layout.fragment_bookshelf2)
     }
 
     override fun getItems(): List<Any> {
-        if (groupId != BookGroup.IdRoot) {
-            return books
+        return if (groupId == BookGroup.IdRoot) {
+            bookGroups + books
+        } else {
+            books
         }
-        return bookGroups + books
     }
 
+
     @SuppressLint("NotifyDataSetChanged")
     override fun observeLiveBus() {
         super.observeLiveBus()

+ 2 - 4
app/src/main/java/io/legato/kazusa/ui/widget/LabelsBar.kt

@@ -2,11 +2,10 @@ package io.legato.kazusa.ui.widget
 
 import android.content.Context
 import android.content.res.ColorStateList
-import android.graphics.Color
 import android.util.AttributeSet
 import com.google.android.material.chip.Chip
 import com.google.android.material.chip.ChipGroup
-import com.google.android.material.color.MaterialColors
+import io.legato.kazusa.utils.themeColor
 
 @Suppress("unused", "MemberVisibilityCanBePrivate")
 class LabelsBar @JvmOverloads constructor(
@@ -44,10 +43,9 @@ class LabelsBar @JvmOverloads constructor(
                 chipStrokeWidth = 0f
 
                 chipBackgroundColor = ColorStateList.valueOf(
-                    MaterialColors.getColor(this, com.google.android.material.R.attr.colorPrimaryContainer, Color.LTGRAY)
+                    context.themeColor(com.google.android.material.R.attr.colorPrimaryContainer)
                 )
 
-
                 usedChips.add(this)
             }
         } else {

+ 1 - 1
app/src/main/res/anim/anim_readbook_bottom_in.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:duration="320"
+    android:duration="280"
     android:interpolator="@android:interpolator/decelerate_cubic">
     <translate
         android:fromYDelta="100%"

+ 1 - 1
app/src/main/res/anim/anim_readbook_top_in.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:duration="320"
+    android:duration="280"
     android:interpolator="@android:interpolator/decelerate_cubic">
     <translate
         android:fromYDelta="-100%"

+ 1 - 1
app/src/main/res/drawable/bg_book_mask.xml

@@ -2,5 +2,5 @@
 <!-- res/drawable/bg_semi_transparent_overlay.xml -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="rectangle">
-    <solid android:color="@color/transparent70" />
+    <solid android:color="@color/transparent50" />
 </shape>

+ 6 - 0
app/src/main/res/drawable/bg_book_mask_land.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- res/drawable/bg_semi_transparent_overlay.xml -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@color/transparent70" />
+</shape>

+ 0 - 8
app/src/main/res/drawable/bg_gradient.xml

@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <!--实现应用背景颜色渐变-->
-    <gradient
-        android:startColor="#000000"
-        android:endColor="#00000000"
-        android:angle="90" />
-</shape>

+ 1 - 1
app/src/main/res/drawable/bg_gradient_mask_land.xml

@@ -2,7 +2,7 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="rectangle">
 
-    <solid android:color="?attr/colorSurfaceContainer" />
+    <solid android:color="?attr/colorSurface" />
 
     <corners
         android:topLeftRadius="16dp"

+ 4 - 0
app/src/main/res/drawable/bg_surface_variant.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="?attr/colorSurfaceContainerHigh" />
+</shape>

+ 248 - 237
app/src/main/res/layout-land/activity_book_info.xml

@@ -10,7 +10,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:contentDescription="@string/bg_image"
-        android:foreground="@drawable/bg_book_mask"
+        android:foreground="@drawable/bg_book_mask_land"
         android:scaleType="centerCrop" />
 
     <com.google.android.material.appbar.AppBarLayout
@@ -45,306 +45,317 @@
         android:layout_height="match_parent"
         app:layout_behavior="@string/appbar_scrolling_view_behavior">
 
-        <LinearLayout
-            android:layout_width="match_parent"
+        <androidx.core.widget.NestedScrollView
+            android:id="@+id/scroll_view"
+            android:layout_width="0dp"
             android:layout_height="match_parent"
-            android:orientation="horizontal">
+            android:layout_weight="2"
+            android:fillViewport="true"
+            android:fitsSystemWindows="false"
+            android:focusable="true"
+            android:padding="0dp"
+            android:scrollbarStyle="outsideOverlay"
+            android:scrollbars="vertical">
 
             <LinearLayout
-                android:layout_width="wrap_content"
+                android:layout_width="match_parent"
                 android:layout_height="match_parent"
-                android:orientation="vertical"
-                android:paddingHorizontal="32dp"
-                tools:ignore="RtlSymmetry">
-
-                <androidx.cardview.widget.CardView
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_marginEnd="120dp"
-                    android:layout_marginStart="16dp"
-                    android:layout_marginVertical="16dp"
-                    app:cardCornerRadius="16dp">
-
-                    <io.legato.kazusa.ui.widget.image.CoverImageView
-                        android:id="@+id/iv_cover"
-                        android:layout_width="150dp"
-                        android:layout_height="210dp"
-                        android:contentDescription="@string/img_cover"
-                        android:scaleType="centerCrop"
-                        android:src="@drawable/image_cover_default" />
-
-                </androidx.cardview.widget.CardView>
+                android:baselineAligned="false"
+                android:orientation="horizontal">
 
                 <LinearLayout
-                    android:layout_width="match_parent"
+                    android:layout_width="0dp"
                     android:layout_height="match_parent"
-                    android:layout_gravity="center"
+                    android:layout_weight="1"
                     android:orientation="vertical"
-                    android:padding="12dp">
+                    android:paddingHorizontal="32dp"
+                    tools:ignore="RtlSymmetry">
 
-                    <TextView
-                        android:id="@+id/tv_name"
+                    <androidx.cardview.widget.CardView
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
-                        android:text="@string/book_name"
-                        style="@style/TextAppearance.Material3.ActionBar.Title"
-                        tools:ignore="RtlHardcoded" />
+                        android:layout_marginTop="16dp"
+                        android:layout_marginStart="32dp"
+                        app:cardCornerRadius="16dp">
+
+                        <io.legato.kazusa.ui.widget.image.CoverImageView
+                            android:id="@+id/iv_cover"
+                            android:layout_width="150dp"
+                            android:layout_height="210dp"
+                            android:contentDescription="@string/img_cover"
+                            android:scaleType="centerCrop"
+                            android:src="@drawable/image_cover_default" />
+
+                    </androidx.cardview.widget.CardView>
 
                     <LinearLayout
                         android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:gravity="center"
-                        android:orientation="horizontal"
-                        android:paddingTop="6dp"
-                        android:paddingBottom="2dp"
-                        tools:ignore="UseCompoundDrawables">
-
-                        <ImageView
-                            android:layout_width="18sp"
-                            android:layout_height="18sp"
-                            android:contentDescription="@string/author"
-                            android:src="@drawable/ic_author"
-                            tools:ignore="RtlHardcoded,RtlSymmetry" />
+                        android:layout_height="match_parent"
+                        android:layout_gravity="center"
+                        android:orientation="vertical"
+                        android:paddingTop="16dp"
+                        android:paddingHorizontal="30dp">
 
                         <TextView
-                            android:id="@+id/tv_author"
-                            android:layout_width="0dp"
+                            android:id="@+id/tv_name"
+                            android:layout_width="wrap_content"
                             android:layout_height="wrap_content"
-                            android:layout_weight="1"
-                            android:layout_marginStart="2dp"
-                            android:ellipsize="end"
-                            android:singleLine="true"
-                            android:text="@string/author"
-                            android:textSize="14sp"
-                            tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry" />
-
-                    </LinearLayout>
+                            android:text="@string/book_name"
+                            style="@style/TextAppearance.Material3.ActionBar.Title"
+                            tools:ignore="RtlHardcoded" />
 
-                    <LinearLayout
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:gravity="center_vertical"
-                        android:orientation="horizontal">
+                        <LinearLayout
+                            android:layout_width="match_parent"
+                            android:layout_height="wrap_content"
+                            android:gravity="center"
+                            android:orientation="horizontal"
+                            android:paddingTop="6dp"
+                            android:paddingBottom="2dp"
+                            tools:ignore="UseCompoundDrawables">
+
+                            <ImageView
+                                android:layout_width="18sp"
+                                android:layout_height="18sp"
+                                android:contentDescription="@string/author"
+                                android:src="@drawable/ic_author"
+                                tools:ignore="RtlHardcoded,RtlSymmetry" />
+
+                            <TextView
+                                android:id="@+id/tv_author"
+                                android:layout_width="0dp"
+                                android:layout_height="wrap_content"
+                                android:layout_weight="1"
+                                android:layout_marginStart="2dp"
+                                android:ellipsize="end"
+                                android:singleLine="true"
+                                android:text="@string/author"
+                                android:textSize="14sp"
+                                tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry" />
 
-                        <ImageView
-                            android:id="@+id/iv_web"
-                            android:layout_width="18sp"
-                            android:layout_height="18sp"
-                            android:contentDescription="@string/origin_format"
-                            android:src="@drawable/ic_web_outline"
-                            tools:ignore="RtlHardcoded,RtlSymmetry" />
+                        </LinearLayout>
 
-                        <TextView
-                            android:id="@+id/tv_origin"
-                            android:layout_width="0dp"
+                        <LinearLayout
+                            android:layout_width="match_parent"
                             android:layout_height="wrap_content"
-                            android:layout_weight="1"
-                            android:layout_marginStart="2dp"
-                            android:ellipsize="end"
-                            android:paddingRight="6dp"
-                            android:singleLine="true"
-                            android:textSize="14sp"
-                            tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry"
-                            tools:text="@string/origin_format" />
+                            android:gravity="center_vertical"
+                            android:orientation="horizontal"
+                            tools:ignore="UseCompoundDrawables">
+
+                            <ImageView
+                                android:id="@+id/iv_web"
+                                android:layout_width="18sp"
+                                android:layout_height="18sp"
+                                android:contentDescription="@string/origin_format"
+                                android:src="@drawable/ic_web_outline"
+                                tools:ignore="RtlHardcoded,RtlSymmetry" />
+
+                            <TextView
+                                android:id="@+id/tv_origin"
+                                android:layout_width="0dp"
+                                android:layout_height="wrap_content"
+                                android:layout_weight="1"
+                                android:layout_marginStart="2dp"
+                                android:ellipsize="end"
+                                android:paddingRight="6dp"
+                                android:singleLine="true"
+                                android:textSize="14sp"
+                                tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry"
+                                tools:text="@string/origin_format" />
 
+                        </LinearLayout>
                     </LinearLayout>
                 </LinearLayout>
-            </LinearLayout>
 
-            <androidx.core.widget.NestedScrollView
-                android:id="@+id/scroll_view"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="1"
-                android:fillViewport="true"
-                android:fitsSystemWindows="false"
-                android:focusable="true"
-                android:padding="0dp"
-                android:scrollbarStyle="outsideOverlay"
-                android:scrollbars="vertical">
+                <LinearLayout
+                    android:id="@+id/ll_info"
+                    android:layout_width="0dp"
+                    android:layout_height="match_parent"
+                    android:layout_weight="2"
+                    android:background="@drawable/bg_gradient_mask_land"
+                    android:orientation="vertical"
+                    android:padding="8dp">
+
+                    <HorizontalScrollView
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:layout_marginStart="8dp"
+                        android:scrollbars="none">
+
+                        <io.legato.kazusa.ui.widget.LabelsBar
+                            android:id="@+id/lb_kind"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:visibility="visible" />
+
+                    </HorizontalScrollView>
 
                     <LinearLayout
-                        android:id="@+id/ll_info"
                         android:layout_width="match_parent"
-                        android:layout_height="match_parent"
-                        android:background="@drawable/bg_gradient_mask_land"
+                        android:layout_height="wrap_content"
                         android:orientation="vertical"
-                        android:layout_marginStart="16dp"
-                        android:padding="8dp">
+                        android:paddingHorizontal="8dp"
+                        android:paddingBottom="8dp">
 
-                        <HorizontalScrollView
+                        <LinearLayout
                             android:layout_width="match_parent"
                             android:layout_height="wrap_content"
-                            android:layout_marginStart="8dp"
-                            android:scrollbars="none">
+                            android:orientation="horizontal">
 
-                            <io.legato.kazusa.ui.widget.LabelsBar
-                                android:id="@+id/lb_kind"
+                            <com.google.android.material.button.MaterialButton
+                                android:id="@+id/btn_shelf"
+                                style="@style/Widget.Material3.Button.TonalButton"
+                                android:backgroundTint="?attr/colorSurfaceContainer"
+                                app:cornerRadius="16dp"
+                                android:layout_width="wrap_content"
+                                android:layout_height="wrap_content"
+                                android:layout_marginEnd="6dp"
+                                android:padding="8dp"
+                                android:text="@string/remove_from_bookshelf"
+                                android:textSize="13sp"
+                                app:icon="@drawable/ic_clear_all"
+                                app:iconGravity="top"
+                                app:iconPadding="4dp"
+                                app:iconSize="20dp" />
+
+                            <!-- 查看目录按钮 -->
+                            <com.google.android.material.button.MaterialButton
+                                android:id="@+id/tv_toc_view"
+                                style="@style/Widget.Material3.Button.TonalButton"
+                                android:backgroundTint="?attr/colorSurfaceContainer"
+                                app:cornerRadius="16dp"
+                                android:layout_width="wrap_content"
+                                android:layout_height="wrap_content"
+                                android:layout_marginHorizontal="8dp"
+                                android:padding="8dp"
+                                android:text="@string/view_toc"
+                                android:textSize="13sp"
+                                app:icon="@drawable/ic_toc"
+                                app:iconGravity="top"
+                                app:iconPadding="4dp"
+                                app:iconSize="20dp" />
+
+                            <!-- 更换分组按钮 -->
+                            <com.google.android.material.button.MaterialButton
+                                android:id="@+id/tv_change_group"
+                                style="@style/Widget.Material3.Button.TonalButton"
+                                android:backgroundTint="?attr/colorSurfaceContainer"
+                                app:cornerRadius="16dp"
+                                android:layout_width="wrap_content"
+                                android:layout_height="wrap_content"
+                                android:layout_marginHorizontal="8dp"
+                                android:padding="8dp"
+                                android:text="@string/change_group"
+                                android:textSize="13sp"
+                                app:icon="@drawable/ic_groups"
+                                app:iconGravity="top"
+                                app:iconPadding="4dp"
+                                app:iconSize="20dp" />
+
+                            <!-- 更换来源按钮(保持原样) -->
+                            <com.google.android.material.button.MaterialButton
+                                android:id="@+id/btn_change_source"
+                                style="@style/Widget.Material3.Button.TonalButton"
+                                android:backgroundTint="?attr/colorSurfaceContainer"
+                                app:cornerRadius="16dp"
                                 android:layout_width="wrap_content"
                                 android:layout_height="wrap_content"
-                                android:visibility="visible" />
+                                android:layout_marginStart="8dp"
+                                android:text="@string/book_source"
+                                android:padding="8dp"
+                                app:icon="@drawable/ic_change_source"
+                                android:textSize="13sp"
+                                app:iconGravity="top"
+                                app:iconPadding="4dp"
+                                app:iconSize="20dp"
+                                tools:contentDescription="@string/change_origin" />
 
-                        </HorizontalScrollView>
+                        </LinearLayout>
 
                         <LinearLayout
                             android:layout_width="match_parent"
                             android:layout_height="wrap_content"
-                            android:orientation="vertical"
-                            android:paddingHorizontal="8dp"
-                            android:paddingBottom="8dp">
+                            android:paddingVertical="16dp"
+                            android:paddingHorizontal="2dp"
+                            android:orientation="vertical">
 
-                            <LinearLayout
+                            <TextView
+                                android:id="@+id/tv_toc"
                                 android:layout_width="match_parent"
                                 android:layout_height="wrap_content"
-                                android:orientation="horizontal">
-
-                                <com.google.android.material.button.MaterialButton
-                                    android:id="@+id/btn_shelf"
-                                    style="@style/Widget.Material3.Button.TonalButton"
-                                    android:layout_width="wrap_content"
-                                    android:layout_height="wrap_content"
-                                    android:layout_marginEnd="6dp"
-                                    android:padding="8dp"
-                                    android:text="@string/remove_from_bookshelf"
-                                    android:textSize="13sp"
-                                    app:icon="@drawable/ic_clear_all"
-                                    app:iconGravity="top"
-                                    app:iconPadding="4dp"
-                                    app:iconSize="20dp" />
-
-                                <!-- 查看目录按钮 -->
-                                <com.google.android.material.button.MaterialButton
-                                    android:id="@+id/tv_toc_view"
-                                    style="@style/Widget.Material3.Button.TonalButton"
-                                    android:layout_width="wrap_content"
-                                    android:layout_height="wrap_content"
-                                    android:layout_marginHorizontal="8dp"
-                                    android:padding="8dp"
-                                    android:text="@string/view_toc"
-                                    android:textSize="13sp"
-                                    app:icon="@drawable/ic_toc"
-                                    app:iconGravity="top"
-                                    app:iconPadding="4dp"
-                                    app:iconSize="20dp" />
-
-                                <!-- 更换分组按钮 -->
-                                <com.google.android.material.button.MaterialButton
-                                    android:id="@+id/tv_change_group"
-                                    style="@style/Widget.Material3.Button.TonalButton"
-                                    android:layout_width="wrap_content"
-                                    android:layout_height="wrap_content"
-                                    android:layout_marginHorizontal="8dp"
-                                    android:padding="8dp"
-                                    android:text="@string/change_group"
-                                    android:textSize="13sp"
-                                    app:icon="@drawable/ic_groups"
-                                    app:iconGravity="top"
-                                    app:iconPadding="4dp"
-                                    app:iconSize="20dp" />
-
-                                <!-- 更换来源按钮(保持原样) -->
-                                <com.google.android.material.button.MaterialButton
-                                    android:id="@+id/btn_change_source"
-                                    style="@style/Widget.Material3.Button.TonalButton"
-                                    android:layout_width="wrap_content"
-                                    android:layout_height="wrap_content"
-                                    android:layout_marginStart="8dp"
-                                    android:text="@string/book_source"
-                                    android:padding="8dp"
-                                    app:icon="@drawable/ic_change_source"
-                                    android:textSize="13sp"
-                                    app:iconGravity="top"
-                                    app:iconPadding="4dp"
-                                    app:iconSize="20dp"
-                                    tools:contentDescription="@string/change_origin" />
+                                android:paddingBottom="6dp"
+                                android:textSize="16sp"
+                                android:textStyle="bold"
+                                tools:text="@string/toc_s" />
 
-                            </LinearLayout>
+                            <TextView
+                                android:id="@+id/tv_lasted"
+                                android:layout_width="wrap_content"
+                                android:layout_height="wrap_content"
+                                android:paddingBottom="6dp"
+                                android:singleLine="true"
+                                android:textSize="14sp"
+                                tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry"
+                                tools:text="@string/read_dur_progress" />
 
                             <LinearLayout
                                 android:layout_width="match_parent"
                                 android:layout_height="wrap_content"
-                                android:paddingVertical="8dp"
-                                android:paddingHorizontal="2dp"
-                                android:orientation="vertical">
+                                android:orientation="horizontal">
 
                                 <TextView
-                                    android:id="@+id/tv_toc"
-                                    android:layout_width="match_parent"
+                                    android:id="@+id/tv_chapter"
+                                    android:layout_width="wrap_content"
                                     android:layout_height="wrap_content"
-                                    android:paddingBottom="6dp"
-                                    android:textSize="16sp"
+                                    android:textColor="?attr/colorPrimary"
+                                    android:singleLine="true"
+                                    android:paddingEnd="8dp"
+                                    android:textSize="14sp"
                                     android:textStyle="bold"
-                                    tools:text="@string/toc_s" />
+                                    tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry"
+                                    tools:text="@string/read_chapter_total" />
+
+                                <TextView
+                                    android:layout_width="wrap_content"
+                                    android:layout_height="match_parent"
+                                    android:text="|"
+                                    android:textColor="?attr/colorSecondary"
+                                    android:layout_marginEnd="8dp"/>
 
                                 <TextView
-                                    android:id="@+id/tv_lasted"
+                                    android:id="@+id/tv_chapter_index"
                                     android:layout_width="wrap_content"
                                     android:layout_height="wrap_content"
-                                    android:paddingBottom="6dp"
+                                    android:textColor="?attr/colorSecondary"
                                     android:singleLine="true"
                                     android:textSize="14sp"
                                     tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry"
-                                    tools:text="@string/read_dur_progress" />
-
-                                <LinearLayout
-                                    android:layout_width="match_parent"
-                                    android:layout_height="wrap_content"
-                                    android:orientation="horizontal">
-
-                                    <TextView
-                                        android:id="@+id/tv_chapter"
-                                        android:layout_width="wrap_content"
-                                        android:layout_height="wrap_content"
-                                        android:textColor="?attr/colorSecondaryVariant"
-                                        android:singleLine="true"
-                                        android:paddingEnd="8dp"
-                                        android:textSize="14sp"
-                                        android:textStyle="bold"
-                                        tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry"
-                                        tools:text="@string/read_chapter_total" />
-
-                                    <TextView
-                                        android:layout_width="wrap_content"
-                                        android:layout_height="match_parent"
-                                        android:text="|"
-                                        android:textColor="?attr/colorSecondary"
-                                        android:layout_marginEnd="8dp"/>
-
-                                    <TextView
-                                        android:id="@+id/tv_chapter_index"
-                                        android:layout_width="wrap_content"
-                                        android:layout_height="wrap_content"
-                                        android:textColor="?attr/colorSecondary"
-                                        android:singleLine="true"
-                                        android:textSize="14sp"
-                                        tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry"
-                                        tools:text="@string/read_chapter_index"/>
-                                </LinearLayout>
-
+                                    tools:text="@string/read_chapter_index"/>
                             </LinearLayout>
 
                         </LinearLayout>
 
-                        <TextView
-                            android:id="@+id/tv_detail"
-                            android:layout_width="match_parent"
-                            android:layout_height="wrap_content"
-                            android:layout_marginTop="8dp"
-                            android:layout_marginHorizontal="8dp"
-                            android:clickable="true"
-                            android:focusable="true"
-                            android:minHeight="48dp"
-                            android:paddingBottom="80dp"
-                            android:lineSpacingExtra="4dp"
-                            android:text="@string/book_intro"
-                            android:textIsSelectable="true"
-                            android:textSize="14sp"
-                            android:visibility="visible"
-                            tools:ignore="RtlHardcoded,RtlSymmetry" />
-
                     </LinearLayout>
-            </androidx.core.widget.NestedScrollView>
-        </LinearLayout>
+
+                    <TextView
+                        android:id="@+id/tv_detail"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="8dp"
+                        android:layout_marginHorizontal="8dp"
+                        android:clickable="true"
+                        android:focusable="true"
+                        android:minHeight="48dp"
+                        android:paddingBottom="80dp"
+                        android:lineSpacingExtra="4dp"
+                        android:text="@string/book_intro"
+                        android:textIsSelectable="true"
+                        android:textSize="14sp"
+                        android:visibility="visible"
+                        tools:ignore="RtlHardcoded,RtlSymmetry" />
+
+                </LinearLayout>
+            </LinearLayout>
+        </androidx.core.widget.NestedScrollView>
     </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
 
     <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton

+ 3 - 4
app/src/main/res/layout/activity_book_info.xml

@@ -22,8 +22,8 @@
 
         <com.google.android.material.appbar.CollapsingToolbarLayout
             android:layout_width="match_parent"
-            android:layout_height="?attr/collapsingToolbarLayoutLargeSize"
-            style="?attr/collapsingToolbarLayoutLargeSize"
+            android:layout_height="?attr/collapsingToolbarLayoutMediumSize"
+            style="?attr/collapsingToolbarLayoutMediumStyle"
             app:collapsedTitleTextAppearance="?attr/textAppearanceTitleLarge"
             app:expandedTitleTextAppearance="?attr/textAppearanceHeadlineLarge"
             app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
@@ -275,7 +275,6 @@
                                 android:layout_width="wrap_content"
                                 android:layout_height="wrap_content"
                                 android:paddingBottom="6dp"
-                                android:singleLine="true"
                                 android:textSize="14sp"
                                 tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry"
                                 tools:text="@string/read_dur_progress" />
@@ -289,7 +288,7 @@
                                     android:id="@+id/tv_chapter"
                                     android:layout_width="wrap_content"
                                     android:layout_height="wrap_content"
-                                    android:textColor="?attr/colorSecondaryVariant"
+                                    android:textColor="?attr/colorPrimaryVariant"
                                     android:singleLine="true"
                                     android:paddingEnd="8dp"
                                     android:textSize="14sp"

+ 14 - 5
app/src/main/res/layout/dialog_book_group_edit.xml

@@ -19,7 +19,7 @@
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:orientation="vertical"
+        android:orientation="horizontal"
         android:gravity="center_horizontal"
         android:padding="16dp">
 
@@ -30,7 +30,7 @@
             android:layout_margin="8dp"
             android:contentDescription="@string/img_cover"
             android:scaleType="centerCrop"
-            android:src="@drawable/image_cover_default"
+            android:src="?attr/colorSurfaceVariant"
             tools:ignore="ImageContrastCheck" />
 
         <LinearLayout
@@ -80,6 +80,7 @@
                 android:layout_height="wrap_content"
                 android:text="@string/allow_drop_down_refresh"
                 android:checked="true" />
+
         </LinearLayout>
     </LinearLayout>
 
@@ -95,10 +96,18 @@
             android:id="@+id/btn_delete"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            style="@style/Widget.Material3.Button.OutlinedButton"
-            android:text="@string/delete"
+            style="@style/Widget.Material3.Button.IconButton.Outlined"
+            android:layout_marginEnd="4dp"
+            app:icon="@drawable/ic_clear_all"
             tools:ignore="RtlHardcoded" />
 
+        <com.google.android.material.button.MaterialButton
+            android:id="@+id/btn_reset"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:icon="@drawable/ic_restore"
+            style="@style/Widget.Material3.Button.IconButton.Outlined"/>
+
         <Space
             android:layout_width="0dp"
             android:layout_height="1dp"
@@ -109,7 +118,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             style="@style/Widget.Material3.Button.TonalButton"
-            android:layout_marginEnd="8dp"
+            android:layout_marginEnd="4dp"
             android:text="@string/cancel" />
 
         <com.google.android.material.button.MaterialButton

+ 62 - 107
app/src/main/res/layout/dialog_bookshelf_config.xml

@@ -9,14 +9,21 @@
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:animateLayoutChanges="true"
         android:orientation="vertical"
         android:padding="16dp">
 
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/bookshelf_layout"
+            android:layout_marginVertical="8dp"
+            style="@style/ChapterNameText"/>
+
         <com.google.android.material.materialswitch.MaterialSwitch
             android:id="@+id/sw_show_unread"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:padding="8dp"
             android:text="@string/show_unread"
             tools:ignore="TouchTargetSizeCheck" />
 
@@ -24,7 +31,6 @@
             android:id="@+id/sw_show_last_update_time"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:padding="8dp"
             android:text="@string/show_last_update_time"
             tools:ignore="TouchTargetSizeCheck" />
 
@@ -32,7 +38,6 @@
             android:id="@+id/sw_show_wait_up_books"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:padding="8dp"
             android:text="@string/show_wait_up_count"
             tools:ignore="TouchTargetSizeCheck" />
 
@@ -40,31 +45,35 @@
             android:id="@+id/sw_show_bookshelf_fast_scroller"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:padding="8dp"
             android:text="@string/show_bookshelf_fast_scroller"
             tools:ignore="TouchTargetSizeCheck" />
 
-        <LinearLayout
-            android:id="@+id/ll_group_style"
+
+        <TextView
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:orientation="horizontal">
+            android:paddingTop="8dp"
+            android:text="@string/group_style"
+            android:textSize="14sp"
+            android:textStyle="bold" />
 
-            <TextView
-                android:layout_width="0dp"
-                android:layout_height="wrap_content"
-                android:layout_weight="1"
-                android:paddingHorizontal="6dp"
-                android:paddingVertical="16dp"
-                android:text="@string/group_style" />
+        <com.google.android.material.chip.ChipGroup
+            android:id="@+id/chip_group_style"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:checkableBehavior="single"
+            android:paddingBottom="8dp"
+            app:singleSelection="true"
+            app:selectionRequired="true">
+        </com.google.android.material.chip.ChipGroup>
 
-            <Spinner
-                android:id="@+id/sp_group_style"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:entries="@array/group_style"
-                tools:ignore="TouchTargetSizeCheck" />
-        </LinearLayout>
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="排列视图"
+            android:textSize="14sp"
+            android:textStyle="bold"
+            android:paddingTop="8dp" />
 
         <com.google.android.material.chip.ChipGroup
             android:id="@+id/chip_group_layout"
@@ -72,8 +81,7 @@
             android:layout_height="wrap_content"
             app:singleSelection="true"
             app:selectionRequired="true"
-            android:paddingVertical="8dp"
-            android:paddingHorizontal="4dp">
+            android:paddingBottom="8dp">
 
             <com.google.android.material.chip.Chip
                 android:id="@+id/chip_list"
@@ -90,95 +98,42 @@
                 android:text="网格" />
         </com.google.android.material.chip.ChipGroup>
 
-        <LinearLayout
-            android:id="@+id/ll_grid_slider"
+        <TextView
+            android:id="@+id/slider_text"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:orientation="vertical"
-            android:visibility="visible">
+            android:text="每行列数"
+            android:textSize="14sp"
+            android:textStyle="bold"
+            android:paddingTop="8dp" />
 
-            <TextView
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:text="每行列数:"
-                android:textSize="14sp"
-                android:paddingVertical="8dp"
-                android:paddingHorizontal="4dp"/>
-
-            <com.google.android.material.slider.Slider
-                android:id="@+id/slider_grid_count"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:paddingVertical="8dp"
-                android:valueFrom="1"
-                android:valueTo="9"
-                android:stepSize="1"
-                android:value="3" />
-        </LinearLayout>
-
-        <LinearLayout
+        <com.google.android.material.slider.Slider
+            android:id="@+id/slider_grid_count"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:baselineAligned="false"
-            android:orientation="horizontal">
+            android:paddingVertical="8dp"
+            android:valueFrom="1"
+            android:valueTo="9"
+            android:stepSize="1"
+            android:value="3" />
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingTop="8dp"
+            android:text="@string/sort"
+            android:textSize="14sp"
+            android:textStyle="bold" />
+
+        <com.google.android.material.chip.ChipGroup
+            android:id="@+id/chip_group_sort"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:singleSelection="true"
+            app:selectionRequired="true"
+            android:layout_marginTop="4dp"
+            android:layout_marginBottom="4dp"
+            android:orientation="horizontal" />
 
-            <LinearLayout
-                android:id="@+id/ll_sort"
-                android:layout_width="0dp"
-                android:layout_height="wrap_content"
-                android:layout_weight="1"
-                android:orientation="vertical">
-
-                <TextView
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:padding="6dp"
-                    android:text="@string/sort"
-                    android:textSize="16sp" />
-
-                <RadioGroup
-                    android:id="@+id/rg_sort"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:orientation="vertical">
-
-                    <com.google.android.material.radiobutton.MaterialRadioButton
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:text="@string/bookshelf_px_0"
-                        tools:ignore="TouchTargetSizeCheck" />
-
-                    <com.google.android.material.radiobutton.MaterialRadioButton
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:text="@string/bookshelf_px_1"
-                        tools:ignore="TouchTargetSizeCheck" />
-
-                    <com.google.android.material.radiobutton.MaterialRadioButton
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:text="@string/bookshelf_px_2"
-                        tools:ignore="TouchTargetSizeCheck" />
-
-                    <com.google.android.material.radiobutton.MaterialRadioButton
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:text="@string/bookshelf_px_3"
-                        tools:ignore="TouchTargetSizeCheck" />
-
-                    <com.google.android.material.radiobutton.MaterialRadioButton
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:text="@string/bookshelf_px_4"
-                        tools:ignore="TouchTargetSizeCheck" />
-
-                    <com.google.android.material.radiobutton.MaterialRadioButton
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:text="@string/bookshelf_px_5"
-                        tools:ignore="TouchTargetSizeCheck" />
-                </RadioGroup>
-            </LinearLayout>
-        </LinearLayout>
     </LinearLayout>
 </ScrollView>

+ 2 - 2
app/src/main/res/layout/fragment_bookshelf1.xml

@@ -16,8 +16,8 @@
 
         <com.google.android.material.appbar.CollapsingToolbarLayout
             android:layout_width="match_parent"
-            android:layout_height="?attr/collapsingToolbarLayoutLargeSize"
-            style="?attr/collapsingToolbarLayoutLargeSize"
+            android:layout_height="?attr/collapsingToolbarLayoutMediumSize"
+            style="?attr/collapsingToolbarLayoutMediumStyle"
             app:collapsedTitleTextAppearance="?attr/textAppearanceTitleLarge"
             app:expandedTitleTextAppearance="?attr/textAppearanceHeadlineLarge"
             app:contentScrim="?attr/colorSurface"

+ 2 - 1
app/src/main/res/layout/fragment_bookshelf2.xml

@@ -18,10 +18,11 @@
         android:id="@+id/refresh_layout"
         android:layout_width="match_parent"
         android:layout_height="0dp"
+        android:paddingHorizontal="8dp"
         app:layout_constraintTop_toBottomOf="@+id/title_bar"
         app:layout_constraintBottom_toBottomOf="parent">
 
-        <io.legato.kazusa.ui.widget.recycler.RecyclerViewAtPager2
+        <androidx.recyclerview.widget.RecyclerView
             android:id="@+id/rv_bookshelf"
             android:layout_width="match_parent"
             android:layout_height="match_parent"

+ 45 - 0
app/src/main/res/layout/fragment_bookshelf3.xml

@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.coordinatorlayout.widget.CoordinatorLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <com.google.android.material.appbar.AppBarLayout
+        android:id="@+id/title_bar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:fitsSystemWindows="true"
+        app:liftOnScroll="false"
+        android:stateListAnimator="@null">
+
+        <com.google.android.material.appbar.CollapsingToolbarLayout
+            android:id="@+id/coll_top_bar"
+            android:layout_width="match_parent"
+            android:layout_height="?attr/collapsingToolbarLayoutLargeSize"
+            style="?attr/collapsingToolbarLayoutLargeStyle"
+            app:collapsedTitleTextAppearance="?attr/textAppearanceTitleLarge"
+            app:expandedTitleTextAppearance="?attr/textAppearanceHeadlineLarge"
+            app:titleCollapseMode="scale"
+            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
+
+            <com.google.android.material.appbar.MaterialToolbar
+                android:id="@+id/top_bar"
+                android:layout_width="match_parent"
+                android:layout_height="?attr/actionBarSize"
+                app:title="@string/bookshelf"
+                app:layout_collapseMode="pin" />
+
+        </com.google.android.material.appbar.CollapsingToolbarLayout>
+
+    </com.google.android.material.appbar.AppBarLayout>
+
+    <!-- ViewPager -->
+    <androidx.viewpager2.widget.ViewPager2
+        android:id="@+id/view_pager_bookshelf"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:paddingHorizontal="8dp"
+        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
+
+</androidx.coordinatorlayout.widget.CoordinatorLayout>

+ 128 - 66
app/src/main/res/layout/item_bookshelf_grid_group.xml

@@ -2,88 +2,150 @@
 <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
-    android:id="@+id/cv_content"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:clickable="true"
-    android:focusable="true"
-    tools:ignore="UnusedAttribute">
+    android:layout_height="wrap_content">
 
-
-    <LinearLayout
+    <com.google.android.material.card.MaterialCardView
+        android:id="@+id/cv_content"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:orientation="vertical"
-        android:paddingTop="8dp"
-        android:paddingLeft="8dp"
-        android:paddingRight="8dp"
+        android:layout_marginBottom="4dp"
+        android:foreground="?attr/selectableItemBackground"
+        android:clickable="true"
+        android:focusable="true"
+        app:strokeWidth="0dp"
         app:layout_constraintTop_toTopOf="parent">
 
-        <androidx.constraintlayout.widget.ConstraintLayout
+        <LinearLayout
             android:layout_width="match_parent"
-            android:layout_height="wrap_content">
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
 
-            <io.legato.kazusa.ui.widget.image.CoverImageView
-                android:id="@+id/iv_cover"
+            <androidx.constraintlayout.widget.ConstraintLayout
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_margin="8dp"
-                android:scaleType="centerCrop"
-                android:src="@drawable/image_cover_default"
-                android:transitionName="img_cover"
-                app:layout_constraintTop_toTopOf="parent"
-                tools:ignore="UnusedAttribute,ImageContrastCheck"
-                tools:layout_editor_absoluteX="8dp" />
+                android:layout_height="wrap_content">
 
-            <io.legato.kazusa.ui.widget.text.BadgeView
-                android:id="@+id/bv_unread"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="right"
-                android:includeFontPadding="false"
-                app:layout_constraintRight_toRightOf="parent"
-                app:layout_constraintTop_toTopOf="parent"
-                tools:ignore="RtlHardcoded" />
+                <io.legato.kazusa.ui.widget.image.CoverImageView
+                    android:id="@+id/iv_cover"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:layout_margin="8dp"
+                    android:scaleType="centerCrop"
+                    android:src="?attr/colorSurfaceVariant"
+                    android:transitionName="img_cover"
+                    app:layout_constraintTop_toTopOf="parent"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    tools:ignore="ImageContrastCheck" />
 
-            <io.legato.kazusa.ui.widget.anima.RotateLoading
-                android:id="@+id/rl_loading"
-                android:layout_width="22dp"
-                android:layout_height="22dp"
-                android:layout_gravity="right"
-                android:visibility="invisible"
-                app:layout_constraintRight_toRightOf="parent"
-                app:layout_constraintTop_toTopOf="parent"
-                app:loading_width="2dp"
-                tools:ignore="RtlHardcoded" />
+                <com.google.android.material.progressindicator.LinearProgressIndicator
+                    android:id="@+id/rl_loading"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginVertical="4dp"
+                    android:layout_marginHorizontal="12dp"
+                    android:indeterminate="true"
+                    android:visibility="invisible"
+                    android:layout_gravity="top"
+                    app:layout_constraintTop_toTopOf="@id/iv_cover"
+                    app:layout_constraintStart_toStartOf="@id/iv_cover"
+                    app:layout_constraintEnd_toEndOf="@id/iv_cover" />
 
-        </androidx.constraintlayout.widget.ConstraintLayout>
+                <com.google.android.material.card.MaterialCardView
+                    android:id="@+id/cd_unread"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_margin="4dp"
+                    android:visibility="visible"
+                    app:cardCornerRadius="4dp"
+                    app:strokeWidth="0dp"
+                    app:cardBackgroundColor="?attr/colorSecondaryVariant"
+                    app:layout_constraintTop_toTopOf="@id/iv_cover"
+                    app:layout_constraintEnd_toEndOf="@id/iv_cover">
 
-        <TextView
-            android:id="@+id/tv_name"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="6dp"
-            android:ellipsize="end"
-            android:gravity="top|center_horizontal"
-            android:includeFontPadding="false"
-            android:lines="2"
-            android:text="@string/book_name"
-            android:textSize="12sp"
-            tools:ignore="RtlHardcoded,RtlSymmetry" />
+                    <TextView
+                        android:id="@+id/tv_unread"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:textColor="?attr/colorSurface"
+                        android:textSize="10sp"
+                        android:padding="4dp"
+                        android:textStyle="bold"
+                        android:includeFontPadding="false"
+                        tools:text="5"
+                        tools:ignore="SmallSp" />
 
-    </LinearLayout>
+                </com.google.android.material.card.MaterialCardView>
 
-    <View
-        android:id="@+id/vw_foreground"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:background="?android:attr/selectableItemBackground"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintVertical_bias="0.0"
-        tools:layout_editor_absoluteX="0dp" />
+                <io.legato.kazusa.ui.widget.image.CoverImageView
+                    android:id="@+id/iv_cover1"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="4dp"
+                    android:layout_marginBottom="2dp"
+                    android:layout_marginStart="4dp"
+                    android:layout_marginEnd="2dp"
+                    app:layout_constraintTop_toTopOf="@id/iv_cover"
+                    app:layout_constraintStart_toStartOf="@id/iv_cover"
+                    app:layout_constraintEnd_toStartOf="@id/iv_cover2"
+                    app:layout_constraintBottom_toTopOf="@id/iv_cover3"/>
 
-</androidx.constraintlayout.widget.ConstraintLayout>
+                <io.legato.kazusa.ui.widget.image.CoverImageView
+                    android:id="@+id/iv_cover2"
+                    android:layout_width="0dp"
+                    android:layout_height="match_parent"
+                    android:layout_marginTop="4dp"
+                    android:layout_marginBottom="2dp"
+                    android:layout_marginEnd="4dp"
+                    android:layout_marginStart="2dp"
+                    app:layout_constraintTop_toTopOf="@id/iv_cover"
+                    app:layout_constraintEnd_toEndOf="@id/iv_cover"
+                    app:layout_constraintStart_toEndOf="@id/iv_cover1"
+                    app:layout_constraintBottom_toTopOf="@id/iv_cover4"/>
 
+                <io.legato.kazusa.ui.widget.image.CoverImageView
+                    android:id="@+id/iv_cover3"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="2dp"
+                    android:layout_marginStart="4dp"
+                    android:layout_marginBottom="4dp"
+                    android:layout_marginEnd="2dp"
+                    app:layout_constraintStart_toStartOf="@id/iv_cover"
+                    app:layout_constraintBottom_toBottomOf="@id/iv_cover"
+                    app:layout_constraintTop_toBottomOf="@id/iv_cover1"
+                    app:layout_constraintEnd_toStartOf="@id/iv_cover4"/>
 
+                <io.legato.kazusa.ui.widget.image.CoverImageView
+                    android:id="@+id/iv_cover4"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="2dp"
+                    android:layout_marginEnd="4dp"
+                    android:layout_marginBottom="4dp"
+                    android:layout_marginStart="2dp"
+                    app:layout_constraintBottom_toBottomOf="@id/iv_cover"
+                    app:layout_constraintEnd_toEndOf="@id/iv_cover"
+                    app:layout_constraintTop_toBottomOf="@id/iv_cover2"
+                    app:layout_constraintStart_toEndOf="@id/iv_cover3"/>
 
+            </androidx.constraintlayout.widget.ConstraintLayout>
+
+            <!-- 书名 -->
+            <TextView
+                android:id="@+id/tv_name"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="8dp"
+                android:layout_marginBottom="4dp"
+                android:layout_marginHorizontal="8dp"
+                android:ellipsize="end"
+                android:gravity="left"
+                android:lines="2"
+                android:text="@string/book_name"
+                android:textSize="12sp"
+                android:textStyle="bold"
+                tools:ignore="RtlHardcoded" />
+        </LinearLayout>
+    </com.google.android.material.card.MaterialCardView>
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 189 - 186
app/src/main/res/layout/item_bookshelf_list.xml

@@ -1,196 +1,199 @@
 <?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
-    android:id="@+id/cv_content"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:background="@drawable/bg_item_focused_on_tv"
-    android:clickable="true"
-    android:focusable="true"
-    android:scrollbars="none"
-    tools:ignore="UnusedAttribute">
-
-    <io.legato.kazusa.ui.widget.image.CoverImageView
-        android:id="@+id/iv_cover"
-        android:layout_width="66dp"
-        android:layout_height="90dp"
-        android:layout_marginStart="16dp"
-        android:layout_marginTop="16dp"
-        android:layout_marginBottom="12dp"
-        android:contentDescription="@string/img_cover"
-        android:scaleType="centerCrop"
-        android:src="@drawable/image_cover_default"
-        android:transitionName="img_cover"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintLeft_toLeftOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
-        tools:ignore="UnusedAttribute" />
-
-    <FrameLayout
-        android:id="@+id/fl_has_new"
-        android:layout_width="wrap_content"
+    android:layout_marginVertical="4dp"
+    app:strokeWidth="0dp">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/cv_content"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginEnd="16dp"
-        app:layout_constraintBottom_toBottomOf="@+id/tv_name"
-        app:layout_constraintRight_toRightOf="parent"
-        app:layout_constraintTop_toTopOf="@+id/tv_name">
+        android:clickable="true"
+        android:focusable="true"
+        android:background="?attr/selectableItemBackground"
+        tools:ignore="UnusedAttribute">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:id="@+id/cl_cover"
+            android:layout_width="70dp"
+            android:layout_height="98dp"
+            android:layout_marginStart="8dp"
+            android:layout_marginVertical="8dp"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent">
+            <!-- 书封面 -->
+            <io.legato.kazusa.ui.widget.image.CoverImageView
+                android:id="@+id/iv_cover"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:scaleType="centerCrop"
+                android:src="@drawable/image_cover_default"
+                android:transitionName="img_cover"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                tools:ignore="ImageContrastCheck" />
+
+            <com.google.android.material.progressindicator.LinearProgressIndicator
+                android:id="@+id/rl_loading"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginVertical="4dp"
+                android:layout_marginHorizontal="6dp"
+                android:indeterminate="true"
+                android:visibility="invisible"
+                android:layout_gravity="top"
+                app:layout_constraintTop_toTopOf="@id/iv_cover"
+                app:layout_constraintStart_toStartOf="@id/iv_cover"
+                app:layout_constraintEnd_toEndOf="@id/iv_cover" />
+
+            <com.google.android.material.card.MaterialCardView
+                android:id="@+id/cd_unread"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_margin="4dp"
+                android:visibility="visible"
+                app:cardCornerRadius="4dp"
+                app:strokeWidth="0dp"
+                app:cardBackgroundColor="?attr/colorSecondaryVariant"
+                app:layout_constraintTop_toTopOf="@id/iv_cover"
+                app:layout_constraintEnd_toEndOf="@id/iv_cover">
+
+                <TextView
+                    android:id="@+id/tv_unread"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textColor="?attr/colorSurface"
+                    android:textSize="10sp"
+                    android:padding="4dp"
+                    android:textStyle="bold"
+                    android:includeFontPadding="false"
+                    tools:text="5"
+                    tools:ignore="SmallSp" />
+
+            </com.google.android.material.card.MaterialCardView>
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+        <TextView
+            android:id="@+id/tv_name"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:scrollbars="none"
+            android:singleLine="true"
+            android:layout_marginTop="4dp"
+            android:layout_marginHorizontal="8dp"
+            android:text="@string/book_name"
+            style="@style/ChapterNameText"
+            tools:ignore="RtlHardcoded,RtlSymmetry"
+            app:layout_constraintStart_toEndOf="@+id/cl_cover"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="@id/cl_cover" />
 
-        <io.legato.kazusa.ui.widget.text.BadgeView
-            android:id="@+id/bv_unread"
+        <androidx.appcompat.widget.AppCompatImageView
+            android:id="@+id/iv_author"
+            android:layout_width="18dp"
+            android:layout_height="18dp"
+            android:layout_marginTop="2dp"
+            android:contentDescription="@string/author"
+            android:scrollbars="none"
+            android:src="@drawable/ic_author"
+            tools:ignore="RtlHardcoded,RtlSymmetry"
+            app:layout_constraintStart_toStartOf="@+id/tv_name"
+            app:layout_constraintTop_toBottomOf="@+id/tv_name"/>
+
+        <TextView
+            android:id="@+id/tv_author"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_gravity="right"
-            android:layout_margin="5dp"
-            android:includeFontPadding="false"
+            android:layout_marginStart="4dp"
+            android:maxLines="1"
             android:scrollbars="none"
-            tools:ignore="RtlHardcoded" />
-
-        <io.legato.kazusa.ui.widget.anima.RotateLoading
-            android:id="@+id/rl_loading"
-            android:layout_width="26dp"
-            android:layout_height="26dp"
-            android:layout_gravity="right"
-            android:visibility="invisible"
-            app:loading_width="2dp"
-            tools:ignore="RtlHardcoded" />
-    </FrameLayout>
-
-    <TextView
-        android:id="@+id/tv_name"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_marginLeft="10dp"
-        android:layout_marginTop="12dp"
-        android:includeFontPadding="false"
-        android:paddingLeft="2dp"
-        android:paddingBottom="4dp"
-        android:scrollbars="none"
-        android:singleLine="true"
-        android:text="@string/book_name"
-        android:textSize="16sp"
-        app:layout_constraintBottom_toTopOf="@+id/tv_author"
-        app:layout_constraintLeft_toRightOf="@+id/iv_cover"
-        app:layout_constraintRight_toLeftOf="@id/fl_has_new"
-        app:layout_constraintTop_toTopOf="parent"
-        tools:ignore="RtlHardcoded,RtlSymmetry" />
-
-    <androidx.appcompat.widget.AppCompatImageView
-        android:id="@+id/iv_author"
-        android:layout_width="@dimen/desc_icon_size"
-        android:layout_height="@dimen/desc_icon_size"
-        android:contentDescription="@string/author"
-        android:paddingStart="2dp"
-        android:paddingEnd="2dp"
-        android:scrollbars="none"
-        android:src="@drawable/ic_author"
-        app:layout_constraintBottom_toBottomOf="@+id/tv_author"
-        app:layout_constraintLeft_toLeftOf="@+id/tv_name"
-        app:layout_constraintTop_toTopOf="@+id/tv_author"
-        tools:ignore="RtlHardcoded,RtlSymmetry" />
-
-    <TextView
-        android:id="@+id/tv_author"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:includeFontPadding="false"
-        android:maxLines="1"
-        android:paddingEnd="6dp"
-        android:scrollbars="none"
-        android:singleLine="true"
-        android:text="@string/author"
-        android:textSize="13sp"
-        app:layout_constraintBottom_toTopOf="@+id/tv_read"
-        app:layout_constraintLeft_toRightOf="@+id/iv_author"
-        app:layout_constraintRight_toLeftOf="@id/tv_last_update_time"
-        app:layout_constraintTop_toBottomOf="@+id/tv_name"
-        tools:ignore="RtlSymmetry,TextContrastCheck" />
-
-    <TextView
-        android:id="@+id/tv_last_update_time"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:includeFontPadding="false"
-        android:maxLines="1"
-        android:paddingEnd="6dp"
-        android:scrollbars="none"
-        android:singleLine="true"
-        android:text=""
-        android:textSize="13sp"
-        app:layout_constraintBottom_toTopOf="@+id/tv_read"
-        app:layout_constraintHorizontal_bias="0.0"
-        app:layout_constraintLeft_toRightOf="@+id/tv_author"
-        app:layout_constraintRight_toRightOf="@id/fl_has_new"
-        app:layout_constraintTop_toBottomOf="@+id/tv_name"
-        tools:ignore="RtlSymmetry,TextContrastCheck" />
-
-    <androidx.appcompat.widget.AppCompatImageView
-        android:id="@+id/iv_read"
-        android:layout_width="@dimen/desc_icon_size"
-        android:layout_height="@dimen/desc_icon_size"
-        android:contentDescription="@string/read_dur_progress"
-        android:paddingStart="2dp"
-        android:paddingEnd="2dp"
-        android:scrollbars="none"
-        android:src="@drawable/ic_history"
-        app:layout_constraintBottom_toBottomOf="@+id/tv_read"
-        app:layout_constraintLeft_toLeftOf="@+id/tv_name"
-        app:layout_constraintTop_toTopOf="@+id/tv_read"
-        tools:ignore="RtlHardcoded,RtlSymmetry" />
-
-    <TextView
-        android:id="@+id/tv_read"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:includeFontPadding="false"
-        android:scrollbars="none"
-        android:singleLine="true"
-        android:text="@string/read_dur_progress"
-        android:textSize="13sp"
-        app:layout_constraintBottom_toTopOf="@id/tv_last"
-        app:layout_constraintLeft_toRightOf="@+id/iv_read"
-        app:layout_constraintRight_toRightOf="@+id/fl_has_new"
-        app:layout_constraintTop_toBottomOf="@+id/tv_author"
-        tools:ignore="TextContrastCheck" />
-
-    <androidx.appcompat.widget.AppCompatImageView
-        android:id="@+id/iv_last"
-        android:layout_width="@dimen/desc_icon_size"
-        android:layout_height="@dimen/desc_icon_size"
-        android:contentDescription="@string/lasted_show"
-        android:paddingStart="2dp"
-        android:paddingEnd="2dp"
-        android:scrollbars="none"
-        android:src="@drawable/ic_book_last"
-        app:layout_constraintBottom_toBottomOf="@+id/tv_last"
-        app:layout_constraintLeft_toLeftOf="@+id/tv_name"
-        app:layout_constraintTop_toTopOf="@+id/tv_last"
-        tools:ignore="RtlHardcoded,RtlSymmetry" />
-
-    <TextView
-        android:id="@+id/tv_last"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="8dp"
-        android:scrollbars="none"
-        android:singleLine="true"
-        android:text="@string/lasted_show"
-        android:textSize="13sp"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintLeft_toRightOf="@+id/iv_last"
-        app:layout_constraintRight_toRightOf="@id/fl_has_new"
-        app:layout_constraintTop_toBottomOf="@+id/tv_read"
-        tools:ignore="TextContrastCheck" />
-
-    <View
-        android:id="@+id/vw_foreground"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:background="?android:attr/selectableItemBackground"
-        android:scrollbars="none"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintVertical_bias="0.0"
-        tools:layout_editor_absoluteX="0dp" />
-
-</androidx.constraintlayout.widget.ConstraintLayout>
+            android:singleLine="true"
+            android:text="@string/author"
+            android:textSize="13sp"
+            tools:ignore="RtlSymmetry,TextContrastCheck"
+            app:layout_constraintStart_toEndOf="@+id/iv_author"
+            app:layout_constraintBottom_toTopOf="@+id/iv_author"
+            app:layout_constraintTop_toBottomOf="@+id/iv_author" />
+
+        <TextView
+            android:id="@+id/tv_last_update_time"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_marginEnd="8dp"
+            android:maxLines="1"
+            android:scrollbars="none"
+            android:singleLine="true"
+            android:text="123"
+            android:textSize="13sp"
+            app:layout_constraintBottom_toBottomOf="@+id/tv_author"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="@+id/tv_author" />
+
+        <androidx.appcompat.widget.AppCompatImageView
+            android:id="@+id/iv_read"
+            android:layout_width="18dp"
+            android:layout_height="18dp"
+            android:layout_marginTop="4dp"
+            android:contentDescription="@string/read_dur_progress"
+            android:scrollbars="none"
+            android:src="@drawable/ic_history"
+            tools:ignore="RtlHardcoded,RtlSymmetry"
+            app:layout_constraintStart_toStartOf="@+id/tv_name"
+            app:layout_constraintTop_toBottomOf="@id/tv_author" />
+
+        <TextView
+            android:id="@+id/tv_read"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="4dp"
+            android:layout_marginEnd="8dp"
+            android:layout_weight="1"
+            android:scrollbars="none"
+            android:singleLine="true"
+            android:text="@string/read_dur_progress"
+            android:textSize="13sp"
+            tools:ignore="TextContrastCheck"
+            app:layout_constraintBottom_toTopOf="@id/iv_read"
+            app:layout_constraintStart_toEndOf="@+id/iv_read"
+            app:layout_constraintTop_toBottomOf="@+id/iv_read"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+        <androidx.appcompat.widget.AppCompatImageView
+            android:id="@+id/iv_last"
+            android:layout_width="18dp"
+            android:layout_height="18dp"
+            android:layout_marginTop="4dp"
+            android:contentDescription="@string/lasted_show"
+            android:scrollbars="none"
+            android:src="@drawable/ic_book_last"
+            tools:ignore="RtlHardcoded,RtlSymmetry"
+            app:layout_constraintStart_toStartOf="@+id/tv_name"
+            app:layout_constraintTop_toBottomOf="@id/iv_read" />
+
+        <TextView
+            android:id="@+id/tv_last"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="4dp"
+            android:layout_marginEnd="8dp"
+            android:layout_weight="1"
+            android:scrollbars="none"
+            android:singleLine="true"
+            android:text="@string/lasted_show"
+            android:textSize="13sp"
+            tools:ignore="TextContrastCheck"
+            app:layout_constraintBottom_toTopOf="@id/iv_last"
+            app:layout_constraintStart_toEndOf="@+id/iv_last"
+            app:layout_constraintTop_toBottomOf="@+id/iv_last"
+            app:layout_constraintEnd_toEndOf="parent"/>
+
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</com.google.android.material.card.MaterialCardView>

+ 134 - 147
app/src/main/res/layout/item_bookshelf_list_group.xml

@@ -1,165 +1,152 @@
 <?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
-    android:id="@+id/cv_content"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:clickable="true"
-    android:focusable="true"
-    tools:ignore="UnusedAttribute">
+    android:layout_marginVertical="4dp"
+    app:strokeWidth="0dp">
 
-    <io.legato.kazusa.ui.widget.image.CoverImageView
-        android:id="@+id/iv_cover"
-        android:layout_width="66dp"
-        android:layout_height="90dp"
-        android:layout_marginStart="16dp"
-        android:layout_marginTop="16dp"
-        android:layout_marginBottom="12dp"
-        android:contentDescription="@string/img_cover"
-        android:scaleType="centerCrop"
-        android:src="@drawable/image_cover_default"
-        android:transitionName="img_cover"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintLeft_toLeftOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
-        tools:ignore="UnusedAttribute" />
-
-    <FrameLayout
-        android:id="@+id/fl_has_new"
-        android:layout_width="wrap_content"
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/cv_content"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginEnd="16dp"
-        app:layout_constraintRight_toRightOf="parent"
-        app:layout_constraintTop_toTopOf="@+id/iv_cover">
+        android:clickable="true"
+        android:focusable="true"
+        android:background="?attr/selectableItemBackground"
+        tools:ignore="UnusedAttribute">
 
-        <io.legato.kazusa.ui.widget.text.BadgeView
-            android:id="@+id/bv_unread"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="right"
-            android:layout_margin="5dp"
-            android:includeFontPadding="false"
-            tools:ignore="RtlHardcoded" />
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:id="@+id/cl_cover"
+            android:layout_width="70dp"
+            android:layout_height="98dp"
+            android:layout_marginStart="8dp"
+            android:layout_marginVertical="8dp"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent">
+            <!-- 书封面 -->
+            <io.legato.kazusa.ui.widget.image.CoverImageView
+                android:id="@+id/iv_cover"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:scaleType="centerCrop"
+                android:src="?attr/colorSurfaceVariant"
+                android:transitionName="img_cover"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                tools:ignore="ImageContrastCheck" />
 
-        <io.legato.kazusa.ui.widget.anima.RotateLoading
-            android:id="@+id/rl_loading"
-            android:layout_width="26dp"
-            android:layout_height="26dp"
-            android:layout_gravity="right"
-            android:visibility="invisible"
-            app:loading_width="2dp"
-            tools:ignore="RtlHardcoded" />
-    </FrameLayout>
+            <com.google.android.material.progressindicator.LinearProgressIndicator
+                android:id="@+id/rl_loading"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginVertical="4dp"
+                android:layout_marginHorizontal="6dp"
+                android:indeterminate="true"
+                android:visibility="invisible"
+                android:layout_gravity="top"
+                app:layout_constraintTop_toTopOf="@id/iv_cover"
+                app:layout_constraintStart_toStartOf="@id/iv_cover"
+                app:layout_constraintEnd_toEndOf="@id/iv_cover" />
 
-    <TextView
-        android:id="@+id/tv_name"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_marginLeft="10dp"
-        android:layout_marginTop="12dp"
-        android:includeFontPadding="false"
-        android:paddingBottom="4dp"
-        android:paddingLeft="2dp"
-        android:singleLine="true"
-        android:text="@string/book_name"
-        android:textSize="16sp"
-        app:layout_constraintBottom_toTopOf="@+id/tv_author"
-        app:layout_constraintLeft_toRightOf="@+id/iv_cover"
-        app:layout_constraintRight_toLeftOf="@id/fl_has_new"
-        app:layout_constraintTop_toTopOf="parent"
-        tools:ignore="RtlHardcoded,RtlSymmetry" />
+            <com.google.android.material.card.MaterialCardView
+                android:id="@+id/cd_unread"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_margin="4dp"
+                android:visibility="visible"
+                app:cardCornerRadius="4dp"
+                app:strokeWidth="0dp"
+                app:cardBackgroundColor="?attr/colorSecondaryVariant"
+                app:layout_constraintTop_toTopOf="@id/iv_cover"
+                app:layout_constraintEnd_toEndOf="@id/iv_cover">
 
-    <androidx.appcompat.widget.AppCompatImageView
-        android:id="@+id/iv_author"
-        android:layout_width="@dimen/desc_icon_size"
-        android:layout_height="@dimen/desc_icon_size"
-        android:contentDescription="@string/author"
-        android:paddingStart="2dp"
-        android:paddingEnd="2dp"
-        android:src="@drawable/ic_author"
-        app:layout_constraintBottom_toBottomOf="@+id/tv_author"
-        app:layout_constraintLeft_toLeftOf="@+id/tv_name"
-        app:layout_constraintTop_toTopOf="@+id/tv_author"
-        tools:ignore="RtlHardcoded,RtlSymmetry" />
+                <TextView
+                    android:id="@+id/tv_unread"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textColor="?attr/colorSurface"
+                    android:textSize="10sp"
+                    android:padding="4dp"
+                    android:textStyle="bold"
+                    android:includeFontPadding="false"
+                    tools:text="5"
+                    tools:ignore="SmallSp" />
 
-    <TextView
-        android:id="@+id/tv_author"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:includeFontPadding="false"
-        android:maxLines="1"
-        android:paddingEnd="6dp"
-        android:text="@string/author"
-        android:textSize="13sp"
-        app:layout_constraintBottom_toTopOf="@+id/tv_read"
-        app:layout_constraintLeft_toRightOf="@+id/iv_author"
-        app:layout_constraintRight_toRightOf="@id/fl_has_new"
-        app:layout_constraintTop_toBottomOf="@+id/tv_name"
-        tools:ignore="RtlSymmetry,TextContrastCheck" />
+            </com.google.android.material.card.MaterialCardView>
 
-    <androidx.appcompat.widget.AppCompatImageView
-        android:id="@+id/iv_read"
-        android:layout_width="@dimen/desc_icon_size"
-        android:layout_height="@dimen/desc_icon_size"
-        android:contentDescription="@string/read_dur_progress"
-        android:paddingStart="2dp"
-        android:paddingEnd="2dp"
-        android:src="@drawable/ic_history"
-        app:layout_constraintBottom_toBottomOf="@+id/tv_read"
-        app:layout_constraintLeft_toLeftOf="@+id/tv_name"
-        app:layout_constraintTop_toTopOf="@+id/tv_read"
-        tools:ignore="RtlHardcoded,RtlSymmetry" />
+            <io.legato.kazusa.ui.widget.image.CoverImageView
+                android:id="@+id/iv_cover1"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="4dp"
+                android:layout_marginBottom="2dp"
+                android:layout_marginStart="4dp"
+                android:layout_marginEnd="2dp"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintEnd_toStartOf="@id/iv_cover2"
+                app:layout_constraintBottom_toTopOf="@id/iv_cover3"/>
 
-    <TextView
-        android:id="@+id/tv_read"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:includeFontPadding="false"
-        android:singleLine="true"
-        android:text="@string/read_dur_progress"
-        android:textSize="13sp"
-        app:layout_constraintBottom_toTopOf="@id/tv_last"
-        app:layout_constraintLeft_toRightOf="@+id/iv_read"
-        app:layout_constraintRight_toRightOf="@+id/fl_has_new"
-        app:layout_constraintTop_toBottomOf="@+id/tv_author"
-        tools:ignore="TextContrastCheck" />
+            <io.legato.kazusa.ui.widget.image.CoverImageView
+                android:id="@+id/iv_cover2"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_marginTop="4dp"
+                android:layout_marginBottom="2dp"
+                android:layout_marginEnd="4dp"
+                android:layout_marginStart="2dp"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toEndOf="@id/iv_cover1"
+                app:layout_constraintBottom_toTopOf="@id/iv_cover4"/>
 
-    <androidx.appcompat.widget.AppCompatImageView
-        android:id="@+id/iv_last"
-        android:layout_width="@dimen/desc_icon_size"
-        android:layout_height="@dimen/desc_icon_size"
-        android:contentDescription="@string/lasted_show"
-        android:paddingStart="2dp"
-        android:paddingEnd="2dp"
-        android:src="@drawable/ic_book_last"
-        app:layout_constraintBottom_toBottomOf="@+id/tv_last"
-        app:layout_constraintLeft_toLeftOf="@+id/tv_name"
-        app:layout_constraintTop_toTopOf="@+id/tv_last"
-        tools:ignore="RtlHardcoded,RtlSymmetry" />
+            <io.legato.kazusa.ui.widget.image.CoverImageView
+                android:id="@+id/iv_cover3"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="2dp"
+                android:layout_marginStart="4dp"
+                android:layout_marginBottom="4dp"
+                android:layout_marginEnd="2dp"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintTop_toBottomOf="@id/iv_cover1"
+                app:layout_constraintEnd_toStartOf="@id/iv_cover4"/>
 
-    <TextView
-        android:id="@+id/tv_last"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="8dp"
-        android:singleLine="true"
-        android:text="@string/lasted_show"
-        android:textSize="13sp"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintLeft_toRightOf="@+id/iv_last"
-        app:layout_constraintRight_toRightOf="@id/fl_has_new"
-        app:layout_constraintTop_toBottomOf="@+id/tv_read"
-        tools:ignore="TextContrastCheck" />
+            <io.legato.kazusa.ui.widget.image.CoverImageView
+                android:id="@+id/iv_cover4"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="2dp"
+                android:layout_marginEnd="4dp"
+                android:layout_marginBottom="4dp"
+                android:layout_marginStart="2dp"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintTop_toBottomOf="@id/iv_cover2"
+                app:layout_constraintStart_toEndOf="@id/iv_cover3"/>
 
-    <View
-        android:id="@+id/vw_foreground"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:background="?android:attr/selectableItemBackground"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintVertical_bias="0.0"
-        tools:layout_editor_absoluteX="0dp" />
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+        <TextView
+            android:id="@+id/tv_name"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:scrollbars="none"
+            android:singleLine="true"
+            android:layout_marginHorizontal="8dp"
+            android:text="@string/book_name"
+            android:textSize="18sp"
+            android:textStyle="bold"
+            tools:ignore="RtlHardcoded,RtlSymmetry"
+            app:layout_constraintStart_toEndOf="@+id/cl_cover"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="@id/cl_cover"
+            app:layout_constraintBottom_toBottomOf="@id/cl_cover"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
 
-</androidx.constraintlayout.widget.ConstraintLayout>
+</com.google.android.material.card.MaterialCardView>

+ 1 - 1
app/src/main/res/layout/view_preference.xml

@@ -39,7 +39,7 @@
 
         <TextView
             android:id="@+id/preference_desc"
-            android:textSize="12sp"
+            android:textSize="13sp"
             android:text="@string/default1"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"

+ 11 - 0
app/src/main/res/values-zh/arrays.xml

@@ -10,6 +10,7 @@
     <string-array name="group_style">
         <item>标签</item>
         <item>文件夹</item>
+        <item>隐藏标签</item>
     </string-array>
 
     <string-array name="text_suffix">
@@ -113,4 +114,14 @@
         <item>自定义</item>
     </string-array>
 
+    <string-array name="bookshelf_px_array">
+        <item>@string/bookshelf_px_0</item>
+        <item>@string/bookshelf_px_1</item>
+        <item>@string/bookshelf_px_2</item>
+        <item>@string/bookshelf_px_3</item>
+        <item>@string/bookshelf_px_4</item>
+        <item>@string/bookshelf_px_5</item>
+    </string-array>
+
+
 </resources>

+ 30 - 30
app/src/main/res/values-zh/strings.xml

@@ -2,7 +2,7 @@
     <!--App-->
     <string name="app_name">阅读</string>
     <string name="app_name_a">阅读·A</string>
-    <string name="receiving_shared_label">阅读·搜索</string>
+    <string name="receiving_shared_label">阅读 · 搜索</string>
     <string name="tip_perm_request_storage">阅读需要访问存储卡权限,请前往“设置”—“应用权限”,打开所需权限</string>
 
     <!--Other-->
@@ -42,7 +42,7 @@
     <string name="replace_purify_desc">配置替换净化规则</string>
     <string name="not_available">暂无</string>
     <string name="enable">启用</string>
-    <string name="replace_purify_search">替换净化-搜索</string>
+    <string name="replace_purify_search">替换净化 - 搜索</string>
     <string name="bookshelf">书架</string>
     <string name="favorites">收藏夹</string>
     <string name="favorite">收藏</string>
@@ -449,7 +449,7 @@
     <string name="r_image">图片 URL 规则(ruleImage)</string>
     <string name="r_content">内容规则(ruleContent)</string>
     <string name="r_style">样式(style)</string>
-    <string name="r_inject_js">注入Js(injectJs)</string>
+    <string name="r_inject_js">注入Js (injectJs)</string>
     <string name="r_link">链接规则(ruleLink)</string>
     <string name="check_key_word">校验关键字(checkKeyWord)</string>
     <string name="rule_actions">操作(actions)</string>
@@ -461,7 +461,7 @@
     <string name="error_get_book_info">书籍信息获取失败</string>
     <string name="error_get_content">内容获取失败</string>
     <string name="error_get_chapter_list">目录获取失败</string>
-    <string name="error_get_web_content">访问网站失败:%s</string>
+    <string name="error_get_web_content">访问网站失败: %s</string>
     <string name="error_read_file">文件读取失败</string>
     <string name="error_load_toc">加载目录失败</string>
     <string name="error_get_data">获取数据失败!</string>
@@ -501,10 +501,10 @@
     <string name="yes">是</string>
     <string name="no">否</string>
     <string name="sure">确认</string>
-    <string name="sure_del">是否确认删除?</string>
-    <string name="sure_del_any">是否确认删除 %s?</string>
-    <string name="sure_del_all_book">是否删除全部书籍?</string>
-    <string name="sure_del_download_book">是否同时删除已下载的书籍目录?</string>
+    <string name="sure_del">是否确认删除?</string>
+    <string name="sure_del_any">是否确认删除 %s ?</string>
+    <string name="sure_del_all_book">是否删除全部书籍?</string>
+    <string name="sure_del_download_book">是否同时删除已下载的书籍目录?</string>
     <string name="qr_per">扫描二维码需相机权限</string>
     <string name="aloud_can_not_auto_page">朗读正在运行,不能自动翻页</string>
     <string name="input_charset">输入编码</string>
@@ -648,7 +648,7 @@
     <string name="app_folder_picker">自带文件夹选择器</string>
     <string name="app_file_picker">自带文件选择器</string>
     <string name="a10_permission_toast">Android 10 以上因系统权限限制可能无法读写文件</string>
-    <string name="add_to_text_context_menu_s">长按文字在操作菜单中显示阅读·搜索</string>
+    <string name="add_to_text_context_menu_s">长按文字在操作菜单中显示阅读 · 搜索</string>
     <string name="add_to_text_context_menu_t">文字操作显示搜索</string>
     <string name="record_log">记录日志</string>
     <string name="log">日志</string>
@@ -690,7 +690,7 @@
     <string name="btn_default_s">默认</string>
     <string name="main_menu">主菜单</string>
     <string name="request_permission">点击授予权限</string>
-    <string name="tip_local_perm_request_storage">阅读需要访问存储卡权限,请点击下方的“授予权限”按钮,或前往“设置”—“应用权限”,打开所需权限。如果授予权限后仍然不正常,请点击右上角的“选择文件夹”,使用系统文件夹选择器。</string>
+    <string name="tip_local_perm_request_storage">阅读需要访问存储卡权限,请点击下方的“授予权限”按钮,或前往“设置” — “应用权限”,打开所需权限。如果授予权限后仍然不正常,请点击右上角的“选择文件夹”,使用系统文件夹选择器。</string>
     <string name="alouding_disable">全文朗读中不能朗读选中文字</string>
     <string name="read_body_to_lh">扩展到刘海</string>
     <string name="toc_updateing">更新目录中</string>
@@ -756,7 +756,7 @@
     <string name="clear_cache_summary">清除已下载书籍和字体缓存</string>
     <string name="default_cover">默认封面</string>
     <string name="restore_ignore">恢复忽略列表</string>
-    <string name="restore_ignore_summary">恢复时忽略一些内容不恢复,方便不同手机配置不同</string>
+    <string name="restore_ignore_summary">选择恢复忽略内容</string>
     <string name="read_config">阅读界面设置</string>
     <string name="group_name">分组名称</string>
     <string name="note_content">备注内容</string>
@@ -882,7 +882,7 @@
     <string name="autobackup_fail">自动备份失败\n%s</string>
     <string name="end">结束</string>
     <string name="custom_group_summary">关闭替换分组/开启添加分组</string>
-    <string name="pref_media_button_per_next">媒体按钮•上一首|下一首</string>
+    <string name="pref_media_button_per_next">媒体按钮 • 上一首|下一首</string>
     <string name="pref_media_button_per_next_summary">上一段|下一段/上一章|下一章</string>
     <string name="read_aloud_by_page_summary">及时翻页,翻页时会停顿一下</string>
     <string name="check_source_show_debug_message">校验显示详细信息</string>
@@ -932,7 +932,7 @@
     <string name="show_login_header">查看登录头</string>
     <string name="login_header">登录头</string>
     <string name="font_scale">字体大小</string>
-    <string name="font_scale_summary">当前字体大小:%.1f</string>
+    <string name="font_scale_summary">当前字体大小: %.1f</string>
     <string name="tts_speech_reduce">语速减</string>
     <string name="tts_speech_add">语速加</string>
     <string name="open_sys_dir_picker_error">打开系统文件夹选择器出错,将自动打开应用文件夹选择器</string>
@@ -951,7 +951,7 @@
     <string name="timeout">超时</string>
     <string name="seconds">秒</string>
     <string name="less_than">小于</string>
-    <string name="check_source_config_summary">校验超时:%1$s秒\n校验项目:%2$s</string>
+    <string name="check_source_config_summary">校验超时: %1$s秒\n校验项目: %2$s</string>
     <string name="record_debug_log">记录调试日志</string>
     <string name="sub_dir">子文件夹</string>
     <string name="general">全局</string>
@@ -1022,8 +1022,8 @@
     <string name="remote_book">远程书籍</string>
     <string name="reading_time_sort">阅读时长排序</string>
     <string name="last_read_time_sort">阅读时间排序</string>
-    <string name="reading_time_tag">阅读时长:</string>
-    <string name="last_read_time_tag">最后阅读时间:</string>
+    <string name="reading_time_tag">阅读时长:</string>
+    <string name="last_read_time_tag">最后阅读时间:</string>
     <string name="page_touch_slop_title">滑动翻页阈值</string>
     <string name="page_touch_slop_dialog_title">滑动翻页阈值(0 = 系统默认值)</string>
     <string name="page_touch_slop_summary">滑动多长距离才会触发滑动翻页(系统默认值 %s px)</string>
@@ -1059,19 +1059,19 @@
     <string name="parallel_export_book">多线程导出</string>
     <string name="progress_bar_behavior">进度条行为</string>
     <string name="source_edit_text_max_line">源编辑框最大行数</string>
-    <string name="source_edit_max_line_summary">%s,设置行数小于屏幕可显示的最大行数可以更方便的滑动到其他的字段进行编辑</string>
+    <string name="source_edit_max_line_summary">%s,设置行数小于屏幕可显示的最大行数可以更方便的滑动到其他的字段进行编辑</string>
     <string name="restore_last_book_process">是否恢复到跳转前的阅读进度?</string>
     <string name="search_scope">搜索范围</string>
     <string name="toggle_search_scope">切换</string>
     <string name="sure_clear_search_history">是否确认清除所有搜索历史记录</string>
     <string name="no_anim_scroll_page">禁用滚动点击动画</string>
     <string name="webdav_device_name">设备名称</string>
-    <string name="web_service_wake_lock">WebService唤醒锁</string>
-    <string name="web_service_wake_lock_summary">开启web服务的时候启用唤醒锁,有些手机开启唤醒锁会被杀后台</string>
+    <string name="web_service_wake_lock">WebService 唤醒锁</string>
+    <string name="web_service_wake_lock_summary">开启 Web 服务的时候启用唤醒锁,有些手机开启唤醒锁会被杀后台</string>
     <string name="read_aloud_wake_lock">朗读服务唤醒锁</string>
-    <string name="read_aloud_wake_lock_summary">开启朗读的时候启用唤醒锁,有些手机开启唤醒锁会被杀后台</string>
+    <string name="read_aloud_wake_lock_summary">开启朗读的时候启用唤醒锁,有些手机开启唤醒锁会被杀后台</string>
     <string name="audio_play_wake_lock">音频服务唤醒锁</string>
-    <string name="audio_play_wake_lock_summary">播放音频的时候启用唤醒锁,有些手机开启唤醒锁会被杀后台</string>
+    <string name="audio_play_wake_lock_summary">播放音频的时候启用唤醒锁,有些手机开启唤醒锁会被杀后台</string>
     <string name="change_search_scope">切换搜索范围</string>
     <string name="copy_rule">拷贝规则</string>
     <string name="paste_rule">粘贴规则</string>
@@ -1083,7 +1083,7 @@
     <string name="same_title_removed">移除重复标题</string>
     <string name="update_book_fail">更新失败</string>
     <string name="notification_permission_rationale">阅读需要发送通知来显示朗读控制和下载进度</string>
-    <string name="webdav_after_local_restore_confirm">webDav书源比本地新,是否恢复</string>
+    <string name="webdav_after_local_restore_confirm">WebDav 书源比本地新,是否恢复</string>
     <string name="c_whitelist">白名单(contentWhitelist)</string>
     <string name="c_blacklist">黑名单(contentBlacklist)</string>
     <string name="confirm">确认</string>
@@ -1094,13 +1094,13 @@
     <string name="dict_rule">字典规则</string>
     <string name="config_dict_rule">配置字典规则</string>
     <string name="create">新建</string>
-    <string name="url_rule">url规则(urlRule)</string>
-    <string name="show_rule">显示规则(showRule)</string>
+    <string name="url_rule">Url规则 (urlRule)</string>
+    <string name="show_rule">显示规则 (showRule)</string>
     <string name="config_txt_toc_rule">配置 TXT 目录规则</string>
     <string name="import_dict_rule">导入字典规则</string>
     <string name="keep_group">保留分组</string>
     <string name="server_config">服务器配置</string>
-    <string name="sure_upload">远程webDav链接已存在,是否继续</string>
+    <string name="sure_upload">远程 WebDav 链接已存在,是否继续</string>
     <string name="unsupport_archivefile_entry">压缩文件内没有支持的文件</string>
     <string name="no_books_dir">没有设置书籍保存位置!</string>
     <string name="delete_alert">删除提醒</string>
@@ -1117,10 +1117,10 @@
     <string name="select_new_source">选中新增源</string>
     <string name="select_update_source">选中更新源</string>
     <string name="set_local_password">设置本地密码</string>
-    <string name="set_local_password_summary">本地密码用来对备份的敏感信息加密和解密,如需在不同设备之间同步,本地密码需一致.</string>
+    <string name="set_local_password_summary">本地密码用来对备份的敏感信息加密和解密,如需在不同设备之间同步,本地密码需一致。</string>
     <string name="only_latest_backup_t">仅保留最新备份</string>
     <string name="only_latest_backup_s">本地备份仅保留最新备份文件</string>
-    <string name="webdav_application_authorization_error">webDav应用验证失败</string>
+    <string name="webdav_application_authorization_error">WebDav 应用验证失败</string>
     <string name="load_word_count">加载字数</string>
     <string name="replace_exclude_scope">排除范围,选填书名或者书源 URL</string>
     <string name="format_js_rule">格式化规则(formatJs)</string>
@@ -1141,7 +1141,7 @@
     <string name="bookshelf_px_5">按作者</string>
     <string name="effective_replaces">起效的替换</string>
     <string name="export_book">导出书籍</string>
-    <string name="export_book_notification_content">正在导出(%1$s),还有%2$d本待导出</string>
+    <string name="export_book_notification_content">正在导出 (%1$s) ,还有 %2$d 本待导出</string>
     <string name="export_md">导出(MD)</string>
     <string name="change_source_delay">换源间隔</string>
     <string name="change_source_progress">结果 %1$d, 当前进度 %2$d / %3$d: %4$s</string>
@@ -1181,7 +1181,7 @@
     <string name="beta_release_version">测试版</string>
     <string name="beta_releaseA_version">共存版</string>
     <string name="stream_read_aloud_audio">流式播放音频</string>
-    <string name="stream_read_aloud_audio_summary">即边下边播,网络不好时播放会断断续续,仅TTS源有效</string>
+    <string name="stream_read_aloud_audio_summary">边下边播,网络不好时播放会断断续续,仅TTS源有效</string>
     <string name="pause_read_aloud_while_phone_calls_title">来电期间暂停朗读</string>
     <string name="pause_read_aloud_while_phone_calls_summary">在通话期间暂停朗读,需要读取手机状态权限</string>
     <string name="read_aloud_read_phone_state_permission_rationale">阅读需要读取手机状态实现来电期间暂停朗读功能</string>
@@ -1197,7 +1197,7 @@
     <string name="enable_auto_scroll">开启滚动</string>
     <string name="manga_footer_config">页脚配置</string>
     <string name="setting_manga_auto_page_speed">设置自动翻页速度</string>
-    <string name="book_reader_info_bar">页数. %1$d/%2$d  章节. %3$d/%4$d -> %5$s%%</string>
+    <string name="book_reader_info_bar">页数. %1$d/%2$d 章节. %3$d/%4$d -> %5$s%%</string>
     <string name="menu_download_after">下载之后章节</string>
     <string name="menu_download_all">下载全部章节</string>
     <string name="manga_header_chapter">《章节和文案》隐藏</string>

+ 10 - 0
app/src/main/res/values/arrays.xml

@@ -15,6 +15,7 @@
 	<string-array name="group_style">
 		<item>Tab</item>
 		<item>Folder</item>
+		<item>Hide Tab</item>
 	</string-array>
 
 	<string-array name="book_sort">
@@ -195,4 +196,13 @@
 		<item>Customize</item>
 	</string-array>
 
+	<string-array name="bookshelf_px_array">
+		<item>@string/bookshelf_px_0</item>
+		<item>@string/bookshelf_px_1</item>
+		<item>@string/bookshelf_px_2</item>
+		<item>@string/bookshelf_px_3</item>
+		<item>@string/bookshelf_px_4</item>
+		<item>@string/bookshelf_px_5</item>
+	</string-array>
+
 </resources>