From 01e03ec0f089d99b21c556723f3c9a383b540221 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Tue, 15 Aug 2023 22:25:35 +0800 Subject: [PATCH 01/10] Support search in DownloadsScene --- .../ehviewer/download/DownloadManager.kt | 13 ++-- .../hippo/ehviewer/ui/scene/DownloadsScene.kt | 60 ++++++++++----- .../hippo/ehviewer/ui/scene/FavoritesScene.kt | 3 +- .../hippo/ehviewer/ui/scene/SearchBarScene.kt | 75 +++++++++++-------- .../com/hippo/ehviewer/util/StringUtils.kt | 2 + app/src/main/res/menu/scene_download.xml | 17 ++--- app/src/main/res/values-de/strings.xml | 2 +- app/src/main/res/values-es/strings.xml | 2 +- app/src/main/res/values-fr/strings.xml | 2 +- app/src/main/res/values-ja/strings.xml | 2 +- app/src/main/res/values-ko/strings.xml | 2 +- app/src/main/res/values-th/strings.xml | 2 +- app/src/main/res/values-zh-rCN/strings.xml | 2 +- app/src/main/res/values-zh-rHK/strings.xml | 2 +- app/src/main/res/values-zh-rTW/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- 16 files changed, 111 insertions(+), 79 deletions(-) diff --git a/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.kt b/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.kt index c45b7a4c9..3b7cbc86f 100644 --- a/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.kt +++ b/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.kt @@ -503,7 +503,7 @@ object DownloadManager : OnSpiderListener { ensureDownload() } - fun moveDownload(fromPosition: Int, toPosition: Int): List { + private fun moveDownload(fromPosition: Int, toPosition: Int): List { val info = allInfoList.removeAt(fromPosition) allInfoList.add(toPosition, info) val range = if (fromPosition < toPosition) fromPosition..toPosition else toPosition..fromPosition @@ -515,12 +515,11 @@ object DownloadManager : OnSpiderListener { return list } - fun moveDownload(label: String?, fromPosition: Int, toPosition: Int) = - getInfoListForLabel(label)!!.let { - val absFromPosition = allInfoList.indexOf(it[fromPosition]) - val absToPosition = allInfoList.indexOf(it[toPosition]) - moveDownload(absFromPosition, absToPosition) - } + fun moveDownload(fromItem: DownloadInfo, toItem: DownloadInfo): List { + val fromPosition = allInfoList.indexOf(fromItem) + val toPosition = allInfoList.indexOf(toItem) + return moveDownload(fromPosition, toPosition) + } suspend fun resetAllReadingProgress() = coroutineScope { allInfoList.forEach { info -> diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt index 205afa326..4059e4ea6 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt @@ -102,6 +102,7 @@ import com.hippo.ehviewer.ui.tools.CropDefaults import com.hippo.ehviewer.ui.tools.DialogState import com.hippo.ehviewer.util.FileUtils import com.hippo.ehviewer.util.LongList +import com.hippo.ehviewer.util.containsIgnoreCase import com.hippo.ehviewer.util.sendTo import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.util.lang.launchIO @@ -116,12 +117,13 @@ import com.hippo.ehviewer.download.DownloadManager as downloadManager @SuppressLint("RtlHardcoded") class DownloadsScene : - BaseToolbarScene(), + SearchBarScene(), DownloadInfoListener, OnClickFabListener { /*--------------- Whole life cycle ---------------*/ + private var mKeyword: String? = null private var mLabel: String? = null private var mList: List? = null @@ -141,13 +143,16 @@ class DownloadsScene : private val dialogState = DialogState() + override val fabLayout get() = binding.fabLayout + private fun initLabels() { context ?: return val listLabel = downloadManager.labelList - mLabels = ArrayList(listLabel.size + LABEL_OFFSET) // Add "All" and "Default" label names - mLabels.add(getString(R.string.download_all)) - mLabels.add(getString(R.string.default_download_label_name)) + mLabels = arrayListOf( + getString(R.string.download_all), + getString(R.string.default_download_label_name), + ) listLabel.forEach { mLabels.add(it.label) } @@ -207,10 +212,14 @@ class DownloadsScene : else -> DownloadManager.getLabelDownloadInfoList(mLabel) ?: DownloadManager.allInfoList.also { mLabel = null } } - mList = if (mType != -1) { - list.filter { it.state == mType } - } else { - list + mList = list.filter { + (mType == -1 || it.state == mType) && + mKeyword?.let { keyword -> + it.galleryInfo.run { + title.containsIgnoreCase(keyword) || titleJpn.containsIgnoreCase(keyword) || + uploader.containsIgnoreCase(keyword) + } + } ?: true } if (mAdapter != null) { mAdapter!!.notifyDataSetChanged() @@ -219,12 +228,9 @@ class DownloadsScene : } private fun updateTitle() { - setTitle( - getString( - R.string.scene_download_title, - if (mLabel != null) mLabel else getString(R.string.download_all), - ), - ) + val title = getString(R.string.scene_download_title, mLabel ?: getString(R.string.download_all)) + setSearchBarHint(title) + setEditTextHint(getString(R.string.search_bar_hint, title)) } private fun onInit() { @@ -325,10 +331,11 @@ class DownloadsScene : if (fromPosition == toPosition) { return false } - val list = when (mLabel) { - null -> DownloadManager.moveDownload(fromPosition, toPosition) - getString(R.string.default_download_label_name) -> DownloadManager.moveDownload(null, fromPosition, toPosition) - else -> DownloadManager.moveDownload(mLabel, fromPosition, toPosition) + val fromItem = mList!![fromPosition] + val toItem = mList!![toPosition] + val list = DownloadManager.moveDownload(fromItem, toItem) + if (mType != -1 || mKeyword != null) { + mList = mList!!.sortedByDescending { it.position } } mAdapter!!.notifyItemMoved(fromPosition, toPosition) lifecycleScope.launchIO { @@ -360,13 +367,28 @@ class DownloadsScene : addAboveSnackView(fabLayout) updateView() } + setOnApplySearch { + mKeyword = it.takeUnless { it.isEmpty() } + updateForLabel() + updateView() + } return binding.root } + override fun onSearchViewExpanded() { + super.onSearchViewExpanded() + if (tracker.isInCustomChoice) tracker.clearSelection() + showSearchFab(true) + } + + override fun onSearchViewHidden() { + super.onSearchViewHidden() + hideSearchFab(true) + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) updateTitle() - setNavigationIcon(R.drawable.ic_baseline_menu_24) } override fun onDestroyView() { diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.kt index 357883457..4c93dfaff 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.kt @@ -203,7 +203,7 @@ class FavoritesScene : SearchBarScene() { } else { setSearchBarHint(getString(R.string.favorites_title_2, favCatName, keyword)) } - setEditTextHint(getString(R.string.favorites_search_bar_hint, favCatName)) + setEditTextHint(getString(R.string.search_bar_hint, favCatName)) Settings.recentFavCat = urlBuilder.favCat } @@ -422,7 +422,6 @@ class FavoritesScene : SearchBarScene() { } }) itemTouchHelper.attachToRecyclerView(binding.recyclerView) - allowEmptySearch = false return binding.root } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/SearchBarScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/SearchBarScene.kt index 1de4690aa..d1113dada 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/SearchBarScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/SearchBarScene.kt @@ -164,52 +164,68 @@ abstract class SearchBarScene : BaseScene(), ToolBarScene { privLockModeStart = null } - fun selectSearchFab(animation: Boolean) { - _binding ?: return - mShowActionFab = false + fun showSearchFab(animation: Boolean, delay: Long = 0) { if (animation) { - val fab: View? = fabLayout.primaryFab - val delay: Long - if (View.INVISIBLE == fab!!.visibility) { - delay = 0L - } else { - delay = ANIMATE_TIME - fabLayout.setExpanded(expanded = false, animation = true) - fab.animate().scaleX(0.0f).scaleY(0.0f).setListener(mActionFabAnimatorListener) - .setDuration(ANIMATE_TIME).setStartDelay(0L) - .setInterpolator(AnimationUtils.SLOW_FAST_INTERPOLATOR).start() - } mSearchFab.visibility = View.VISIBLE mSearchFab.rotation = -45.0f mSearchFab.animate().scaleX(1.0f).scaleY(1.0f).rotation(0.0f).setListener(null) .setDuration(ANIMATE_TIME).setStartDelay(delay) .setInterpolator(AnimationUtils.FAST_SLOW_INTERPOLATOR).start() } else { - fabLayout.setExpanded(expanded = false, animation = false) - val fab = fabLayout.primaryFab!! - fab.visibility = View.INVISIBLE - fab.scaleX = 0.0f - fab.scaleY = 0.0f mSearchFab.visibility = View.VISIBLE mSearchFab.scaleX = 1.0f mSearchFab.scaleY = 1.0f } } - fun selectActionFab(animation: Boolean) { - _binding ?: return - mShowActionFab = true - if (animation) { - val delay: Long - if (View.INVISIBLE == mSearchFab.visibility) { - delay = 0L - } else { - delay = ANIMATE_TIME + fun hideSearchFab(animation: Boolean): Long { + return if (View.INVISIBLE == mSearchFab.visibility) { + 0L + } else { + if (animation) { mSearchFab.animate().scaleX(0.0f).scaleY(0.0f) .setListener(mSearchFabAnimatorListener) .setDuration(ANIMATE_TIME).setStartDelay(0L) .setInterpolator(AnimationUtils.SLOW_FAST_INTERPOLATOR).start() + } else { + mSearchFab.visibility = View.INVISIBLE + mSearchFab.scaleX = 0.0f + mSearchFab.scaleY = 0.0f } + ANIMATE_TIME + } + } + + fun selectSearchFab(animation: Boolean) { + _binding ?: return + mShowActionFab = false + val delay = if (animation) { + val fab = fabLayout.primaryFab!! + if (View.INVISIBLE == fab.visibility) { + 0L + } else { + fabLayout.setExpanded(expanded = false, animation = true) + fab.animate().scaleX(0.0f).scaleY(0.0f).setListener(mActionFabAnimatorListener) + .setDuration(ANIMATE_TIME).setStartDelay(0L) + .setInterpolator(AnimationUtils.SLOW_FAST_INTERPOLATOR).start() + ANIMATE_TIME + } + } else { + fabLayout.setExpanded(expanded = false, animation = false) + val fab = fabLayout.primaryFab!! + fab.visibility = View.INVISIBLE + fab.scaleX = 0.0f + fab.scaleY = 0.0f + 0L + } + showSearchFab(animation, delay) + } + + fun selectActionFab(animation: Boolean) { + _binding ?: return + mShowActionFab = true + val delay = hideSearchFab(animation) + if (animation) { val fab = fabLayout.primaryFab!! fab.visibility = View.VISIBLE fab.rotation = -45.0f @@ -222,9 +238,6 @@ abstract class SearchBarScene : BaseScene(), ToolBarScene { fab.visibility = View.VISIBLE fab.scaleX = 1.0f fab.scaleY = 1.0f - mSearchFab.visibility = View.INVISIBLE - mSearchFab.scaleX = 0.0f - mSearchFab.scaleY = 0.0f } } diff --git a/app/src/main/java/com/hippo/ehviewer/util/StringUtils.kt b/app/src/main/java/com/hippo/ehviewer/util/StringUtils.kt index 9e4b9a739..bfa54d224 100644 --- a/app/src/main/java/com/hippo/ehviewer/util/StringUtils.kt +++ b/app/src/main/java/com/hippo/ehviewer/util/StringUtils.kt @@ -21,3 +21,5 @@ fun String.toLongOrDefault(defaultValue: Long): Long { fun String.toFloatOrDefault(defaultValue: Float): Float { return toFloatOrNull() ?: defaultValue } + +fun String?.containsIgnoreCase(other: String) = this?.contains(other, true) ?: false diff --git a/app/src/main/res/menu/scene_download.xml b/app/src/main/res/menu/scene_download.xml index 406b369e2..54034a95a 100644 --- a/app/src/main/res/menu/scene_download.xml +++ b/app/src/main/res/menu/scene_download.xml @@ -17,29 +17,26 @@ - - + + + app:showAsAction="never" /> + app:showAsAction="never" /> Protokolldatei wurde auf %s gespeichert. Cloud-Favoriten Lokale Favoriten - %s durchsuchen + %s durchsuchen %s %1$s - %2$s Aus Favoriten löschen diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 6f91868f8..138bfa307 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -354,7 +354,7 @@ Favoritos en la nube Favoritos locales - Buscar %s + Buscar %s %s %1$s - %2$s Eliminar de favoritos diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index e04c66fab..f9e67ab24 100755 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -393,7 +393,7 @@ Favoris de cloud Favoris locales - Chercher dans %s + Chercher dans %s %s %1$s - %2$s Effacer de favoris diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 3ab693179..95ff0ab01 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -390,7 +390,7 @@ Cloudのお気に入り ローカルのお気に入り - %sを検索 + %sを検索 %s %1$s - %2$s お気に入りから削除 diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 70bacb526..3fb553d57 100755 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -364,7 +364,7 @@ 클라우드 즐겨찾기 로컬 즐겨찾기 - %s 검색 + %s 검색 %s %1$s - %2$s 즐겨찾기에서 삭제 diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index 72c9cedb6..c2d7e2382 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -379,7 +379,7 @@ รายการโปรดบนเว็บ รายการโปรดบนเครื่อง - ค้นหา %s + ค้นหา %s %s %1$s - %2$s ลบออกจากรายการโปรด diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index ca30eaeee..46b92593f 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -409,7 +409,7 @@ 云端收藏 本地收藏 - 搜索 %s + 搜索 %s %s %1$s - %2$s 删除收藏 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index d68327dbd..8b68f2a7a 100755 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -419,7 +419,7 @@ 雲端收藏 本地收藏 - 搜索 %s + 搜索 %s %s %1$s - %2$s 刪除收藏 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index d9d2c9f7b..d0e7f596d 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -400,7 +400,7 @@ 雲端收藏 本機收藏 - 搜尋 %s + 搜尋 %s %s %1$s - %2$s 移除收藏 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4acfd051c..8eee01959 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -467,7 +467,7 @@ Cloud Favorites Local Favorites - Search %s + Search %s %s %1$s - %2$s Delete from favorites From 23a44e2f3b8f7890a2feb724c8ce94b136910b42 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Wed, 16 Aug 2023 20:23:57 +0800 Subject: [PATCH 02/10] Migrate DownloadAdapter to ListAdapter --- .../com/hippo/ehviewer/dao/DownloadInfo.kt | 2 +- .../hippo/ehviewer/ui/scene/DownloadsScene.kt | 182 +++++++----------- 2 files changed, 70 insertions(+), 114 deletions(-) diff --git a/app/src/main/java/com/hippo/ehviewer/dao/DownloadInfo.kt b/app/src/main/java/com/hippo/ehviewer/dao/DownloadInfo.kt index d34c466d8..3cc4e826e 100644 --- a/app/src/main/java/com/hippo/ehviewer/dao/DownloadInfo.kt +++ b/app/src/main/java/com/hippo/ehviewer/dao/DownloadInfo.kt @@ -25,7 +25,7 @@ import java.time.Instant ), ], ) -class DownloadEntity( +data class DownloadEntity( @PrimaryKey @ColumnInfo(name = "GID") var gid: Long, diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt index 4059e4ea6..f5400097b 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt @@ -55,9 +55,11 @@ import androidx.core.content.ContextCompat import androidx.core.view.GravityCompat import androidx.drawerlayout.widget.DrawerLayout import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_DRAG import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.StaggeredGridLayoutManager import arrow.core.partially1 @@ -125,7 +127,7 @@ class DownloadsScene : ---------------*/ private var mKeyword: String? = null private var mLabel: String? = null - private var mList: List? = null + private val mList get() = mAdapter?.currentList /*--------------- View life cycle @@ -136,7 +138,7 @@ class DownloadsScene : private var mAdapter: DownloadAdapter? = null private var mItemTouchHelper: ItemTouchHelper? = null private val tracker get() = mAdapter!!.tracker!! - private var mInitPosition = -1 + private var mGid = -1L private var mLabelAdapter: DownloadLabelAdapter? = null private lateinit var mLabels: MutableList private var mType = -1 @@ -170,18 +172,7 @@ class DownloadsScene : val info = DownloadManager.getDownloadInfo(gid) if (null != info) { mLabel = info.label - updateForLabel() - updateView() - - // Get position - if (null != mList) { - val position = mList!!.indexOf(info) - if (position >= 0 && null != _binding) { - binding.recyclerView.scrollToPosition(position) - } else { - mInitPosition = position - } - } + mGid = gid return true } } @@ -200,30 +191,40 @@ class DownloadsScene : override fun onDestroy() { super.onDestroy() - mList = null DownloadManager.removeDownloadInfoListener(this) } - @SuppressLint("NotifyDataSetChanged") - private fun updateForLabel() { - val list = when (mLabel) { - null -> DownloadManager.allInfoList - getString(R.string.default_download_label_name) -> DownloadManager.defaultInfoList - else -> DownloadManager.getLabelDownloadInfoList(mLabel) - ?: DownloadManager.allInfoList.also { mLabel = null } - } - mList = list.filter { - (mType == -1 || it.state == mType) && - mKeyword?.let { keyword -> - it.galleryInfo.run { - title.containsIgnoreCase(keyword) || titleJpn.containsIgnoreCase(keyword) || - uploader.containsIgnoreCase(keyword) + private fun filter(info: DownloadInfo) = info.run { + (mType == -1 || state == mType) && + mKeyword?.let { + title.containsIgnoreCase(it) || titleJpn.containsIgnoreCase(it) || + uploader.containsIgnoreCase(it) + } ?: true + } + + private fun updateInfoList() { + mAdapter?.run { + val list = when (mLabel) { + null -> DownloadManager.allInfoList + getString(R.string.default_download_label_name) -> DownloadManager.defaultInfoList + else -> DownloadManager.getLabelDownloadInfoList(mLabel) + ?: DownloadManager.allInfoList.also { mLabel = null } + }.filter(::filter) + submitList(list) { + if (mGid != -1L) { + val position = list.indexOfFirst { it.gid == mGid } + if (position != -1) { + binding.recyclerView.scrollToPosition(position) } - } ?: true - } - if (mAdapter != null) { - mAdapter!!.notifyDataSetChanged() + mGid = -1L + } + updateView() + } } + } + + private fun updateForLabel() { + updateInfoList() Settings.recentDownloadLabel = mLabel } @@ -236,13 +237,11 @@ class DownloadsScene : private fun onInit() { if (!handleArguments(arguments)) { mLabel = Settings.recentDownloadLabel - updateForLabel() } } private fun onRestore(savedInstanceState: Bundle) { mLabel = savedInstanceState.getString(KEY_LABEL) - updateForLabel() } override fun onSaveInstanceState(outState: Bundle) { @@ -306,10 +305,6 @@ class DownloadsScene : } } recyclerView.addItemDecoration(decoration) - if (mInitPosition >= 0) { - recyclerView.scrollToPosition(mInitPosition) - mInitPosition = -1 - } mItemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.Callback() { override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { super.onSelectedChanged(viewHolder, actionState) @@ -334,10 +329,7 @@ class DownloadsScene : val fromItem = mList!![fromPosition] val toItem = mList!![toPosition] val list = DownloadManager.moveDownload(fromItem, toItem) - if (mType != -1 || mKeyword != null) { - mList = mList!!.sortedByDescending { it.position } - } - mAdapter!!.notifyItemMoved(fromPosition, toPosition) + updateInfoList() lifecycleScope.launchIO { EhDB.updateDownloadInfo(list) } @@ -350,7 +342,6 @@ class DownloadsScene : dialogState.confirmRemoveDownload(info) { mAdapter!!.notifyItemChanged(position) } - mAdapter!!.notifyItemRemoved(position) } } }) @@ -365,12 +356,11 @@ class DownloadsScene : fabLayout.setAutoCancel(false) fabLayout.setOnClickFabListener(this@DownloadsScene) addAboveSnackView(fabLayout) - updateView() + updateForLabel() } setOnApplySearch { mKeyword = it.takeUnless { it.isEmpty() } - updateForLabel() - updateView() + updateInfoList() } return binding.root } @@ -424,8 +414,7 @@ class DownloadsScene : mType + 1, ) { dialog: DialogInterface, which: Int -> mType = which - 1 - updateForLabel() - updateView() + updateInfoList() dialog.dismiss() } .show() @@ -478,7 +467,7 @@ class DownloadsScene : } } - fun updateView() { + private fun updateView() { if (mViewTransition != null) { if (mList.isNullOrEmpty()) { mViewTransition!!.showView(1) @@ -626,59 +615,28 @@ class DownloadsScene : } } + private fun updateUI() = viewLifecycleOwner.lifecycleScope.launchUI { + updateInfoList() + } + override fun onAdd(info: DownloadInfo, list: List, position: Int) { - if (mList !== list) { - return - } - lifecycleScope.launchUI { - if (mAdapter != null) { - mAdapter!!.notifyItemInserted(position) - } - updateView() - } + updateUI() } override fun onUpdate(info: DownloadInfo, list: List) { - if (mLabel != null && mList !== list) { - return - } - val index = mList!!.indexOf(info) - lifecycleScope.launchUI { - if (index >= 0 && mAdapter != null) { - mAdapter!!.notifyItemChanged(index, PAYLOAD_STATE) - } - } + updateUI() } - @SuppressLint("NotifyDataSetChanged") override fun onUpdateAll() { - lifecycleScope.launchUI { - if (mAdapter != null) { - mAdapter!!.notifyDataSetChanged() - } - } + updateUI() } - @SuppressLint("NotifyDataSetChanged") override fun onReload() { - lifecycleScope.launchUI { - if (mAdapter != null) { - mAdapter!!.notifyDataSetChanged() - } - updateView() - } + updateUI() } override fun onRemove(info: DownloadInfo, list: List, position: Int) { - if (mList !== list) { - return - } - lifecycleScope.launchUI { - if (mAdapter != null) { - mAdapter!!.notifyItemRemoved(position) - } - updateView() - } + updateUI() } private class DownloadLabelHolder(val binding: ItemDrawerListBinding) : @@ -699,7 +657,6 @@ class DownloadsScene : if (mLabel != label1) { mLabel = label1 updateForLabel() - updateView() closeSideSheet() } } @@ -739,7 +696,7 @@ class DownloadsScene : } holder.binding.run { if (list != null) { - text.text = label + " [" + list.size + "]" + text.text = "$label [${list.size}]" } else { text.text = label } @@ -983,17 +940,23 @@ class DownloadsScene : } } - private inner class DownloadAdapter : RecyclerView.Adapter() { + private val diffCallback = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: DownloadInfo, newItem: DownloadInfo) = oldItem.gid == newItem.gid + override fun areContentsTheSame(oldItem: DownloadInfo, newItem: DownloadInfo) = oldItem == newItem + override fun getChangePayload(oldItem: DownloadInfo, newItem: DownloadInfo): Any? { + return if (oldItem.downloadInfo != newItem.downloadInfo) { + PAYLOAD_DOWNLOAD_INFO + } else { + super.getChangePayload(oldItem, newItem) + } + } + } + + private inner class DownloadAdapter : ListAdapter(diffCallback) { private val mInflater: LayoutInflater = layoutInflater var tracker: GallerySelectionTracker? = null - override fun getItemId(position: Int): Long { - return if (mList == null || position < 0 || position >= mList!!.size) { - 0 - } else { - mList!![position].gid - } - } + override fun getItemId(position: Int) = getItem(position).gid override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DownloadHolder { val holder = DownloadHolder(ItemDownloadBinding.inflate(mInflater, parent, false)) @@ -1002,10 +965,8 @@ class DownloadsScene : } override fun onBindViewHolder(holder: DownloadHolder, position: Int) { - mList?.let { - val info = it[position] - holder.bind(info, tracker?.isSelected(info.gid) ?: false) - } + val info = getItem(position) + holder.bind(info, tracker?.isSelected(info.gid) ?: false) } override fun onBindViewHolder( @@ -1015,18 +976,15 @@ class DownloadsScene : ) { payloads.forEach { payload -> when (payload) { - PAYLOAD_STATE -> { - mList?.let { holder.bindForState(it[position]) } + PAYLOAD_DOWNLOAD_INFO -> { + val info = getItem(position) + holder.bindForState(info) return } } } super.onBindViewHolder(holder, position, payloads) } - - override fun getItemCount(): Int { - return if (mList == null) 0 else mList!!.size - } } private inner class RenameLabelDialogHelper( @@ -1061,7 +1019,6 @@ class DownloadsScene : if (mLabel == mOriginalLabel) { mLabel = text updateForLabel() - updateView() } } } @@ -1150,7 +1107,6 @@ class DownloadsScene : withUIContext { mLabel = null updateForLabel() - updateView() mLabelAdapter!!.notifyItemRemoved(position) } } @@ -1168,7 +1124,7 @@ class DownloadsScene : const val ACTION_CLEAR_DOWNLOAD_SERVICE = "clear_download_service" private const val KEY_LABEL = "label" private const val LABEL_OFFSET = 2 - private const val PAYLOAD_STATE = 0 + private const val PAYLOAD_DOWNLOAD_INFO = 0 } } From a148fd6ec3d6000f067089ae0c32beac881d5a3e Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Wed, 16 Aug 2023 22:15:04 +0800 Subject: [PATCH 03/10] Use flow for DownloadsScene UI updates --- .../ehviewer/download/DownloadManager.kt | 14 +++-- .../com/hippo/ehviewer/ui/scene/BaseScene.kt | 9 ++- .../hippo/ehviewer/ui/scene/DownloadsScene.kt | 57 ++++--------------- .../hippo/ehviewer/ui/scene/FavoritesScene.kt | 8 +-- .../ehviewer/ui/scene/GalleryListScene.kt | 7 +-- 5 files changed, 32 insertions(+), 63 deletions(-) diff --git a/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.kt b/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.kt index 3b7cbc86f..ed7896c07 100644 --- a/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.kt +++ b/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.kt @@ -272,23 +272,23 @@ object DownloadManager : OnSpiderListener { private val _stateFlow = callbackFlow { val listener = object : DownloadInfoListener { override fun onAdd(info: DownloadInfo, list: List, position: Int) { - trySend(info) + trySend(info.gid) } override fun onUpdate(info: DownloadInfo, list: List) { - trySend(info) + trySend(info.gid) } override fun onUpdateAll() { - // No-op + trySend(0) } override fun onReload() { - // No-op + trySend(0) } override fun onRemove(info: DownloadInfo, list: List, position: Int) { - trySend(info) + trySend(info.gid) } } addDownloadInfoListener(listener) @@ -297,7 +297,9 @@ object DownloadManager : OnSpiderListener { } }.shareIn(callbackFlowScope, SharingStarted.Eagerly) - fun stateFlow(gid: Long) = _stateFlow.filter { it.gid == gid } + fun stateFlow(gid: Long) = _stateFlow.filter { it == gid } + + fun stateFlow() = _stateFlow suspend fun startAllDownload() { var update = false diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/BaseScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/BaseScene.kt index fa81f4efa..020371c7f 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/BaseScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/BaseScene.kt @@ -26,7 +26,10 @@ import androidx.core.view.GravityCompat import androidx.core.view.SoftwareKeyboardControllerCompat import androidx.drawerlayout.widget.DrawerLayout import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelStoreOwner import androidx.lifecycle.setViewTreeLifecycleOwner +import androidx.lifecycle.setViewTreeViewModelStoreOwner +import androidx.savedstate.SavedStateRegistryOwner import androidx.savedstate.setViewTreeSavedStateRegistryOwner import com.google.android.material.sidesheet.SideSheetDialog import com.hippo.ehviewer.ui.MainActivity @@ -155,8 +158,10 @@ abstract class BaseScene : Fragment() { createDrawerView(savedInstanceState)?.let { sideSheetDialog = SideSheetDialog(requireContext()).apply { window?.decorView?.apply { - setViewTreeLifecycleOwner(viewLifecycleOwner) - setViewTreeSavedStateRegistryOwner(this@BaseScene) + val owner = viewLifecycleOwner + setViewTreeLifecycleOwner(owner) + setViewTreeViewModelStoreOwner(owner as ViewModelStoreOwner) + setViewTreeSavedStateRegistryOwner(owner as SavedStateRegistryOwner) } setContentView(it) } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt index f5400097b..370f869eb 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.kt @@ -81,7 +81,6 @@ import com.hippo.ehviewer.databinding.ItemDownloadBinding import com.hippo.ehviewer.databinding.ItemDrawerListBinding import com.hippo.ehviewer.databinding.SceneDownloadBinding import com.hippo.ehviewer.download.DownloadManager -import com.hippo.ehviewer.download.DownloadManager.DownloadInfoListener import com.hippo.ehviewer.download.DownloadService import com.hippo.ehviewer.download.DownloadService.Companion.clear import com.hippo.ehviewer.download.downloadDir @@ -113,6 +112,7 @@ import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.lang.withUIContext import eu.kanade.tachiyomi.util.system.pxToDp import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import rikka.core.res.resolveColor import com.hippo.ehviewer.download.DownloadManager as downloadManager @@ -120,7 +120,6 @@ import com.hippo.ehviewer.download.DownloadManager as downloadManager @SuppressLint("RtlHardcoded") class DownloadsScene : SearchBarScene(), - DownloadInfoListener, OnClickFabListener { /*--------------- Whole life cycle @@ -181,7 +180,6 @@ class DownloadsScene : override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - DownloadManager.addDownloadInfoListener(this) if (savedInstanceState == null) { onInit() } else { @@ -189,11 +187,6 @@ class DownloadsScene : } } - override fun onDestroy() { - super.onDestroy() - DownloadManager.removeDownloadInfoListener(this) - } - private fun filter(info: DownloadInfo) = info.run { (mType == -1 || state == mType) && mKeyword?.let { @@ -358,6 +351,11 @@ class DownloadsScene : addAboveSnackView(fabLayout) updateForLabel() } + viewLifecycleOwner.lifecycleScope.launch { + DownloadManager.stateFlow().collectLatest { + updateInfoList() + } + } setOnApplySearch { mKeyword = it.takeUnless { it.isEmpty() } updateInfoList() @@ -376,11 +374,6 @@ class DownloadsScene : hideSearchFab(true) } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - updateTitle() - } - override fun onDestroyView() { super.onDestroyView() binding.recyclerView.stopScroll() @@ -615,30 +608,6 @@ class DownloadsScene : } } - private fun updateUI() = viewLifecycleOwner.lifecycleScope.launchUI { - updateInfoList() - } - - override fun onAdd(info: DownloadInfo, list: List, position: Int) { - updateUI() - } - - override fun onUpdate(info: DownloadInfo, list: List) { - updateUI() - } - - override fun onUpdateAll() { - updateUI() - } - - override fun onReload() { - updateUI() - } - - override fun onRemove(info: DownloadInfo, list: List, position: Int) { - updateUI() - } - private class DownloadLabelHolder(val binding: ItemDrawerListBinding) : RecyclerView.ViewHolder(binding.root) @@ -649,16 +618,14 @@ class DownloadsScene : DownloadLabelHolder(ItemDrawerListBinding.inflate(mInflater, parent, false)) holder.itemView.setOnClickListener { val position = holder.bindingAdapterPosition - val label1: String? = if (position == 0) { - null - } else { - mLabels[position] - } - if (mLabel != label1) { - mLabel = label1 + val label = mLabels[position].takeUnless { position == 0 } + if (mLabel != label) { + mKeyword = null + setSearchBarText(null) + mLabel = label updateForLabel() - closeSideSheet() } + closeSideSheet() } holder.binding.edit.setOnClickListener { val context = context diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.kt index 4c93dfaff..bdda08917 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.kt @@ -87,12 +87,11 @@ import com.hippo.ehviewer.util.getValue import com.hippo.ehviewer.util.lazyMut import com.hippo.ehviewer.util.setValue import eu.kanade.tachiyomi.util.lang.launchIO -import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.lang.withIOContext import kotlinx.coroutines.Job import kotlinx.coroutines.delay -import kotlinx.coroutines.ensureActive import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch import moe.tarsin.coroutines.runSuspendCatching import rikka.core.res.resolveColor import java.time.Instant @@ -337,9 +336,8 @@ class FavoritesScene : SearchBarScene() { binding.refreshLayout.setOnRefreshListener { mAdapter?.refresh() } val transition = ViewTransition(binding.refreshLayout, binding.progress, binding.tip) val empty = getString(R.string.gallery_list_empty_hit) - adapter.addLoadStateListener { - viewLifecycleOwner.lifecycleScope.launchUI { - ensureActive() + viewLifecycleOwner.lifecycleScope.launch { + adapter.loadStateFlow.collectLatest { when (val state = it.refresh) { is LoadState.Loading -> { showSearchBar() diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.kt index f5649faaa..0573f3c38 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.kt @@ -104,11 +104,9 @@ import com.hippo.ehviewer.util.getValue import com.hippo.ehviewer.util.lazyMut import com.hippo.ehviewer.util.setValue import eu.kanade.tachiyomi.util.lang.launchIO -import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.lang.withIOContext import eu.kanade.tachiyomi.util.lang.withUIContext import kotlinx.coroutines.delay -import kotlinx.coroutines.ensureActive import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize @@ -365,9 +363,8 @@ class GalleryListScene : SearchBarScene() { binding.refreshLayout.setOnRefreshListener { mAdapter?.refresh() } val transition = ViewTransition(binding.refreshLayout, binding.progress, binding.tip) val empty = getString(R.string.gallery_list_empty_hit) - adapter.addLoadStateListener { - viewLifecycleOwner.lifecycleScope.launchUI { - ensureActive() + launch { + adapter.loadStateFlow.collectLatest { when (val state = it.refresh) { is LoadState.Loading -> { showSearchBar() From 4593714298bb8d977cb8165693a37afa518bf60b Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Wed, 16 Aug 2023 22:16:36 +0800 Subject: [PATCH 04/10] Cleanup DownloadManager --- .../ehviewer/download/DownloadManager.kt | 148 +++--------------- 1 file changed, 24 insertions(+), 124 deletions(-) diff --git a/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.kt b/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.kt index ed7896c07..5eda17d11 100644 --- a/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.kt +++ b/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.kt @@ -71,7 +71,7 @@ object DownloadManager : OnSpiderListener { // Store download info wait to start private val mWaitList = LinkedList() private val mSpeedReminder = SpeedReminder() - private val mDownloadInfoListeners = mutableListOf() + private var mDownloadInfoListener: DownloadInfoListener? = null private val mNotifyTaskPool = ConcurrentPool(5) private var mDownloadListener: DownloadListener? = null private var mCurrentTask: DownloadInfo? = null @@ -125,14 +125,6 @@ object DownloadManager : OnSpiderListener { return info?.state ?: DownloadInfo.STATE_INVALID } - fun addDownloadInfoListener(downloadInfoListener: DownloadInfoListener) { - mDownloadInfoListeners.add(downloadInfoListener) - } - - fun removeDownloadInfoListener(downloadInfoListener: DownloadInfoListener) { - mDownloadInfoListeners.remove(downloadInfoListener) - } - fun setDownloadListener(listener: DownloadListener?) { mDownloadListener = listener } @@ -166,12 +158,7 @@ object DownloadManager : OnSpiderListener { mDownloadListener!!.onStart(info) } // Notify state update - val list: List? = getInfoListForLabel(info.label) - if (list != null) { - for (l in mDownloadInfoListeners) { - l.onUpdate(info, list) - } - } + mDownloadInfoListener?.onUpdate(info) } } @@ -192,12 +179,7 @@ object DownloadManager : OnSpiderListener { // Update in DB EhDB.putDownloadInfo(info) // Notify state update - val list: List? = getInfoListForLabel(info.label) - if (list != null) { - for (l in mDownloadInfoListeners) { - l.onUpdate(info, list) - } - } + mDownloadInfoListener?.onUpdate(info) // Make sure download is running ensureDownload() } @@ -227,9 +209,7 @@ object DownloadManager : OnSpiderListener { EhDB.putDownloadInfo(info) // Notify - for (l in mDownloadInfoListeners) { - l.onAdd(info, list, list.size - 1) - } + mDownloadInfoListener?.onUpdate(info) // Make sure download is running ensureDownload() @@ -259,9 +239,7 @@ object DownloadManager : OnSpiderListener { } if (update) { // Notify Listener - for (l in mDownloadInfoListeners) { - l.onUpdateAll() - } + mDownloadInfoListener?.onUpdateAll() // Ensure download ensureDownload() } @@ -271,29 +249,17 @@ object DownloadManager : OnSpiderListener { private val _stateFlow = callbackFlow { val listener = object : DownloadInfoListener { - override fun onAdd(info: DownloadInfo, list: List, position: Int) { - trySend(info.gid) - } - - override fun onUpdate(info: DownloadInfo, list: List) { + override fun onUpdate(info: DownloadInfo) { trySend(info.gid) } override fun onUpdateAll() { trySend(0) } - - override fun onReload() { - trySend(0) - } - - override fun onRemove(info: DownloadInfo, list: List, position: Int) { - trySend(info.gid) - } } - addDownloadInfoListener(listener) + mDownloadInfoListener = listener awaitClose { - removeDownloadInfoListener(listener) + mDownloadInfoListener = null } }.shareIn(callbackFlowScope, SharingStarted.Eagerly) @@ -317,9 +283,7 @@ object DownloadManager : OnSpiderListener { } if (update) { // Notify Listener - for (l in mDownloadInfoListeners) { - l.onUpdateAll() - } + mDownloadInfoListener?.onUpdateAll() // Ensure download ensureDownload() } @@ -355,9 +319,7 @@ object DownloadManager : OnSpiderListener { // Notify if (notify) { - for (l in mDownloadInfoListeners) { - l.onReload() - } + mDownloadInfoListener?.onUpdateAll() } } @@ -388,9 +350,7 @@ object DownloadManager : OnSpiderListener { // Notify withUIContext { - for (l in mDownloadInfoListeners) { - l.onAdd(info, list, list.size - 1) - } + mDownloadInfoListener?.onUpdate(info) } } @@ -398,12 +358,7 @@ object DownloadManager : OnSpiderListener { val info = stopDownloadInternal(gid) if (info != null) { // Update listener - val list: List? = getInfoListForLabel(info.label) - if (list != null) { - for (l in mDownloadInfoListeners) { - l.onUpdate(info, list) - } - } + mDownloadInfoListener?.onUpdate(info) // Ensure download ensureDownload() } @@ -413,12 +368,7 @@ object DownloadManager : OnSpiderListener { val info = stopCurrentDownloadInternal() if (info != null) { // Update listener - val list: List? = getInfoListForLabel(info.label) - if (list != null) { - for (l in mDownloadInfoListeners) { - l.onUpdate(info, list) - } - } + mDownloadInfoListener?.onUpdate(info) // Ensure download ensureDownload() } @@ -428,9 +378,7 @@ object DownloadManager : OnSpiderListener { stopRangeDownloadInternal(gidList) // Update listener - for (l in mDownloadInfoListeners) { - l.onUpdateAll() - } + mDownloadInfoListener?.onUpdateAll() // Ensure download ensureDownload() @@ -449,9 +397,7 @@ object DownloadManager : OnSpiderListener { stopCurrentDownloadInternal() // Notify mDownloadInfoListener - for (l in mDownloadInfoListeners) { - l.onUpdateAll() - } + mDownloadInfoListener?.onUpdateAll() } suspend fun deleteDownload(gid: Long, deleteFiles: Boolean = false) { @@ -472,9 +418,7 @@ object DownloadManager : OnSpiderListener { if (index >= 0) { list.remove(info) // Update listener - for (l in mDownloadInfoListeners) { - l.onRemove(info, list, index) - } + mDownloadInfoListener?.onUpdate(info) } } @@ -497,9 +441,7 @@ object DownloadManager : OnSpiderListener { allInfoList.removeAll(list.toSet()) // Update listener - for (l in mDownloadInfoListeners) { - l.onReload() - } + mDownloadInfoListener?.onUpdateAll() // Ensure download ensureDownload() @@ -659,9 +601,7 @@ object DownloadManager : OnSpiderListener { // Save to DB EhDB.putDownloadInfo(info) } - for (l in mDownloadInfoListeners) { - l.onReload() - } + mDownloadInfoListener?.onUpdateAll() } suspend fun addLabel(label: String?) { @@ -811,30 +751,15 @@ object DownloadManager : OnSpiderListener { } interface DownloadInfoListener { - /** - * Add the special info to the special position - */ - fun onAdd(info: DownloadInfo, list: List, position: Int) - /** * The special info is changed */ - fun onUpdate(info: DownloadInfo, list: List) + fun onUpdate(info: DownloadInfo) /** * Maybe all data is changed, but size is the same */ fun onUpdateAll() - - /** - * Maybe all data is changed, maybe list is changed - */ - fun onReload() - - /** - * Remove the special info from the special position - */ - fun onRemove(info: DownloadInfo, list: List, position: Int) } interface DownloadListener { @@ -941,12 +866,7 @@ object DownloadManager : OnSpiderListener { Log.e(TAG, "Current task is null, but it should not be") } else { info.total = mPages - val list: List? = getInfoListForLabel(info.label) - if (list != null) { - for (l in mDownloadInfoListeners) { - l.onUpdate(info, list) - } - } + mDownloadInfoListener?.onUpdate(info) } } @@ -975,12 +895,7 @@ object DownloadManager : OnSpiderListener { if (mDownloadListener != null) { mDownloadListener!!.onGetPage(info) } - val list: List? = getInfoListForLabel(info.label) - if (list != null) { - for (l in mDownloadInfoListeners) { - l.onUpdate(info, list) - } - } + mDownloadInfoListener?.onUpdate(info) } } @@ -993,12 +908,7 @@ object DownloadManager : OnSpiderListener { info.finished = mFinished info.downloaded = mDownloaded info.total = mTotal - val list: List? = getInfoListForLabel(info.label) - if (list != null) { - for (l in mDownloadInfoListeners) { - l.onUpdate(info, list) - } - } + mDownloadInfoListener?.onUpdate(info) } } @@ -1037,12 +947,7 @@ object DownloadManager : OnSpiderListener { if (mDownloadListener != null) { mDownloadListener!!.onFinish(info) } - val list: List? = getInfoListForLabel(info.label) - if (list != null) { - for (l in mDownloadInfoListeners) { - l.onUpdate(info, list) - } - } + mDownloadInfoListener?.onUpdate(info) // Start next download ensureDownload() } @@ -1128,12 +1033,7 @@ object DownloadManager : OnSpiderListener { if (mDownloadListener != null) { mDownloadListener!!.onDownload(info) } - val list: List? = getInfoListForLabel(info.label) - if (list != null) { - for (l in mDownloadInfoListeners) { - l.onUpdate(info, list) - } - } + mDownloadInfoListener?.onUpdate(info) } mBytesRead = 0 if (!mStop) { From f117abd4834ebfc5d9bb22db9fd8c64b93cf3981 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Wed, 16 Aug 2023 23:26:15 +0800 Subject: [PATCH 05/10] Revert "Add option for QUIC support" This reverts commit 861c05db --- app/src/main/java/com/hippo/ehviewer/Settings.kt | 1 - .../java/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt | 5 ----- app/src/main/java/com/hippo/ehviewer/util/CronetUtils.kt | 4 +--- app/src/main/res/values-zh-rCN/strings.xml | 2 -- app/src/main/res/values/strings.xml | 2 -- 5 files changed, 1 insertion(+), 13 deletions(-) diff --git a/app/src/main/java/com/hippo/ehviewer/Settings.kt b/app/src/main/java/com/hippo/ehviewer/Settings.kt index 6f1f42428..4cde64a0b 100755 --- a/app/src/main/java/com/hippo/ehviewer/Settings.kt +++ b/app/src/main/java/com/hippo/ehviewer/Settings.kt @@ -62,7 +62,6 @@ object Settings : DataStorePreferences(null) { var blackDarkTheme by boolPref("black_dark_theme", false).observed { updateWhenAmoledModeChanges() } var preloadThumbAggressively by boolPref("preload_thumb_aggressively", false) var downloadOriginImage by boolPref("download_origin_image", false) - var enableQuic by boolPref("enable_quic", true) var thumbSizeDp by intPref("thumb_size_", 120) var recentFavCat by intPref("recent_fav_cat", FavListUrlBuilder.FAV_CAT_LOCAL) var defaultFavSlot by intPref("default_favorite_slot", -2) diff --git a/app/src/main/java/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt b/app/src/main/java/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt index 1326a3086..f7a18d2be 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt @@ -126,11 +126,6 @@ fun AdvancedScreen() { entryValueRes = R.array.app_language_entry_values, value = Settings::language, ) - SwitchPreference( - title = stringResource(id = R.string.settings_advanced_enable_quic), - summary = stringResource(id = R.string.settings_advanced_enable_quic_summary), - value = Settings::enableQuic, - ) SwitchPreference( title = stringResource(id = R.string.preload_thumb_aggressively), value = Settings::preloadThumbAggressively, diff --git a/app/src/main/java/com/hippo/ehviewer/util/CronetUtils.kt b/app/src/main/java/com/hippo/ehviewer/util/CronetUtils.kt index 6b1770cd2..fa3a73c18 100644 --- a/app/src/main/java/com/hippo/ehviewer/util/CronetUtils.kt +++ b/app/src/main/java/com/hippo/ehviewer/util/CronetUtils.kt @@ -1,6 +1,4 @@ package com.hippo.ehviewer.util -import com.hippo.ehviewer.Settings - val isCronetSupported: Boolean - get() = Settings.enableQuic && isAtLeastQ + get() = isAtLeastQ diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 46b92593f..7a21c6906 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -376,8 +376,6 @@ 已保存日志至 %s 阅读缓存大小 App 界面语言 - [实验性] QUIC 支持 - 启用 QUIC 协议支持(需要重启应用) 导出数据 保存数据至外置存储器,例如下载列表,快速搜索列表 已导出数据至 %s diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8eee01959..161e15fab 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -420,8 +420,6 @@ Logcat dumped to %s Read cache size App language - [Experimental] QUIC support - Enable QUIC protocol support (App restart required) Export data Save data like download list, quick search list, to external storage Exported data to %s From 37a3edd44bc21fb010f97d0db54c4270ea77d095 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Wed, 16 Aug 2023 23:50:20 +0800 Subject: [PATCH 06/10] Revert "Add quichint" This reverts commit 4e427f27 --- app/src/main/java/com/hippo/ehviewer/cronet/CronetKt.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/src/main/java/com/hippo/ehviewer/cronet/CronetKt.kt b/app/src/main/java/com/hippo/ehviewer/cronet/CronetKt.kt index c7e02195e..feb31a1b4 100644 --- a/app/src/main/java/com/hippo/ehviewer/cronet/CronetKt.kt +++ b/app/src/main/java/com/hippo/ehviewer/cronet/CronetKt.kt @@ -33,9 +33,6 @@ val cronetHttpClient: CronetEngine = CronetEngine.Builder(appCtx).apply { val cache = (appCtx.cacheDir.toOkioPath() / "http_cache").toFile().apply { mkdirs() } setStoragePath(cache.absolutePath) enableHttpCache(CronetEngine.Builder.HTTP_CACHE_DISK_NO_HTTP, 100 * 1024) - addQuicHint("e-hentai.org", 443, 443) - addQuicHint("forums.e-hentai.org", 443, 443) - addQuicHint("exhentai.org", 443, 443) }.build() val cronetHttpClientExecutor = EhApplication.baseOkHttpClient.dispatcher.executorService From 4a0df0780556ce8b59bf87e1b099b43d96a41cf1 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Thu, 17 Aug 2023 00:03:13 +0800 Subject: [PATCH 07/10] Set User-Agent in CronetEngine.Builder --- app/src/main/java/com/hippo/ehviewer/cronet/CronetKt.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/hippo/ehviewer/cronet/CronetKt.kt b/app/src/main/java/com/hippo/ehviewer/cronet/CronetKt.kt index feb31a1b4..cbe0d677a 100644 --- a/app/src/main/java/com/hippo/ehviewer/cronet/CronetKt.kt +++ b/app/src/main/java/com/hippo/ehviewer/cronet/CronetKt.kt @@ -33,6 +33,7 @@ val cronetHttpClient: CronetEngine = CronetEngine.Builder(appCtx).apply { val cache = (appCtx.cacheDir.toOkioPath() / "http_cache").toFile().apply { mkdirs() } setStoragePath(cache.absolutePath) enableHttpCache(CronetEngine.Builder.HTTP_CACHE_DISK_NO_HTTP, 100 * 1024) + setUserAgent(CHROME_USER_AGENT) }.build() val cronetHttpClientExecutor = EhApplication.baseOkHttpClient.dispatcher.executorService @@ -72,7 +73,6 @@ class CronetRequest { inline fun cronetRequest(url: String, referer: String? = null, conf: UrlRequest.Builder.() -> Unit = {}) = CronetRequest().apply { request = cronetHttpClient.newUrlRequestBuilder(url, callback, cronetHttpClientExecutor).apply { addHeader("Cookie", EhCookieStore.getCookieHeader(url.toHttpUrl())) - addHeader("User-Agent", CHROME_USER_AGENT) addHeader("Accept", CHROME_ACCEPT) addHeader("Accept-Language", CHROME_ACCEPT_LANGUAGE) referer?.let { addHeader("Referer", it) } From 9c5eddb9034f5ee12e5faffa5c0827ff0a2dd094 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Thu, 17 Aug 2023 17:51:44 +0800 Subject: [PATCH 08/10] Remove embedded Cronet library --- app/build.gradle.kts | 2 -- .../ehviewer/coil/CronetHttpUriFetcher.kt | 5 +++- .../com/hippo/ehviewer/cronet/CronetKt.kt | 29 ++++++++++++------- .../com/hippo/ehviewer/spider/SpiderDen.kt | 4 +-- .../com/hippo/ehviewer/util/CronetUtils.kt | 6 +++- .../java/com/hippo/ehviewer/util/SDKUtils.kt | 3 ++ 6 files changed, 32 insertions(+), 17 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index bf7a96103..bd5a5ec63 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -250,8 +250,6 @@ dependencies { implementation("io.ktor:ktor-io-jvm:$ktor") implementation("io.ktor:ktor-utils-jvm:$ktor") - implementation("org.chromium.net:cronet-embedded:113.5672.61") - val serialization = "1.5.1" implementation("org.jetbrains.kotlinx:kotlinx-serialization-cbor:$serialization") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$serialization") diff --git a/app/src/main/java/com/hippo/ehviewer/coil/CronetHttpUriFetcher.kt b/app/src/main/java/com/hippo/ehviewer/coil/CronetHttpUriFetcher.kt index 6339b9857..4fe24db14 100644 --- a/app/src/main/java/com/hippo/ehviewer/coil/CronetHttpUriFetcher.kt +++ b/app/src/main/java/com/hippo/ehviewer/coil/CronetHttpUriFetcher.kt @@ -1,9 +1,12 @@ @file:Suppress("BlockingMethodInNonBlockingContext") +@file:RequiresExtension(extension = Build.VERSION_CODES.S, version = 7) package com.hippo.ehviewer.coil import android.net.Uri +import android.os.Build import android.webkit.MimeTypeMap +import androidx.annotation.RequiresExtension import coil.ComponentRegistry import coil.ImageLoader import coil.decode.DataSource @@ -27,7 +30,7 @@ class CronetHttpUriFetcher(private val data: String, private val options: Option val diskCache = requireNotNull(imageLoader.diskCache) val snapshot = diskCache.openSnapshot(diskCacheKey) ?: run { val success = cronetRequest(data) { - disableCache() + setCacheDisabled(true) }.execute { diskCache.suspendEdit(diskCacheKey) { RandomAccessFile(data.toFile(), "rw").use { diff --git a/app/src/main/java/com/hippo/ehviewer/cronet/CronetKt.kt b/app/src/main/java/com/hippo/ehviewer/cronet/CronetKt.kt index cbe0d677a..8c4d3925b 100644 --- a/app/src/main/java/com/hippo/ehviewer/cronet/CronetKt.kt +++ b/app/src/main/java/com/hippo/ehviewer/cronet/CronetKt.kt @@ -1,5 +1,13 @@ +@file:RequiresExtension(extension = Build.VERSION_CODES.S, version = 7) + package com.hippo.ehviewer.cronet +import android.net.http.HttpEngine +import android.net.http.HttpException +import android.net.http.UrlRequest +import android.net.http.UrlResponseInfo +import android.os.Build +import androidx.annotation.RequiresExtension import com.hippo.ehviewer.EhApplication import com.hippo.ehviewer.client.CHROME_ACCEPT import com.hippo.ehviewer.client.CHROME_ACCEPT_LANGUAGE @@ -12,10 +20,6 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.suspendCancellableCoroutine import okhttp3.HttpUrl.Companion.toHttpUrl import okio.Path.Companion.toOkioPath -import org.chromium.net.CronetEngine -import org.chromium.net.CronetException -import org.chromium.net.UrlRequest -import org.chromium.net.UrlResponseInfo import splitties.init.appCtx import java.nio.ByteBuffer import java.nio.channels.FileChannel @@ -28,24 +32,23 @@ import kotlin.coroutines.resumeWithException private const val TAG = "CronetRequest" val pool = DirectByteBufferPool(32) -val cronetHttpClient: CronetEngine = CronetEngine.Builder(appCtx).apply { - enableBrotli(true) +val cronetHttpClient: HttpEngine = HttpEngine.Builder(appCtx).apply { + setEnableBrotli(true) val cache = (appCtx.cacheDir.toOkioPath() / "http_cache").toFile().apply { mkdirs() } setStoragePath(cache.absolutePath) - enableHttpCache(CronetEngine.Builder.HTTP_CACHE_DISK_NO_HTTP, 100 * 1024) + setEnableHttpCache(HttpEngine.Builder.HTTP_CACHE_DISK_NO_HTTP, 100 * 1024) setUserAgent(CHROME_USER_AGENT) }.build() val cronetHttpClientExecutor = EhApplication.baseOkHttpClient.dispatcher.executorService -// TODO: Rewrite this to use android.net.http.HttpEngine and make it Android 14 only when released class CronetRequest { lateinit var consumer: (ByteBuffer) -> Unit lateinit var onResponse: CronetRequest.(UrlResponseInfo) -> Unit lateinit var request: UrlRequest lateinit var onError: (Throwable) -> Unit lateinit var readerCont: Continuation - val callback = object : UrlRequest.Callback() { + val callback = object : UrlRequest.Callback { override fun onRedirectReceived(req: UrlRequest, info: UrlResponseInfo, url: String) { logcat(tag = TAG) { "Redirected to $url" } req.followRedirect() @@ -64,14 +67,18 @@ class CronetRequest { readerCont.resume(Unit) } - override fun onFailed(req: UrlRequest, info: UrlResponseInfo?, e: CronetException) { + override fun onFailed(req: UrlRequest, info: UrlResponseInfo?, e: HttpException) { onError(e) } + + override fun onCanceled(req: UrlRequest, info: UrlResponseInfo?) { + // No-op + } } } inline fun cronetRequest(url: String, referer: String? = null, conf: UrlRequest.Builder.() -> Unit = {}) = CronetRequest().apply { - request = cronetHttpClient.newUrlRequestBuilder(url, callback, cronetHttpClientExecutor).apply { + request = cronetHttpClient.newUrlRequestBuilder(url, cronetHttpClientExecutor, callback).apply { addHeader("Cookie", EhCookieStore.getCookieHeader(url.toHttpUrl())) addHeader("Accept", CHROME_ACCEPT) addHeader("Accept-Language", CHROME_ACCEPT_LANGUAGE) diff --git a/app/src/main/java/com/hippo/ehviewer/spider/SpiderDen.kt b/app/src/main/java/com/hippo/ehviewer/spider/SpiderDen.kt index 368581798..eff6c5cb8 100644 --- a/app/src/main/java/com/hippo/ehviewer/spider/SpiderDen.kt +++ b/app/src/main/java/com/hippo/ehviewer/spider/SpiderDen.kt @@ -135,9 +135,9 @@ class SpiderDen(mGalleryInfo: GalleryInfo) { ): Boolean { return if (isCronetSupported) { cronetRequest(url, referer) { - disableCache() + setCacheDisabled(true) }.execute { info -> - val headers = info.allHeaders + val headers = info.headers.asMap val type = headers["Content-Type"]?.first()?.toMediaType()?.subtype ?: "jpg" val length = headers["Content-Length"]!!.first().toLong() saveResponseMeta(index, type, length) { file -> diff --git a/app/src/main/java/com/hippo/ehviewer/util/CronetUtils.kt b/app/src/main/java/com/hippo/ehviewer/util/CronetUtils.kt index fa3a73c18..c45915fa0 100644 --- a/app/src/main/java/com/hippo/ehviewer/util/CronetUtils.kt +++ b/app/src/main/java/com/hippo/ehviewer/util/CronetUtils.kt @@ -1,4 +1,8 @@ package com.hippo.ehviewer.util +import android.os.Build +import androidx.annotation.ChecksSdkIntAtLeast + +@get:ChecksSdkIntAtLeast(Build.VERSION_CODES.S, extension = 7) val isCronetSupported: Boolean - get() = isAtLeastQ + get() = isAtLeastSExtension7 diff --git a/app/src/main/java/com/hippo/ehviewer/util/SDKUtils.kt b/app/src/main/java/com/hippo/ehviewer/util/SDKUtils.kt index 41e4f35ea..676312bb0 100644 --- a/app/src/main/java/com/hippo/ehviewer/util/SDKUtils.kt +++ b/app/src/main/java/com/hippo/ehviewer/util/SDKUtils.kt @@ -1,5 +1,8 @@ package com.hippo.ehviewer.util import android.os.Build +import android.os.ext.SdkExtensions val isAtLeastQ = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q +val isAtLeastR = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R +val isAtLeastSExtension7 = isAtLeastR && SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 7 From c62ca4dd23d54aab9aee3ae98a1169d19a053c46 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Thu, 17 Aug 2023 20:24:34 +0800 Subject: [PATCH 09/10] Fix DownloadsScene crash java.lang.LinkageError: Method com.hippo.ehviewer.databinding.ActivityMainBinding com.hippo.ehviewer.ui.scene.DownloadsScene.getBinding() overrides final method in class Lcom/hippo/ehviewer/ui/scene/SearchBarScene --- app/proguard-rules.pro | 1 + 1 file changed, 1 insertion(+) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index ae734e2ee..d452f2ffb 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -17,6 +17,7 @@ -keep class com.hippo.ehviewer.client.parser.GalleryListResult { *; } -keep class com.hippo.ehviewer.client.parser.Limits { *; } -keep class com.hippo.ehviewer.client.data.BaseGalleryInfo { *; } +-keep class com.hippo.ehviewer.ui.scene.SearchBarScene { getBinding(); } # Ktor logger -dontwarn org.slf4j.impl.StaticLoggerBinder From 41767f0fd75123ffe86b46f71c38a4858b44a73a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 17 Aug 2023 10:40:19 +0000 Subject: [PATCH 10/10] Update dependency gradle to v8.3 --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9f4197d5f..ac72c34e8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME