mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-19 10:48:19 +00:00
feat: Lawnchair Settings Material 3 Expressive
Signed-off-by: Pun Butrach <pun.butrach@gmail.com>
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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)) }
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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 = "")
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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].
|
||||
*/
|
||||
|
||||
@@ -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() },
|
||||
)
|
||||
|
||||
@@ -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)) }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -388,4 +388,4 @@ public class PipAccessibilityInteractionConnection {
|
||||
int interactionId,
|
||||
IAccessibilityInteractionConnectionCallback callback) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user