forked from LawnchairLauncher/lawnchair
-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'LawnchairLauncher:14-dev' into trunk
- Loading branch information
Showing
11 changed files
with
355 additions
and
165 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
open_collective: lawnchair |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
292 changes: 292 additions & 0 deletions
292
lawnchair/src/app/lawnchair/ui/preferences/components/QuickActionsPreferences.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,292 @@ | ||
package app.lawnchair.ui.preferences.components | ||
|
||
import android.view.HapticFeedbackConstants | ||
import androidx.compose.animation.AnimatedVisibility | ||
import androidx.compose.foundation.clickable | ||
import androidx.compose.foundation.interaction.MutableInteractionSource | ||
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.width | ||
import androidx.compose.material.icons.Icons | ||
import androidx.compose.material.icons.rounded.DragHandle | ||
import androidx.compose.material3.Card | ||
import androidx.compose.material3.CardDefaults | ||
import androidx.compose.material3.HorizontalDivider | ||
import androidx.compose.material3.Icon | ||
import androidx.compose.material3.IconButton | ||
import androidx.compose.material3.MaterialTheme | ||
import androidx.compose.material3.Surface | ||
import androidx.compose.material3.Switch | ||
import androidx.compose.material3.Text | ||
import androidx.compose.material3.ripple | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.key | ||
import androidx.compose.runtime.mutableIntStateOf | ||
import androidx.compose.runtime.mutableStateOf | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.graphics.Color | ||
import androidx.compose.ui.platform.LocalView | ||
import androidx.compose.ui.res.stringResource | ||
import androidx.compose.ui.semantics.CustomAccessibilityAction | ||
import androidx.compose.ui.semantics.customActions | ||
import androidx.compose.ui.semantics.semantics | ||
import androidx.compose.ui.unit.dp | ||
import app.lawnchair.preferences.PreferenceAdapter | ||
import app.lawnchair.ui.preferences.components.controls.ClickablePreference | ||
import app.lawnchair.ui.preferences.components.layout.ExpandAndShrink | ||
import app.lawnchair.ui.preferences.components.layout.PreferenceGroup | ||
import app.lawnchair.ui.preferences.components.layout.PreferenceGroupHeading | ||
import app.lawnchair.ui.preferences.components.layout.PreferenceTemplate | ||
import com.android.launcher3.R | ||
import com.android.launcher3.Utilities | ||
import sh.calvin.reorderable.ReorderableColumn | ||
import sh.calvin.reorderable.ReorderableScope | ||
|
||
data class RecentsQuickAction( | ||
val id: Int, | ||
val label: String, | ||
val adapter: PreferenceAdapter<Boolean>, | ||
val description: String? = null, | ||
) | ||
|
||
fun sortListByIdOrder(list: List<RecentsQuickAction>, order: String): List<RecentsQuickAction> { | ||
val orderList = order.split(",").map { it.toInt() } | ||
return list.sortedBy { orderList.indexOf(it.id) } | ||
} | ||
|
||
private const val DEFAULT_ORDER = "0,1,2,3,4" | ||
|
||
@Composable | ||
fun QuickActionsPreferences( | ||
adapter: PreferenceAdapter<String>, | ||
items: List<RecentsQuickAction>, | ||
modifier: Modifier = Modifier, | ||
) { | ||
QuickActionsPreferences( | ||
order = adapter.state.value, | ||
onOrderChange = adapter::onChange, | ||
items = items, | ||
modifier = modifier, | ||
) | ||
} | ||
|
||
@Composable | ||
fun QuickActionsPreferences( | ||
order: String, | ||
onOrderChange: (String) -> Unit, | ||
items: List<RecentsQuickAction>, | ||
modifier: Modifier = Modifier, | ||
) { | ||
var orderedItems = remember { | ||
sortListByIdOrder(items, order).toMutableList() | ||
} | ||
|
||
val isAnyDragging = remember { mutableStateOf(false) } | ||
val lastItemIdIndex = remember { mutableIntStateOf(4) } | ||
|
||
val view = LocalView.current | ||
|
||
Column(modifier) { | ||
PreferenceGroupHeading( | ||
stringResource(id = R.string.recents_actions_label), | ||
) | ||
Surface( | ||
modifier = Modifier.padding(horizontal = 16.dp), | ||
shape = MaterialTheme.shapes.large, | ||
tonalElevation = if (!isAnyDragging.value) 1.dp else 0.dp, | ||
) { | ||
ReorderableColumn( | ||
modifier = Modifier, | ||
list = orderedItems, | ||
onSettle = { fromIndex, toIndex -> | ||
orderedItems = orderedItems.apply { | ||
add(toIndex, removeAt(fromIndex)) | ||
} | ||
|
||
isAnyDragging.value = false | ||
|
||
onOrderChange( | ||
orderedItems.map { it.id }.joinToString(separator = ","), | ||
) | ||
|
||
lastItemIdIndex.intValue = orderedItems.last().id | ||
}, | ||
onMove = { | ||
isAnyDragging.value = true | ||
if (Utilities.ATLEAST_U) { | ||
view.performHapticFeedback(HapticFeedbackConstants.SEGMENT_FREQUENT_TICK) | ||
} | ||
}, | ||
) { index, item, isDragging -> | ||
key(item) { | ||
val scope = this | ||
|
||
val interactionSource = remember { MutableInteractionSource() } | ||
val isLastIndex = remember { index != lastItemIdIndex.intValue } | ||
|
||
Card( | ||
elevation = if (isDragging) { | ||
CardDefaults.elevatedCardElevation() | ||
} else { | ||
CardDefaults.cardElevation( | ||
0.dp, | ||
) | ||
}, | ||
colors = if (isDragging) { | ||
CardDefaults.elevatedCardColors() | ||
} else { | ||
CardDefaults.cardColors( | ||
Color.Transparent, | ||
) | ||
}, | ||
modifier = Modifier | ||
.semantics { | ||
customActions = listOf( | ||
CustomAccessibilityAction( | ||
label = "Move up", | ||
action = { | ||
if (index > 0) { | ||
orderedItems = | ||
orderedItems | ||
.toMutableList() | ||
.apply { | ||
add(index - 1, removeAt(index)) | ||
} | ||
true | ||
} else { | ||
false | ||
} | ||
}, | ||
), | ||
CustomAccessibilityAction( | ||
label = "Move down", | ||
action = { | ||
if (index < orderedItems.size - 1) { | ||
orderedItems = | ||
orderedItems | ||
.toMutableList() | ||
.apply { | ||
add(index + 1, removeAt(index)) | ||
} | ||
true | ||
} else { | ||
false | ||
} | ||
}, | ||
), | ||
) | ||
}, | ||
) { | ||
DraggableSwitchPreference( | ||
checked = item.adapter.state.value, | ||
onCheckedChange = item.adapter::onChange, | ||
label = item.label, | ||
description = item.description, | ||
interactionSource = interactionSource, | ||
dragIndicator = { | ||
DragHandle( | ||
interactionSource = interactionSource, | ||
scope = scope, | ||
) | ||
}, | ||
) | ||
AnimatedVisibility(visible = !isAnyDragging.value) { | ||
if (isLastIndex) { | ||
HorizontalDivider() | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
ExpandAndShrink(visible = order != DEFAULT_ORDER) { | ||
PreferenceGroup { | ||
ClickablePreference(label = stringResource(id = R.string.action_reset)) { | ||
orderedItems = sortListByIdOrder(items, DEFAULT_ORDER).toMutableList() | ||
onOrderChange(DEFAULT_ORDER) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
@Composable | ||
fun DraggableSwitchPreference( | ||
checked: Boolean, | ||
onCheckedChange: (Boolean) -> Unit, | ||
label: String, | ||
interactionSource: MutableInteractionSource, | ||
dragIndicator: @Composable () -> Unit, | ||
modifier: Modifier = Modifier, | ||
enabled: Boolean = true, | ||
description: String? = null, | ||
) { | ||
PreferenceTemplate( | ||
modifier = modifier.clickable( | ||
enabled = enabled, | ||
onClick = { | ||
onCheckedChange(!checked) | ||
}, | ||
interactionSource = interactionSource, | ||
indication = ripple(), | ||
), | ||
contentModifier = Modifier | ||
.fillMaxHeight() | ||
.padding(vertical = 16.dp) | ||
.padding(start = 16.dp), | ||
title = { Text(text = label) }, | ||
description = { description?.let { Text(text = it) } }, | ||
startWidget = { | ||
dragIndicator() | ||
}, | ||
endWidget = { | ||
Switch( | ||
modifier = Modifier | ||
.padding(all = 16.dp) | ||
.height(24.dp), | ||
checked = checked, | ||
onCheckedChange = onCheckedChange, | ||
enabled = enabled, | ||
) | ||
}, | ||
enabled = enabled, | ||
applyPaddings = false, | ||
) | ||
} | ||
|
||
@Composable | ||
private fun DragHandle( | ||
scope: ReorderableScope, | ||
interactionSource: MutableInteractionSource, | ||
modifier: Modifier = Modifier, | ||
) { | ||
val view = LocalView.current | ||
IconButton( | ||
modifier = with(scope) { | ||
modifier.longPressDraggableHandle( | ||
onDragStarted = { | ||
if (Utilities.ATLEAST_U) { | ||
view.performHapticFeedback(HapticFeedbackConstants.DRAG_START) | ||
} | ||
}, | ||
onDragStopped = { | ||
if (Utilities.ATLEAST_R) { | ||
view.performHapticFeedback(HapticFeedbackConstants.GESTURE_END) | ||
} | ||
}, | ||
) | ||
}, | ||
onClick = {}, | ||
interactionSource = interactionSource, | ||
) { | ||
Icon( | ||
imageVector = Icons.Rounded.DragHandle, | ||
contentDescription = "Drag indicator", | ||
modifier = Modifier.width(24.dp), | ||
) | ||
} | ||
} |
Oops, something went wrong.