feat: Lawnchair Settings Material 3 Expressive

Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
This commit is contained in:
Pun Butrach
2025-09-17 23:17:49 +07:00
parent cdad1f5c80
commit f113685837
35 changed files with 422 additions and 46 deletions

View File

@@ -16,6 +16,8 @@ older (i.e., Lawnchair `15-dev`).
* Reimplement Hotseat background customisation
* Make haptic on a locked workspace use MSDL vibration
* Make Launcher3 colour more accurate to upstream Android 16
* ProvideComposeSheetHandler now have expressive blur
* Lawnchair Settings now uses Material 3 Expressive
#### Fixes

View File

@@ -188,6 +188,12 @@
<string name="experimental_features_label">Experimental features</string>
<!-- Experimental features -->
<string name="workspace_label">Workspace</string>
<string name="smartspace_label">Smartspace</string>
<string name="internal_label">Internal</string>
<string name="internal_description">Internal may be more unstable than other experimental group</string>
<string name="font_picker_label">Font customization</string>
<string name="font_picker_description">Some text remains unchanged</string>
@@ -199,6 +205,7 @@
<string name="always_reload_icons_label">Always reload icons</string>
<string name="always_reload_icons_description">Avoid using cached icons from icon packs</string>
<string name="always_reload_icons_warning">Will cause icon to reload regularly, this is not required if icon pack doesn\'t update regularly</string>
<string name="icon_swipe_gestures">Icon swipe gestures</string>
<string name="icon_swipe_gestures_description">Perform actions when swiping left or right on icons instead of moving the home screen</string>
@@ -211,6 +218,7 @@
<string name="gesturenavcontract_label">GestureNavContract API</string>
<string name="gesturenavcontract_description">Render launcher animations without root, does not work with heavily modified AOSP</string>
<string name="gesturenavcontract_warning_incompatibility">GestureNavContract may cause artifacting during animations</string>
<string name="recents_lock_unlock">Lock/unlock</string>

View File

@@ -30,6 +30,9 @@ import android.util.Log
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.ui.Modifier
@@ -233,6 +236,7 @@ class LawnchairApp : Application() {
@JvmStatic
val isAtleastT: Boolean get() = instance.isAtleastT
@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
fun Launcher.showQuickstepWarningIfNecessary() {
val launcher = this
if (!lawnchairApp.isRecentsComponent || isRecentsEnabled) return
@@ -253,12 +257,14 @@ class LawnchairApp : Application() {
openAppInfo(launcher)
close(true)
},
shapes = ButtonDefaults.shapes()
) {
Text(text = stringResource(id = R.string.app_info_drop_target_label))
}
Spacer(modifier = Modifier.requiredWidth(8.dp))
Button(
onClick = { close(true) },
shapes = ButtonDefaults.shapes()
) {
Text(text = stringResource(id = android.R.string.ok))
}

View File

@@ -29,6 +29,9 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -129,6 +132,7 @@ class SleepMethodDeviceAdmin(context: Context) : SleepGestureHandler.SleepMethod
}
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
@Composable
fun ServiceWarningDialog(
title: Int,
@@ -145,6 +149,7 @@ fun ServiceWarningDialog(
buttons = {
OutlinedButton(
onClick = handleClose,
shapes = ButtonDefaults.shapes()
) {
Text(text = stringResource(id = android.R.string.cancel))
}
@@ -154,6 +159,7 @@ fun ServiceWarningDialog(
context.startActivity(settingsIntent)
handleClose()
},
shapes = ButtonDefaults.shapes()
) {
Text(text = stringResource(id = R.string.dt2s_recents_warning_open_settings))
}

View File

@@ -27,6 +27,7 @@ sealed class SmartspaceCalendar(@StringRes val nameResourceId: Int, val formatCu
override fun toString() = "gregorian"
}
object Persian : SmartspaceCalendar(nameResourceId = R.string.smartspace_calendar_persian) {
// Officially known as Solar Hijri
override fun toString() = "persian"
}
}

View File

@@ -15,7 +15,9 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.OutlinedButton
@@ -34,7 +36,7 @@ import com.android.launcher3.R
import java.time.Instant
import java.util.concurrent.TimeUnit
@OptIn(ExperimentalMaterial3Api::class)
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun ChangesDialog(
changelogState: ChangelogState?,
@@ -100,6 +102,7 @@ fun ChangesDialog(
) {
OutlinedButton(
onClick = onDismiss,
shapes = ButtonDefaults.shapes()
) {
Text(text = stringResource(android.R.string.cancel))
}
@@ -109,6 +112,7 @@ fun ChangesDialog(
onDownload()
onDismiss()
},
shapes = ButtonDefaults.shapes()
) {
Text(text = stringResource(R.string.download_update))
}

View File

@@ -4,7 +4,9 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@@ -16,6 +18,7 @@ import androidx.compose.ui.unit.dp
import com.android.launcher3.R
import java.io.File
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun UpdateSection(
updateState: UpdateState,
@@ -43,6 +46,7 @@ fun UpdateSection(
is UpdateState.Available -> {
Button(
onClick = onViewChanges,
shapes = ButtonDefaults.shapes()
) {
Text(text = stringResource(R.string.download_update))
}
@@ -64,6 +68,7 @@ fun UpdateSection(
onClick = {
onInstall(updateState.file)
},
shapes = ButtonDefaults.shapes()
) {
Text(text = stringResource(R.string.install_update))
}

View File

@@ -9,17 +9,23 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.rounded.DragHandle
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
@@ -203,6 +209,21 @@ fun DraggableSwitchPreference(
checked = checked,
onCheckedChange = onCheckedChange,
enabled = enabled,
thumbContent = {
if (checked) {
Icon(
imageVector = Icons.Filled.Check,
contentDescription = null,
modifier = Modifier.size(SwitchDefaults.IconSize),
)
} else {
Icon(
imageVector = Icons.Filled.Close,
contentDescription = null,
modifier = Modifier.size(SwitchDefaults.IconSize),
)
}
},
)
},
enabled = enabled,
@@ -210,6 +231,7 @@ fun DraggableSwitchPreference(
)
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun DragHandle(
scope: ReorderableScope,
@@ -240,6 +262,7 @@ fun DragHandle(
enabled = isDraggable,
onClick = {},
interactionSource = interactionSource,
shapes = IconButtonDefaults.shapes(),
) {
Icon(
imageVector = Icons.Rounded.DragHandle,

View File

@@ -10,6 +10,9 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
@@ -49,6 +52,7 @@ val options = listOf(
GestureHandlerOption.OpenAssistant,
)
@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
@Composable
fun GestureHandlerPreference(
adapter: PreferenceAdapter<GestureHandlerConfig>,
@@ -85,7 +89,10 @@ fun GestureHandlerPreference(
ModalBottomSheetContent(
title = { Text(label) },
buttons = {
OutlinedButton(onClick = { bottomSheetHandler.hide() }) {
OutlinedButton(
onClick = { bottomSheetHandler.hide() },
shapes = ButtonDefaults.shapes()
) {
Text(text = stringResource(id = AndroidR.string.cancel))
}
},

View File

@@ -19,6 +19,8 @@ import androidx.compose.material.icons.rounded.Check
import androidx.compose.material.icons.rounded.Close
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
@@ -55,6 +57,7 @@ import com.google.accompanist.permissions.shouldShowRationale
* @param onGoToSettings Called when the user clicks the "Go to settings" button.
* @param modifier The modifier to be applied to the dialog.
*/
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun PermissionDialog(
title: String,
@@ -76,6 +79,7 @@ fun PermissionDialog(
if (isPermanentlyDenied) onGoToSettings() else onConfirm()
onDismiss()
},
shapes = ButtonDefaults.shapes()
) {
Text(
stringResource(
@@ -85,7 +89,10 @@ fun PermissionDialog(
}
},
dismissButton = {
TextButton(onClick = onDismiss) { Text(stringResource(android.R.string.cancel)) }
TextButton(
onClick = onDismiss,
shapes = ButtonDefaults.shapes()
) { Text(stringResource(android.R.string.cancel)) }
},
)
}
@@ -97,7 +104,7 @@ fun PermissionDialog(
* @param modifier The modifier to be applied to the dialog.
* @param onPermissionRequest Called when a permission request is initiated.
*/
@OptIn(ExperimentalPermissionsApi::class)
@OptIn(ExperimentalPermissionsApi::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun WallpaperAccessPermissionDialog(
managedFilesChecked: Boolean,
@@ -119,7 +126,10 @@ fun WallpaperAccessPermissionDialog(
Text(stringResource(R.string.manage_storage_access_denied_description, stringResource(id = R.string.derived_app_name)))
},
confirmButton = {
FilledTonalButton(onClick = onDismiss) { Text(stringResource(R.string.dismiss)) }
FilledTonalButton(
onClick = onDismiss,
shapes = ButtonDefaults.shapes()
) { Text(stringResource(R.string.dismiss)) }
},
)
} else {
@@ -164,7 +174,10 @@ fun WallpaperAccessPermissionDialog(
}
},
confirmButton = {
TextButton(onClick = onDismiss) { Text(stringResource(android.R.string.cancel)) }
TextButton(
onClick = onDismiss,
shapes = ButtonDefaults.shapes()
) { Text(stringResource(android.R.string.cancel)) }
},
)

View File

@@ -14,6 +14,8 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Wallpaper
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
@@ -41,6 +43,7 @@ import com.google.accompanist.drawablepainter.rememberDrawablePainter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun ColumnScope.WithWallpaper(
modifier: Modifier = Modifier,
@@ -67,6 +70,7 @@ fun ColumnScope.WithWallpaper(
) {
FilledTonalButton(
onClick = { showPermissionDialog = true },
shapes = ButtonDefaults.shapes()
) {
Icon(
imageVector = Icons.Rounded.Wallpaper,

View File

@@ -11,6 +11,8 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
@@ -34,6 +36,7 @@ import com.android.launcher3.R
import com.patrykmichalik.opto.domain.Preference
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun ColorSelection(
label: String,
@@ -84,6 +87,7 @@ fun ColorSelection(
modifier = Modifier
.fillMaxWidth()
.padding(all = 16.dp),
shapes = ButtonDefaults.shapes()
) {
Text(text = stringResource(id = R.string.action_apply))
}

View File

@@ -20,6 +20,8 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -63,6 +65,7 @@ fun ClickablePreference(
)
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun PreferenceClickConfirmation(
title: String,
@@ -77,6 +80,7 @@ fun PreferenceClickConfirmation(
buttons = {
OutlinedButton(
onClick = onDismissRequest,
shapes = ButtonDefaults.shapes()
) {
Text(text = stringResource(id = android.R.string.cancel))
}
@@ -86,6 +90,7 @@ fun PreferenceClickConfirmation(
onDismissRequest()
onConfirm()
},
shapes = ButtonDefaults.shapes()
) {
Text(text = stringResource(id = android.R.string.ok))
}

View File

@@ -22,6 +22,8 @@ import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
@@ -58,6 +60,7 @@ fun <T> ListPreference(
)
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun <T> ListPreference(
entries: List<ListPreferenceEntry<T>>,
@@ -89,7 +92,10 @@ fun <T> ListPreference(
ModalBottomSheetContent(
title = { Text(label) },
buttons = {
OutlinedButton(onClick = { bottomSheetHandler.hide() }) {
OutlinedButton(
onClick = { bottomSheetHandler.hide() },
shapes = ButtonDefaults.shapes()
) {
Text(text = stringResource(id = AndroidR.string.cancel))
}
},

View File

@@ -8,9 +8,15 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
@@ -128,6 +134,21 @@ fun MainSwitchPreference(
onCheckedChange = onCheckedChange,
enabled = enabled,
interactionSource = interactionSource,
thumbContent = {
if (checked) {
Icon(
imageVector = Icons.Filled.Check,
contentDescription = null,
modifier = Modifier.size(SwitchDefaults.IconSize),
)
} else {
Icon(
imageVector = Icons.Filled.Close,
contentDescription = null,
modifier = Modifier.size(SwitchDefaults.IconSize),
)
}
},
)
},
enabled = enabled,

View File

@@ -23,8 +23,14 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.Icon
import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
@@ -112,6 +118,21 @@ fun SwitchPreference(
onCheckedChange = onCheckedChange,
enabled = enabled,
interactionSource = interactionSource,
thumbContent = {
if (checked) {
Icon(
imageVector = Icons.Filled.Check,
contentDescription = null,
modifier = Modifier.size(SwitchDefaults.IconSize),
)
} else {
Icon(
imageVector = Icons.Filled.Close,
contentDescription = null,
modifier = Modifier.size(SwitchDefaults.IconSize),
)
}
},
)
},
enabled = enabled,

View File

@@ -5,6 +5,8 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
@@ -71,6 +73,7 @@ fun TextPreference(
)
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun TextPreferenceDialog(
title: String,
@@ -94,6 +97,7 @@ fun TextPreferenceDialog(
buttons = {
OutlinedButton(
onClick = onDismissRequest,
shapes = ButtonDefaults.shapes()
) {
Text(text = stringResource(id = android.R.string.cancel))
}
@@ -103,6 +107,7 @@ fun TextPreferenceDialog(
onDismissRequest()
onConfirm(value)
},
shapes = ButtonDefaults.shapes()
) {
Text(text = stringResource(id = android.R.string.ok))
}

View File

@@ -1,8 +1,10 @@
package app.lawnchair.ui.preferences.components.layout
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.LocalContentColor
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -12,6 +14,7 @@ import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.rememberVectorPainter
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun ClickableIcon(
painter: Painter,
@@ -24,6 +27,7 @@ fun ClickableIcon(
onClick = onClick,
modifier = modifier,
enabled = enabled,
shapes = IconButtonDefaults.shapes(),
) {
val contentAlpha = if (enabled) tint.alpha else 0.38f
val alpha by animateFloatAsState(targetValue = contentAlpha, label = "")

View File

@@ -1,5 +1,6 @@
package app.lawnchair.ui.preferences.components.layout
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -14,14 +15,13 @@ import androidx.compose.ui.layout.Layout
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import app.lawnchair.ui.theme.dividerColor
import kotlin.math.roundToInt
@Composable
fun DividerColumn(
modifier: Modifier = Modifier,
color: Color = dividerColor(),
thickness: Dp = 1.dp,
color: Color = MaterialTheme.colorScheme.surface,
thickness: Dp = 3.dp,
startIndent: Dp = 0.dp,
endIndent: Dp = 0.dp,
dividersToSkip: Int = 0,
@@ -30,8 +30,8 @@ fun DividerColumn(
val state = remember { DividersState() }
val density = LocalDensity.current
val thicknessPx = with(density) { thickness.toPx() }
val startIndentPx = with(density) { (startIndent + 16.dp).toPx() }
val endIndentPx = with(density) { (endIndent + 16.dp).toPx() }
val startIndentPx = with(density) { (startIndent).toPx() }
val endIndentPx = with(density) { (endIndent).toPx() }
Layout(
modifier = modifier
.drawDividers(state, color, thicknessPx, startIndentPx, endIndentPx),

View File

@@ -20,16 +20,20 @@ import androidx.compose.animation.Crossfade
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.foundation.layout.size
import androidx.compose.material3.ContainedLoadingIndicator
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
/**
* Creates a simple loading animation with [Crossfade] and [CircularProgressIndicator].
* Creates a simple loading animation with [Crossfade] and [ContainedLoadingIndicator].
* @param isLoading Defines whether the content is still loading or not
* @param content Content to appear or disappear based on the value of [isLoading]
*/
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun LoadingScreen(
isLoading: Boolean,
@@ -47,7 +51,9 @@ fun LoadingScreen(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
CircularProgressIndicator()
ContainedLoadingIndicator(
modifier = Modifier.size(128.dp)
)
}
} else {
content()
@@ -56,7 +62,7 @@ fun LoadingScreen(
}
/**
* Creates a simple loading animation with [Crossfade] and [CircularProgressIndicator]. [obj] will be passed as a parameter of [content].
* Creates a simple loading animation with [Crossfade] and [ContainedLoadingIndicator]. [obj] will be passed as a parameter of [content].
* @param obj A key representing the content object
* @param content Content to appear or disappear based on the value of [obj].
*/

View File

@@ -32,9 +32,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.semantics.heading
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import app.lawnchair.ui.theme.dividerColor
import app.lawnchair.ui.theme.preferenceGroupColor
@Composable
@@ -47,7 +47,7 @@ fun PreferenceGroup(
dividerStartIndent: Dp = 0.dp,
dividerEndIndent: Dp = 0.dp,
dividersToSkip: Int = 0,
dividerColor: Color = dividerColor(),
dividerColor: Color = MaterialTheme.colorScheme.surface,
content: @Composable () -> Unit,
) {
Column(
@@ -56,7 +56,7 @@ fun PreferenceGroup(
PreferenceGroupHeading(heading)
Surface(
modifier = Modifier.padding(horizontal = 16.dp),
shape = MaterialTheme.shapes.large,
shape = MaterialTheme.shapes.extraLarge,
color = preferenceGroupColor(),
) {
if (showDividers) {
@@ -93,6 +93,7 @@ fun PreferenceGroupHeading(
Text(
text = heading,
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.SemiBold,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier.semantics { this.heading() },
)

View File

@@ -13,11 +13,19 @@ import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Icon
import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.ripple
@@ -208,7 +216,7 @@ private fun ManageExternalStorageSetting(
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@OptIn(ExperimentalPermissionsApi::class)
@OptIn(ExperimentalPermissionsApi::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun VisualMediaSetting(
accessState: FileAccessState,
@@ -283,6 +291,7 @@ private fun VisualMediaSetting(
showPartialAccessDialog = false
},
modifier = Modifier.fillMaxWidth(),
shapes = ButtonDefaults.shapes()
) { Text(stringResource(R.string.permissions_photos_videos_grant_full)) }
TextButton(
@@ -291,11 +300,13 @@ private fun VisualMediaSetting(
showPartialAccessDialog = false
},
modifier = Modifier.fillMaxWidth(),
shapes = ButtonDefaults.shapes()
) { Text(stringResource(R.string.permissions_photos_videos_manage_selected)) }
TextButton(
onClick = { showPartialAccessDialog = false },
modifier = Modifier.fillMaxWidth(),
shapes = ButtonDefaults.shapes()
) { Text(stringResource(android.R.string.cancel)) }
}
},
@@ -403,6 +414,21 @@ internal fun TwoTargetSwitchPreference(
onCheckedChange = onCheckedChange,
enabled = switchEnabled,
interactionSource = interactionSource,
thumbContent = {
if (checked) {
Icon(
imageVector = Icons.Filled.Check,
contentDescription = null,
modifier = Modifier.size(SwitchDefaults.IconSize),
)
} else {
Icon(
imageVector = Icons.Filled.Close,
contentDescription = null,
modifier = Modifier.size(SwitchDefaults.IconSize),
)
}
},
)
},
enabled = enabled,
@@ -422,7 +448,7 @@ internal fun TwoTargetSwitchPreference(
* @param rationale The rationale to show to the user.
* @param onPermissionRequest Called when the permission is requested.
*/
@OptIn(ExperimentalPermissionsApi::class)
@OptIn(ExperimentalPermissionsApi::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun FileAccessPermissionDialog(
onDismiss: () -> Unit,
@@ -457,7 +483,10 @@ private fun FileAccessPermissionDialog(
Text(stringResource(R.string.manage_storage_access_denied_description, stringResource(id = R.string.derived_app_name)))
},
confirmButton = {
FilledTonalButton(onClick = onDismiss) { Text(stringResource(R.string.dismiss)) }
FilledTonalButton(
onClick = onDismiss,
shapes = ButtonDefaults.shapes()
) { Text(stringResource(R.string.dismiss)) }
},
)
}

View File

@@ -10,8 +10,14 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.Icon
import androidx.compose.material3.Switch
import androidx.compose.material3.SwitchDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -101,6 +107,21 @@ fun SearchProviderPreferenceItem(
checked = enabled && adapter.state.value,
onCheckedChange = adapter::onChange,
enabled = enabled,
thumbContent = {
if (enabled && adapter.state.value) {
Icon(
imageVector = Icons.Filled.Check,
contentDescription = null,
modifier = Modifier.size(SwitchDefaults.IconSize),
)
} else {
Icon(
imageVector = Icons.Filled.Close,
contentDescription = null,
modifier = Modifier.size(SwitchDefaults.IconSize),
)
}
},
)
},
applyPaddings = false,

View File

@@ -9,6 +9,8 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
@@ -79,6 +81,7 @@ fun WebSearchProvider(
}
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun SearchPopupPreference(
title: String,
@@ -101,6 +104,7 @@ fun SearchPopupPreference(
showPopup = false
onConfirm(value.text)
},
shapes = ButtonDefaults.shapes()
) {
Text(text = stringResource(id = android.R.string.ok))
}
@@ -110,6 +114,7 @@ fun SearchPopupPreference(
onClick = {
showPopup = false
},
shapes = ButtonDefaults.shapes()
) {
Text(text = stringResource(id = android.R.string.cancel))
}

View File

@@ -13,8 +13,11 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Add
import androidx.compose.material.icons.rounded.Delete
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
@@ -246,6 +249,7 @@ fun AppDrawerFoldersPreference(
}
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun FolderEditSheet(
folderInfo: FolderInfo,
@@ -262,6 +266,7 @@ fun FolderEditSheet(
buttons = {
OutlinedButton(
onClick = onDismiss,
shapes = ButtonDefaults.shapes()
) {
Text(stringResource(android.R.string.cancel))
}
@@ -271,6 +276,7 @@ fun FolderEditSheet(
onRename(folderInfo, textFieldValue.text)
onDismiss()
},
shapes = ButtonDefaults.shapes()
) {
Text(stringResource(android.R.string.ok))
}
@@ -308,6 +314,7 @@ fun FolderEditSheet(
}
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun FolderItem(
folderInfo: FolderInfo,
@@ -338,6 +345,7 @@ fun FolderItem(
onClick = {
onItemDelete(folderInfo)
},
shapes = IconButtonDefaults.shapes(),
) {
Icon(Icons.Rounded.Delete, contentDescription = "Delete", tint = MaterialTheme.colorScheme.onSurfaceVariant)
}

View File

@@ -17,6 +17,8 @@ import androidx.compose.material.icons.rounded.ArrowDropDown
import androidx.compose.material.icons.rounded.ContentCopy
import androidx.compose.material.icons.rounded.ContentPaste
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
@@ -56,6 +58,7 @@ import app.lawnchair.util.getClipboardContent
import com.android.launcher3.R
import kotlin.math.roundToInt
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun CustomIconShapePreference(
modifier: Modifier = Modifier,
@@ -89,6 +92,7 @@ fun CustomIconShapePreference(
modifier = Modifier
.fillMaxWidth()
.padding(all = 16.dp),
shapes = ButtonDefaults.shapes()
) {
Text(
text = if (appliedIconShape != null) {
@@ -264,6 +268,7 @@ private fun IconShapeCornerPreference(
)
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun CornerSlider(
label: String,
@@ -337,7 +342,10 @@ private fun CornerSlider(
ModalBottomSheetContent(
title = { Text(stringResource(id = R.string.custom_icon_shape_corner)) },
buttons = {
OutlinedButton(onClick = { bottomSheetHandler.hide() }) {
OutlinedButton(
onClick = { bottomSheetHandler.hide() },
shapes = ButtonDefaults.shapes()
) {
Text(text = stringResource(id = android.R.string.cancel))
}
},

View File

@@ -1,8 +1,11 @@
package app.lawnchair.ui.preferences.destinations
import android.Manifest
import android.content.pm.PackageManager
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.datastore.preferences.core.Preferences
import app.lawnchair.preferences.PreferenceManager
@@ -14,6 +17,7 @@ import app.lawnchair.ui.preferences.LocalIsExpandedScreen
import app.lawnchair.ui.preferences.LocalNavController
import app.lawnchair.ui.preferences.components.controls.ClickablePreference
import app.lawnchair.ui.preferences.components.controls.MainSwitchPreference
import app.lawnchair.ui.preferences.components.controls.PreferenceCategory
import app.lawnchair.ui.preferences.components.controls.SwitchPreference
import app.lawnchair.ui.preferences.components.controls.TextPreference
import app.lawnchair.ui.preferences.components.layout.PreferenceGroup
@@ -23,6 +27,7 @@ import app.lawnchair.ui.preferences.data.liveinfo.liveInformationManager
import app.lawnchair.ui.preferences.data.liveinfo.model.LiveInformation
import app.lawnchair.ui.preferences.navigation.FeatureFlags
import com.android.launcher3.R
import com.android.systemui.shared.system.BlurUtils
import com.patrykmichalik.opto.domain.Preference
import kotlinx.coroutines.runBlocking
@@ -102,6 +107,28 @@ fun DebugMenuPreferences(
)
}
}
PreferenceGroup(heading = "Launcher3 Readiness") {
var apmSupport = false
if (LocalContext.current.checkCallingOrSelfPermission(Manifest.permission.PACKAGE_USAGE_STATS)
== PackageManager.PERMISSION_GRANTED) {
apmSupport = true
}
PreferenceCategory(
label = "Blur effect",
description = BlurUtils.supportsBlursOnWindows().toString(),
iconResource = R.drawable.ic_search,
onNavigate = { null },
isSelected = false,
)
PreferenceCategory(
label = "App Prediction",
description = apmSupport.toString(),
iconResource = R.drawable.ic_search,
onNavigate = { null },
isSelected = false,
)
}
}
}
}

View File

@@ -17,6 +17,7 @@ import app.lawnchair.ui.preferences.LocalIsExpandedScreen
import app.lawnchair.ui.preferences.components.WallpaperAccessPermissionDialog
import app.lawnchair.ui.preferences.components.controls.SliderPreference
import app.lawnchair.ui.preferences.components.controls.SwitchPreference
import app.lawnchair.ui.preferences.components.controls.WarningPreference
import app.lawnchair.ui.preferences.components.layout.DividerColumn
import app.lawnchair.ui.preferences.components.layout.ExpandAndShrink
import app.lawnchair.ui.preferences.components.layout.PreferenceGroup
@@ -24,6 +25,8 @@ import app.lawnchair.ui.preferences.components.layout.PreferenceLayout
import app.lawnchair.util.FileAccessManager
import app.lawnchair.util.FileAccessState
import com.android.launcher3.R
import com.android.launcher3.Utilities.ATLEAST_S
import com.android.systemui.shared.system.BlurUtils
@Composable
fun ExperimentalFeaturesPreferences(
@@ -36,27 +39,36 @@ fun ExperimentalFeaturesPreferences(
backArrowVisible = !LocalIsExpandedScreen.current,
modifier = modifier,
) {
PreferenceGroup {
PreferenceGroup(
Modifier,
stringResource(R.string.workspace_label),
) {
// pE-FeatureTaskForce-TODO(N/A): Make Material 3 Expressive Toggle
val enableMaterialExpressiveAdapter = prefs.enableMaterialExpressive.getAdapter()
SwitchPreference(
adapter = enableMaterialExpressiveAdapter,
label = stringResource(id = R.string.material_expressive_label),
description = stringResource(id = R.string.material_expressive_description),
)
ExpandAndShrink(visible = enableMaterialExpressiveAdapter.state.value) {
if (!ATLEAST_S || !BlurUtils.supportsBlursOnWindows()) {
WarningPreference(
"Expressive Blur will be ignored because blur effect required at " +
"least Android 12 or above, and device need performant GPU to render " +
"blur and need to enable support rendering cross window blur by the " +
"device manufacturer.")
}
}
SwitchPreference(
adapter = prefs2.enableFontSelection.getAdapter(),
label = stringResource(id = R.string.font_picker_label),
description = stringResource(id = R.string.font_picker_description),
)
SwitchPreference(
adapter = prefs2.enableSmartspaceCalendarSelection.getAdapter(),
label = stringResource(id = R.string.smartspace_calendar_label),
description = stringResource(id = R.string.smartspace_calendar_description),
)
SwitchPreference(
adapter = prefs.workspaceIncreaseMaxGridSize.getAdapter(),
label = stringResource(id = R.string.workspace_increase_max_grid_size_label),
description = stringResource(id = R.string.workspace_increase_max_grid_size_description),
)
SwitchPreference(
adapter = prefs2.alwaysReloadIcons.getAdapter(),
label = stringResource(id = R.string.always_reload_icons_label),
description = stringResource(id = R.string.always_reload_icons_description),
)
SwitchPreference(
adapter = prefs2.iconSwipeGestures.getAdapter(),
label = stringResource(R.string.icon_swipe_gestures),
@@ -119,5 +131,45 @@ fun ExperimentalFeaturesPreferences(
onPauseOrDispose { }
}
}
PreferenceGroup(
Modifier,
stringResource(R.string.smartspace_label),
) {
SwitchPreference(
adapter = prefs2.enableSmartspaceCalendarSelection.getAdapter(),
label = stringResource(id = R.string.smartspace_calendar_label),
description = stringResource(id = R.string.smartspace_calendar_description),
)
}
PreferenceGroup(
Modifier,
stringResource(R.string.internal_label),
stringResource(R.string.internal_description),
) {
// Lawnchair-TODO(Merge): Investigate Always Reload Icons
val alwaysReloadIconsAdapter = prefs2.alwaysReloadIcons.getAdapter()
SwitchPreference(
adapter = alwaysReloadIconsAdapter,
label = stringResource(id = R.string.always_reload_icons_label),
description = stringResource(id = R.string.always_reload_icons_description),
)
ExpandAndShrink(visible = alwaysReloadIconsAdapter.state.value) {
WarningPreference(stringResource(R.string.always_reload_icons_warning))
}
// pE-FeatureTaskForce-TODO(N/A): Make GestureNavContract API Toggle
val enableGncAdapter = prefs.enableGnc.getAdapter()
SwitchPreference(
adapter = enableGncAdapter,
label = stringResource(id = R.string.gesturenavcontract_label),
description = stringResource(id = R.string.gesturenavcontract_description),
enabled = ATLEAST_S,
)
ExpandAndShrink(visible = enableGncAdapter.state.value) {
WarningPreference(stringResource(R.string.gesturenavcontract_warning_incompatibility))
}
}
}
}

View File

@@ -22,8 +22,10 @@ import androidx.compose.material.icons.rounded.Delete
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
@@ -202,6 +204,7 @@ fun FontSelection(
}
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun FontSelectionItem(
adapter: PreferenceAdapter<FontCache.Font>,
@@ -243,6 +246,7 @@ private fun FontSelectionItem(
IconButton(
onClick = onDelete,
modifier = Modifier.padding(end = 8.dp),
shapes = IconButtonDefaults.shapes(),
) {
Icon(
imageVector = Icons.Rounded.Delete,
@@ -273,6 +277,7 @@ private fun removeFamilyPrefix(
return fontName.removePrefix(familyName).trim().toString()
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun VariantDropdown(
adapter: PreferenceAdapter<FontCache.Font>,
@@ -300,6 +305,7 @@ private fun VariantDropdown(
onClick = { showVariants = true },
colors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colorScheme.onSurface),
contentPadding = VariantButtonContentPadding,
shapes = ButtonDefaults.shapes()
) {
AndroidText(
modifier = Modifier.wrapContentWidth(),

View File

@@ -6,6 +6,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableIntStateOf
@@ -28,6 +30,7 @@ import app.lawnchair.ui.preferences.components.layout.PreferenceLayout
import com.android.launcher3.LauncherAppState
import com.android.launcher3.R
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun HomeScreenGridPreferences(
modifier: Modifier = Modifier,
@@ -96,6 +99,7 @@ fun HomeScreenGridPreferences(
.align(Alignment.CenterEnd)
.fillMaxWidth(),
enabled = columns.intValue != originalColumns || rows.intValue != originalRows,
shapes = ButtonDefaults.shapes()
) {
Text(text = stringResource(id = R.string.action_apply))
}

View File

@@ -17,11 +17,8 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Backup
import androidx.compose.material.icons.outlined.Science
import androidx.compose.material.icons.outlined.SettingsBackupRestore
import androidx.compose.material.icons.rounded.Backup
import androidx.compose.material.icons.rounded.Build
import androidx.compose.material.icons.rounded.Refresh
import androidx.compose.material.icons.rounded.Science
import androidx.compose.material.icons.rounded.SettingsBackupRestore
import androidx.compose.material.icons.rounded.TipsAndUpdates
import androidx.compose.material3.DropdownMenuItem
@@ -213,7 +210,7 @@ fun PreferenceCategoryGroup(
Surface(
modifier = modifier.padding(horizontal = 16.dp),
shape = MaterialTheme.shapes.large,
shape = MaterialTheme.shapes.extraLarge,
color = color,
tonalElevation = if (isSelectedThemeDark) 1.dp else 0.dp,
) {
@@ -222,7 +219,6 @@ fun PreferenceCategoryGroup(
startIndent = (-16).dp,
endIndent = (-16).dp,
color = MaterialTheme.colorScheme.surface,
thickness = 2.dp,
)
}
}

View File

@@ -4,6 +4,8 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
@@ -201,6 +203,7 @@ private fun Options(
}
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun SponsorDisclaimer(
sponsor: String,
@@ -209,7 +212,10 @@ private fun SponsorDisclaimer(
) {
ModalBottomSheetContent(
buttons = {
OutlinedButton(onClick = onAcknowledge) {
OutlinedButton(
onClick = onAcknowledge,
shapes = ButtonDefaults.shapes()
) {
Text(text = stringResource(id = android.R.string.ok))
}
},

View File

@@ -22,9 +22,12 @@ import androidx.activity.SystemBarStyle
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialExpressiveTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MotionScheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.material3.expressiveLightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
@@ -45,17 +48,19 @@ import app.lawnchair.ui.preferences.components.ThemeChoice
import app.lawnchair.wallpaper.WallpaperManagerCompat
import com.android.launcher3.Utilities
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun LawnchairTheme(
darkTheme: Boolean = isSelectedThemeDark,
content: @Composable () -> Unit,
) {
val colorScheme = getColorScheme(darkTheme = darkTheme)
MaterialTheme(
MaterialExpressiveTheme(
colorScheme = colorScheme,
typography = Typography,
content = content,
shapes = Shapes,
motionScheme = MotionScheme.expressive(),
)
}
@@ -101,10 +106,11 @@ fun getColorScheme(darkTheme: Boolean): ColorScheme {
return colorScheme.toComposeColorScheme(isDark = darkTheme)
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
private fun getPreviewColorScheme(darkTheme: Boolean) = if (darkTheme) {
darkColorScheme()
} else {
lightColorScheme()
expressiveLightColorScheme()
}
val isSelectedThemeDark: Boolean

View File

@@ -16,9 +16,16 @@
package app.lawnchair.ui.util
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBars
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.SheetValue
import androidx.compose.material3.rememberModalBottomSheetState
@@ -31,6 +38,9 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.blur
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
@@ -78,10 +88,54 @@ fun ProvideBottomSheetHandler(
}
CompositionLocalProvider(LocalBottomSheetHandler provides bottomSheetHandler) {
content()
val windowInsets = if (bottomSheetState.isVisible) WindowInsets.navigationBars else WindowInsets(0.dp)
var maxOffsetPx by remember(showBottomSheet) { mutableStateOf<Float?>(null) }
// Live Edit doesn't like that we call requireOffset(), rebuild it instead of update.
val currentOffsetPx = try {
bottomSheetState.requireOffset()
} catch (_: IllegalStateException) {
null
}
if (showBottomSheet && currentOffsetPx != null) {
maxOffsetPx = maxOf(maxOffsetPx ?: 0f, currentOffsetPx)
}
val rawFraction = when {
!showBottomSheet -> 0f
currentOffsetPx == null || maxOffsetPx == null || maxOffsetPx == 0f -> 1f
else -> 1f - (currentOffsetPx / maxOffsetPx!!)
}.coerceIn(0f, 1f)
val animatedFraction by animateFloatAsState(
targetValue = rawFraction,
animationSpec = spring(stiffness = Spring.StiffnessMediumLow),
label = "BottomSheetBlurFraction"
)
// See R.dimen.max_depth_blur_radius_enhanced
val blur = (34f * animatedFraction).dp
val scrimAlpha = 0.32f * animatedFraction
Box(modifier = Modifier.fillMaxSize()) {
Box(
modifier = Modifier
.fillMaxSize()
.blur(blur)
) {
content()
}
if (showBottomSheet) {
Box(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.onSurface.copy(alpha = scrimAlpha))
)
}
}
if (showBottomSheet) {
ModalBottomSheet(
sheetState = bottomSheetState,
@@ -91,6 +145,8 @@ fun ProvideBottomSheetHandler(
contentWindowInsets = {
windowInsets
},
// We render our own scrim to control the scrim's blur and alpha
scrimColor = Color.Transparent,
) {
bottomSheetContent.content()
}

View File

@@ -388,4 +388,4 @@ public class PipAccessibilityInteractionConnection {
int interactionId,
IAccessibilityInteractionConnectionCallback callback) {}
}
}
}