Horis 4 лет назад
Родитель
Сommit
e52407e7ec

+ 1 - 0
app/src/main/java/io/legado/app/constant/PreferKey.kt

@@ -120,6 +120,7 @@ object PreferKey {
     const val noAnimScrollPage = "noAnimScrollPage"
     const val noAnimScrollPage = "noAnimScrollPage"
     const val webDavDeviceName = "webDavDeviceName"
     const val webDavDeviceName = "webDavDeviceName"
     const val wakeLock = "wakeLock"
     const val wakeLock = "wakeLock"
+    const val showLastUpdateTime = "showLastUpdateTime"
 
 
     const val cPrimary = "colorPrimary"
     const val cPrimary = "colorPrimary"
     const val cAccent = "colorAccent"
     const val cAccent = "colorAccent"

+ 6 - 0
app/src/main/java/io/legado/app/help/config/AppConfig.kt

@@ -82,6 +82,12 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
             appCtx.putPrefBoolean(PreferKey.showUnread, value)
             appCtx.putPrefBoolean(PreferKey.showUnread, value)
         }
         }
 
 
+    var showLastUpdateTime: Boolean
+        get() = appCtx.getPrefBoolean(PreferKey.showLastUpdateTime, false)
+        set(value) {
+            appCtx.putPrefBoolean(PreferKey.showLastUpdateTime, value)
+        }
+
     var readBrightness: Int
     var readBrightness: Int
         get() = if (isNightTheme) {
         get() = if (isNightTheme) {
             appCtx.getPrefInt(PreferKey.nightBrightness, 100)
             appCtx.getPrefInt(PreferKey.nightBrightness, 100)

+ 5 - 0
app/src/main/java/io/legado/app/ui/main/bookshelf/BaseBookshelfFragment.kt

@@ -136,6 +136,7 @@ abstract class BaseBookshelfFragment(layoutId: Int) : VMBaseFragment<BookshelfVi
                     .apply {
                     .apply {
                         spGroupStyle.setSelection(AppConfig.bookGroupStyle)
                         spGroupStyle.setSelection(AppConfig.bookGroupStyle)
                         swShowUnread.isChecked = AppConfig.showUnread
                         swShowUnread.isChecked = AppConfig.showUnread
+                        swShowLastUpdateTime.isChecked = AppConfig.showLastUpdateTime
                         rgLayout.checkByIndex(bookshelfLayout)
                         rgLayout.checkByIndex(bookshelfLayout)
                         rgSort.checkByIndex(bookshelfSort)
                         rgSort.checkByIndex(bookshelfSort)
                     }
                     }
@@ -150,6 +151,10 @@ abstract class BaseBookshelfFragment(layoutId: Int) : VMBaseFragment<BookshelfVi
                         AppConfig.showUnread = swShowUnread.isChecked
                         AppConfig.showUnread = swShowUnread.isChecked
                         postEvent(EventBus.BOOKSHELF_REFRESH, "")
                         postEvent(EventBus.BOOKSHELF_REFRESH, "")
                     }
                     }
+                    if (AppConfig.showLastUpdateTime != swShowLastUpdateTime.isChecked) {
+                        AppConfig.showLastUpdateTime = swShowLastUpdateTime.isChecked
+                        postEvent(EventBus.BOOKSHELF_REFRESH, "")
+                    }
                     var changed = false
                     var changed = false
                     if (bookshelfLayout != rgLayout.getCheckedIndex()) {
                     if (bookshelfLayout != rgLayout.getCheckedIndex()) {
                         putPrefInt(PreferKey.bookshelfLayout, rgLayout.getCheckedIndex())
                         putPrefInt(PreferKey.bookshelfLayout, rgLayout.getCheckedIndex())

+ 5 - 1
app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BaseBooksAdapter.kt

@@ -66,13 +66,17 @@ abstract class BaseBooksAdapter<VB : ViewBinding>(context: Context) :
         for (i in 0 until itemCount) {
         for (i in 0 until itemCount) {
             getItem(i)?.let {
             getItem(i)?.let {
                 if (it.bookUrl == bookUrl) {
                 if (it.bookUrl == bookUrl) {
-                    notifyItemChanged(i, bundleOf(Pair("refresh", null)))
+                    notifyItemChanged(i, bundleOf(Pair("refresh", null), Pair("lastUpdateTime", null)))
                     return
                     return
                 }
                 }
             }
             }
         }
         }
     }
     }
 
 
+    fun upLastUpdateTime() {
+        notifyItemRangeChanged(0, itemCount, bundleOf(Pair("lastUpdateTime", null)))
+    }
+
     interface CallBack {
     interface CallBack {
         fun open(book: Book)
         fun open(book: Book)
         fun openBookInfo(book: Book)
         fun openBookInfo(book: Book)

+ 11 - 0
app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksAdapterList.kt

@@ -9,6 +9,7 @@ import io.legado.app.databinding.ItemBookshelfListBinding
 import io.legado.app.help.book.isLocal
 import io.legado.app.help.book.isLocal
 import io.legado.app.help.config.AppConfig
 import io.legado.app.help.config.AppConfig
 import io.legado.app.utils.invisible
 import io.legado.app.utils.invisible
+import io.legado.app.utils.toTimeAgo
 import splitties.views.onLongClick
 import splitties.views.onLongClick
 
 
 class BooksAdapterList(context: Context, private val callBack: CallBack) :
 class BooksAdapterList(context: Context, private val callBack: CallBack) :
@@ -32,6 +33,7 @@ class BooksAdapterList(context: Context, private val callBack: CallBack) :
             tvLast.text = item.latestChapterTitle
             tvLast.text = item.latestChapterTitle
             ivCover.load(item.getDisplayCover(), item.name, item.author, false, item.origin)
             ivCover.load(item.getDisplayCover(), item.name, item.author, false, item.origin)
             upRefresh(binding, item)
             upRefresh(binding, item)
+            upLastUpdateTime(binding, item)
         } else {
         } else {
             tvRead.text = item.durChapterTitle
             tvRead.text = item.durChapterTitle
             tvLast.text = item.latestChapterTitle
             tvLast.text = item.latestChapterTitle
@@ -41,6 +43,7 @@ class BooksAdapterList(context: Context, private val callBack: CallBack) :
                     "author" -> tvAuthor.text = item.author
                     "author" -> tvAuthor.text = item.author
                     "cover" -> ivCover.load(item.getDisplayCover(), item.name, item.author, false, item.origin)
                     "cover" -> ivCover.load(item.getDisplayCover(), item.name, item.author, false, item.origin)
                     "refresh" -> upRefresh(binding, item)
                     "refresh" -> upRefresh(binding, item)
+                    "lastUpdateTime" -> upLastUpdateTime(binding, item)
                 }
                 }
             }
             }
         }
         }
@@ -61,6 +64,14 @@ class BooksAdapterList(context: Context, private val callBack: CallBack) :
         }
         }
     }
     }
 
 
+    private fun upLastUpdateTime(binding: ItemBookshelfListBinding, item: Book) {
+        if (AppConfig.showLastUpdateTime && !item.isLocal) {
+            binding.tvLastUpdateTime.text = item.latestChapterTime.toTimeAgo()
+        } else {
+            binding.tvLastUpdateTime.text = ""
+        }
+    }
+
     override fun registerListener(holder: ItemViewHolder, binding: ItemBookshelfListBinding) {
     override fun registerListener(holder: ItemViewHolder, binding: ItemBookshelfListBinding) {
         holder.itemView.apply {
         holder.itemView.apply {
             setOnClickListener {
             setOnClickListener {

+ 17 - 4
app/src/main/java/io/legado/app/ui/main/bookshelf/style1/books/BooksFragment.kt

@@ -27,14 +27,11 @@ import io.legado.app.ui.book.read.ReadBookActivity
 import io.legado.app.ui.main.MainViewModel
 import io.legado.app.ui.main.MainViewModel
 import io.legado.app.utils.*
 import io.legado.app.utils.*
 import io.legado.app.utils.viewbindingdelegate.viewBinding
 import io.legado.app.utils.viewbindingdelegate.viewBinding
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.delay
+import kotlinx.coroutines.*
 import kotlinx.coroutines.flow.catch
 import kotlinx.coroutines.flow.catch
 import kotlinx.coroutines.flow.conflate
 import kotlinx.coroutines.flow.conflate
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.launch
 import kotlin.math.max
 import kotlin.math.max
 
 
 /**
 /**
@@ -66,6 +63,7 @@ class BooksFragment() : BaseFragment(R.layout.fragment_books),
     private var savedInstanceState: Bundle? = null
     private var savedInstanceState: Bundle? = null
     private var position = 0
     private var position = 0
     private var groupId = -1L
     private var groupId = -1L
+    private var upLastUpdateTimeJob : Job? = null
 
 
     override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
     override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
         this.savedInstanceState = savedInstanceState
         this.savedInstanceState = savedInstanceState
@@ -107,6 +105,7 @@ class BooksFragment() : BaseFragment(R.layout.fragment_books),
                 }
                 }
             }
             }
         })
         })
+        startLastUpdateTimeJob()
     }
     }
 
 
     private fun upRecyclerData() {
     private fun upRecyclerData() {
@@ -153,6 +152,19 @@ class BooksFragment() : BaseFragment(R.layout.fragment_books),
         }
         }
     }
     }
 
 
+    private fun startLastUpdateTimeJob() {
+        upLastUpdateTimeJob?.cancel()
+        if (!AppConfig.showLastUpdateTime) {
+            return
+        }
+        upLastUpdateTimeJob = launch {
+            while (isActive) {
+                booksAdapter.upLastUpdateTime()
+                delay(30 * 1000)
+            }
+        }
+    }
+
     fun getBooks(): List<Book> {
     fun getBooks(): List<Book> {
         return booksAdapter.getItems()
         return booksAdapter.getItems()
     }
     }
@@ -222,6 +234,7 @@ class BooksFragment() : BaseFragment(R.layout.fragment_books),
         }
         }
         observeEvent<String>(EventBus.BOOKSHELF_REFRESH) {
         observeEvent<String>(EventBus.BOOKSHELF_REFRESH) {
             booksAdapter.notifyDataSetChanged()
             booksAdapter.notifyDataSetChanged()
+            startLastUpdateTimeJob()
         }
         }
     }
     }
 }
 }

+ 39 - 0
app/src/main/java/io/legado/app/utils/TimeUtils.kt

@@ -0,0 +1,39 @@
+package io.legado.app.utils
+
+import kotlin.math.abs
+
+fun Long.toTimeAgo(): String {
+    val curTime = System.currentTimeMillis()
+    val time = this
+    val seconds = abs(System.currentTimeMillis() - time) / 1000f
+    val end = if (time < curTime) "前" else "后"
+
+    val start = when {
+        seconds < 60 -> "${seconds.toInt()}秒"
+        seconds < 3600 -> {
+            val minutes = seconds / 60f
+            "${minutes.toInt()}分钟"
+        }
+        seconds < 86400 -> {
+            val hours = seconds / 3600f
+            "${hours.toInt()}小时"
+        }
+        seconds < 604800 -> {
+            val days = seconds / 86400f
+            "${days.toInt()}天"
+        }
+        seconds < 2_628_000 -> {
+            val weeks = seconds / 604800f
+            "${weeks.toInt()}周"
+        }
+        seconds < 31_536_000 -> {
+            val months = seconds / 2_628_000f
+            "${months.toInt()}月"
+        }
+        else -> {
+            val years = seconds / 31_536_000f
+            "${years.toInt()}年"
+        }
+    }
+    return start + end
+}

+ 11 - 2
app/src/main/res/layout/dialog_bookshelf_config.xml

@@ -40,13 +40,22 @@
         app:layout_constraintTop_toBottomOf="@+id/ll_group_style"
         app:layout_constraintTop_toBottomOf="@+id/ll_group_style"
         tools:ignore="TouchTargetSizeCheck" />
         tools:ignore="TouchTargetSizeCheck" />
 
 
+    <io.legado.app.lib.theme.view.ThemeSwitch
+        android:id="@+id/sw_show_last_update_time"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:padding="6dp"
+        android:text="@string/show_last_update_time"
+        app:layout_constraintTop_toBottomOf="@+id/sw_show_unread"
+        tools:ignore="TouchTargetSizeCheck" />
+
     <LinearLayout
     <LinearLayout
         android:id="@+id/ll_layout"
         android:id="@+id/ll_layout"
         android:layout_width="0dp"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_height="wrap_content"
         android:orientation="vertical"
         android:orientation="vertical"
         app:layout_constraintRight_toLeftOf="@+id/ll_sort"
         app:layout_constraintRight_toLeftOf="@+id/ll_sort"
-        app:layout_constraintTop_toBottomOf="@+id/sw_show_unread"
+        app:layout_constraintTop_toBottomOf="@+id/sw_show_last_update_time"
         app:layout_constraintLeft_toLeftOf="parent">
         app:layout_constraintLeft_toLeftOf="parent">
 
 
         <io.legado.app.ui.widget.text.AccentTextView
         <io.legado.app.ui.widget.text.AccentTextView
@@ -101,7 +110,7 @@
         android:layout_height="wrap_content"
         android:layout_height="wrap_content"
         android:orientation="vertical"
         android:orientation="vertical"
         app:layout_constraintLeft_toRightOf="@+id/ll_layout"
         app:layout_constraintLeft_toRightOf="@+id/ll_layout"
-        app:layout_constraintTop_toBottomOf="@id/sw_show_unread"
+        app:layout_constraintTop_toBottomOf="@id/sw_show_last_update_time"
         app:layout_constraintRight_toRightOf="parent">
         app:layout_constraintRight_toRightOf="parent">
 
 
         <io.legado.app.ui.widget.text.AccentTextView
         <io.legado.app.ui.widget.text.AccentTextView

+ 17 - 0
app/src/main/res/layout/item_bookshelf_list.xml

@@ -98,6 +98,23 @@
         android:textSize="13sp"
         android:textSize="13sp"
         app:layout_constraintBottom_toTopOf="@+id/tv_read"
         app:layout_constraintBottom_toTopOf="@+id/tv_read"
         app:layout_constraintLeft_toRightOf="@+id/iv_author"
         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:text=""
+        android:textColor="@color/tv_text_summary"
+        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_constraintRight_toRightOf="@id/fl_has_new"
         app:layout_constraintTop_toBottomOf="@+id/tv_name"
         app:layout_constraintTop_toBottomOf="@+id/tv_name"
         tools:ignore="RtlSymmetry,TextContrastCheck" />
         tools:ignore="RtlSymmetry,TextContrastCheck" />

+ 1 - 0
app/src/main/res/values-es-rES/strings.xml

@@ -1052,4 +1052,5 @@
     <string name="paste_rule">粘贴规则</string>
     <string name="paste_rule">粘贴规则</string>
     <string name="groups_or_source">多分组/书源</string>
     <string name="groups_or_source">多分组/书源</string>
     <string name="replace_state_change">替换(启用/禁用)</string>
     <string name="replace_state_change">替换(启用/禁用)</string>
+    <string name="show_last_update_time">显示上次更新时间</string>
 </resources>
 </resources>

+ 1 - 0
app/src/main/res/values-ja-rJP/strings.xml

@@ -1055,4 +1055,5 @@
     <string name="paste_rule">粘贴规则</string>
     <string name="paste_rule">粘贴规则</string>
     <string name="groups_or_source">多分组/书源</string>
     <string name="groups_or_source">多分组/书源</string>
     <string name="replace_state_change">替换(启用/禁用)</string>
     <string name="replace_state_change">替换(启用/禁用)</string>
+    <string name="show_last_update_time">显示上次更新时间</string>
 </resources>
 </resources>

+ 1 - 0
app/src/main/res/values-pt-rBR/strings.xml

@@ -1055,4 +1055,5 @@
     <string name="paste_rule">粘贴规则</string>
     <string name="paste_rule">粘贴规则</string>
     <string name="groups_or_source">多分组/书源</string>
     <string name="groups_or_source">多分组/书源</string>
     <string name="replace_state_change">替换(启用/禁用)</string>
     <string name="replace_state_change">替换(启用/禁用)</string>
+    <string name="show_last_update_time">显示上次更新时间</string>
 </resources>
 </resources>

+ 1 - 0
app/src/main/res/values-zh-rHK/strings.xml

@@ -1052,4 +1052,5 @@
     <string name="paste_rule">粘贴规则</string>
     <string name="paste_rule">粘贴规则</string>
     <string name="groups_or_source">多分组/书源</string>
     <string name="groups_or_source">多分组/书源</string>
     <string name="replace_state_change">替换(启用/禁用)</string>
     <string name="replace_state_change">替换(启用/禁用)</string>
+    <string name="show_last_update_time">显示上次更新时间</string>
 </resources>
 </resources>

+ 1 - 0
app/src/main/res/values-zh-rTW/strings.xml

@@ -1054,4 +1054,5 @@
     <string name="paste_rule">粘贴规则</string>
     <string name="paste_rule">粘贴规则</string>
     <string name="groups_or_source">多分组/书源</string>
     <string name="groups_or_source">多分组/书源</string>
     <string name="replace_state_change">替换(启用/禁用)</string>
     <string name="replace_state_change">替换(启用/禁用)</string>
+    <string name="show_last_update_time">显示上次更新时间</string>
 </resources>
 </resources>

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

@@ -1054,4 +1054,5 @@
     <string name="paste_rule">粘贴规则</string>
     <string name="paste_rule">粘贴规则</string>
     <string name="groups_or_source">多分组/书源</string>
     <string name="groups_or_source">多分组/书源</string>
     <string name="replace_state_change">替换(启用/禁用)</string>
     <string name="replace_state_change">替换(启用/禁用)</string>
+    <string name="show_last_update_time">显示上次更新时间</string>
 </resources>
 </resources>

+ 1 - 0
app/src/main/res/values/strings.xml

@@ -1055,4 +1055,5 @@
     <string name="paste_rule">粘贴规则</string>
     <string name="paste_rule">粘贴规则</string>
     <string name="groups_or_source">多分组/书源</string>
     <string name="groups_or_source">多分组/书源</string>
     <string name="replace_state_change">替换(启用/禁用)</string>
     <string name="replace_state_change">替换(启用/禁用)</string>
+    <string name="show_last_update_time">显示上次更新时间</string>
 </resources>
 </resources>