Migrate card settings
This commit is contained in:
parent
a4ead54329
commit
8e96e2b2db
@ -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()
|
||||
|
||||
@ -119,5 +119,11 @@ fun createFactorySettings(context: Context): Settings {
|
||||
.setLightNavBar(false)
|
||||
.setLightStatusBar(false)
|
||||
)
|
||||
.setCards(
|
||||
Settings.CardSettings.newBuilder()
|
||||
.setBorderWidth(0)
|
||||
.setRadius(8)
|
||||
.setOpacity(1f)
|
||||
)
|
||||
.build()
|
||||
}
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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
|
||||
)
|
||||
}
|
||||
@ -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())
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
|
||||
@ -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() }
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user