Use Compose on Clear Database screen (#7639)

This commit is contained in:
Andreas
2022-07-30 17:51:47 +02:00
committed by GitHub
parent 4774deb1ef
commit 99ac30e59f
16 changed files with 354 additions and 325 deletions

View File

@@ -1,171 +1,20 @@
package eu.kanade.tachiyomi.ui.setting.database
import android.annotation.SuppressLint
import android.app.Dialog
import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import androidx.core.view.forEach
import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import dev.chrisbanes.insetter.applyInsetter
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.Payload
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.ClearDatabaseControllerBinding
import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.ui.base.controller.FabController
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.util.system.toast
import androidx.compose.runtime.Composable
import eu.kanade.presentation.more.settings.database.ClearDatabaseScreen
import eu.kanade.tachiyomi.ui.base.controller.FullComposeController
class ClearDatabaseController :
NucleusController<ClearDatabaseControllerBinding, ClearDatabasePresenter>(),
FlexibleAdapter.OnItemClickListener,
FlexibleAdapter.OnUpdateListener,
FabController {
private var recycler: RecyclerView? = null
private var adapter: FlexibleAdapter<ClearDatabaseSourceItem>? = null
private var menu: Menu? = null
private var actionFab: ExtendedFloatingActionButton? = null
init {
setHasOptionsMenu(true)
}
override fun createBinding(inflater: LayoutInflater): ClearDatabaseControllerBinding {
return ClearDatabaseControllerBinding.inflate(inflater)
}
class ClearDatabaseController : FullComposeController<ClearDatabasePresenter>() {
override fun createPresenter(): ClearDatabasePresenter {
return ClearDatabasePresenter()
}
override fun getTitle(): String? {
return activity?.getString(R.string.pref_clear_database)
}
override fun onViewCreated(view: View) {
super.onViewCreated(view)
binding.recycler.applyInsetter {
type(navigationBars = true) {
padding()
}
}
adapter = FlexibleAdapter<ClearDatabaseSourceItem>(null, this, true)
binding.recycler.adapter = adapter
binding.recycler.layoutManager = LinearLayoutManager(view.context)
binding.recycler.setHasFixedSize(true)
adapter?.fastScroller = binding.fastScroller
recycler = binding.recycler
}
override fun onDestroyView(view: View) {
adapter = null
super.onDestroyView(view)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.generic_selection, menu)
this.menu = menu
menu.forEach { menuItem -> menuItem.isVisible = (adapter?.itemCount ?: 0) > 0 }
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val adapter = adapter ?: return false
when (item.itemId) {
R.id.action_select_all -> adapter.selectAll()
R.id.action_select_inverse -> {
adapter.currentItems.forEachIndexed { index, _ ->
adapter.toggleSelection(index)
}
}
}
updateFab()
adapter.notifyItemRangeChanged(0, adapter.itemCount, Payload.SELECTION)
return super.onOptionsItemSelected(item)
}
override fun onUpdateEmptyView(size: Int) {
if (size > 0) {
binding.emptyView.hide()
} else {
binding.emptyView.show(activity!!.getString(R.string.database_clean))
}
menu?.forEach { menuItem -> menuItem.isVisible = size > 0 }
}
override fun onItemClick(view: View?, position: Int): Boolean {
val adapter = adapter ?: return false
adapter.toggleSelection(position)
adapter.notifyItemChanged(position, Payload.SELECTION)
updateFab()
return true
}
fun setItems(items: List<ClearDatabaseSourceItem>) {
adapter?.updateDataSet(items)
}
override fun configureFab(fab: ExtendedFloatingActionButton) {
fab.setIconResource(R.drawable.ic_delete_24dp)
fab.setText(R.string.action_delete)
fab.hide()
fab.setOnClickListener {
val ctrl = ClearDatabaseSourcesDialog()
ctrl.targetController = this
ctrl.showDialog(router)
}
actionFab = fab
}
private fun updateFab() {
val adapter = adapter ?: return
if (adapter.selectedItemCount > 0) {
actionFab?.show()
} else {
actionFab?.hide()
}
}
override fun cleanupFab(fab: ExtendedFloatingActionButton) {
actionFab?.setOnClickListener(null)
actionFab = null
}
class ClearDatabaseSourcesDialog : DialogController() {
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
return MaterialAlertDialogBuilder(activity!!)
.setMessage(R.string.clear_database_confirmation)
.setPositiveButton(android.R.string.ok) { _, _ ->
(targetController as? ClearDatabaseController)?.clearDatabaseForSelectedSources()
}
.setNegativeButton(android.R.string.cancel, null)
.create()
}
}
@SuppressLint("NotifyDataSetChanged")
private fun clearDatabaseForSelectedSources() {
val adapter = adapter ?: return
val selectedSourceIds = adapter.selectedPositions.mapNotNull { position ->
adapter.getItem(position)?.source?.id
}
presenter.clearDatabaseForSourceIds(selectedSourceIds)
actionFab!!.isVisible = false
adapter.clearSelection()
adapter.notifyDataSetChanged()
activity?.toast(R.string.clear_database_completed)
@Composable
override fun ComposeContent() {
ClearDatabaseScreen(
presenter = presenter,
navigateUp = { router.popCurrentController() },
)
}
}

View File

@@ -2,18 +2,21 @@ package eu.kanade.tachiyomi.ui.setting.database
import android.os.Bundle
import eu.kanade.domain.source.interactor.GetSourcesWithNonLibraryManga
import eu.kanade.domain.source.model.Source
import eu.kanade.presentation.more.settings.database.ClearDatabaseState
import eu.kanade.presentation.more.settings.database.ClearDatabaseStateImpl
import eu.kanade.tachiyomi.Database
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.withUIContext
import kotlinx.coroutines.flow.collectLatest
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class ClearDatabasePresenter(
private val state: ClearDatabaseStateImpl = ClearDatabaseState() as ClearDatabaseStateImpl,
private val database: Database = Injekt.get(),
private val getSourcesWithNonLibraryManga: GetSourcesWithNonLibraryManga = Injekt.get(),
) : BasePresenter<ClearDatabaseController>() {
) : BasePresenter<ClearDatabaseController>(), ClearDatabaseState by state {
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
@@ -21,17 +24,39 @@ class ClearDatabasePresenter(
presenterScope.launchIO {
getSourcesWithNonLibraryManga.subscribe()
.collectLatest { list ->
val items = list
.map { (source, count) -> ClearDatabaseSourceItem(source, count) }
.sortedBy { it.source.name }
withUIContext { view?.setItems(items) }
state.items = list.sortedBy { it.name }
}
}
}
fun clearDatabaseForSourceIds(sources: List<Long>) {
database.mangasQueries.deleteMangasNotInLibraryBySourceIds(sources)
fun removeMangaBySourceId(sourceIds: List<Long>) {
database.mangasQueries.deleteMangasNotInLibraryBySourceIds(sourceIds)
database.historyQueries.removeResettedHistory()
}
fun toggleSelection(source: Source) {
val mutableList = state.selection.toMutableList()
if (mutableList.contains(source.id)) {
mutableList.remove(source.id)
} else {
mutableList.add(source.id)
}
state.selection = mutableList
}
fun clearSelection() {
state.selection = emptyList()
}
fun selectAll() {
state.selection = state.items.map { it.id }
}
fun invertSelection() {
state.selection = state.items.map { it.id }.filterNot { it in state.selection }
}
sealed class Dialog {
data class Delete(val sourceIds: List<Long>) : Dialog()
}
}

View File

@@ -1,48 +0,0 @@
package eu.kanade.tachiyomi.ui.setting.database
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.ClearDatabaseSourceItemBinding
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.icon
data class ClearDatabaseSourceItem(val source: Source, private val mangaCount: Long) : AbstractFlexibleItem<ClearDatabaseSourceItem.Holder>() {
override fun getLayoutRes(): Int {
return R.layout.clear_database_source_item
}
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): Holder {
return Holder(view, adapter)
}
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>?, holder: Holder?, position: Int, payloads: MutableList<Any>?) {
holder?.bind(source, mangaCount)
}
class Holder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter) {
private val binding = ClearDatabaseSourceItemBinding.bind(view)
fun bind(source: Source, count: Long) {
binding.title.text = source.toString()
binding.description.text = itemView.context.getString(R.string.clear_database_source_item_count, count)
itemView.post {
when {
source.icon() != null && source.id != LocalSource.ID ->
binding.thumbnail.setImageDrawable(source.icon())
else -> binding.thumbnail.setImageResource(R.mipmap.ic_local_source)
}
}
binding.checkbox.isChecked = (bindingAdapter as FlexibleAdapter<*>).isSelected(bindingAdapterPosition)
}
}
}