From f11368583700380deee1abbdd020955b1812e571 Mon Sep 17 00:00:00 2001 From: Pun Butrach Date: Wed, 17 Sep 2025 23:17:49 +0700 Subject: [PATCH] feat: Lawnchair Settings Material 3 Expressive Signed-off-by: Pun Butrach --- GITHUB_CHANGELOG.md | 2 + lawnchair/res/values/strings.xml | 8 ++ lawnchair/src/app/lawnchair/LawnchairApp.kt | 6 ++ .../gestures/handlers/SleepGestureHandler.kt | 6 ++ .../smartspace/model/SmartspaceCalendar.kt | 1 + .../ui/preferences/about/ChangesDialog.kt | 6 +- .../ui/preferences/about/UpdateSection.kt | 5 ++ .../components/DraggablePreference.kt | 23 ++++++ .../components/GestureHandlerPreference.kt | 9 ++- .../components/PermissionDialog.kt | 21 +++++- .../components/WallpaperPreview.kt | 4 + .../ColorSelectionPreference.kt | 4 + .../controls/ClickablePreference.kt | 5 ++ .../components/controls/ListPreference.kt | 8 +- .../controls/MainSwitchPreference.kt | 21 ++++++ .../components/controls/SwitchPreference.kt | 21 ++++++ .../components/controls/TextPreference.kt | 5 ++ .../components/layout/ClickableIcon.kt | 4 + .../components/layout/DividerColumn.kt | 10 +-- .../components/layout/LoadingScreen.kt | 14 +++- .../components/layout/PreferenceGroup.kt | 7 +- .../components/search/FileSearchProvider.kt | 35 ++++++++- .../search/SearchProviderPreference.kt | 21 ++++++ .../components/search/WebSearchProvider.kt | 5 ++ .../AppDrawerFoldersPreference.kt | 8 ++ .../destinations/CustomIconShapePreference.kt | 10 ++- .../destinations/DebugMenuPreferences.kt | 27 +++++++ .../ExperimentalFeaturesPreferences.kt | 74 ++++++++++++++++--- .../destinations/FontSelectionPreference.kt | 6 ++ .../destinations/HomeScreenGridPreferences.kt | 4 + .../destinations/PreferencesDashboard.kt | 6 +- .../destinations/SearchProviderPreferences.kt | 8 +- lawnchair/src/app/lawnchair/ui/theme/Theme.kt | 12 ++- .../ui/util/ProvideBottomSheetHandler.kt | 60 ++++++++++++++- ...PipAccessibilityInteractionConnection.java | 2 +- 35 files changed, 422 insertions(+), 46 deletions(-) diff --git a/GITHUB_CHANGELOG.md b/GITHUB_CHANGELOG.md index 4e354724ad..7eb78c04c7 100644 --- a/GITHUB_CHANGELOG.md +++ b/GITHUB_CHANGELOG.md @@ -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 diff --git a/lawnchair/res/values/strings.xml b/lawnchair/res/values/strings.xml index 47c3bbbc03..ec1e66c0aa 100644 --- a/lawnchair/res/values/strings.xml +++ b/lawnchair/res/values/strings.xml @@ -188,6 +188,12 @@ Experimental features + Workspace + Smartspace + + Internal + Internal may be more unstable than other experimental group + Font customization Some text remains unchanged @@ -199,6 +205,7 @@ Always reload icons Avoid using cached icons from icon packs + Will cause icon to reload regularly, this is not required if icon pack doesn\'t update regularly Icon swipe gestures Perform actions when swiping left or right on icons instead of moving the home screen @@ -211,6 +218,7 @@ GestureNavContract API Render launcher animations without root, does not work with heavily modified AOSP + GestureNavContract may cause artifacting during animations Lock/unlock diff --git a/lawnchair/src/app/lawnchair/LawnchairApp.kt b/lawnchair/src/app/lawnchair/LawnchairApp.kt index 20909f9f15..14128fa828 100644 --- a/lawnchair/src/app/lawnchair/LawnchairApp.kt +++ b/lawnchair/src/app/lawnchair/LawnchairApp.kt @@ -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)) } diff --git a/lawnchair/src/app/lawnchair/gestures/handlers/SleepGestureHandler.kt b/lawnchair/src/app/lawnchair/gestures/handlers/SleepGestureHandler.kt index 00fdfdfef7..d9d8013115 100644 --- a/lawnchair/src/app/lawnchair/gestures/handlers/SleepGestureHandler.kt +++ b/lawnchair/src/app/lawnchair/gestures/handlers/SleepGestureHandler.kt @@ -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)) } diff --git a/lawnchair/src/app/lawnchair/smartspace/model/SmartspaceCalendar.kt b/lawnchair/src/app/lawnchair/smartspace/model/SmartspaceCalendar.kt index 77d20e62b0..e10e873b1a 100644 --- a/lawnchair/src/app/lawnchair/smartspace/model/SmartspaceCalendar.kt +++ b/lawnchair/src/app/lawnchair/smartspace/model/SmartspaceCalendar.kt @@ -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" } } diff --git a/lawnchair/src/app/lawnchair/ui/preferences/about/ChangesDialog.kt b/lawnchair/src/app/lawnchair/ui/preferences/about/ChangesDialog.kt index da978c72c7..74c37bd6ab 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/about/ChangesDialog.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/about/ChangesDialog.kt @@ -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)) } diff --git a/lawnchair/src/app/lawnchair/ui/preferences/about/UpdateSection.kt b/lawnchair/src/app/lawnchair/ui/preferences/about/UpdateSection.kt index 247cf14305..f4c8c4d7ae 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/about/UpdateSection.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/about/UpdateSection.kt @@ -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)) } diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/DraggablePreference.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/DraggablePreference.kt index 36cf72e93a..11839b1cf4 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/components/DraggablePreference.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/components/DraggablePreference.kt @@ -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, diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/GestureHandlerPreference.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/GestureHandlerPreference.kt index 63320dce80..2dc9458772 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/components/GestureHandlerPreference.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/components/GestureHandlerPreference.kt @@ -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, @@ -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)) } }, diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/PermissionDialog.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/PermissionDialog.kt index d149805cae..a04ec0de9e 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/components/PermissionDialog.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/components/PermissionDialog.kt @@ -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)) } }, ) diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/WallpaperPreview.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/WallpaperPreview.kt index 00d3497353..d1f19d3a71 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/components/WallpaperPreview.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/components/WallpaperPreview.kt @@ -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, diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/colorpreference/ColorSelectionPreference.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/colorpreference/ColorSelectionPreference.kt index f357bef305..92007cb3c4 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/components/colorpreference/ColorSelectionPreference.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/components/colorpreference/ColorSelectionPreference.kt @@ -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)) } diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/controls/ClickablePreference.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/controls/ClickablePreference.kt index fc75ffe108..cea526d905 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/components/controls/ClickablePreference.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/components/controls/ClickablePreference.kt @@ -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)) } diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/controls/ListPreference.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/controls/ListPreference.kt index 5999047131..3af7947741 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/components/controls/ListPreference.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/components/controls/ListPreference.kt @@ -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 ListPreference( ) } +@OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable fun ListPreference( entries: List>, @@ -89,7 +92,10 @@ fun ListPreference( ModalBottomSheetContent( title = { Text(label) }, buttons = { - OutlinedButton(onClick = { bottomSheetHandler.hide() }) { + OutlinedButton( + onClick = { bottomSheetHandler.hide() }, + shapes = ButtonDefaults.shapes() + ) { Text(text = stringResource(id = AndroidR.string.cancel)) } }, diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/controls/MainSwitchPreference.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/controls/MainSwitchPreference.kt index 1d219cc53e..59c3e59372 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/components/controls/MainSwitchPreference.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/components/controls/MainSwitchPreference.kt @@ -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, diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/controls/SwitchPreference.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/controls/SwitchPreference.kt index 5a859f520e..5a13eeaa37 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/components/controls/SwitchPreference.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/components/controls/SwitchPreference.kt @@ -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, diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/controls/TextPreference.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/controls/TextPreference.kt index 679bf10306..4d0052764d 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/components/controls/TextPreference.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/components/controls/TextPreference.kt @@ -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)) } diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/layout/ClickableIcon.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/layout/ClickableIcon.kt index 7bd790c206..5a92a8ad15 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/components/layout/ClickableIcon.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/components/layout/ClickableIcon.kt @@ -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 = "") diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/layout/DividerColumn.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/layout/DividerColumn.kt index d6b915a975..8e04a84152 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/components/layout/DividerColumn.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/components/layout/DividerColumn.kt @@ -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), diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/layout/LoadingScreen.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/layout/LoadingScreen.kt index 82e8d2834b..2cc07419a8 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/components/layout/LoadingScreen.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/components/layout/LoadingScreen.kt @@ -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]. */ diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/layout/PreferenceGroup.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/layout/PreferenceGroup.kt index ed079c94d3..201f4503ee 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/components/layout/PreferenceGroup.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/components/layout/PreferenceGroup.kt @@ -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() }, ) diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/search/FileSearchProvider.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/search/FileSearchProvider.kt index 7b0e5cd2cb..2fd22fdd5f 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/components/search/FileSearchProvider.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/components/search/FileSearchProvider.kt @@ -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)) } }, ) } diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/search/SearchProviderPreference.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/search/SearchProviderPreference.kt index ac5f1570c2..757e764383 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/components/search/SearchProviderPreference.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/components/search/SearchProviderPreference.kt @@ -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, diff --git a/lawnchair/src/app/lawnchair/ui/preferences/components/search/WebSearchProvider.kt b/lawnchair/src/app/lawnchair/ui/preferences/components/search/WebSearchProvider.kt index 5dac9b04ba..f83172000b 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/components/search/WebSearchProvider.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/components/search/WebSearchProvider.kt @@ -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)) } diff --git a/lawnchair/src/app/lawnchair/ui/preferences/destinations/AppDrawerFoldersPreference.kt b/lawnchair/src/app/lawnchair/ui/preferences/destinations/AppDrawerFoldersPreference.kt index 7524da9847..4373eb24bf 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/destinations/AppDrawerFoldersPreference.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/destinations/AppDrawerFoldersPreference.kt @@ -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) } diff --git a/lawnchair/src/app/lawnchair/ui/preferences/destinations/CustomIconShapePreference.kt b/lawnchair/src/app/lawnchair/ui/preferences/destinations/CustomIconShapePreference.kt index 202a29c350..438764f7f3 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/destinations/CustomIconShapePreference.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/destinations/CustomIconShapePreference.kt @@ -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)) } }, diff --git a/lawnchair/src/app/lawnchair/ui/preferences/destinations/DebugMenuPreferences.kt b/lawnchair/src/app/lawnchair/ui/preferences/destinations/DebugMenuPreferences.kt index 29f4bfcb92..99eb182b61 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/destinations/DebugMenuPreferences.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/destinations/DebugMenuPreferences.kt @@ -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, + ) + } } } } diff --git a/lawnchair/src/app/lawnchair/ui/preferences/destinations/ExperimentalFeaturesPreferences.kt b/lawnchair/src/app/lawnchair/ui/preferences/destinations/ExperimentalFeaturesPreferences.kt index bf7f669165..b4c2cf60bf 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/destinations/ExperimentalFeaturesPreferences.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/destinations/ExperimentalFeaturesPreferences.kt @@ -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)) + } + } } } diff --git a/lawnchair/src/app/lawnchair/ui/preferences/destinations/FontSelectionPreference.kt b/lawnchair/src/app/lawnchair/ui/preferences/destinations/FontSelectionPreference.kt index d0618c228a..7349c5b37b 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/destinations/FontSelectionPreference.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/destinations/FontSelectionPreference.kt @@ -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, @@ -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, @@ -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(), diff --git a/lawnchair/src/app/lawnchair/ui/preferences/destinations/HomeScreenGridPreferences.kt b/lawnchair/src/app/lawnchair/ui/preferences/destinations/HomeScreenGridPreferences.kt index 5c49086ecb..db72e21c63 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/destinations/HomeScreenGridPreferences.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/destinations/HomeScreenGridPreferences.kt @@ -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)) } diff --git a/lawnchair/src/app/lawnchair/ui/preferences/destinations/PreferencesDashboard.kt b/lawnchair/src/app/lawnchair/ui/preferences/destinations/PreferencesDashboard.kt index af00bae3b8..572a774a59 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/destinations/PreferencesDashboard.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/destinations/PreferencesDashboard.kt @@ -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, ) } } diff --git a/lawnchair/src/app/lawnchair/ui/preferences/destinations/SearchProviderPreferences.kt b/lawnchair/src/app/lawnchair/ui/preferences/destinations/SearchProviderPreferences.kt index 3a482578c7..9c8dc21d29 100644 --- a/lawnchair/src/app/lawnchair/ui/preferences/destinations/SearchProviderPreferences.kt +++ b/lawnchair/src/app/lawnchair/ui/preferences/destinations/SearchProviderPreferences.kt @@ -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)) } }, diff --git a/lawnchair/src/app/lawnchair/ui/theme/Theme.kt b/lawnchair/src/app/lawnchair/ui/theme/Theme.kt index 21676e4aaf..b987dc9b17 100644 --- a/lawnchair/src/app/lawnchair/ui/theme/Theme.kt +++ b/lawnchair/src/app/lawnchair/ui/theme/Theme.kt @@ -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 diff --git a/lawnchair/src/app/lawnchair/ui/util/ProvideBottomSheetHandler.kt b/lawnchair/src/app/lawnchair/ui/util/ProvideBottomSheetHandler.kt index c4cc2ec5e7..a3ec43b998 100644 --- a/lawnchair/src/app/lawnchair/ui/util/ProvideBottomSheetHandler.kt +++ b/lawnchair/src/app/lawnchair/ui/util/ProvideBottomSheetHandler.kt @@ -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(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() } diff --git a/wmshell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/wmshell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java index 118ad9c4bf..eefd9e431e 100644 --- a/wmshell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java +++ b/wmshell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java @@ -388,4 +388,4 @@ public class PipAccessibilityInteractionConnection { int interactionId, IAccessibilityInteractionConnectionCallback callback) {} } -} \ No newline at end of file +}