Просмотр исходного кода

[修复] 偶现的无法退出书源管理的问题

HapeLee 8 месяцев назад
Родитель
Сommit
50cd2f56fe

+ 32 - 12
app/src/main/java/io/legado/app/base/adapter/RecyclerAdapter.kt

@@ -5,6 +5,7 @@ import android.content.Context
 import android.util.SparseArray
 import android.view.LayoutInflater
 import android.view.ViewGroup
+import androidx.core.util.size
 import androidx.recyclerview.widget.DiffUtil
 import androidx.recyclerview.widget.GridLayoutManager
 import androidx.recyclerview.widget.RecyclerView
@@ -37,6 +38,8 @@ abstract class RecyclerAdapter<ITEM, VB : ViewBinding>(protected val context: Co
 
     private var diffJob: Coroutine<*>? = null
 
+    private var isResumed = false
+
     var itemAnimation: ItemAnimation? = null
 
     fun setOnItemClickListener(listener: (holder: ItemViewHolder, item: ITEM) -> Unit) {
@@ -54,8 +57,8 @@ abstract class RecyclerAdapter<ITEM, VB : ViewBinding>(protected val context: Co
     @Synchronized
     fun addHeaderView(header: ((parent: ViewGroup) -> ViewBinding)) {
         kotlin.runCatching {
-            val index = headerItems.size()
-            headerItems.put(TYPE_HEADER_VIEW + headerItems.size(), header)
+            val index = headerItems.size
+            headerItems.put(TYPE_HEADER_VIEW + headerItems.size, header)
             notifyItemInserted(index)
         }
     }
@@ -63,8 +66,8 @@ abstract class RecyclerAdapter<ITEM, VB : ViewBinding>(protected val context: Co
     @Synchronized
     fun addFooterView(footer: ((parent: ViewGroup) -> ViewBinding)) {
         kotlin.runCatching {
-            val index = getActualItemCount() + footerItems.size()
-            footerItems.put(TYPE_FOOTER_VIEW + footerItems.size(), footer)
+            val index = getActualItemCount() + footerItems.size
+            footerItems.put(TYPE_FOOTER_VIEW + footerItems.size, footer)
             notifyItemInserted(index)
         }
     }
@@ -113,6 +116,10 @@ abstract class RecyclerAdapter<ITEM, VB : ViewBinding>(protected val context: Co
         skipDiff: Boolean = false
     ) {
         kotlin.runCatching {
+            if (!isResumed) { //全量标记更新
+                setItems(items)
+                return@runCatching
+            }
             val oldItems = this.items.toList()
             val itemsSize = items?.size ?: 0
             val headerCount = getHeaderCount()
@@ -153,6 +160,10 @@ abstract class RecyclerAdapter<ITEM, VB : ViewBinding>(protected val context: Co
                     return itemCallback.getChangePayload(oldItem, newItem)
                 }
             }
+            if (!isResumed) {
+                setItems(items)
+                return@runCatching
+            }
             diffJob?.cancel()
             diffJob = Coroutine.async {
                 val diffResult = if (skipDiff) withTimeoutOrNullAsync(500L) {
@@ -162,7 +173,7 @@ abstract class RecyclerAdapter<ITEM, VB : ViewBinding>(protected val context: Co
                 }
                 ensureActive()
                 handler.post {
-                    if (diffResult == null) {
+                    if (isResumed || diffResult == null) {
                         setItems(items)
                         return@post
                     }
@@ -172,6 +183,10 @@ abstract class RecyclerAdapter<ITEM, VB : ViewBinding>(protected val context: Co
                     if (items != null) {
                         this@RecyclerAdapter.items.addAll(items)
                     }
+                    if (!isResumed) {
+                        return@post
+                    }
+                    ensureActive()
                     diffResult.dispatchUpdatesTo(this@RecyclerAdapter)
                     onCurrentListChanged()
                 }
@@ -329,10 +344,10 @@ abstract class RecyclerAdapter<ITEM, VB : ViewBinding>(protected val context: Co
     fun getActualItemCount() = items.size
 
 
-    fun getHeaderCount() = headerItems.size()
+    fun getHeaderCount() = headerItems.size
 
 
-    fun getFooterCount() = footerItems.size()
+    fun getFooterCount() = footerItems.size
 
     fun getItem(position: Int): ITEM? = items.getOrNull(position)
 
@@ -431,6 +446,15 @@ abstract class RecyclerAdapter<ITEM, VB : ViewBinding>(protected val context: Co
         }
     }
 
+    fun upResumed(isResumed: Boolean) {
+        if (!isResumed) {
+            diffJob?.cancel()
+            diffJob = null
+            handler.removeCallbacksAndMessages(null)
+        }
+        this.isResumed = isResumed
+    }
+
     private fun isHeader(position: Int) = position < getHeaderCount()
 
     private fun isFooter(position: Int) = position >= getActualItemCount() + getHeaderCount()
@@ -479,8 +503,4 @@ abstract class RecyclerAdapter<ITEM, VB : ViewBinding>(protected val context: Co
         private val handler by lazy { buildMainHandler() }
     }
 
-}
-
-
-
-
+}

+ 18 - 12
app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt

@@ -4,7 +4,6 @@ import android.annotation.SuppressLint
 import android.os.Bundle
 import android.view.Menu
 import android.view.MenuItem
-import android.view.MotionEvent
 import android.view.SubMenu
 import android.view.WindowManager
 import androidx.activity.viewModels
@@ -31,8 +30,7 @@ import io.legado.app.databinding.DialogEditTextBinding
 import io.legado.app.help.DirectLinkUpload
 import io.legado.app.help.config.LocalConfig
 import io.legado.app.lib.dialogs.alert
-//import io.legado.app.lib.theme.primaryColor
-//import io.legado.app.lib.theme.primaryTextColor
+import io.legado.app.lib.theme.primaryColor
 import io.legado.app.model.CheckSource
 import io.legado.app.model.Debug
 import io.legado.app.ui.association.ImportBookSourceDialog
@@ -53,13 +51,12 @@ import io.legado.app.utils.cnCompare
 import io.legado.app.utils.dpToPx
 import io.legado.app.utils.flowWithLifecycleAndDatabaseChange
 import io.legado.app.utils.flowWithLifecycleAndDatabaseChangeFirst
-import io.legado.app.utils.hideSoftInput
 import io.legado.app.utils.isAbsUrl
 import io.legado.app.utils.launch
 import io.legado.app.utils.observeEvent
 import io.legado.app.utils.sendToClip
+import io.legado.app.utils.setEdgeEffectColor
 import io.legado.app.utils.share
-import io.legado.app.utils.shouldHideSoftInput
 import io.legado.app.utils.showDialogFragment
 import io.legado.app.utils.showHelp
 import io.legado.app.utils.splitNotBlank
@@ -272,7 +269,7 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
     }
 
     private fun initRecyclerView() {
-        //binding.recyclerView.setEdgeEffectColor(primaryColor)
+        binding.recyclerView.setEdgeEffectColor(primaryColor)
         binding.recyclerView.addItemDecoration(VerticalDivider(this))
         binding.recyclerView.adapter = adapter
         binding.recyclerView.recycledViewPool.setMaxRecycledViews(0, 15)
@@ -286,7 +283,6 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
     }
 
     private fun initSearchView() {
-        //searchView.applyTint(primaryTextColor)
         searchView.queryHint = getString(R.string.search_book_source)
         searchView.setOnQueryTextListener(this)
     }
@@ -394,6 +390,16 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
         }
     }
 
+    override fun onResume() {
+        super.onResume()
+        adapter.upResumed(true)
+    }
+
+    override fun onPause() {
+        adapter.upResumed(false)
+        super.onPause()
+    }
+
     private fun initLiveDataGroup() {
         lifecycleScope.launch {
             appDb.bookSourceDao.flowGroups()
@@ -472,11 +478,11 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
                 searchView.query?.toString(),
                 sortAscending,
                 sort
-            ) { file ->
+            ) { file, name ->
                 exportDir.launch {
                     mode = HandleFileContract.EXPORT
                     fileData = HandleFileContract.FileData(
-                        "bookSource.json",
+                        name,
                         file,
                         "application/json"
                     )
@@ -488,8 +494,8 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
                 searchView.query?.toString(),
                 sortAscending,
                 sort
-            ) {
-                share(it)
+            ) { file, name ->
+                share(file)
             }
 
             R.id.menu_check_selected_interval -> adapter.checkSelectedInterval()
@@ -594,7 +600,7 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
             ?.toMutableList() ?: mutableListOf()
         alert(titleResource = R.string.import_on_line) {
             val alertBinding = DialogEditTextBinding.inflate(layoutInflater).apply {
-                editLayout.hint = "URL"
+                editView.hint = "url"
                 editView.setFilterValues(cacheUrls)
                 editView.delCallBack = {
                     cacheUrls.remove(it)

+ 20 - 5
app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt

@@ -12,6 +12,7 @@ import io.legado.app.help.source.SourceHelp
 import io.legado.app.utils.FileUtils
 import io.legado.app.utils.GSON
 import io.legado.app.utils.cnCompare
+import io.legado.app.utils.normalizeFileName
 import io.legado.app.utils.outputStream
 import io.legado.app.utils.splitNotBlank
 import io.legado.app.utils.stackTraceStr
@@ -19,6 +20,8 @@ import io.legado.app.utils.toastOnUi
 import io.legado.app.utils.writeToOutputStream
 import splitties.init.appCtx
 import java.io.File
+import java.util.Date
+import java.util.Locale
 
 /**
  * 书源管理数据修改
@@ -123,7 +126,11 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
         }
     }
 
-    private fun saveToFile(sources: List<BookSource>, success: (file: File) -> Unit) {
+    private fun saveToFile(
+        sources: List<BookSource>,
+        name: String,
+        success: (file: File, name: String) -> Unit
+    ) {
         execute {
             val path = "${context.filesDir}/shareBookSource.json"
             FileUtils.delete(path)
@@ -133,7 +140,7 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
             }
             file
         }.onSuccess {
-            success.invoke(it)
+            success.invoke(it, name)
         }.onError {
             context.toastOnUi(it.stackTraceStr)
         }
@@ -144,11 +151,12 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
         searchKey: String?,
         sortAscending: Boolean,
         sort: BookSourceSort,
-        success: (file: File) -> Unit
+        success: (file: File, name: String) -> Unit
     ) {
         execute {
             val selection = adapter.selection
-            val selectedRate = selection.size.toFloat() / adapter.itemCount.toFloat()
+            val selectionSize = selection.size
+            val selectedRate = selectionSize.toFloat() / adapter.itemCount.toFloat()
             val sources = if (selectedRate == 1f) {
                 getBookSources(searchKey, sortAscending, sort)
             } else if (selectedRate < 0.3) {
@@ -160,7 +168,14 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
                     keys.contains(it.bookSourceUrl)
                 }
             }
-            saveToFile(sources, success)
+            val name = if (selectionSize == 1) {
+                "bookSource_${selection.first().bookSourceName.normalizeFileName()}.json"
+            } else {
+                val timestamp =
+                    java.text.SimpleDateFormat("yyyyMMddHHmm", Locale.getDefault()).format(Date())
+                "bookSource_$timestamp.json"
+            }
+            saveToFile(sources, name, success)
         }
     }