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

View File

@ -119,5 +119,11 @@ fun createFactorySettings(context: Context): Settings {
.setLightNavBar(false) .setLightNavBar(false)
.setLightStatusBar(false) .setLightStatusBar(false)
) )
.setCards(
Settings.CardSettings.newBuilder()
.setBorderWidth(0)
.setRadius(8)
.setOpacity(1f)
)
.build() .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 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 cardRadius by IntPreference("card_radius", default = 8)
var easterEggEnabled by BooleanPreference("easter_egg", default = false) var easterEggEnabled by BooleanPreference("easter_egg", default = false)

View File

@ -164,4 +164,11 @@ message Settings {
bool lightNavBar = 2; bool lightNavBar = 2;
} }
SystemBarsSettings system_bars = 23; 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 package de.mm20.launcher2.ui.component
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
@ -7,20 +8,23 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import de.mm20.launcher2.ui.locals.LocalCardStyle
@Composable @Composable
fun LauncherCard( fun LauncherCard(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
elevation: Dp = 2.dp, elevation: Dp = 2.dp,
backgroundOpacity: Float = 1f, backgroundOpacity: Float = LocalCardStyle.current.opacity,
content: @Composable () -> Unit = {} content: @Composable () -> Unit = {}
) { ) {
Surface( Surface(
modifier = modifier, 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, content = content,
color = MaterialTheme.colorScheme.surface.copy(alpha = backgroundOpacity.coerceIn(0f, 1f)), color = MaterialTheme.colorScheme.surface.copy(alpha = backgroundOpacity.coerceIn(0f, 1f)),
shadowElevation = elevation, shadowElevation = if (backgroundOpacity == 1f) elevation else 0.dp,
tonalElevation = elevation 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.R
import de.mm20.launcher2.ui.component.LauncherCard import de.mm20.launcher2.ui.component.LauncherCard
import de.mm20.launcher2.ui.launcher.LauncherActivityVM import de.mm20.launcher2.ui.launcher.LauncherActivityVM
import de.mm20.launcher2.ui.locals.LocalCardStyle
import de.mm20.launcher2.ui.settings.SettingsActivity import de.mm20.launcher2.ui.settings.SettingsActivity
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import org.koin.androidx.compose.inject import org.koin.androidx.compose.inject
@ -176,10 +177,11 @@ fun SearchBar(
durationMillis = 200, durationMillis = 200,
delayMillis = 200 delayMillis = 200
) )
else -> tween(durationMillis = 500) else -> tween(durationMillis = 200)
} }
}) { }) {
when { when {
it == SearchBarLevel.Active -> LocalCardStyle.current.opacity
style != SearchBarSettings.SearchBarStyle.Transparent -> 1f style != SearchBarSettings.SearchBarStyle.Transparent -> 1f
it == SearchBarLevel.Resting -> 0f it == SearchBarLevel.Resting -> 0f
else -> 1f else -> 1f

View File

@ -6,21 +6,32 @@ import android.util.Log
import android.view.MotionEvent import android.view.MotionEvent
import android.widget.FrameLayout import android.widget.FrameLayout
import androidx.compose.foundation.layout.Box 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.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ComposeView
import androidx.lifecycle.MutableLiveData 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.LegacyLauncherTheme
import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.launcher.search.SearchBar import de.mm20.launcher2.ui.launcher.search.SearchBar
import de.mm20.launcher2.ui.launcher.search.SearchBarLevel 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( class SearchBar @JvmOverloads constructor(
context: Context, context: Context,
attrs: AttributeSet? = null, attrs: AttributeSet? = null,
defStyleAttr: Int = R.attr.materialCardViewStyle defStyleAttr: Int = R.attr.materialCardViewStyle
) : FrameLayout(context, attrs, defStyleAttr) { ) : FrameLayout(context, attrs, defStyleAttr), KoinComponent {
var level: SearchBarLevel = SearchBarLevel.Resting var level: SearchBarLevel = SearchBarLevel.Resting
set(value) { set(value) {
@ -28,6 +39,7 @@ class SearchBar @JvmOverloads constructor(
field = value field = value
} }
private val dataStore: LauncherDataStore by inject()
private val levelState = MutableLiveData(level) private val levelState = MutableLiveData(level)
var onFocus: (() -> Unit)? = null var onFocus: (() -> Unit)? = null
@ -36,12 +48,21 @@ class SearchBar @JvmOverloads constructor(
val view = ComposeView(context) val view = ComposeView(context)
view.setContent { view.setContent {
val level by levelState.observeAsState(SearchBarLevel.Resting) val level by levelState.observeAsState(SearchBarLevel.Resting)
LegacyLauncherTheme { val cardStyle by remember {
Box(contentAlignment = Alignment.TopCenter) { dataStore.data.map { it.cards }.distinctUntilChanged()
SearchBar( }.collectAsState(
level, Settings.CardSettings.getDefaultInstance()
onFocus = { onFocus?.invoke() } )
) 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.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.card.MaterialCardView import com.google.android.material.card.MaterialCardView
import de.mm20.launcher2.ktx.dp 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.preferences.LauncherPreferences
import de.mm20.launcher2.ui.R 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( open class InnerCardView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = R.attr.materialCardViewStyle context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = R.attr.materialCardViewStyle
) : MaterialCardView(context, attrs, defStyleAttr) { ) : MaterialCardView(context, attrs, defStyleAttr), KoinComponent {
init { init {
radius = LauncherPreferences.instance.cardRadius * dp radius = LauncherCardView.currentCardStyle.radius * dp
strokeColor = ContextCompat.getColor(context, R.color.color_divider) strokeColor = ContextCompat.getColor(context, R.color.color_divider)
strokeWidth = (1 * dp).toInt() strokeWidth = (1 * dp).toInt()
cardElevation = 2 * dp cardElevation = 2 * dp
outlineProvider = null 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.Context
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.util.AttributeSet import android.util.AttributeSet
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.card.MaterialCardView import com.google.android.material.card.MaterialCardView
import de.mm20.launcher2.ktx.dp 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 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 import kotlin.math.roundToInt
/** /**
@ -15,15 +27,12 @@ import kotlin.math.roundToInt
* (2) Elevation overlay color * (2) Elevation overlay color
*/ */
open class LauncherCardView @JvmOverloads constructor( open class LauncherCardView @JvmOverloads constructor(
context: Context, context: Context,
attrs: AttributeSet? = null, attrs: AttributeSet? = null,
defStyleAttr: Int = R.attr.materialCardViewStyle defStyleAttr: Int = R.attr.materialCardViewStyle
) : MaterialCardView(context, attrs, defStyleAttr) { ) : MaterialCardView(context, attrs, defStyleAttr), KoinComponent {
private val isDarkTheme = resources.getBoolean(R.bool.is_dark_theme) var backgroundOpacity: Int = (currentCardStyle.opacity * 255).roundToInt()
var backgroundOpacity: Int = LauncherPreferences.instance.cardOpacity
set(value) { set(value) {
setCardBackgroundColor(cardBackgroundColor.defaultColor.let { setCardBackgroundColor(cardBackgroundColor.defaultColor.let {
ColorStateList.valueOf((it and 0xFFFFFF) or (value shl 24)) ColorStateList.valueOf((it and 0xFFFFFF) or (value shl 24))
@ -31,7 +40,7 @@ open class LauncherCardView @JvmOverloads constructor(
field = value 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) { set(value) {
setStrokeColor(strokeColorStateList?.defaultColor?.let { setStrokeColor(strokeColorStateList?.defaultColor?.let {
ColorStateList.valueOf((it and 0xFFFFFF) or (value shl 24)) ColorStateList.valueOf((it and 0xFFFFFF) or (value shl 24))
@ -39,23 +48,24 @@ open class LauncherCardView @JvmOverloads constructor(
field = value field = value
} }
private var overrideBackgroundOpacity = false
init { 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 strokeColor = cardBackgroundColor.defaultColor
strokeWidth = (LauncherPreferences.instance.cardStrokeWidth * dp).roundToInt() strokeWidth = (currentCardStyle.borderWidth * dp).roundToInt()
radius = LauncherPreferences.instance.cardRadius * dp radius = currentCardStyle.radius * dp
context.theme.obtainStyledAttributes( context.theme.obtainStyledAttributes(
attrs, attrs,
R.styleable.LauncherCardView, R.styleable.LauncherCardView,
0, 0).apply { 0, 0
).apply {
try { 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 { } finally {
recycle() recycle()
} }
@ -64,4 +74,36 @@ open class LauncherCardView @JvmOverloads constructor(
elevation = if (backgroundOpacity == 255) elevation else 0f elevation = if (backgroundOpacity == 255) elevation else 0f
cardElevation = elevation 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.appcompat.app.AppCompatActivity
import androidx.core.animation.doOnEnd import androidx.core.animation.doOnEnd
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.*
import androidx.lifecycle.asLiveData
import androidx.lifecycle.lifecycleScope
import com.google.android.material.card.MaterialCardView import com.google.android.material.card.MaterialCardView
import de.mm20.launcher2.favorites.FavoritesRepository import de.mm20.launcher2.favorites.FavoritesRepository
import de.mm20.launcher2.ktx.dp 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.search.data.Searchable
import de.mm20.launcher2.transition.ChangingLayoutTransition import de.mm20.launcher2.transition.ChangingLayoutTransition
import de.mm20.launcher2.ui.R 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.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
import kotlin.math.abs import kotlin.math.abs
class SwipeCardView @JvmOverloads constructor( class SwipeCardView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : MaterialCardView(context, attrs, defStyleAttr) { ) : MaterialCardView(context, attrs, defStyleAttr), KoinComponent {
private val backdrop = FrameLayout(context) private val backdrop = FrameLayout(context)
private val icon = ImageView(context) private val icon = ImageView(context)
@ -44,10 +49,9 @@ class SwipeCardView @JvmOverloads constructor(
icon.setColorFilter(iconColor) icon.setColorFilter(iconColor)
super.addView(content) super.addView(content)
content.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) content.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
content.radius = radius
content.transitionName = "SwipeCardView/content" content.transitionName = "SwipeCardView/content"
radius = LauncherPreferences.instance.cardRadius * dp radius = LauncherCardView.currentCardStyle.radius * dp
//content.setCardBackgroundColor(cardBackgroundColor) content.radius = radius
super.setCardBackgroundColor( super.setCardBackgroundColor(
ContextCompat.getColor( ContextCompat.getColor(
context, 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( open class SwipeAction(
@DrawableRes var icon: Int, @DrawableRes var icon: Int,
var color: Int, var color: Int,

View File

@ -4,6 +4,7 @@ import android.appwidget.AppWidgetHost
import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.geometry.Size import androidx.compose.ui.geometry.Size
import androidx.navigation.NavController import androidx.navigation.NavController
import de.mm20.launcher2.preferences.Settings
import de.mm20.launcher2.ui.theme.WallpaperColors import de.mm20.launcher2.ui.theme.WallpaperColors
import de.mm20.launcher2.ui.theme.colors.ColorPalette import de.mm20.launcher2.ui.theme.colors.ColorPalette
import de.mm20.launcher2.ui.theme.colors.DefaultColorPalette import de.mm20.launcher2.ui.theme.colors.DefaultColorPalette
@ -16,4 +17,6 @@ val LocalWallpaperColors = compositionLocalOf<WallpaperColors?> { null }
val LocalColorScheme = compositionLocalOf<ColorPalette> { DefaultColorPalette() } 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.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.navigation.navArgument import androidx.navigation.navArgument
import com.google.accompanist.navigation.animation.AnimatedNavHost 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 com.google.accompanist.navigation.animation.rememberAnimatedNavController
import de.mm20.launcher2.licenses.AppLicense import de.mm20.launcher2.licenses.AppLicense
import de.mm20.launcher2.licenses.OpenSourceLicenses 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.LegacyLauncherTheme
import de.mm20.launcher2.ui.base.BaseActivity 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.locals.LocalNavController
import de.mm20.launcher2.ui.settings.about.AboutSettingsScreen 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.appearance.AppearanceSettingsScreen
import de.mm20.launcher2.ui.settings.badges.BadgeSettingsScreen 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.calendarwidget.CalendarWidgetSettingsScreen
import de.mm20.launcher2.ui.settings.cards.CardsSettingsScreen
import de.mm20.launcher2.ui.settings.clockwidget.ClockWidgetSettingsScreen import de.mm20.launcher2.ui.settings.clockwidget.ClockWidgetSettingsScreen
import de.mm20.launcher2.ui.settings.debug.DebugSettingsScreen 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.license.LicenseScreen
import de.mm20.launcher2.ui.settings.main.MainSettingsScreen import de.mm20.launcher2.ui.settings.main.MainSettingsScreen
import de.mm20.launcher2.ui.settings.musicwidget.MusicWidgetSettingsScreen import de.mm20.launcher2.ui.settings.musicwidget.MusicWidgetSettingsScreen
import de.mm20.launcher2.ui.settings.search.SearchSettingsScreen 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.weatherwidget.WeatherWidgetSettingsScreen
import de.mm20.launcher2.ui.settings.websearch.WebSearchSettingsScreen import de.mm20.launcher2.ui.settings.websearch.WebSearchSettingsScreen
import de.mm20.launcher2.ui.settings.widgets.WidgetsSettingsScreen import de.mm20.launcher2.ui.settings.widgets.WidgetsSettingsScreen
import de.mm20.launcher2.ui.settings.wikipedia.WikipediaSettingsScreen 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() { class SettingsActivity : BaseActivity() {
private val dataStore: LauncherDataStore by inject()
@OptIn(ExperimentalAnimationApi::class) @OptIn(ExperimentalAnimationApi::class)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContent { setContent {
val navController = rememberAnimatedNavController() 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 { LegacyLauncherTheme {
AnimatedNavHost( AnimatedNavHost(
navController = navController, navController = navController,
@ -60,6 +79,9 @@ class SettingsActivity : BaseActivity() {
composable("settings/appearance") { composable("settings/appearance") {
AppearanceSettingsScreen() AppearanceSettingsScreen()
} }
composable("settings/appearance/cards") {
CardsSettingsScreen()
}
composable("settings/search") { composable("settings/search") {
SearchSettingsScreen() 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.SearchBar
import de.mm20.launcher2.ui.launcher.search.SearchBarLevel import de.mm20.launcher2.ui.launcher.search.SearchBarLevel
import de.mm20.launcher2.ui.legacy.view.LauncherIconView import de.mm20.launcher2.ui.legacy.view.LauncherIconView
import de.mm20.launcher2.ui.locals.LocalNavController
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
@ -44,6 +45,7 @@ import kotlinx.coroutines.isActive
fun AppearanceSettingsScreen() { fun AppearanceSettingsScreen() {
val viewModel: AppearanceSettingsScreenVM = viewModel() val viewModel: AppearanceSettingsScreenVM = viewModel()
val context = LocalContext.current val context = LocalContext.current
val navController = LocalNavController.current
PreferenceScreen(title = stringResource(id = R.string.preference_screen_appearance)) { PreferenceScreen(title = stringResource(id = R.string.preference_screen_appearance)) {
item { item {
PreferenceCategory { PreferenceCategory {
@ -74,6 +76,13 @@ fun AppearanceSettingsScreen() {
viewModel.setColorScheme(newValue) 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)) { PreferenceCategory(title = stringResource(R.string.preference_category_grid)) {
val columnCount by viewModel.columnCount.observeAsState() 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()
}
}
}
}