Settings: Tint icon with primary color and separate info item layout (#8217)
This commit is contained in:
parent
e4292719d3
commit
aea0cadbfb
12 changed files with 142 additions and 106 deletions
|
@ -15,6 +15,7 @@ import eu.kanade.domain.track.service.TrackPreferences
|
||||||
import eu.kanade.domain.ui.UiPreferences
|
import eu.kanade.domain.ui.UiPreferences
|
||||||
import eu.kanade.presentation.more.settings.widget.AppThemePreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.AppThemePreferenceWidget
|
||||||
import eu.kanade.presentation.more.settings.widget.EditTextPreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.EditTextPreferenceWidget
|
||||||
|
import eu.kanade.presentation.more.settings.widget.InfoWidget
|
||||||
import eu.kanade.presentation.more.settings.widget.ListPreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.ListPreferenceWidget
|
||||||
import eu.kanade.presentation.more.settings.widget.MultiSelectListPreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.MultiSelectListPreferenceWidget
|
||||||
import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
|
import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget
|
||||||
|
@ -163,6 +164,9 @@ internal fun PreferenceItem(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
is Preference.PreferenceItem.InfoPreference -> {
|
||||||
|
InfoWidget(text = item.title)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
package eu.kanade.presentation.more.settings
|
package eu.kanade.presentation.more.settings
|
||||||
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.outlined.Info
|
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import eu.kanade.domain.ui.model.AppTheme
|
import eu.kanade.domain.ui.model.AppTheme
|
||||||
import eu.kanade.tachiyomi.data.track.TrackService
|
import eu.kanade.tachiyomi.data.track.TrackService
|
||||||
|
@ -127,6 +125,15 @@ sealed class Preference {
|
||||||
override val icon: ImageVector? = null
|
override val icon: ImageVector? = null
|
||||||
override val onValueChanged: suspend (newValue: String) -> Boolean = { true }
|
override val onValueChanged: suspend (newValue: String) -> Boolean = { true }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class InfoPreference(
|
||||||
|
override val title: String,
|
||||||
|
) : PreferenceItem<String>() {
|
||||||
|
override val enabled: Boolean = true
|
||||||
|
override val subtitle: String? = null
|
||||||
|
override val icon: ImageVector? = null
|
||||||
|
override val onValueChanged: suspend (newValue: String) -> Boolean = { true }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class PreferenceGroup(
|
data class PreferenceGroup(
|
||||||
|
@ -135,12 +142,4 @@ sealed class Preference {
|
||||||
|
|
||||||
val preferenceItems: List<PreferenceItem<out Any>>,
|
val preferenceItems: List<PreferenceItem<out Any>>,
|
||||||
) : Preference()
|
) : Preference()
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun infoPreference(info: String) = PreferenceItem.TextPreference(
|
|
||||||
title = "",
|
|
||||||
subtitle = info,
|
|
||||||
icon = Icons.Outlined.Info,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -356,7 +356,7 @@ class SettingsBackupScreen : SearchableSettings {
|
||||||
title = stringResource(R.string.pref_backup_slots),
|
title = stringResource(R.string.pref_backup_slots),
|
||||||
entries = listOf(2, 3, 4, 5).associateWith { it.toString() },
|
entries = listOf(2, 3, 4, 5).associateWith { it.toString() },
|
||||||
),
|
),
|
||||||
Preference.infoPreference(stringResource(R.string.backup_info)),
|
Preference.PreferenceItem.InfoPreference(stringResource(R.string.backup_info)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ class SettingsBrowseScreen : SearchableSettings {
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Preference.infoPreference(stringResource(R.string.parental_controls_info)),
|
Preference.PreferenceItem.InfoPreference(stringResource(R.string.parental_controls_info)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -265,7 +265,7 @@ class SettingsDownloadScreen : SearchableSettings {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Preference.infoPreference(stringResource(R.string.download_ahead_info)),
|
Preference.PreferenceItem.InfoPreference(stringResource(R.string.download_ahead_info)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,9 +182,10 @@ private fun SearchResult(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is Preference.PreferenceItem<*> -> sequenceOf(null to p)
|
is Preference.PreferenceItem<*> -> sequenceOf(null to p)
|
||||||
else -> emptySequence() // Ignore other prefs
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Don't show info preference
|
||||||
|
.filterNot { it.second is Preference.PreferenceItem.InfoPreference }
|
||||||
// Filter by search query
|
// Filter by search query
|
||||||
.filter { (_, p) ->
|
.filter { (_, p) ->
|
||||||
val inTitle = p.title.contains(searchKey, true)
|
val inTitle = p.title.contains(searchKey, true)
|
||||||
|
|
|
@ -76,7 +76,7 @@ class SettingsSecurityScreen : SearchableSettings {
|
||||||
entries = SecurityPreferences.SecureScreenMode.values()
|
entries = SecurityPreferences.SecureScreenMode.values()
|
||||||
.associateWith { stringResource(it.titleResId) },
|
.associateWith { stringResource(it.titleResId) },
|
||||||
),
|
),
|
||||||
Preference.infoPreference(stringResource(R.string.secure_screen_summary)),
|
Preference.PreferenceItem.InfoPreference(stringResource(R.string.secure_screen_summary)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,7 +145,7 @@ class SettingsTrackingScreen : SearchableSettings {
|
||||||
login = { context.openInBrowser(BangumiApi.authUrl(), forceDefaultBrowser = true) },
|
login = { context.openInBrowser(BangumiApi.authUrl(), forceDefaultBrowser = true) },
|
||||||
logout = { dialog = LogoutDialog(trackManager.bangumi) },
|
logout = { dialog = LogoutDialog(trackManager.bangumi) },
|
||||||
),
|
),
|
||||||
Preference.infoPreference(stringResource(R.string.tracking_info)),
|
Preference.PreferenceItem.InfoPreference(stringResource(R.string.tracking_info)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Preference.PreferenceGroup(
|
Preference.PreferenceGroup(
|
||||||
|
@ -168,7 +168,7 @@ class SettingsTrackingScreen : SearchableSettings {
|
||||||
},
|
},
|
||||||
logout = trackManager.komga::logout,
|
logout = trackManager.komga::logout,
|
||||||
),
|
),
|
||||||
Preference.infoPreference(stringResource(R.string.enhanced_tracking_info)),
|
Preference.PreferenceItem.InfoPreference(stringResource(R.string.enhanced_tracking_info)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -15,7 +15,6 @@ import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.sizeIn
|
import androidx.compose.foundation.layout.sizeIn
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
@ -28,68 +27,18 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.composed
|
import androidx.compose.ui.composed
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import eu.kanade.presentation.more.settings.LocalPreferenceHighlighted
|
import eu.kanade.presentation.more.settings.LocalPreferenceHighlighted
|
||||||
import eu.kanade.presentation.util.secondaryItemAlpha
|
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun BasePreferenceWidget(
|
internal fun BasePreferenceWidget(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
title: String,
|
title: String? = null,
|
||||||
subtitle: String? = null,
|
|
||||||
icon: ImageVector? = null,
|
|
||||||
onClick: (() -> Unit)? = null,
|
|
||||||
widget: @Composable (() -> Unit)? = null,
|
|
||||||
) {
|
|
||||||
BasePreferenceWidget(
|
|
||||||
modifier = modifier,
|
|
||||||
title = title,
|
|
||||||
subcomponent = if (!subtitle.isNullOrBlank()) {
|
|
||||||
{
|
|
||||||
Text(
|
|
||||||
text = subtitle,
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(
|
|
||||||
start = HorizontalPadding,
|
|
||||||
top = 0.dp,
|
|
||||||
end = HorizontalPadding,
|
|
||||||
)
|
|
||||||
.secondaryItemAlpha(),
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
maxLines = 10,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
},
|
|
||||||
icon = icon,
|
|
||||||
onClick = onClick,
|
|
||||||
widget = widget,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
internal fun BasePreferenceWidget(
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
title: String,
|
|
||||||
subcomponent: @Composable (ColumnScope.() -> Unit)? = null,
|
subcomponent: @Composable (ColumnScope.() -> Unit)? = null,
|
||||||
icon: ImageVector? = null,
|
icon: @Composable (() -> Unit)? = null,
|
||||||
onClick: (() -> Unit)? = null,
|
|
||||||
widget: @Composable (() -> Unit)? = null,
|
|
||||||
) {
|
|
||||||
BasePreferenceWidgetImpl(modifier, title, subcomponent, icon, onClick, widget)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun BasePreferenceWidgetImpl(
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
title: String,
|
|
||||||
subcomponent: @Composable (ColumnScope.() -> Unit)? = null,
|
|
||||||
icon: ImageVector? = null,
|
|
||||||
onClick: (() -> Unit)? = null,
|
onClick: (() -> Unit)? = null,
|
||||||
widget: @Composable (() -> Unit)? = null,
|
widget: @Composable (() -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
|
@ -103,11 +52,9 @@ private fun BasePreferenceWidgetImpl(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
if (icon != null) {
|
if (icon != null) {
|
||||||
Icon(
|
Box(
|
||||||
imageVector = icon,
|
modifier = Modifier.padding(start = HorizontalPadding),
|
||||||
contentDescription = null,
|
content = { icon() },
|
||||||
modifier = Modifier
|
|
||||||
.padding(start = HorizontalPadding, end = 0.dp),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Column(
|
Column(
|
||||||
|
@ -115,26 +62,23 @@ private fun BasePreferenceWidgetImpl(
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
.padding(vertical = 16.dp),
|
.padding(vertical = 16.dp),
|
||||||
) {
|
) {
|
||||||
if (title.isNotBlank()) {
|
if (!title.isNullOrBlank()) {
|
||||||
Row(
|
Text(
|
||||||
modifier = Modifier.padding(horizontal = HorizontalPadding),
|
modifier = Modifier.padding(horizontal = HorizontalPadding),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
text = title,
|
||||||
) {
|
overflow = TextOverflow.Ellipsis,
|
||||||
Text(
|
maxLines = 2,
|
||||||
text = title,
|
style = MaterialTheme.typography.titleLarge,
|
||||||
overflow = TextOverflow.Ellipsis,
|
fontSize = 20.sp,
|
||||||
maxLines = 2,
|
)
|
||||||
style = MaterialTheme.typography.titleLarge,
|
|
||||||
fontSize = 20.sp,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
subcomponent?.invoke(this)
|
subcomponent?.invoke(this)
|
||||||
}
|
}
|
||||||
if (widget != null) {
|
if (widget != null) {
|
||||||
Box(modifier = Modifier.padding(end = HorizontalPadding)) {
|
Box(
|
||||||
widget()
|
modifier = Modifier.padding(end = HorizontalPadding),
|
||||||
}
|
content = { widget() },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
package eu.kanade.presentation.more.settings.widget
|
||||||
|
|
||||||
|
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.Info
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||||
|
import eu.kanade.presentation.util.secondaryItemAlpha
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun InfoWidget(text: String) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = HorizontalPadding, vertical = 16.dp)
|
||||||
|
.secondaryItemAlpha(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Info,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(
|
||||||
|
name = "Light",
|
||||||
|
showBackground = true,
|
||||||
|
)
|
||||||
|
@Preview(
|
||||||
|
name = "Dark",
|
||||||
|
showBackground = true,
|
||||||
|
uiMode = UI_MODE_NIGHT_YES,
|
||||||
|
|
||||||
|
)
|
||||||
|
@Composable
|
||||||
|
private fun InfoWidgetPreview() {
|
||||||
|
TachiyomiTheme {
|
||||||
|
Surface {
|
||||||
|
InfoWidget(text = stringResource(id = R.string.download_ahead_info))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,23 +20,24 @@ fun SwitchPreferenceWidget(
|
||||||
checked: Boolean = false,
|
checked: Boolean = false,
|
||||||
onCheckedChanged: (Boolean) -> Unit,
|
onCheckedChanged: (Boolean) -> Unit,
|
||||||
) {
|
) {
|
||||||
BasePreferenceWidget(
|
TextPreferenceWidget(
|
||||||
title = title,
|
title = title,
|
||||||
subtitle = subtitle,
|
subtitle = subtitle,
|
||||||
icon = icon,
|
icon = icon,
|
||||||
onClick = { onCheckedChanged(!checked) },
|
widget = {
|
||||||
) {
|
Switch(
|
||||||
Switch(
|
checked = checked,
|
||||||
checked = checked,
|
onCheckedChange = null,
|
||||||
onCheckedChange = null,
|
modifier = Modifier.padding(start = TrailingWidgetBuffer),
|
||||||
modifier = Modifier.padding(start = TrailingWidgetBuffer),
|
)
|
||||||
)
|
},
|
||||||
}
|
onPreferenceClick = { onCheckedChanged(!checked) },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun SwitchPreferenceWidgetPreview() {
|
private fun SwitchPreferenceWidgetPreview() {
|
||||||
MaterialTheme {
|
MaterialTheme {
|
||||||
Surface {
|
Surface {
|
||||||
Column {
|
Column {
|
||||||
|
|
|
@ -1,37 +1,66 @@
|
||||||
package eu.kanade.presentation.more.settings.widget
|
package eu.kanade.presentation.more.settings.widget
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Preview
|
import androidx.compose.material.icons.filled.Preview
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import eu.kanade.presentation.util.secondaryItemAlpha
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TextPreferenceWidget(
|
fun TextPreferenceWidget(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
title: String,
|
title: String? = null,
|
||||||
subtitle: String? = null,
|
subtitle: String? = null,
|
||||||
icon: ImageVector? = null,
|
icon: ImageVector? = null,
|
||||||
|
iconTint: Color = MaterialTheme.colorScheme.primary,
|
||||||
|
widget: @Composable (() -> Unit)? = null,
|
||||||
onPreferenceClick: (() -> Unit)? = null,
|
onPreferenceClick: (() -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
BasePreferenceWidget(
|
BasePreferenceWidget(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
title = title,
|
title = title,
|
||||||
subtitle = subtitle,
|
subcomponent = if (!subtitle.isNullOrBlank()) {
|
||||||
icon = icon,
|
{
|
||||||
|
Text(
|
||||||
|
text = subtitle,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = HorizontalPadding)
|
||||||
|
.secondaryItemAlpha(),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
maxLines = 10,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
},
|
||||||
|
icon = if (icon != null) {
|
||||||
|
{
|
||||||
|
Icon(
|
||||||
|
imageVector = icon,
|
||||||
|
tint = iconTint,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
},
|
||||||
onClick = onPreferenceClick,
|
onClick = onPreferenceClick,
|
||||||
|
widget = widget,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun TextPreferenceWidgetPreview() {
|
private fun TextPreferenceWidgetPreview() {
|
||||||
MaterialTheme {
|
MaterialTheme {
|
||||||
Surface {
|
Surface {
|
||||||
Column {
|
Column {
|
||||||
|
|
Reference in a new issue