Jelajahi Sumber

更新界面

HapeLee 1 tahun lalu
induk
melakukan
bf514ffd26

+ 3 - 1
app/src/main/java/io/legato/kazusa/ui/book/bookmark/BookmarkDecoration.kt

@@ -5,6 +5,7 @@ import android.graphics.Canvas
 import android.graphics.Color
 import android.graphics.Paint
 import android.graphics.Rect
+import android.graphics.Typeface
 import android.text.TextPaint
 import android.view.View
 import androidx.recyclerview.widget.LinearLayoutManager
@@ -28,10 +29,11 @@ class BookmarkDecoration(
     }
 
     private val textPaint = TextPaint().apply {
-        textSize = 16f.spToPx()
+        textSize = 14f.spToPx()
         color =
             MaterialColors.getColor(context, androidx.appcompat.R.attr.colorPrimary, Color.BLACK)
         isAntiAlias = true
+        typeface = Typeface.DEFAULT_BOLD
     }
 
     private val textRect = Rect()

+ 7 - 2
app/src/main/java/io/legato/kazusa/ui/book/read/ReadBookActivity.kt

@@ -8,6 +8,7 @@ import android.net.Uri
 import android.os.Bundle
 import android.os.Looper
 import android.view.Gravity
+import android.view.HapticFeedbackConstants
 import android.view.InputDevice
 import android.view.KeyEvent
 import android.view.Menu
@@ -788,21 +789,25 @@ class ReadBookActivity : BaseReadBookActivity(),
     /**
      * 更新文字选择开始位置
      */
-    override fun upSelectedStart(x: Float, y: Float, top: Float) = binding.run {
+    override fun upSelectedStart(x: Float, y: Float, top: Float): Unit = binding.run {
         cursorLeft.x = x - cursorLeft.width
         cursorLeft.y = y
         cursorLeft.visible(true)
         textMenuPosition.x = x
         textMenuPosition.y = top
+
+        root.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
     }
 
     /**
      * 更新文字选择结束位置
      */
-    override fun upSelectedEnd(x: Float, y: Float) = binding.run {
+    override fun upSelectedEnd(x: Float, y: Float): Unit = binding.run {
         cursorRight.x = x
         cursorRight.y = y
         cursorRight.visible(true)
+
+        root.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
     }
 
     /**

+ 2 - 2
app/src/main/java/io/legato/kazusa/ui/book/toc/BookmarkAdapter.kt

@@ -32,12 +32,12 @@ class BookmarkAdapter(context: Context, val callback: Callback) :
     override fun registerListener(holder: ItemViewHolder, binding: ItemBookmarkBinding) {
         binding.root.setOnClickListener {
             getItem(holder.layoutPosition)?.let { bookmark ->
-                callback.onClick(bookmark)
+                callback.onLongClick(bookmark, holder.layoutPosition)
             }
         }
         binding.root.onLongClick {
             getItem(holder.layoutPosition)?.let { bookmark ->
-                callback.onLongClick(bookmark, holder.layoutPosition)
+                callback.onClick(bookmark)
             }
         }
 

+ 11 - 7
app/src/main/java/io/legato/kazusa/ui/book/toc/BookmarkFragment.kt

@@ -19,6 +19,7 @@ import io.legato.kazusa.ui.widget.recycler.VerticalDivider
 import io.legato.kazusa.utils.applyNavigationBarPadding
 import io.legato.kazusa.utils.showDialogFragment
 import io.legato.kazusa.utils.viewbindingdelegate.viewBinding
+import io.legato.kazusa.utils.visible
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.flow.catch
@@ -51,24 +52,27 @@ class BookmarkFragment : VMBaseFragment<TocViewModel>(R.layout.fragment_bookmark
         binding.recyclerView.addItemDecoration(VerticalDivider(requireContext()))
         binding.recyclerView.adapter = adapter
         binding.recyclerView.applyNavigationBarPadding()
+
+        binding.tvEmptyMsg.visible(false)
     }
 
     override fun upBookmark(searchKey: String?) {
         val book = viewModel.bookData.value ?: return
         lifecycleScope.launch {
-            when {
+            val flow = when {
                 searchKey.isNullOrBlank() -> appDb.bookmarkDao.flowByBook(book.name, book.author)
                 else -> appDb.bookmarkDao.flowSearch(book.name, book.author, searchKey)
-            }.catch {
+            }
+
+            flow.catch {
                 AppLog.put("目录界面获取书签数据失败\n${it.localizedMessage}", it)
-            }.flowOn(IO).collect {
-                adapter.setItems(it)
+            }.flowOn(IO).collect { list ->
+                adapter.setItems(list)
+                binding.tvEmptyMsg.visible(list.isEmpty())
                 var scrollPos = 0
                 withContext(Dispatchers.Default) {
                     adapter.getItems().forEachIndexed { index, bookmark ->
-                        if (bookmark.chapterIndex >= durChapterIndex) {
-                            return@withContext
-                        }
+                        if (bookmark.chapterIndex >= durChapterIndex) return@withContext
                         scrollPos = index
                     }
                 }

+ 39 - 43
app/src/main/java/io/legato/kazusa/ui/book/toc/ChapterListAdapter.kt

@@ -14,15 +14,17 @@ import io.legato.kazusa.data.entities.BookChapter
 import io.legato.kazusa.databinding.ItemChapterListBinding
 import io.legato.kazusa.help.book.ContentProcessor
 import io.legato.kazusa.help.config.AppConfig
-import io.legato.kazusa.help.coroutine.Coroutine
 import io.legato.kazusa.lib.theme.ThemeUtils
 import io.legato.kazusa.utils.gone
 import io.legato.kazusa.utils.longToastOnUi
 import io.legato.kazusa.utils.themeColor
 import io.legato.kazusa.utils.visible
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
 import kotlinx.coroutines.ensureActive
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 import java.util.concurrent.ConcurrentHashMap
 
 class ChapterListAdapter(context: Context, val callback: Callback) :
@@ -30,6 +32,7 @@ class ChapterListAdapter(context: Context, val callback: Callback) :
 
     val cacheFileNames = hashSetOf<String>()
     private val displayTitleMap = ConcurrentHashMap<String, String>()
+
     private val handler = Handler(Looper.getMainLooper())
 
     override val diffItemCallback: DiffUtil.ItemCallback<BookChapter>
@@ -58,7 +61,7 @@ class ChapterListAdapter(context: Context, val callback: Callback) :
 
         }
 
-    private var upDisplayTileJob: Coroutine<*>? = null
+    private var upDisplayTileJob: Job? = null
 
     override fun onCurrentListChanged() {
         super.onCurrentListChanged()
@@ -72,42 +75,31 @@ class ChapterListAdapter(context: Context, val callback: Callback) :
 
     fun upDisplayTitles(startIndex: Int) {
         upDisplayTileJob?.cancel()
-        upDisplayTileJob = Coroutine.async(callback.scope) {
-            val book = callback.book ?: return@async
+        upDisplayTileJob = callback.scope.launch(Dispatchers.Default) {
+            val book = callback.book ?: return@launch
             val replaceRules = ContentProcessor.get(book.name, book.origin).getTitleReplaceRules()
             val useReplace = AppConfig.tocUiUseReplace && book.getUseReplaceRule()
             val items = getItems()
-            launch {
-                for (i in startIndex until items.size) {
-                    val item = items[i]
-                    if (displayTitleMap[item.title] == null) {
-                        ensureActive()
-                        val displayTitle = item.getDisplayTitle(replaceRules, useReplace)
-                        ensureActive()
-                        displayTitleMap[item.title] = displayTitle
-                        handler.post {
-                            notifyItemChanged(i, true)
-                        }
-                    }
-                }
-            }
-            launch {
-                for (i in startIndex downTo 0) {
-                    val item = items[i]
-                    if (displayTitleMap[item.title] == null) {
-                        ensureActive()
-                        val displayTitle = item.getDisplayTitle(replaceRules, useReplace)
-                        ensureActive()
-                        displayTitleMap[item.title] = displayTitle
-                        handler.post {
-                            notifyItemChanged(i, true)
-                        }
+
+            val indices = (startIndex until items.size) + (startIndex downTo 0)
+
+            for (i in indices) {
+                val item = items[i]
+                val key = item.url
+                if (displayTitleMap[key] == null) {
+                    ensureActive()
+                    val displayTitle = item.getDisplayTitle(replaceRules, useReplace)
+                    displayTitleMap[key] = displayTitle
+                    ensureActive()
+                    withContext(Dispatchers.Main) {
+                        notifyItemChanged(i, true)
                     }
                 }
             }
         }
     }
 
+
     private fun getDisplayTitle(chapter: BookChapter): String {
         return displayTitleMap[chapter.title] ?: chapter.title
     }
@@ -128,17 +120,22 @@ class ChapterListAdapter(context: Context, val callback: Callback) :
                     || item.isVolume
                     || cacheFileNames.contains(item.getFileName())
             if (payloads.isEmpty()) {
-                if (isDur) {
-                    tvChapterName.setTextColor(context.themeColor(androidx.appcompat.R.attr.colorPrimary))
-                    tvChapterItem.setBackgroundColor(context.themeColor(com.google.android.material.R.attr.colorSurfaceContainer))
-                } else {
-                    tvChapterName.setTextColor(context.themeColor(com.google.android.material.R.attr.colorOnSurface))
-                    tvChapterItem.setBackgroundColor(context.themeColor(com.google.android.material.R.attr.colorSurface))
-                }
-                tvChapterName.text = getDisplayTitle(item)
+
                 if (item.isVolume) {
-                    tvChapterItem.setBackgroundColor(context.themeColor(com.google.android.material.R.attr.colorSecondaryContainer))
+                    ivVolume.visible()
+                    tvChapterName.text = getDisplayTitle(item)
+                    tvChapterName.textSize = 12f
+                    tvChapterName.setTextColor(context.themeColor(com.google.android.material.R.attr.colorTertiary))
                 } else {
+                    ivVolume.gone()
+                    if (isDur) {
+                        tvChapterName.setTextColor(context.themeColor(androidx.appcompat.R.attr.colorPrimary))
+                        tvChapterItem.setBackgroundColor(context.themeColor(com.google.android.material.R.attr.colorSurfaceContainer))
+                    } else {
+                        tvChapterName.setTextColor(context.themeColor(com.google.android.material.R.attr.colorOnSurface))
+                        tvChapterItem.setBackgroundColor(context.themeColor(com.google.android.material.R.attr.colorSurface))
+                    }
+                    tvChapterName.text = getDisplayTitle(item)
                     tvChapterItem.foreground =
                         ThemeUtils.resolveDrawable(context, android.R.attr.selectableItemBackground)
                 }
@@ -165,10 +162,10 @@ class ChapterListAdapter(context: Context, val callback: Callback) :
                     ivLocked.gone()
                 }
 
-                upHasCache(binding, isDur, cached)
+                upHasCache(binding, cached)
             } else {
                 tvChapterName.text = getDisplayTitle(item)
-                upHasCache(binding, isDur, cached)
+                upHasCache(binding, cached)
             }
         }
     }
@@ -187,12 +184,11 @@ class ChapterListAdapter(context: Context, val callback: Callback) :
         }
     }
 
-    private fun upHasCache(binding: ItemChapterListBinding, isDur: Boolean, cached: Boolean) =
+    private fun upHasCache(binding: ItemChapterListBinding, cached: Boolean) =
         binding.apply {
             when {
-                isDur -> ivChecked.gone()
                 cached -> {
-                    ivChecked.setImageResource(R.drawable.ic_check)
+                    ivChecked.setImageResource(R.drawable.ic_download_done)
                     ivChecked.visible()
                 }
 

+ 8 - 22
app/src/main/java/io/legato/kazusa/ui/book/toc/ChapterListFragment.kt

@@ -1,7 +1,5 @@
 package io.legato.kazusa.ui.book.toc
 
-//import io.legado.app.lib.theme.bottomBackground
-//import io.legado.app.lib.theme.getPrimaryTextColor
 import android.annotation.SuppressLint
 import android.app.Activity.RESULT_OK
 import android.content.Intent
@@ -36,7 +34,6 @@ import io.legato.kazusa.utils.themeColor
 import io.legato.kazusa.utils.toastOnUi
 import io.legato.kazusa.utils.viewbindingdelegate.viewBinding
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers.Default
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.Dispatchers.Main
 import kotlinx.coroutines.launch
@@ -79,7 +76,7 @@ class ChapterListFragment : VMBaseFragment<TocViewModel>(R.layout.fragment_chapt
 
                 toastOnUi("开始下载: ${adapter.getItem(position)?.title}")
                 CacheBook.start(requireContext(), book, chapter.index, chapter.index)
-                VibrationUtils.vibrate(context!!, 100)
+                VibrationUtils.vibratePattern(requireContext(), longArrayOf(0, 50, 30, 50), -1)
                 adapter.notifyItemChanged(position)
             }
 
@@ -191,16 +188,11 @@ class ChapterListFragment : VMBaseFragment<TocViewModel>(R.layout.fragment_chapt
     override fun observeLiveBus() {
         observeEvent<Pair<Book, BookChapter>>(EventBus.SAVE_CONTENT) { (book, chapter) ->
             viewModel.bookData.value?.bookUrl?.let { bookUrl ->
-                if (book.bookUrl == bookUrl) {
+                if (book.bookUrl == viewModel.bookData.value?.bookUrl) {
                     adapter.cacheFileNames.add(chapter.getFileName())
-                    if (viewModel.searchKey.isNullOrEmpty()) {
-                        adapter.notifyItemChanged(chapter.index, true)
-                    } else {
-                        adapter.getItems().forEachIndexed { index, bookChapter ->
-                            if (bookChapter.index == chapter.index) {
-                                adapter.notifyItemChanged(index, true)
-                            }
-                        }
+                    val index = adapter.getItems().indexOfFirst { it.index == chapter.index }
+                    if (index >= 0) {
+                        adapter.notifyItemChanged(index, true)
                     }
                 }
             }
@@ -225,15 +217,9 @@ class ChapterListFragment : VMBaseFragment<TocViewModel>(R.layout.fragment_chapt
 
     override fun onListChanged() {
         lifecycleScope.launch {
-            var scrollPos = 0
-            withContext(Default) {
-                adapter.getItems().forEachIndexed { index, bookChapter ->
-                    if (bookChapter.index >= durChapterIndex) {
-                        return@withContext
-                    }
-                    scrollPos = index
-                }
-            }
+            val scrollPos = adapter.getItems()
+                .indexOfLast { it.index < durChapterIndex }
+                .coerceAtLeast(0)
             mLayoutManager.scrollToPositionWithOffset(scrollPos, 0)
             adapter.upDisplayTitles(scrollPos)
         }

+ 3 - 2
app/src/main/java/io/legato/kazusa/ui/book/toc/TocActivity.kt

@@ -60,8 +60,9 @@ class TocActivity : VMBaseActivity<ActivityChapterListBinding, TocViewModel>(),
         tabLayout = binding.tabLayout
         binding.viewPager.adapter = TabFragmentPageAdapter()
         tabLayout.setupWithViewPager(binding.viewPager)
-        viewModel.bookData.observe(this) {
-            menu?.setGroupVisible(R.id.menu_group_text, it.isLocalTxt)
+        viewModel.bookData.observe(this) { book ->
+            menu?.setGroupVisible(R.id.menu_group_text, book.isLocalTxt)
+            supportActionBar?.title = book.name
         }
         intent.getStringExtra("bookUrl")?.let {
             viewModel.initBook(it)

+ 10 - 6
app/src/main/res/drawable/ic_divider.xml

@@ -1,6 +1,10 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-    <size android:height="1px" />
-    <solid android:color="@color/bg_divider_line" />
-</shape>
+<?xml version="1.0" encoding="utf-8"?><!-- res/drawable/md3_divider_inset.xml -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+    android:insetLeft="16dp"
+    android:insetRight="16dp">
+    <shape android:shape="rectangle">
+        <size android:height="1dp" />
+        <solid android:color="?attr/colorSurfaceContainer" />
+    </shape>
+</inset>
+

+ 11 - 0
app/src/main/res/drawable/ic_download_done.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?attr/colorControlNormal"
+    android:viewportWidth="960"
+    android:viewportHeight="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M382,640L155,413L212,356L382,526L748,160L805,217L382,640ZM200,800L200,720L760,720L760,800L200,800Z" />
+</vector>

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

@@ -19,7 +19,7 @@
             android:layout_height="?attr/actionBarSize"
             app:layout_collapseMode="pin"
             app:navigationIcon="@drawable/ic_arrow_back"
-            app:title="@string/chapter_list" />
+            app:title="" />
 
         <com.google.android.material.tabs.TabLayout
             android:id="@+id/tab_layout"

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

@@ -78,7 +78,7 @@
                 android:layout_height="wrap_content"
                 android:text="Σ(#`O′)"
                 android:textColor="@color/white"
-                android:textSize="24sp" />
+                android:textSize="32sp" />
 
             <TextView
                 android:id="@+id/tv_msg"
@@ -90,7 +90,7 @@
                 android:id="@+id/tv_retry"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                style="@style/Widget.Material3Expressive.Button.IconButton.Tonal"
+                style="@style/Widget.Material3Expressive.Button.TonalButton"
                 app:icon="@drawable/ic_refresh"
                 android:layout_margin="16dp"
                 app:iconPadding="4dp"

+ 28 - 14
app/src/main/res/layout/dialog_bookmark.xml

@@ -9,18 +9,34 @@
     android:orientation="vertical"
     tools:ignore="UselessParent">
 
-        <androidx.appcompat.widget.Toolbar
-            android:id="@+id/tool_bar"
-            android:layout_width="match_parent"
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:orientation="vertical"
+        android:paddingHorizontal="16dp"
+        android:paddingBottom="8dp">
+
+        <com.google.android.material.bottomsheet.BottomSheetDragHandleView
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:theme="?attr/actionBarStyle"
-            app:title="@string/bookmark" />
+            android:layout_gravity="center_horizontal" />
+
+        <TextView
+            android:id="@+id/bottom_sheet_title"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:text="@string/bookmark"
+            android:textSize="18sp"
+            android:textStyle="bold" />
+
+    </LinearLayout>
 
         <TextView
             android:id="@+id/tv_chapter_name"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:padding="16dp" />
+            android:paddingHorizontal="16dp" />
 
         <androidx.core.widget.NestedScrollView
             android:layout_width="match_parent"
@@ -36,8 +52,7 @@
 
                 <com.google.android.material.textfield.TextInputLayout
                     android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:paddingTop="3dp">
+                    android:layout_height="wrap_content">
 
                     <com.google.android.material.textfield.TextInputEditText
                         android:id="@+id/edit_book_text"
@@ -74,11 +89,10 @@
             <!-- 删除按钮靠左 -->
             <com.google.android.material.button.MaterialButton
                 android:id="@+id/btn_delete"
-                style="@style/Widget.Material3Expressive.Button.TonalButton"
+                style="@style/Widget.Material3Expressive.Button.IconButton.Outlined"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:padding="12dp"
-                android:text="@string/delete"
+                app:icon="@drawable/ic_clear_all"
                 android:visibility="invisible"
                 app:layout_constraintStart_toStartOf="parent"
                 app:layout_constraintTop_toTopOf="parent"
@@ -90,7 +104,7 @@
                 style="@style/Widget.Material3Expressive.Button.TonalButton"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:padding="12dp"
+                android:minWidth="0dp"
                 android:text="@string/cancel"
                 app:layout_constraintEnd_toStartOf="@id/btn_ok"
                 app:layout_constraintTop_toTopOf="parent"
@@ -100,10 +114,10 @@
             <!-- 确认按钮靠右 -->
             <com.google.android.material.button.MaterialButton
                 android:id="@+id/btn_ok"
-                style="@style/Widget.Material3Expressive.Button.TonalButton"
+                style="@style/Widget.Material3Expressive.Button"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:padding="12dp"
+                android:minWidth="0dp"
                 android:text="@string/ok"
                 app:layout_constraintEnd_toEndOf="parent"
                 app:layout_constraintTop_toTopOf="parent"

+ 6 - 8
app/src/main/res/layout/dialog_read_aloud.xml

@@ -7,22 +7,20 @@
     android:layout_height="wrap_content"
     android:orientation="vertical"
     android:paddingHorizontal="16dp"
-    android:paddingVertical="8dp">
+    android:paddingBottom="8dp">
 
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="vertical"
         android:gravity="center"
-        android:paddingVertical="8dp"
+        android:paddingBottom="8dp"
         android:paddingHorizontal="16dp">
 
-        <View
-            android:layout_width="36dp"
-            android:layout_height="4dp"
-            android:layout_gravity="center_horizontal"
-            android:background="@drawable/bg_bottom_sheet_handle"
-            android:layout_marginBottom="16dp" />
+        <com.google.android.material.bottomsheet.BottomSheetDragHandleView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal" />
 
         <TextView
             android:id="@+id/bottom_sheet_title"

+ 28 - 0
app/src/main/res/layout/fragment_bookmark.xml

@@ -2,6 +2,7 @@
 <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:descendantFocusability="blocksDescendants">
 
     <io.legato.kazusa.ui.widget.recycler.scroller.FastScrollRecyclerView
@@ -11,4 +12,31 @@
         android:clipToPadding="false"
         android:overScrollMode="never" />
 
+    <LinearLayout
+        android:id="@+id/tv_empty_msg"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_margin="16dp"
+        android:gravity="center"
+        android:orientation="vertical"
+        android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="(๑`^´๑)"
+            android:textSize="32sp" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="8dp"
+            android:text="还没有添加书签哦!" />
+    </LinearLayout>
+
 </androidx.constraintlayout.widget.ConstraintLayout>

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

@@ -30,7 +30,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:text="(๑`^´๑)"
-            android:textSize="30sp"/>
+            android:textSize="32sp" />
         <TextView
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"

+ 67 - 41
app/src/main/res/layout/item_chapter_list.xml

@@ -10,54 +10,80 @@
     android:paddingHorizontal="16dp"
     android:paddingVertical="12dp">
 
-    <ImageView
-        android:id="@+id/iv_locked"
-        android:layout_width="24dp"
-        android:layout_height="24dp"
-        android:paddingEnd="8dp"
-        android:src="@drawable/ic_lock_outline"
-        android:visibility="gone"
+    <LinearLayout
+        android:id="@+id/ll_info"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="center"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
-        tools:ignore="RtlSymmetry" />
+        app:layout_constraintTop_toTopOf="parent">
 
-    <TextView
-        android:id="@+id/tv_chapter_name"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:textSize="14sp"
-        android:textStyle="bold"
-        android:maxLines="2"
-        app:layout_constraintEnd_toStartOf="@+id/iv_checked"
-        app:layout_constraintStart_toEndOf="@+id/iv_locked"
-        app:layout_constraintTop_toTopOf="parent" />
+        <ImageView
+            android:id="@+id/iv_locked"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:padding="4dp"
+            android:src="@drawable/ic_lock_outline"
+            android:visibility="visible" />
 
-    <TextView
-        android:id="@+id/tv_word_count"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="4dp"
-        android:paddingEnd="18dp"
-        android:singleLine="true"
-        android:textSize="12sp"
-        android:visibility="gone"
-        app:layout_constraintEnd_toStartOf="@+id/iv_checked"
-        app:layout_constraintStart_toEndOf="@+id/iv_locked"
-        app:layout_constraintTop_toBottomOf="@id/tv_chapter_name"
-        tools:ignore="RtlSymmetry" />
+        <com.google.android.material.card.MaterialCardView
+            android:id="@+id/iv_volume"
+            android:layout_width="4dp"
+            android:layout_height="16dp"
+            android:layout_marginEnd="4dp"
+            android:visibility="visible"
+            app:cardBackgroundColor="?attr/colorTertiary"
+            app:strokeWidth="0dp" />
+    </LinearLayout>
 
-    <TextView
-        android:id="@+id/tv_tag"
+    <LinearLayout
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_marginTop="4dp"
-        android:singleLine="true"
-        android:textSize="12sp"
-        android:visibility="gone"
-        app:layout_constraintEnd_toStartOf="@+id/iv_checked"
-        app:layout_constraintStart_toEndOf="@+id/iv_locked"
-        app:layout_constraintTop_toBottomOf="@id/tv_word_count" />
+        android:layout_marginHorizontal="4dp"
+        android:orientation="vertical"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/iv_checked"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toEndOf="@id/ll_info">
+
+        <TextView
+            android:id="@+id/tv_chapter_name"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:maxLines="2"
+            android:textSize="14sp"
+            android:textStyle="bold"
+            app:layout_constraintEnd_toStartOf="@+id/iv_checked"
+            app:layout_constraintStart_toEndOf="@+id/iv_locked"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <TextView
+            android:id="@+id/tv_word_count"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="4dp"
+            android:paddingEnd="18dp"
+            android:singleLine="true"
+            android:textSize="12sp"
+            android:visibility="gone"
+            app:layout_constraintEnd_toStartOf="@+id/iv_checked"
+            app:layout_constraintStart_toEndOf="@+id/iv_locked"
+            app:layout_constraintTop_toBottomOf="@id/tv_chapter_name"
+            tools:ignore="RtlSymmetry" />
+
+        <TextView
+            android:id="@+id/tv_tag"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="4dp"
+            android:singleLine="true"
+            android:textSize="12sp"
+            android:visibility="gone"
+            app:layout_constraintEnd_toStartOf="@+id/iv_checked"
+            app:layout_constraintStart_toEndOf="@+id/iv_locked"
+            app:layout_constraintTop_toBottomOf="@id/tv_word_count" />
+    </LinearLayout>
 
     <ImageView
         android:id="@+id/iv_checked"

+ 1 - 0
app/src/main/res/layout/item_search.xml

@@ -88,6 +88,7 @@
                 android:id="@+id/cd_count"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:visibility="gone"
                 android:layout_marginVertical="4dp"
                 app:cardBackgroundColor="?attr/colorSurfaceContainer"
                 app:cardCornerRadius="4dp"

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

@@ -5,7 +5,7 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content">
 
-    <com.google.android.material.progressindicator.CircularProgressIndicator
+    <com.google.android.material.loadingindicator.LoadingIndicator
         android:id="@+id/rotate_loading"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"