Use a compose Adaptive Sheet for pre-migration sheet

This commit is contained in:
Jobobby04 2023-07-15 19:53:14 -04:00
parent c8c9e79a3e
commit cd4c217a7f
3 changed files with 83 additions and 135 deletions

View File

@ -1,109 +1,56 @@
package eu.kanade.tachiyomi.ui.browse.migration.advanced.design
import android.content.Context
import android.content.res.Resources
import android.os.Build
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.CompoundButton
import android.widget.RadioButton
import android.widget.RadioGroup
import android.widget.Toast
import androidx.core.content.getSystemService
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.isVisible
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.getElevation
import eu.kanade.presentation.components.AdaptiveSheet
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.MigrationBottomSheetBinding
import eu.kanade.tachiyomi.ui.browse.migration.MigrationFlags
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.isNightMode
import eu.kanade.tachiyomi.util.system.isTabletUi
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.setNavigationBarTransparentCompat
import tachiyomi.core.preference.Preference
import tachiyomi.core.util.lang.toLong
import tachiyomi.domain.UnsortedPreferences
import uy.kohesive.injekt.injectLazy
class MigrationBottomSheetDialog(private val baseContext: Context, private val listener: StartMigrationListener) : BottomSheetDialog(baseContext) {
@Composable
fun MigrationBottomSheetDialog(
onDismissRequest: () -> Unit,
onStartMigration: (extraParam: String?) -> Unit,
) {
val startMigration = rememberUpdatedState(onStartMigration)
val state = remember {
MigrationBottomSheetDialogState(startMigration)
}
AdaptiveSheet(onDismissRequest = onDismissRequest) {
AndroidView(
factory = { factoryContext ->
val binding = MigrationBottomSheetBinding.inflate(LayoutInflater.from(factoryContext))
state.initPreferences(binding)
binding.root
},
modifier = Modifier.fillMaxWidth(),
)
}
}
class MigrationBottomSheetDialogState(private val onStartMigration: State<(extraParam: String?) -> Unit>) {
private val preferences: UnsortedPreferences by injectLazy()
lateinit var binding: MigrationBottomSheetBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val rootView = createView()
setContentView(rootView)
// Enforce max width for tablets
if (context.isTabletUi()) {
behavior.maxWidth = 480.dpToPx
} else {
behavior.maxWidth = 0.dpToPx
}
// Set peek height to 50% display height
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
context.display
} else {
@Suppress("DEPRECATION")
context.getSystemService<WindowManager>()?.defaultDisplay
}?.let {
val metrics = DisplayMetrics()
@Suppress("DEPRECATION")
it.getRealMetrics(metrics)
behavior.peekHeight = metrics.heightPixels / 2
}
// Set navbar color to transparent for edge-to-edge bottom sheet if we can use light navigation bar
// TODO Replace deprecated systemUiVisibility when material-components uses new API to modify status bar icons
@Suppress("DEPRECATION")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
window?.setNavigationBarTransparentCompat(context, behavior.getElevation())
val bottomSheet = rootView.parent as ViewGroup
var flags = bottomSheet.systemUiVisibility
flags = if (context.isNightMode()) {
flags and View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv()
} else {
flags or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
}
bottomSheet.systemUiVisibility = flags
}
initPreferences()
binding.migrateBtn.setOnClickListener {
preferences.skipPreMigration().set(binding.skipStep.isChecked)
preferences.hideNotFoundMigration().set(binding.HideNotFoundManga.isChecked)
listener.startMigration(
if (binding.useSmartSearch.isChecked && binding.extraSearchParamText.text.isNotBlank()) {
binding.extraSearchParamText.toString()
} else {
null
},
)
dismiss()
}
behavior.peekHeight = Resources.getSystem().displayMetrics.heightPixels
behavior.state = BottomSheetBehavior.STATE_COLLAPSED
}
fun createView(): View {
binding = MigrationBottomSheetBinding.inflate(LayoutInflater.from(baseContext))
return binding.root
}
/**
* Init general reader preferences.
*/
private fun initPreferences() {
fun initPreferences(binding: MigrationBottomSheetBinding) {
val flags = preferences.migrateFlags().get()
binding.migChapters.isChecked = MigrationFlags.hasChapters(flags)
@ -112,11 +59,11 @@ class MigrationBottomSheetDialog(private val baseContext: Context, private val l
binding.migCustomCover.isChecked = MigrationFlags.hasCustomCover(flags)
binding.migExtra.isChecked = MigrationFlags.hasExtra(flags)
binding.migChapters.setOnCheckedChangeListener { _, _ -> setFlags() }
binding.migCategories.setOnCheckedChangeListener { _, _ -> setFlags() }
binding.migTracking.setOnCheckedChangeListener { _, _ -> setFlags() }
binding.migCustomCover.setOnCheckedChangeListener { _, _ -> setFlags() }
binding.migExtra.setOnCheckedChangeListener { _, _ -> setFlags() }
binding.migChapters.setOnCheckedChangeListener { _, _ -> setFlags(binding) }
binding.migCategories.setOnCheckedChangeListener { _, _ -> setFlags(binding) }
binding.migTracking.setOnCheckedChangeListener { _, _ -> setFlags(binding) }
binding.migCustomCover.setOnCheckedChangeListener { _, _ -> setFlags(binding) }
binding.migExtra.setOnCheckedChangeListener { _, _ -> setFlags(binding) }
binding.useSmartSearch.bindToPreference(preferences.smartMigration())
binding.extraSearchParamText.isVisible = false
@ -129,15 +76,27 @@ class MigrationBottomSheetDialog(private val baseContext: Context, private val l
binding.HideNotFoundManga.isChecked = preferences.hideNotFoundMigration().get()
binding.skipStep.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
context.toast(
binding.root.context.toast(
R.string.pre_migration_skip_toast,
Toast.LENGTH_LONG,
)
}
}
binding.migrateBtn.setOnClickListener {
preferences.skipPreMigration().set(binding.skipStep.isChecked)
preferences.hideNotFoundMigration().set(binding.HideNotFoundManga.isChecked)
onStartMigration.value(
if (binding.useSmartSearch.isChecked && binding.extraSearchParamText.text.isNotBlank()) {
binding.extraSearchParamText.toString()
} else {
null
},
)
}
}
private fun setFlags() {
private fun setFlags(binding: MigrationBottomSheetBinding) {
var flags = 0
if (binding.migChapters.isChecked) flags = flags or MigrationFlags.CHAPTERS
if (binding.migCategories.isChecked) flags = flags or MigrationFlags.CATEGORIES
@ -167,7 +126,3 @@ class MigrationBottomSheetDialog(private val baseContext: Context, private val l
}
}
}
interface StartMigrationListener {
fun startMigration(extraParam: String?)
}

View File

@ -12,8 +12,6 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -24,7 +22,6 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
@ -57,17 +54,6 @@ class PreMigrationScreen(val mangaIds: List<Long>) : Screen() {
val navigator = LocalNavigator.currentOrThrow
var fabExpanded by remember { mutableStateOf(true) }
val items by screenModel.state.collectAsState()
val context = LocalContext.current
DisposableEffect(screenModel) {
screenModel.dialog = MigrationBottomSheetDialog(context, screenModel.listener)
onDispose {}
}
LaunchedEffect(screenModel) {
screenModel.startMigration.collect { extraParam ->
navigator replace MigrationListScreen(MigrationProcedureConfig(mangaIds, extraParam))
}
}
val nestedScrollConnection = remember {
// All this lines just for fab state :/
@ -132,9 +118,7 @@ class PreMigrationScreen(val mangaIds: List<Long>) : Screen() {
)
},
onClick = {
if (!screenModel.dialog.isShowing) {
screenModel.dialog.show()
}
screenModel.onMigrationSheet(true)
},
expanded = fabExpanded,
)
@ -182,6 +166,19 @@ class PreMigrationScreen(val mangaIds: List<Long>) : Screen() {
)
}
}
val migrationSheetOpen by screenModel.migrationSheetOpen.collectAsState()
if (migrationSheetOpen) {
MigrationBottomSheetDialog(
onDismissRequest = { screenModel.onMigrationSheet(false) },
onStartMigration = { extraParam ->
screenModel.onMigrationSheet(false)
screenModel.saveEnabledSources()
navigator replace MigrationListScreen(MigrationProcedureConfig(mangaIds, extraParam))
},
)
}
}
companion object {

View File

@ -6,11 +6,9 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.tachiyomi.databinding.PreMigrationListBinding
import eu.kanade.tachiyomi.source.online.HttpSource
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import tachiyomi.core.util.lang.launchIO
import tachiyomi.domain.UnsortedPreferences
import tachiyomi.domain.source.service.SourceManager
@ -26,28 +24,12 @@ class PreMigrationScreenModel(
private val _state = MutableStateFlow(emptyList<MigrationSourceItem>())
val state = _state.asStateFlow()
private val _migrationSheetOpen = MutableStateFlow(false)
val migrationSheetOpen = _migrationSheetOpen.asStateFlow()
lateinit var controllerBinding: PreMigrationListBinding
var adapter: MigrationSourceAdapter? = null
val startMigration = MutableSharedFlow<String?>()
val listener = object : StartMigrationListener {
override fun startMigration(extraParam: String?) {
val listOfSources = adapter?.currentItems
?.filterIsInstance<MigrationSourceItem>()
?.filter {
it.sourceEnabled
}
?.joinToString("/") { it.source.id.toString() }
.orEmpty()
prefs.migrationSources().set(listOfSources)
coroutineScope.launch {
startMigration.emit(extraParam)
}
}
}
val clickListener = FlexibleAdapter.OnItemClickListener { _, position ->
val adapter = adapter ?: return@OnItemClickListener false
adapter.getItem(position)?.let {
@ -57,8 +39,6 @@ class PreMigrationScreenModel(
false
}
lateinit var dialog: MigrationBottomSheetDialog
init {
coroutineScope.launchIO {
val enabledSources = getEnabledSources()
@ -140,4 +120,20 @@ class PreMigrationScreenModel(
val sortedItems = items.sortedBy { it.source.name }.sortedBy { !it.sourceEnabled }
adapter.updateDataSet(sortedItems)
}
fun onMigrationSheet(isOpen: Boolean) {
_migrationSheetOpen.value = isOpen
}
fun saveEnabledSources() {
val listOfSources = adapter?.currentItems
?.filterIsInstance<MigrationSourceItem>()
?.filter {
it.sourceEnabled
}
?.joinToString("/") { it.source.id.toString() }
.orEmpty()
prefs.migrationSources().set(listOfSources)
}
}