diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt index 37d3c81cb203..c5673a3daae1 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt @@ -2,14 +2,21 @@ package io.legado.app.ui.book.source.manage import android.annotation.SuppressLint import android.os.Bundle -import android.view.* +import android.view.Menu +import android.view.MenuItem +import android.view.MotionEvent +import android.view.SubMenu +import android.view.WindowManager import android.widget.EditText import androidx.activity.viewModels import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.SearchView import androidx.core.os.bundleOf +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.recyclerview.widget.ItemTouchHelper import com.google.android.material.snackbar.Snackbar import io.legado.app.R @@ -40,15 +47,31 @@ import io.legado.app.ui.widget.dialog.TextDialog import io.legado.app.ui.widget.recycler.DragSelectTouchHelper import io.legado.app.ui.widget.recycler.ItemTouchCallback import io.legado.app.ui.widget.recycler.VerticalDivider -import io.legado.app.utils.* +import io.legado.app.utils.ACache +import io.legado.app.utils.applyTint +import io.legado.app.utils.cnCompare +import io.legado.app.utils.dpToPx +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.showDialogFragment +import io.legado.app.utils.splitNotBlank +import io.legado.app.utils.startActivity +import io.legado.app.utils.toastOnUi import io.legado.app.utils.viewbindingdelegate.viewBinding -import kotlinx.coroutines.* import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.conflate -import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch /** * 书源管理界面 @@ -67,6 +90,7 @@ class BookSourceActivity : VMBaseActivity() private var groupMenu: SubMenu? = null override var sort = BookSourceSort.Default @@ -75,7 +99,6 @@ class BookSourceActivity : VMBaseActivity data.reversed() } } - }.catch { + }.flowWithLifecycle(lifecycle).catch { AppLog.put("书源界面更新书源出错", it) }.flowOn(IO).conflate().collect { data -> adapter.setItems(data, adapter.diffItemCallback, !Debug.isChecking) @@ -403,7 +425,7 @@ class BookSourceActivity : VMBaseActivity selectionRemoveFromGroups() R.id.menu_export_selection -> viewModel.saveToFile( adapter, - searchKey, + searchView.query?.toString(), sortAscending, sort ) { file -> @@ -419,7 +441,7 @@ class BookSourceActivity : VMBaseActivity viewModel.saveToFile( adapter, - searchKey, + searchView.query?.toString(), sortAscending, sort ) { @@ -452,7 +474,7 @@ class BookSourceActivity : VMBaseActivity= 0 && lastItem >= 0 - checkMessageRefreshJob(firstItem, lastItem).start() + startCheckMessageRefreshJob(firstItem, lastItem) } neutralButton(R.string.check_source_config) cancelButton() @@ -469,7 +491,7 @@ class BookSourceActivity : VMBaseActivity if (group.contains("失效") && searchView.query.isEmpty()) { searchView.setQuery("失效", true) @@ -579,15 +601,11 @@ class BookSourceActivity : VMBaseActivity by lazy { if (bookshelfLayout == 0) { - BooksAdapterList(requireContext(), this, lifecycle) + BooksAdapterList(requireContext(), this, viewLifecycleOwner.lifecycle) } else { BooksAdapterGrid(requireContext(), this) } @@ -154,7 +156,7 @@ class BooksFragment() : BaseFragment(R.layout.fragment_books), */ private fun upRecyclerData() { booksFlowJob?.cancel() - booksFlowJob = lifecycleScope.launch { + booksFlowJob = viewLifecycleOwner.lifecycleScope.launch { appDb.bookDao.flowByGroup(groupId).map { list -> //排序 when (bookSort) { @@ -172,9 +174,9 @@ class BooksFragment() : BaseFragment(R.layout.fragment_books), else -> list.sortedByDescending { it.durChapterTime } } - }.flowOn(Dispatchers.Default).catch { + }.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.RESUMED).catch { AppLog.put("书架更新出错", it) - }.conflate().collect { list -> + }.conflate().flowOn(Dispatchers.Default).collect { list -> binding.tvEmptyMsg.isGone = list.isNotEmpty() binding.refreshLayout.isEnabled = enableRefresh && list.isNotEmpty() booksAdapter.setItems(list) @@ -197,29 +199,17 @@ class BooksFragment() : BaseFragment(R.layout.fragment_books), } } - override fun onPause() { - super.onPause() - upLastUpdateTimeJob?.cancel() - booksFlowJob?.cancel() - } - - override fun onResume() { - super.onResume() - startLastUpdateTimeJob() - upRecyclerData() - } - private fun startLastUpdateTimeJob() { upLastUpdateTimeJob?.cancel() if (!AppConfig.showLastUpdateTime) { return } upLastUpdateTimeJob = lifecycleScope.launch { - while (isActive) { - if (SystemUtils.isScreenOn()) { + repeatOnLifecycle(Lifecycle.State.RESUMED) { + while (isActive) { booksAdapter.upLastUpdateTime() + delay(30 * 1000) } - delay(30 * 1000) } } } diff --git a/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt b/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt index f6f790ba9b58..204aff2cad83 100644 --- a/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt @@ -8,6 +8,8 @@ import android.view.View import androidx.appcompat.widget.SearchView import androidx.core.view.isGone import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -34,7 +36,6 @@ import io.legado.app.utils.viewbindingdelegate.viewBinding import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Job -import kotlinx.coroutines.cancel import kotlinx.coroutines.delay import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.conflate @@ -67,14 +68,13 @@ class ExploreFragment() : VMBaseFragment(R.layout.fragment_exp private val groups = linkedSetOf() private var exploreFlowJob: Job? = null private var groupsMenu: SubMenu? = null - private var searchKey: String? = null override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { setSupportToolbar(binding.titleBar.toolbar) initSearchView() initRecyclerView() initGroupData() - upExploreData(once = true) + upExploreData() } override fun onCompatCreateOptionsMenu(menu: Menu) { @@ -87,7 +87,6 @@ class ExploreFragment() : VMBaseFragment(R.layout.fragment_exp override fun onPause() { super.onPause() searchView.clearFocus() - exploreFlowJob?.cancel() } private fun initSearchView() { @@ -126,7 +125,7 @@ class ExploreFragment() : VMBaseFragment(R.layout.fragment_exp } private fun initGroupData() { - lifecycleScope.launch { + viewLifecycleOwner.lifecycleScope.launch { appDb.bookSourceDao.flowExploreGroups().conflate().collect { groups.clear() groups.addAll(it) @@ -135,10 +134,9 @@ class ExploreFragment() : VMBaseFragment(R.layout.fragment_exp } } - private fun upExploreData(searchKey: String? = null, once: Boolean = false) { - this.searchKey = searchKey + private fun upExploreData(searchKey: String? = null) { exploreFlowJob?.cancel() - exploreFlowJob = lifecycleScope.launch { + exploreFlowJob = viewLifecycleOwner.lifecycleScope.launch { when { searchKey.isNullOrBlank() -> { appDb.bookSourceDao.flowExplore() @@ -152,12 +150,11 @@ class ExploreFragment() : VMBaseFragment(R.layout.fragment_exp else -> { appDb.bookSourceDao.flowExplore(searchKey) } - }.catch { + }.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.RESUMED).catch { AppLog.put("发现界面更新数据出错", it) }.conflate().flowOn(IO).collect { binding.tvEmptyMsg.isGone = it.isNotEmpty() || searchView.query.isNotEmpty() adapter.setItems(it, diffItemCallBack) - if (once) cancel() delay(500) } } @@ -173,7 +170,7 @@ class ExploreFragment() : VMBaseFragment(R.layout.fragment_exp } override val scope: CoroutineScope - get() = lifecycleScope + get() = viewLifecycleOwner.lifecycleScope override fun onCompatOptionsItemSelected(item: MenuItem) { super.onCompatOptionsItemSelected(item) @@ -231,9 +228,4 @@ class ExploreFragment() : VMBaseFragment(R.layout.fragment_exp } } - override fun onResume() { - super.onResume() - upExploreData(searchKey) - } - } diff --git a/app/src/main/java/io/legado/app/ui/main/rss/RssAdapter.kt b/app/src/main/java/io/legado/app/ui/main/rss/RssAdapter.kt index 7866fed0a3ed..256683122d02 100644 --- a/app/src/main/java/io/legado/app/ui/main/rss/RssAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/main/rss/RssAdapter.kt @@ -1,17 +1,11 @@ package io.legado.app.ui.main.rss import android.content.Context -import android.graphics.drawable.Drawable import android.view.View import android.view.ViewGroup import androidx.appcompat.widget.PopupMenu import androidx.lifecycle.Lifecycle -import com.bumptech.glide.load.DataSource -import com.bumptech.glide.load.engine.GlideException -import com.bumptech.glide.load.resource.gif.GifDrawable -import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.RequestOptions -import com.bumptech.glide.request.target.Target import io.legado.app.R import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.RecyclerAdapter @@ -39,35 +33,6 @@ class RssAdapter(context: Context, val callBack: CallBack, val lifecycle: Lifecy val options = RequestOptions() .set(OkHttpModelLoader.sourceOriginOption, item.sourceUrl) ImageLoader.load(lifecycle, item.sourceIcon) - .listener(object : RequestListener { - - override fun onLoadFailed( - e: GlideException?, - model: Any?, - target: Target, - isFirstResource: Boolean - ): Boolean { - return false - } - - override fun onResourceReady( - resource: Drawable, - model: Any, - target: Target?, - dataSource: DataSource, - isFirstResource: Boolean - ): Boolean { - if (resource is GifDrawable - && !lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED) - ) { - ivIcon.post { - resource.stop() - } - } - return false - } - - }) .apply(options) .centerCrop() .placeholder(R.drawable.image_rss) diff --git a/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt b/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt index a10997db3c53..b587955fad3f 100644 --- a/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/rss/RssFragment.kt @@ -7,6 +7,8 @@ import android.view.SubMenu import android.view.View import androidx.appcompat.widget.SearchView import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import io.legado.app.R import io.legado.app.base.VMBaseFragment @@ -58,7 +60,7 @@ class RssFragment() : VMBaseFragment(R.layout.fragment_rss), private val binding by viewBinding(FragmentRssBinding::bind) override val viewModel by viewModels() - private val adapter by lazy { RssAdapter(requireContext(), this, lifecycle) } + private val adapter by lazy { RssAdapter(requireContext(), this, viewLifecycleOwner.lifecycle) } private val searchView: SearchView by lazy { binding.titleBar.findViewById(R.id.search_view) } @@ -142,22 +144,23 @@ class RssFragment() : VMBaseFragment(R.layout.fragment_rss), private fun initGroupData() { groupsFlowJob?.cancel() - groupsFlowJob = lifecycleScope.launch { + groupsFlowJob = viewLifecycleOwner.lifecycleScope.launch { appDb.rssSourceDao.flowGroupEnabled().catch { AppLog.put("订阅界面获取分组数据失败\n${it.localizedMessage}", it) - }.flowOn(IO).conflate().collect { - groups.clear() - it.map { group -> - groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + }.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.RESUMED) + .flowOn(IO).conflate().collect { + groups.clear() + it.map { group -> + groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) + } + upGroupsMenu() } - upGroupsMenu() - } } } private fun upRssFlowJob(searchKey: String? = null) { rssFlowJob?.cancel() - rssFlowJob = lifecycleScope.launch { + rssFlowJob = viewLifecycleOwner.lifecycleScope.launch { when { searchKey.isNullOrEmpty() -> appDb.rssSourceDao.flowEnabled() searchKey.startsWith("group:") -> { @@ -166,7 +169,7 @@ class RssFragment() : VMBaseFragment(R.layout.fragment_rss), } else -> appDb.rssSourceDao.flowEnabled(searchKey) - }.catch { + }.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.RESUMED).catch { AppLog.put("订阅界面更新数据出错", it) }.flowOn(IO).collect { adapter.setItems(it)