Migrate card settings

This commit is contained in:
MM20 2022-01-30 13:32:59 +01:00
parent a4ead54329
commit 8e96e2b2db
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
16 changed files with 466 additions and 54 deletions

View File

@ -24,13 +24,11 @@ class PreferencesCardFragment : Fragment(R.layout.fragment_card_settings) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val previewCard = view.findViewById<LauncherCardView>(R.id.previewCard)
previewCard.strokeOpacity = 0xFF
val prefFragment = PreferenesCardInnerFragment()
prefFragment.onPreferencesReady = {
findPreference<Preference>("card_radius")?.let {
it.summary = preferences.cardRadius.toString()
it.setOnPreferenceChangeListener { pref, newValue ->
val value = newValue as Int
previewCard.radius = value * dp
@ -39,7 +37,6 @@ class PreferencesCardFragment : Fragment(R.layout.fragment_card_settings) {
}
}
findPreference<Preference>("card_opacity")?.let {
it.summary = preferences.cardOpacity.toString()
it.setOnPreferenceChangeListener { pref, newValue ->
val value = newValue as Int
previewCard.backgroundOpacity = value
@ -49,7 +46,6 @@ class PreferencesCardFragment : Fragment(R.layout.fragment_card_settings) {
}
}
findPreference<Preference>("card_stroke_width")?.let {
it.summary = preferences.cardRadius.toString()
it.setOnPreferenceChangeListener { pref, newValue ->
val value = newValue as Int
previewCard.strokeWidth = (value * dp).roundToInt()

View File

@ -119,5 +119,11 @@ fun createFactorySettings(context: Context): Settings {
.setLightNavBar(false)
.setLightStatusBar(false)
)
.setCards(
Settings.CardSettings.newBuilder()
.setBorderWidth(0)
.setRadius(8)
.setOpacity(1f)
)
.build()
}

View File

@ -49,8 +49,6 @@ class LauncherPreferences(val context: Application, version: Int = 3) {
var appStartAnim by EnumPreference("app_start_anim", default = AppStartAnimation.M)
var cardOpacity by IntPreference("card_opacity", default = 0xFF)
var cardStrokeWidth by IntPreference("card_stroke_width", default = 0)
var cardRadius by IntPreference("card_radius", default = 8)
var easterEggEnabled by BooleanPreference("easter_egg", default = false)

View File

@ -164,4 +164,11 @@ message Settings {
bool lightNavBar = 2;
}
SystemBarsSettings system_bars = 23;
message CardSettings {
float opacity = 1;
uint32 radius = 2;
uint32 border_width = 3;
}
CardSettings cards = 24;
}

View File

@ -1,5 +1,6 @@
package de.mm20.launcher2.ui.component
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
@ -7,20 +8,23 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import de.mm20.launcher2.ui.locals.LocalCardStyle
@Composable
fun LauncherCard(
modifier: Modifier = Modifier,
elevation: Dp = 2.dp,
backgroundOpacity: Float = 1f,
backgroundOpacity: Float = LocalCardStyle.current.opacity,
content: @Composable () -> Unit = {}
) {
Surface(
modifier = modifier,
shape = RoundedCornerShape(8.dp),
shape = RoundedCornerShape(LocalCardStyle.current.radius.dp),
border = LocalCardStyle.current.borderWidth.takeIf { it > 0 }
?.let { BorderStroke(it.dp, MaterialTheme.colorScheme.surface) },
content = content,
color = MaterialTheme.colorScheme.surface.copy(alpha = backgroundOpacity.coerceIn(0f, 1f)),
shadowElevation = elevation,
shadowElevation = if (backgroundOpacity == 1f) elevation else 0.dp,
tonalElevation = elevation
)
}

View File

@ -0,0 +1,115 @@
package de.mm20.launcher2.ui.component.preferences
import androidx.compose.foundation.layout.*
import androidx.compose.material.Slider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp
import de.mm20.launcher2.ktx.ceilToInt
import java.text.DecimalFormat
import kotlin.math.floor
import kotlin.math.log
import kotlin.math.pow
import kotlin.math.roundToInt
@Composable
fun SliderPreference(
title: String,
icon: ImageVector? = null,
value: Float,
min: Float = 0f,
max: Float = 1f,
step: Float? = null,
onValueChanged: (Float) -> Unit,
enabled: Boolean = true
) {
var sliderValue by remember(value) { mutableStateOf(value) }
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 16.dp)
.alpha(if (enabled) 1f else 0.38f),
) {
Box(
modifier = Modifier
.width(48.dp)
.padding(start = 4.dp),
contentAlignment = Alignment.CenterStart
) {
if (icon != null) {
Icon(
imageVector = icon,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
)
}
}
Column(
modifier = Modifier.weight(1f)
) {
Text(
modifier = Modifier.padding(start = 8.dp),
text = title,
style = MaterialTheme.typography.titleMedium
)
Row(
verticalAlignment = Alignment.CenterVertically
) {
Slider(
modifier = Modifier.weight(1f),
value = sliderValue,
onValueChange = {
sliderValue = it
},
valueRange = min..max,
steps = step?.let { ((max - min) / it).toInt() - 1 } ?: 0,
onValueChangeFinished = {
onValueChanged(sliderValue)
}
)
val decimalPlaces = -log(step ?: 0.01f, 10f)
val format = remember { DecimalFormat().apply {
maximumFractionDigits = floor(decimalPlaces).toInt()
minimumFractionDigits = 0
} }
Text(
modifier = Modifier.width(56.dp).padding(start = 24.dp),
text = format.format(sliderValue),
style = MaterialTheme.typography.titleSmall
)
}
}
}
}
@Composable
fun SliderPreference(
title: String,
icon: ImageVector? = null,
value: Int,
min: Int = 0,
max: Int = 100,
step: Int = 1,
onValueChanged: (Int) -> Unit,
enabled: Boolean = true
) {
SliderPreference(
title = title,
icon = icon,
value = value.toFloat(),
enabled = enabled,
min = min.toFloat(),
max = max.toFloat(),
step = step.toFloat(),
onValueChanged = {
onValueChanged(it.roundToInt())
}
)
}

View File

@ -49,6 +49,7 @@ import de.mm20.launcher2.search.data.Websearch
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.LauncherCard
import de.mm20.launcher2.ui.launcher.LauncherActivityVM
import de.mm20.launcher2.ui.locals.LocalCardStyle
import de.mm20.launcher2.ui.settings.SettingsActivity
import kotlinx.coroutines.flow.map
import org.koin.androidx.compose.inject
@ -176,10 +177,11 @@ fun SearchBar(
durationMillis = 200,
delayMillis = 200
)
else -> tween(durationMillis = 500)
else -> tween(durationMillis = 200)
}
}) {
when {
it == SearchBarLevel.Active -> LocalCardStyle.current.opacity
style != SearchBarSettings.SearchBarStyle.Transparent -> 1f
it == SearchBarLevel.Resting -> 0f
else -> 1f

View File

@ -6,21 +6,32 @@ import android.util.Log
import android.view.MotionEvent
import android.widget.FrameLayout
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.platform.ComposeView
import androidx.lifecycle.MutableLiveData
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.ui.LegacyLauncherTheme
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.launcher.search.SearchBar
import de.mm20.launcher2.ui.launcher.search.SearchBarLevel
import de.mm20.launcher2.ui.locals.LocalCardStyle
import de.mm20.launcher2.ui.locals.LocalNavController
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
class SearchBar @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = R.attr.materialCardViewStyle
) : FrameLayout(context, attrs, defStyleAttr) {
) : FrameLayout(context, attrs, defStyleAttr), KoinComponent {
var level: SearchBarLevel = SearchBarLevel.Resting
set(value) {
@ -28,6 +39,7 @@ class SearchBar @JvmOverloads constructor(
field = value
}
private val dataStore: LauncherDataStore by inject()
private val levelState = MutableLiveData(level)
var onFocus: (() -> Unit)? = null
@ -36,12 +48,21 @@ class SearchBar @JvmOverloads constructor(
val view = ComposeView(context)
view.setContent {
val level by levelState.observeAsState(SearchBarLevel.Resting)
LegacyLauncherTheme {
Box(contentAlignment = Alignment.TopCenter) {
SearchBar(
level,
onFocus = { onFocus?.invoke() }
)
val cardStyle by remember {
dataStore.data.map { it.cards }.distinctUntilChanged()
}.collectAsState(
Settings.CardSettings.getDefaultInstance()
)
CompositionLocalProvider(
LocalCardStyle provides cardStyle
) {
LegacyLauncherTheme {
Box(contentAlignment = Alignment.TopCenter) {
SearchBar(
level,
onFocus = { onFocus?.invoke() }
)
}
}
}
}

View File

@ -3,20 +3,51 @@ package de.mm20.launcher2.ui.legacy.view
import android.content.Context
import android.util.AttributeSet
import androidx.core.content.ContextCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.card.MaterialCardView
import de.mm20.launcher2.ktx.dp
import de.mm20.launcher2.ktx.lifecycleOwner
import de.mm20.launcher2.ktx.lifecycleScope
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.LauncherPreferences
import de.mm20.launcher2.ui.R
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
open class InnerCardView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = R.attr.materialCardViewStyle
) : MaterialCardView(context, attrs, defStyleAttr) {
) : MaterialCardView(context, attrs, defStyleAttr), KoinComponent {
init {
radius = LauncherPreferences.instance.cardRadius * dp
radius = LauncherCardView.currentCardStyle.radius * dp
strokeColor = ContextCompat.getColor(context, R.color.color_divider)
strokeWidth = (1 * dp).toInt()
cardElevation = 2 * dp
outlineProvider = null
}
private val dataStore: LauncherDataStore by inject()
private var job: Job? = null
override fun onAttachedToWindow() {
super.onAttachedToWindow()
job?.cancel()
job = lifecycleScope.launch {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
dataStore.data.map { it.cards.radius }.distinctUntilChanged().collectLatest {
radius = it * dp
}
}
}
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
job?.cancel()
}
}

View File

@ -3,10 +3,22 @@ package de.mm20.launcher2.ui.legacy.view
import android.content.Context
import android.content.res.ColorStateList
import android.util.AttributeSet
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.card.MaterialCardView
import de.mm20.launcher2.ktx.dp
import de.mm20.launcher2.preferences.LauncherPreferences
import de.mm20.launcher2.ktx.lifecycleOwner
import de.mm20.launcher2.ktx.lifecycleScope
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.ui.R
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import kotlin.math.roundToInt
/**
@ -15,15 +27,12 @@ import kotlin.math.roundToInt
* (2) Elevation overlay color
*/
open class LauncherCardView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = R.attr.materialCardViewStyle
) : MaterialCardView(context, attrs, defStyleAttr) {
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = R.attr.materialCardViewStyle
) : MaterialCardView(context, attrs, defStyleAttr), KoinComponent {
private val isDarkTheme = resources.getBoolean(R.bool.is_dark_theme)
var backgroundOpacity: Int = LauncherPreferences.instance.cardOpacity
var backgroundOpacity: Int = (currentCardStyle.opacity * 255).roundToInt()
set(value) {
setCardBackgroundColor(cardBackgroundColor.defaultColor.let {
ColorStateList.valueOf((it and 0xFFFFFF) or (value shl 24))
@ -31,7 +40,7 @@ open class LauncherCardView @JvmOverloads constructor(
field = value
}
var strokeOpacity: Int = if (LauncherPreferences.instance.cardStrokeWidth > 0) 0xFF else 0
private var strokeOpacity: Int = if (currentCardStyle.borderWidth > 0) 0xFF else 0
set(value) {
setStrokeColor(strokeColorStateList?.defaultColor?.let {
ColorStateList.valueOf((it and 0xFFFFFF) or (value shl 24))
@ -39,23 +48,24 @@ open class LauncherCardView @JvmOverloads constructor(
field = value
}
private var overrideBackgroundOpacity = false
init {
/*val cardColor = when (LauncherPreferences.instance.cardBackground) {
CardBackground.DEFAULT-> context.getColor(R.color.cardview_background)
CardBackground.BLACK -> context.getColor(R.color.cardview_background_black)
}
setCardBackgroundColor(cardColor)*/
strokeColor = cardBackgroundColor.defaultColor
strokeWidth = (LauncherPreferences.instance.cardStrokeWidth * dp).roundToInt()
radius = LauncherPreferences.instance.cardRadius * dp
strokeWidth = (currentCardStyle.borderWidth * dp).roundToInt()
radius = currentCardStyle.radius * dp
context.theme.obtainStyledAttributes(
attrs,
R.styleable.LauncherCardView,
0, 0).apply {
attrs,
R.styleable.LauncherCardView,
0, 0
).apply {
try {
backgroundOpacity = getInt(R.styleable.LauncherCardView_backgroundOpacity, LauncherPreferences.instance.cardOpacity)
if (hasValue(R.styleable.LauncherCardView_backgroundOpacity)) {
overrideBackgroundOpacity = true
backgroundOpacity = getInt(R.styleable.LauncherCardView_backgroundOpacity, 255)
}
} finally {
recycle()
}
@ -64,4 +74,36 @@ open class LauncherCardView @JvmOverloads constructor(
elevation = if (backgroundOpacity == 255) elevation else 0f
cardElevation = elevation
}
private val dataStore: LauncherDataStore by inject()
private var job: Job? = null
override fun onAttachedToWindow() {
super.onAttachedToWindow()
job?.cancel()
job = lifecycleScope.launch {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
dataStore.data.map { it.cards }.distinctUntilChanged().collectLatest {
currentCardStyle = it
if (!overrideBackgroundOpacity) {
backgroundOpacity = (it.opacity * 255).roundToInt()
strokeOpacity = if (backgroundOpacity == 0) 0 else 0xFF
elevation = if (backgroundOpacity == 255) elevation else 0f
cardElevation = elevation
}
strokeWidth = (it.borderWidth * dp).roundToInt()
radius = it.radius * dp
}
}
}
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
job?.cancel()
}
companion object {
internal var currentCardStyle = Settings.CardSettings.getDefaultInstance()
}
}

View File

@ -13,23 +13,28 @@ import androidx.annotation.DrawableRes
import androidx.appcompat.app.AppCompatActivity
import androidx.core.animation.doOnEnd
import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.asLiveData
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.*
import com.google.android.material.card.MaterialCardView
import de.mm20.launcher2.favorites.FavoritesRepository
import de.mm20.launcher2.ktx.dp
import de.mm20.launcher2.preferences.LauncherPreferences
import de.mm20.launcher2.ktx.lifecycleOwner
import de.mm20.launcher2.ktx.lifecycleScope
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.search.data.Searchable
import de.mm20.launcher2.transition.ChangingLayoutTransition
import de.mm20.launcher2.ui.R
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import kotlin.math.abs
class SwipeCardView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : MaterialCardView(context, attrs, defStyleAttr) {
) : MaterialCardView(context, attrs, defStyleAttr), KoinComponent {
private val backdrop = FrameLayout(context)
private val icon = ImageView(context)
@ -44,10 +49,9 @@ class SwipeCardView @JvmOverloads constructor(
icon.setColorFilter(iconColor)
super.addView(content)
content.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
content.radius = radius
content.transitionName = "SwipeCardView/content"
radius = LauncherPreferences.instance.cardRadius * dp
//content.setCardBackgroundColor(cardBackgroundColor)
radius = LauncherCardView.currentCardStyle.radius * dp
content.radius = radius
super.setCardBackgroundColor(
ContextCompat.getColor(
context,
@ -362,6 +366,25 @@ class SwipeCardView @JvmOverloads constructor(
}
}
private val dataStore: LauncherDataStore by inject()
private var job: Job? = null
override fun onAttachedToWindow() {
super.onAttachedToWindow()
job?.cancel()
job = lifecycleScope.launch {
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
dataStore.data.map { it.cards.radius }.distinctUntilChanged().collectLatest {
radius = it * dp
}
}
}
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
job?.cancel()
}
open class SwipeAction(
@DrawableRes var icon: Int,
var color: Int,

View File

@ -4,6 +4,7 @@ import android.appwidget.AppWidgetHost
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.geometry.Size
import androidx.navigation.NavController
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.ui.theme.WallpaperColors
import de.mm20.launcher2.ui.theme.colors.ColorPalette
import de.mm20.launcher2.ui.theme.colors.DefaultColorPalette
@ -16,4 +17,6 @@ val LocalWallpaperColors = compositionLocalOf<WallpaperColors?> { null }
val LocalColorScheme = compositionLocalOf<ColorPalette> { DefaultColorPalette() }
val LocalNavController = compositionLocalOf<NavController?> { null }
val LocalNavController = compositionLocalOf<NavController?> { null }
val LocalCardStyle = compositionLocalOf<Settings.CardSettings> { Settings.CardSettings.getDefaultInstance() }

View File

@ -7,6 +7,8 @@ import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.navigation.navArgument
import com.google.accompanist.navigation.animation.AnimatedNavHost
@ -14,37 +16,54 @@ import com.google.accompanist.navigation.animation.composable
import com.google.accompanist.navigation.animation.rememberAnimatedNavController
import de.mm20.launcher2.licenses.AppLicense
import de.mm20.launcher2.licenses.OpenSourceLicenses
import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.ui.LegacyLauncherTheme
import de.mm20.launcher2.ui.base.BaseActivity
import de.mm20.launcher2.ui.locals.LocalCardStyle
import de.mm20.launcher2.ui.locals.LocalNavController
import de.mm20.launcher2.ui.settings.about.AboutSettingsScreen
import de.mm20.launcher2.ui.settings.accounts.AccountsSettingsScreen
import de.mm20.launcher2.ui.settings.appearance.AppearanceSettingsScreen
import de.mm20.launcher2.ui.settings.badges.BadgeSettingsScreen
import de.mm20.launcher2.ui.settings.buildinfo.BuildInfoSettingsScreen
import de.mm20.launcher2.ui.settings.calendarwidget.CalendarWidgetSettingsScreen
import de.mm20.launcher2.ui.settings.cards.CardsSettingsScreen
import de.mm20.launcher2.ui.settings.clockwidget.ClockWidgetSettingsScreen
import de.mm20.launcher2.ui.settings.debug.DebugSettingsScreen
import de.mm20.launcher2.ui.settings.easteregg.EasterEggSettingsScreen
import de.mm20.launcher2.ui.settings.filesearch.FileSearchSettingsScreen
import de.mm20.launcher2.ui.settings.license.LicenseScreen
import de.mm20.launcher2.ui.settings.main.MainSettingsScreen
import de.mm20.launcher2.ui.settings.musicwidget.MusicWidgetSettingsScreen
import de.mm20.launcher2.ui.settings.search.SearchSettingsScreen
import de.mm20.launcher2.ui.settings.accounts.AccountsSettingsScreen
import de.mm20.launcher2.ui.settings.buildinfo.BuildInfoSettingsScreen
import de.mm20.launcher2.ui.settings.easteregg.EasterEggSettingsScreen
import de.mm20.launcher2.ui.settings.filesearch.FileSearchSettingsScreen
import de.mm20.launcher2.ui.settings.weatherwidget.WeatherWidgetSettingsScreen
import de.mm20.launcher2.ui.settings.websearch.WebSearchSettingsScreen
import de.mm20.launcher2.ui.settings.widgets.WidgetsSettingsScreen
import de.mm20.launcher2.ui.settings.wikipedia.WikipediaSettingsScreen
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import org.koin.android.ext.android.inject
class SettingsActivity : BaseActivity() {
private val dataStore: LauncherDataStore by inject()
@OptIn(ExperimentalAnimationApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val navController = rememberAnimatedNavController()
CompositionLocalProvider(LocalNavController provides navController) {
val cardStyle by remember {
dataStore.data.map { it.cards }.distinctUntilChanged()
}.collectAsState(
Settings.CardSettings.getDefaultInstance()
)
CompositionLocalProvider(
LocalNavController provides navController,
LocalCardStyle provides cardStyle
) {
LegacyLauncherTheme {
AnimatedNavHost(
navController = navController,
@ -60,6 +79,9 @@ class SettingsActivity : BaseActivity() {
composable("settings/appearance") {
AppearanceSettingsScreen()
}
composable("settings/appearance/cards") {
CardsSettingsScreen()
}
composable("settings/search") {
SearchSettingsScreen()
}

View File

@ -37,6 +37,7 @@ import de.mm20.launcher2.ui.component.preferences.*
import de.mm20.launcher2.ui.launcher.search.SearchBar
import de.mm20.launcher2.ui.launcher.search.SearchBarLevel
import de.mm20.launcher2.ui.legacy.view.LauncherIconView
import de.mm20.launcher2.ui.locals.LocalNavController
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
@ -44,6 +45,7 @@ import kotlinx.coroutines.isActive
fun AppearanceSettingsScreen() {
val viewModel: AppearanceSettingsScreenVM = viewModel()
val context = LocalContext.current
val navController = LocalNavController.current
PreferenceScreen(title = stringResource(id = R.string.preference_screen_appearance)) {
item {
PreferenceCategory {
@ -74,6 +76,13 @@ fun AppearanceSettingsScreen() {
viewModel.setColorScheme(newValue)
}
)
Preference(
title = stringResource(R.string.preference_cards),
summary = stringResource(R.string.preference_cards_summary),
onClick = {
navController?.navigate("settings/appearance/cards")
}
)
}
PreferenceCategory(title = stringResource(R.string.preference_category_grid)) {
val columnCount by viewModel.columnCount.observeAsState()

View File

@ -0,0 +1,83 @@
package de.mm20.launcher2.ui.settings.cards
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.BorderStyle
import androidx.compose.material.icons.rounded.LineWeight
import androidx.compose.material.icons.rounded.Opacity
import androidx.compose.material.icons.rounded.RoundedCorner
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.LauncherCard
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
import de.mm20.launcher2.ui.component.preferences.SliderPreference
@Composable
fun CardsSettingsScreen() {
val viewModel: CardsSettingsScreenVM = viewModel()
PreferenceScreen(title = stringResource(R.string.preference_cards)) {
item {
Box(modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.secondary)) {
LauncherCard(
modifier = Modifier
.height(200.dp)
.fillMaxWidth()
.padding(16.dp)
)
}
}
item {
PreferenceCategory {
val opacity by viewModel.opacity.observeAsState(0f)
SliderPreference(
title = stringResource(R.string.preference_cards_opacity),
icon = Icons.Rounded.Opacity,
value = opacity,
min = 0f,
max = 1f,
onValueChanged = {
viewModel.setOpacity(it)
}
)
val radius by viewModel.radius.observeAsState(0)
SliderPreference(
title = stringResource(R.string.preference_cards_corner_radius),
icon = Icons.Rounded.RoundedCorner,
value = radius,
min = 0,
max = 24,
step = 1,
onValueChanged = {
viewModel.setRadius(it)
}
)
val borderWidth by viewModel.borderWidth.observeAsState(0)
SliderPreference(
title = stringResource(R.string.preference_cards_stroke_width),
icon = Icons.Rounded.LineWeight,
value = borderWidth,
min = 0,
max = 8,
step = 1,
onValueChanged = {
viewModel.setBorderWidth(it)
}
)
}
}
}
}

View File

@ -0,0 +1,50 @@
package de.mm20.launcher2.ui.settings.cards
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.preferences.LauncherDataStore
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
class CardsSettingsScreenVM: ViewModel(), KoinComponent {
private val dataStore: LauncherDataStore by inject()
val opacity = dataStore.data.map { it.cards.opacity }.asLiveData()
fun setOpacity(opacity: Float) {
viewModelScope.launch {
dataStore.updateData {
it.toBuilder()
.setCards(it.cards.toBuilder()
.setOpacity(opacity)
).build()
}
}
}
val radius = dataStore.data.map { it.cards.radius }.asLiveData()
fun setRadius(radius: Int) {
viewModelScope.launch {
dataStore.updateData {
it.toBuilder()
.setCards(it.cards.toBuilder()
.setRadius(radius)
).build()
}
}
}
val borderWidth = dataStore.data.map { it.cards.borderWidth }.asLiveData()
fun setBorderWidth(borderWidth: Int) {
viewModelScope.launch {
dataStore.updateData {
it.toBuilder()
.setCards(it.cards.toBuilder()
.setBorderWidth(borderWidth)
).build()
}
}
}
}