Replace numberpicker with wheelpicker (#8501)

* Replace numberpicker with wheelpicker

* cleanups

(cherry picked from commit acc65529a00c94ec902401fadcba0b82fd352c98)

# Conflicts:
#	app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt
#	app/src/main/java/eu/kanade/tachiyomi/widget/MinMaxNumberPicker.kt
This commit is contained in:
Ivan Iskandar 2022-11-12 03:02:45 +07:00 committed by Jobobby04
parent 754feb6751
commit 3e8fa1c572
6 changed files with 100 additions and 90 deletions

View File

@ -170,6 +170,7 @@ dependencies {
implementation(compose.foundation) implementation(compose.foundation)
implementation(compose.material3.core) implementation(compose.material3.core)
implementation(compose.material3.adapter) implementation(compose.material3.adapter)
implementation(compose.material.core)
implementation(compose.material.icons) implementation(compose.material.icons)
implementation(compose.animation) implementation(compose.animation)
implementation(compose.animation.graphics) implementation(compose.animation.graphics)
@ -266,7 +267,6 @@ dependencies {
implementation(libs.markwon) implementation(libs.markwon)
implementation(libs.aboutLibraries.compose) implementation(libs.aboutLibraries.compose)
implementation(libs.cascade) implementation(libs.cascade)
implementation(libs.numberpicker)
implementation(libs.bundles.voyager) implementation(libs.bundles.voyager)
implementation(libs.wheelpicker) implementation(libs.wheelpicker)

View File

@ -1,13 +1,15 @@
package eu.kanade.presentation.more.settings.screen package eu.kanade.presentation.more.settings.screen
import android.content.Context
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -21,15 +23,17 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.draw.alpha
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastMap import androidx.compose.ui.util.fastMap
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import com.bluelinelabs.conductor.Router import com.bluelinelabs.conductor.Router
import com.chargemap.compose.numberpicker.NumberPicker
import eu.kanade.domain.UnsortedPreferences import eu.kanade.domain.UnsortedPreferences
import eu.kanade.domain.category.interactor.GetCategories import eu.kanade.domain.category.interactor.GetCategories
import eu.kanade.domain.category.interactor.ResetCategoryFlags import eu.kanade.domain.category.interactor.ResetCategoryFlags
@ -88,7 +92,6 @@ class SettingsLibraryScreen : SearchableSettings {
@Composable @Composable
private fun getDisplayGroup(libraryPreferences: LibraryPreferences): Preference.PreferenceGroup { private fun getDisplayGroup(libraryPreferences: LibraryPreferences): Preference.PreferenceGroup {
val context = LocalContext.current
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val portraitColumns by libraryPreferences.portraitColumns().stateIn(scope).collectAsState() val portraitColumns by libraryPreferences.portraitColumns().stateIn(scope).collectAsState()
val landscapeColumns by libraryPreferences.landscapeColumns().stateIn(scope).collectAsState() val landscapeColumns by libraryPreferences.landscapeColumns().stateIn(scope).collectAsState()
@ -112,8 +115,8 @@ class SettingsLibraryScreen : SearchableSettings {
preferenceItems = listOf( preferenceItems = listOf(
Preference.PreferenceItem.TextPreference( Preference.PreferenceItem.TextPreference(
title = stringResource(R.string.pref_library_columns), title = stringResource(R.string.pref_library_columns),
subtitle = "${stringResource(R.string.portrait)}: ${getColumnValue(context, portraitColumns)}, " + subtitle = "${stringResource(R.string.portrait)}: ${getColumnValue(portraitColumns)}, " +
"${stringResource(R.string.landscape)}: ${getColumnValue(context, landscapeColumns)}", "${stringResource(R.string.landscape)}: ${getColumnValue(landscapeColumns)}",
onClick = { showDialog = true }, onClick = { showDialog = true },
), ),
), ),
@ -294,7 +297,6 @@ class SettingsLibraryScreen : SearchableSettings {
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
onValueChanged: (portrait: Int, landscape: Int) -> Unit, onValueChanged: (portrait: Int, landscape: Int) -> Unit,
) { ) {
val context = LocalContext.current
var portraitValue by rememberSaveable { mutableStateOf(initialPortrait) } var portraitValue by rememberSaveable { mutableStateOf(initialPortrait) }
var landscapeValue by rememberSaveable { mutableStateOf(initialLandscape) } var landscapeValue by rememberSaveable { mutableStateOf(initialLandscape) }
@ -302,48 +304,30 @@ class SettingsLibraryScreen : SearchableSettings {
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
title = { Text(text = stringResource(R.string.pref_library_columns)) }, title = { Text(text = stringResource(R.string.pref_library_columns)) },
text = { text = {
Column {
Row { Row {
Column(
modifier = Modifier.weight(1f),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text( Text(
modifier = Modifier.weight(1f),
text = stringResource(R.string.portrait), text = stringResource(R.string.portrait),
textAlign = TextAlign.Center,
maxLines = 1,
style = MaterialTheme.typography.labelMedium, style = MaterialTheme.typography.labelMedium,
) )
NumberPicker(
modifier = Modifier
.fillMaxWidth()
.clipToBounds(),
value = portraitValue,
onValueChange = { portraitValue = it },
range = 0..10,
label = { getColumnValue(context, it) },
dividersColor = MaterialTheme.colorScheme.primary,
textStyle = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.onSurface),
)
}
Column(
modifier = Modifier.weight(1f),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text( Text(
modifier = Modifier.weight(1f),
text = stringResource(R.string.landscape), text = stringResource(R.string.landscape),
textAlign = TextAlign.Center,
maxLines = 1,
style = MaterialTheme.typography.labelMedium, style = MaterialTheme.typography.labelMedium,
) )
NumberPicker(
modifier = Modifier
.fillMaxWidth()
.clipToBounds(),
value = landscapeValue,
onValueChange = { landscapeValue = it },
range = 0..10,
label = { getColumnValue(context, it) },
dividersColor = MaterialTheme.colorScheme.primary,
textStyle = LocalTextStyle.current.copy(color = MaterialTheme.colorScheme.onSurface),
)
} }
LibraryColumnsPicker(
modifier = Modifier.fillMaxWidth(),
portraitValue = portraitValue,
onPortraitChange = { portraitValue = it },
landscapeValue = landscapeValue,
onLandscapeChange = { landscapeValue = it },
)
} }
}, },
dismissButton = { dismissButton = {
@ -359,9 +343,78 @@ class SettingsLibraryScreen : SearchableSettings {
) )
} }
private fun getColumnValue(context: Context, value: Int): String { @Composable
private fun LibraryColumnsPicker(
modifier: Modifier = Modifier,
portraitValue: Int,
onPortraitChange: (Int) -> Unit,
landscapeValue: Int,
onLandscapeChange: (Int) -> Unit,
) {
BoxWithConstraints(
modifier = modifier,
contentAlignment = Alignment.Center,
) {
Surface(
modifier = Modifier.size(maxWidth, maxHeight / 3),
shape = MaterialTheme.shapes.large,
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.2f),
border = BorderStroke(1.dp, MaterialTheme.colorScheme.primary),
) {}
val size = DpSize(width = maxWidth / 2, height = 128.dp)
Row {
WheelPicker(
size = size,
count = 10,
startIndex = portraitValue,
onScrollFinished = {
onPortraitChange(it)
null
},
) { index, snappedIndex ->
ColumnPickerLabel(index = index, snappedIndex = snappedIndex)
}
WheelPicker(
size = size,
count = 10,
startIndex = landscapeValue,
onScrollFinished = {
onLandscapeChange(it)
null
},
) { index, snappedIndex ->
ColumnPickerLabel(index = index, snappedIndex = snappedIndex)
}
}
}
}
@Composable
private fun ColumnPickerLabel(
index: Int,
snappedIndex: Int,
) {
Text(
modifier = Modifier.alpha(
when (snappedIndex) {
index + 1 -> 0.2f
index -> 1f
index - 1 -> 0.2f
else -> 0.2f
},
),
text = getColumnValue(index),
style = MaterialTheme.typography.titleMedium,
maxLines = 1,
)
}
@Composable
@ReadOnlyComposable
private fun getColumnValue(value: Int): String {
return if (value == 0) { return if (value == 0) {
context.getString(R.string.label_default) stringResource(R.string.label_default)
} else { } else {
value.toString() value.toString()
} }

View File

@ -1,40 +0,0 @@
package eu.kanade.tachiyomi.widget
import android.content.Context
import android.text.InputType
import android.util.AttributeSet
import android.widget.EditText
import android.widget.NumberPicker
import androidx.core.view.doOnLayout
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.view.findDescendant
class MinMaxNumberPicker @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
NumberPicker(context, attrs) {
override fun setDisplayedValues(displayedValues: Array<out String>?) {
super.setDisplayedValues(displayedValues)
// Disable keyboard input when a value that can't be auto-filled with number exists
val notNumberValue = displayedValues?.find { it.getOrNull(0)?.digitToIntOrNull() == null }
if (notNumberValue != null) {
descendantFocusability = FOCUS_BLOCK_DESCENDANTS
}
}
init {
if (attrs != null) {
val ta = context.obtainStyledAttributes(attrs, R.styleable.MinMaxNumberPicker, 0, 0)
try {
minValue = ta.getInt(R.styleable.MinMaxNumberPicker_min, 0)
maxValue = ta.getInt(R.styleable.MinMaxNumberPicker_max, 0)
} finally {
ta.recycle()
}
}
doOnLayout {
findDescendant<EditText>()?.setRawInputType(InputType.TYPE_CLASS_NUMBER)
}
}
}

View File

@ -1,11 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<declare-styleable name="MinMaxNumberPicker">
<attr name="min" format="integer"/>
<attr name="max" format="integer"/>
</declare-styleable>
<declare-styleable name="MaterialSpinnerView"> <declare-styleable name="MaterialSpinnerView">
<attr name="title" format="reference|string"/> <attr name="title" format="reference|string"/>
<attr name="android:entries"/> <attr name="android:entries"/>

View File

@ -16,6 +16,9 @@ material3-core = { module = "androidx.compose.material3:material3" }
material3-adapter = "com.google.android.material:compose-theme-adapter-3:1.0.21" material3-adapter = "com.google.android.material:compose-theme-adapter-3:1.0.21"
material-icons = { module = "androidx.compose.material:material-icons-extended" } material-icons = { module = "androidx.compose.material:material-icons-extended" }
# Here until M3's swipeable became public https://issuetracker.google.com/issues/234640556
material-core = { module = "androidx.compose.material:material" }
accompanist-webview = { module = "com.google.accompanist:accompanist-webview", version.ref = "accompanist" } accompanist-webview = { module = "com.google.accompanist:accompanist-webview", version.ref = "accompanist" }
accompanist-swiperefresh = { module = "com.google.accompanist:accompanist-swiperefresh", version.ref = "accompanist" } accompanist-swiperefresh = { module = "com.google.accompanist:accompanist-swiperefresh", version.ref = "accompanist" }
accompanist-flowlayout = { module = "com.google.accompanist:accompanist-flowlayout", version.ref = "accompanist" } accompanist-flowlayout = { module = "com.google.accompanist:accompanist-flowlayout", version.ref = "accompanist" }

View File

@ -63,7 +63,6 @@ photoview = "com.github.chrisbanes:PhotoView:2.3.0"
directionalviewpager = "com.github.tachiyomiorg:DirectionalViewPager:1.0.0" directionalviewpager = "com.github.tachiyomiorg:DirectionalViewPager:1.0.0"
insetter = "dev.chrisbanes.insetter:insetter:0.6.1" insetter = "dev.chrisbanes.insetter:insetter:0.6.1"
cascade = "me.saket.cascade:cascade-compose:2.0.0-beta1" cascade = "me.saket.cascade:cascade-compose:2.0.0-beta1"
numberpicker = "com.chargemap.compose:numberpicker:1.0.3"
wheelpicker = "com.github.commandiron:WheelPickerCompose:1.0.11" wheelpicker = "com.github.commandiron:WheelPickerCompose:1.0.11"
conductor-core = { module = "com.bluelinelabs:conductor", version.ref = "conductor_version" } conductor-core = { module = "com.bluelinelabs:conductor", version.ref = "conductor_version" }