mirror of
https://github.com/gedoor/legado.git
synced 2024-07-06 23:47:49 +08:00
优化
This commit is contained in:
parent
9b49200a85
commit
5324dfcec3
@ -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<ActivityBookSourceBinding, BookSourceV
|
||||
binding.titleBar.findViewById(R.id.search_view)
|
||||
}
|
||||
private var sourceFlowJob: Job? = null
|
||||
private var checkMessageRefreshJob: Job? = null
|
||||
private val groups = linkedSetOf<String>()
|
||||
private var groupMenu: SubMenu? = null
|
||||
override var sort = BookSourceSort.Default
|
||||
@ -75,7 +99,6 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
||||
private set
|
||||
private var snackBar: Snackbar? = null
|
||||
private var isPaused = false
|
||||
private var searchKey: String? = null
|
||||
private val qrResult = registerForActivityResult(QrCodeResult()) {
|
||||
it ?: return@registerForActivityResult
|
||||
showDialogFragment(ImportBookSourceDialog(it))
|
||||
@ -256,7 +279,6 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
||||
|
||||
|
||||
private fun upBookSource(searchKey: String? = null) {
|
||||
this.searchKey = searchKey
|
||||
sourceFlowJob?.cancel()
|
||||
sourceFlowJob = lifecycleScope.launch {
|
||||
when {
|
||||
@ -338,7 +360,7 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
||||
else -> 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<ActivityBookSourceBinding, BookSourceV
|
||||
R.id.menu_remove_group -> selectionRemoveFromGroups()
|
||||
R.id.menu_export_selection -> viewModel.saveToFile(
|
||||
adapter,
|
||||
searchKey,
|
||||
searchView.query?.toString(),
|
||||
sortAscending,
|
||||
sort
|
||||
) { file ->
|
||||
@ -419,7 +441,7 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
||||
|
||||
R.id.menu_share_source -> viewModel.saveToFile(
|
||||
adapter,
|
||||
searchKey,
|
||||
searchView.query?.toString(),
|
||||
sortAscending,
|
||||
sort
|
||||
) {
|
||||
@ -452,7 +474,7 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
||||
val firstItem = adapterItems.indexOf(selectItems.firstOrNull())
|
||||
val lastItem = adapterItems.indexOf(selectItems.lastOrNull())
|
||||
Debug.isChecking = firstItem >= 0 && lastItem >= 0
|
||||
checkMessageRefreshJob(firstItem, lastItem).start()
|
||||
startCheckMessageRefreshJob(firstItem, lastItem)
|
||||
}
|
||||
neutralButton(R.string.check_source_config)
|
||||
cancelButton()
|
||||
@ -469,7 +491,7 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
||||
}
|
||||
keepScreenOn(true)
|
||||
CheckSource.resume(this)
|
||||
checkMessageRefreshJob(0, 0).start()
|
||||
startCheckMessageRefreshJob(0, 0)
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
@ -558,11 +580,6 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
||||
.setAction(R.string.cancel) {
|
||||
CheckSource.stop(this)
|
||||
Debug.finishChecking()
|
||||
adapter.notifyItemRangeChanged(
|
||||
0,
|
||||
adapter.itemCount,
|
||||
bundleOf(Pair("checkSourceMessage", null))
|
||||
)
|
||||
}.apply { show() }
|
||||
}
|
||||
}
|
||||
@ -570,6 +587,11 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
||||
keepScreenOn(false)
|
||||
snackBar?.dismiss()
|
||||
snackBar = null
|
||||
adapter.notifyItemRangeChanged(
|
||||
0,
|
||||
adapter.itemCount,
|
||||
bundleOf(Pair("checkSourceMessage", null))
|
||||
)
|
||||
groups.map { group ->
|
||||
if (group.contains("失效") && searchView.query.isEmpty()) {
|
||||
searchView.setQuery("失效", true)
|
||||
@ -579,15 +601,11 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkMessageRefreshJob(firstItem: Int, lastItem: Int): Job {
|
||||
return lifecycleScope.async(start = CoroutineStart.LAZY) {
|
||||
flow {
|
||||
while (true) {
|
||||
emit(Debug.isChecking)
|
||||
delay(300L)
|
||||
}
|
||||
}.collect {
|
||||
if (SystemUtils.isScreenOn() && !isPaused) {
|
||||
private fun startCheckMessageRefreshJob(firstItem: Int, lastItem: Int) {
|
||||
checkMessageRefreshJob?.cancel()
|
||||
checkMessageRefreshJob = lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
while (isActive) {
|
||||
if (lastItem == 0) {
|
||||
adapter.notifyItemRangeChanged(
|
||||
0,
|
||||
@ -601,9 +619,10 @@ class BookSourceActivity : VMBaseActivity<ActivityBookSourceBinding, BookSourceV
|
||||
bundleOf(Pair("checkSourceMessage", null))
|
||||
)
|
||||
}
|
||||
}
|
||||
if (!it) {
|
||||
this.cancel()
|
||||
if (!Debug.isChecking) {
|
||||
checkMessageRefreshJob?.cancel()
|
||||
}
|
||||
delay(300L)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,10 @@ import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.view.isGone
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.flowWithLifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
@ -25,7 +28,6 @@ import io.legado.app.ui.book.audio.AudioPlayActivity
|
||||
import io.legado.app.ui.book.info.BookInfoActivity
|
||||
import io.legado.app.ui.book.read.ReadBookActivity
|
||||
import io.legado.app.ui.main.MainViewModel
|
||||
import io.legado.app.utils.SystemUtils
|
||||
import io.legado.app.utils.cnCompare
|
||||
import io.legado.app.utils.observeEvent
|
||||
import io.legado.app.utils.setEdgeEffectColor
|
||||
@ -62,7 +64,7 @@ class BooksFragment() : BaseFragment(R.layout.fragment_books),
|
||||
private val bookshelfLayout by lazy { AppConfig.bookshelfLayout }
|
||||
private val booksAdapter: BaseBooksAdapter<*> 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<ExploreViewModel>(R.layout.fragment_exp
|
||||
private val groups = linkedSetOf<String>()
|
||||
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<ExploreViewModel>(R.layout.fragment_exp
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
searchView.clearFocus()
|
||||
exploreFlowJob?.cancel()
|
||||
}
|
||||
|
||||
private fun initSearchView() {
|
||||
@ -126,7 +125,7 @@ class ExploreFragment() : VMBaseFragment<ExploreViewModel>(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<ExploreViewModel>(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<ExploreViewModel>(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<ExploreViewModel>(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<ExploreViewModel>(R.layout.fragment_exp
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
upExploreData(searchKey)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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<Drawable> {
|
||||
|
||||
override fun onLoadFailed(
|
||||
e: GlideException?,
|
||||
model: Any?,
|
||||
target: Target<Drawable>,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: Drawable,
|
||||
model: Any,
|
||||
target: Target<Drawable>?,
|
||||
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)
|
||||
|
@ -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<RssViewModel>(R.layout.fragment_rss),
|
||||
|
||||
private val binding by viewBinding(FragmentRssBinding::bind)
|
||||
override val viewModel by viewModels<RssViewModel>()
|
||||
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<RssViewModel>(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<RssViewModel>(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)
|
||||
|
Loading…
Reference in New Issue
Block a user