Redesign gesture settings
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
package de.mm20.launcher2.ui.common
|
package de.mm20.launcher2.ui.common
|
||||||
|
|
||||||
|
import androidx.compose.animation.animateColor
|
||||||
|
import androidx.compose.animation.core.updateTransition
|
||||||
import androidx.compose.foundation.BorderStroke
|
import androidx.compose.foundation.BorderStroke
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
@@ -8,10 +11,11 @@ import androidx.compose.foundation.layout.asPaddingValues
|
|||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.navigationBars
|
import androidx.compose.foundation.layout.navigationBars
|
||||||
|
import androidx.compose.foundation.layout.offset
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.requiredWidth
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.material3.DockedSearchBar
|
import androidx.compose.material3.DockedSearchBar
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
@@ -34,128 +38,153 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import de.mm20.launcher2.search.SavableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.component.DismissableBottomSheet
|
|
||||||
import de.mm20.launcher2.ui.component.ShapedLauncherIcon
|
import de.mm20.launcher2.ui.component.ShapedLauncherIcon
|
||||||
|
import de.mm20.launcher2.ui.ktx.animateShapeAsState
|
||||||
import de.mm20.launcher2.ui.ktx.toPixels
|
import de.mm20.launcher2.ui.ktx.toPixels
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SearchablePicker(
|
fun SearchablePicker(
|
||||||
expanded: Boolean,
|
modifier: Modifier = Modifier,
|
||||||
|
contentPadding: PaddingValues = PaddingValues(),
|
||||||
value: SavableSearchable?,
|
value: SavableSearchable?,
|
||||||
onValueChanged: (SavableSearchable?) -> Unit,
|
onValueChanged: (SavableSearchable?) -> Unit,
|
||||||
onDismissRequest: () -> Unit,
|
|
||||||
) {
|
) {
|
||||||
DismissableBottomSheet(expanded = expanded, onDismissRequest = onDismissRequest) {
|
val viewModel: SearchablePickerVM = viewModel()
|
||||||
val viewModel: SearchablePickerVM = viewModel()
|
|
||||||
|
|
||||||
val colorSurface = MaterialTheme.colorScheme.surfaceContainerLow
|
val colorSurface = MaterialTheme.colorScheme.surfaceContainerLow
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier
|
modifier = modifier,
|
||||||
.fillMaxSize(),
|
contentPadding = contentPadding,
|
||||||
contentPadding = PaddingValues(
|
verticalArrangement = Arrangement.spacedBy(1.dp)
|
||||||
start = 16.dp,
|
) {
|
||||||
end = 16.dp,
|
stickyHeader {
|
||||||
bottom = 16.dp + WindowInsets.navigationBars.asPaddingValues()
|
DockedSearchBar(
|
||||||
.calculateBottomPadding(),
|
modifier = Modifier
|
||||||
top = 16.dp,
|
.fillMaxWidth()
|
||||||
)
|
.drawBehind {
|
||||||
) {
|
drawRect(
|
||||||
stickyHeader {
|
brush = Brush.verticalGradient(
|
||||||
DockedSearchBar(
|
0.5f to colorSurface,
|
||||||
modifier = Modifier
|
0.5f to colorSurface.copy(alpha = 0f),
|
||||||
.fillMaxWidth()
|
|
||||||
.drawBehind {
|
|
||||||
drawRect(
|
|
||||||
brush = Brush.verticalGradient(
|
|
||||||
0.5f to colorSurface,
|
|
||||||
0.5f to colorSurface.copy(alpha = 0f),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
.padding(bottom = 16.dp),
|
|
||||||
expanded = false,
|
|
||||||
onExpandedChange = {},
|
|
||||||
inputField = {
|
|
||||||
SearchBarDefaults.InputField(
|
|
||||||
leadingIcon = {
|
|
||||||
Icon(
|
|
||||||
painterResource(R.drawable.search_24px),
|
|
||||||
contentDescription = null
|
|
||||||
)
|
|
||||||
},
|
|
||||||
trailingIcon = {
|
|
||||||
if (viewModel.searchQuery.isNotEmpty()) {
|
|
||||||
IconButton(
|
|
||||||
onClick = {
|
|
||||||
viewModel.onSearchQueryChanged("")
|
|
||||||
}) {
|
|
||||||
Icon(painterResource(R.drawable.close_24px), null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onSearch = {},
|
|
||||||
expanded = false,
|
|
||||||
onExpandedChange = {},
|
|
||||||
query = viewModel.searchQuery,
|
|
||||||
onQueryChange = {
|
|
||||||
viewModel.onSearchQueryChanged(it)
|
|
||||||
},
|
|
||||||
placeholder = {
|
|
||||||
Text(stringResource(R.string.search_bar_placeholder))
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
) {
|
.padding(bottom = 16.dp),
|
||||||
}
|
expanded = false,
|
||||||
}
|
onExpandedChange = {},
|
||||||
items(viewModel.items) {
|
inputField = {
|
||||||
val iconSize = 32.dp.toPixels()
|
SearchBarDefaults.InputField(
|
||||||
val icon by remember(it.key) {
|
leadingIcon = {
|
||||||
viewModel.getIcon(
|
|
||||||
it,
|
|
||||||
iconSize.toInt()
|
|
||||||
)
|
|
||||||
}.collectAsStateWithLifecycle(null)
|
|
||||||
val selected = it.key == value?.key
|
|
||||||
Surface(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(vertical = 4.dp),
|
|
||||||
shape = MaterialTheme.shapes.small,
|
|
||||||
border = BorderStroke(
|
|
||||||
1.dp,
|
|
||||||
if (selected) MaterialTheme.colorScheme.secondary else MaterialTheme.colorScheme.outline
|
|
||||||
),
|
|
||||||
color = if (selected) MaterialTheme.colorScheme.secondaryContainer else MaterialTheme.colorScheme.surface,
|
|
||||||
onClick = { onValueChanged(it) }) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.padding(16.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
ShapedLauncherIcon(
|
|
||||||
icon = { icon },
|
|
||||||
size = 32.dp,
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = it.label,
|
|
||||||
modifier = Modifier
|
|
||||||
.weight(1f)
|
|
||||||
.padding(horizontal = 16.dp),
|
|
||||||
style = MaterialTheme.typography.labelMedium,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
)
|
|
||||||
if (selected) {
|
|
||||||
Icon(
|
Icon(
|
||||||
painterResource(R.drawable.check_circle_24px_filled),
|
painterResource(R.drawable.search_24px),
|
||||||
contentDescription = null,
|
contentDescription = null
|
||||||
tint = MaterialTheme.colorScheme.secondary,
|
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
|
trailingIcon = {
|
||||||
|
if (viewModel.searchQuery.isNotEmpty()) {
|
||||||
|
IconButton(
|
||||||
|
modifier = Modifier.offset(16.dp),
|
||||||
|
onClick = {
|
||||||
|
viewModel.onSearchQueryChanged("")
|
||||||
|
}) {
|
||||||
|
Icon(painterResource(R.drawable.close_24px), null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSearch = {},
|
||||||
|
expanded = false,
|
||||||
|
onExpandedChange = {},
|
||||||
|
query = viewModel.searchQuery,
|
||||||
|
onQueryChange = {
|
||||||
|
viewModel.onSearchQueryChanged(it)
|
||||||
|
},
|
||||||
|
placeholder = {
|
||||||
|
Text(stringResource(R.string.search_bar_placeholder))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
itemsIndexed(viewModel.items) { i, it ->
|
||||||
|
val iconSize = 32.dp.toPixels()
|
||||||
|
val icon by remember(it.key) {
|
||||||
|
viewModel.getIcon(
|
||||||
|
it,
|
||||||
|
iconSize.toInt()
|
||||||
|
)
|
||||||
|
}.collectAsStateWithLifecycle(null)
|
||||||
|
|
||||||
|
val badge by remember(it.key) {
|
||||||
|
viewModel.getBadge(it)
|
||||||
|
}.collectAsStateWithLifecycle(null)
|
||||||
|
|
||||||
|
|
||||||
|
val selected = it.key == value?.key
|
||||||
|
|
||||||
|
val transition = updateTransition(selected)
|
||||||
|
val background by transition.animateColor {
|
||||||
|
if (it) MaterialTheme.colorScheme.secondaryContainer
|
||||||
|
else MaterialTheme.colorScheme.surfaceBright
|
||||||
|
}
|
||||||
|
val foreground by transition.animateColor {
|
||||||
|
if (it) MaterialTheme.colorScheme.onSecondaryContainer
|
||||||
|
else MaterialTheme.colorScheme.onSurface
|
||||||
|
}
|
||||||
|
|
||||||
|
val md = MaterialTheme.shapes.medium
|
||||||
|
val xs = MaterialTheme.shapes.extraSmall
|
||||||
|
|
||||||
|
val shape by animateShapeAsState(
|
||||||
|
if (selected || viewModel.items.size == 1) md
|
||||||
|
else when (i) {
|
||||||
|
0 -> md.copy(bottomStart = xs.bottomStart, bottomEnd = xs.bottomEnd)
|
||||||
|
viewModel.items.lastIndex -> md.copy(topStart = xs.topStart, topEnd = xs.topEnd)
|
||||||
|
else -> xs
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Surface(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth(),
|
||||||
|
shape = shape,
|
||||||
|
color = background,
|
||||||
|
contentColor = foreground,
|
||||||
|
onClick = { onValueChanged(it) }) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(
|
||||||
|
start = 8.dp,
|
||||||
|
end = if (selected) 8.dp else 16.dp,
|
||||||
|
),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
ShapedLauncherIcon(
|
||||||
|
modifier = Modifier.padding(horizontal = 10.dp),
|
||||||
|
icon = { icon },
|
||||||
|
badge = { badge },
|
||||||
|
size = 36.dp,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = it.label,
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(start = 8.dp, top = 16.dp, bottom = 16.dp),
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
if (selected) {
|
||||||
|
Icon(
|
||||||
|
painterResource(R.drawable.check_24px),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.padding(16.dp),
|
||||||
|
tint = foreground,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import de.mm20.launcher2.badges.Badge
|
||||||
|
import de.mm20.launcher2.badges.BadgeService
|
||||||
import de.mm20.launcher2.icons.IconService
|
import de.mm20.launcher2.icons.IconService
|
||||||
import de.mm20.launcher2.icons.LauncherIcon
|
import de.mm20.launcher2.icons.LauncherIcon
|
||||||
import de.mm20.launcher2.search.SavableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
@@ -24,6 +26,7 @@ class SearchablePickerVM : ViewModel(), KoinComponent {
|
|||||||
|
|
||||||
private val searchService: SearchService by inject()
|
private val searchService: SearchService by inject()
|
||||||
private val iconService: IconService by inject()
|
private val iconService: IconService by inject()
|
||||||
|
private val badgeService: BadgeService by inject()
|
||||||
|
|
||||||
var searchQuery by mutableStateOf("")
|
var searchQuery by mutableStateOf("")
|
||||||
|
|
||||||
@@ -54,4 +57,10 @@ class SearchablePickerVM : ViewModel(), KoinComponent {
|
|||||||
fun getIcon(searchable: SavableSearchable, size: Int): Flow<LauncherIcon?> {
|
fun getIcon(searchable: SavableSearchable, size: Int): Flow<LauncherIcon?> {
|
||||||
return iconService.getIcon(searchable, size)
|
return iconService.getIcon(searchable, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fun getBadge(searchable: SavableSearchable): Flow<Badge?> {
|
||||||
|
return badgeService.getBadge(searchable)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -41,6 +41,7 @@ import de.mm20.launcher2.preferences.GestureAction
|
|||||||
import de.mm20.launcher2.preferences.SearchBarColors
|
import de.mm20.launcher2.preferences.SearchBarColors
|
||||||
import de.mm20.launcher2.preferences.SearchBarStyle
|
import de.mm20.launcher2.preferences.SearchBarStyle
|
||||||
import de.mm20.launcher2.preferences.SystemBarColors
|
import de.mm20.launcher2.preferences.SystemBarColors
|
||||||
|
import de.mm20.launcher2.preferences.WidgetScreenTarget
|
||||||
import de.mm20.launcher2.search.SavableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.ui.base.BaseActivity
|
import de.mm20.launcher2.ui.base.BaseActivity
|
||||||
import de.mm20.launcher2.ui.base.ProvideCompositionLocals
|
import de.mm20.launcher2.ui.base.ProvideCompositionLocals
|
||||||
@@ -289,7 +290,7 @@ abstract class SharedLauncherActivity(
|
|||||||
)
|
)
|
||||||
|
|
||||||
is GestureAction.Widgets ->
|
is GestureAction.Widgets ->
|
||||||
if (widgetsOnHomeScreen == true) {
|
if (widgetsOnHomeScreen == true && action.target == WidgetScreenTarget.Default) {
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
ScaffoldGesture(
|
ScaffoldGesture(
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ internal object ClockAndWidgetsHomeComponent : ScaffoldComponent() {
|
|||||||
scope.launch { state.lock(hideSearchBar = true) }
|
scope.launch { state.lock(hideSearchBar = true) }
|
||||||
editMode = it
|
editMode = it
|
||||||
},
|
},
|
||||||
parentId = WidgetScreenTarget.Default.scopeId.toString(),
|
parentId = WidgetScreenTarget.Default.id.toString(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (editMode) {
|
if (editMode) {
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
package de.mm20.launcher2.ui.launcher.scaffold
|
package de.mm20.launcher2.ui.launcher.scaffold
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.core.animateDpAsState
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
|
||||||
import androidx.compose.animation.expandVertically
|
import androidx.compose.animation.expandVertically
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.shrinkVertically
|
import androidx.compose.animation.shrinkVertically
|
||||||
import androidx.compose.foundation.ScrollState
|
import androidx.compose.foundation.ScrollState
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
@@ -38,8 +35,6 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.composed
|
|
||||||
import androidx.compose.ui.draw.alpha
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.zIndex
|
import androidx.compose.ui.zIndex
|
||||||
@@ -119,7 +114,7 @@ internal class WidgetsComponent(
|
|||||||
scope.launch { state.lock(hideSearchBar = true) }
|
scope.launch { state.lock(hideSearchBar = true) }
|
||||||
editMode = it
|
editMode = it
|
||||||
},
|
},
|
||||||
parentId = target.scopeId.toString(),
|
parentId = target.id.toString(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (editMode) {
|
if (editMode) {
|
||||||
|
|||||||
@@ -0,0 +1,428 @@
|
|||||||
|
package de.mm20.launcher2.ui.settings.gestures
|
||||||
|
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.icu.text.ListFormatter
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.compose.animation.AnimatedContent
|
||||||
|
import androidx.compose.animation.animateColor
|
||||||
|
import androidx.compose.animation.core.updateTransition
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
|
import androidx.compose.foundation.layout.asPaddingValues
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.navigationBars
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalResources
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import de.mm20.launcher2.icons.IconService
|
||||||
|
import de.mm20.launcher2.icons.LauncherIcon
|
||||||
|
import de.mm20.launcher2.preferences.GestureAction
|
||||||
|
import de.mm20.launcher2.preferences.WidgetScreenTarget
|
||||||
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
|
import de.mm20.launcher2.ui.R
|
||||||
|
import de.mm20.launcher2.ui.common.SearchablePicker
|
||||||
|
import de.mm20.launcher2.ui.component.DismissableBottomSheet
|
||||||
|
import de.mm20.launcher2.ui.component.ShapedLauncherIcon
|
||||||
|
import de.mm20.launcher2.ui.component.preferences.Preference
|
||||||
|
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||||
|
import de.mm20.launcher2.ui.ktx.animateShapeAsState
|
||||||
|
import de.mm20.launcher2.ui.ktx.toPixels
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import org.koin.compose.koinInject
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun GesturePreference(
|
||||||
|
title: String,
|
||||||
|
@DrawableRes icon: Int,
|
||||||
|
value: GestureAction?,
|
||||||
|
onValueChanged: (GestureAction, SavableSearchable?) -> Unit,
|
||||||
|
options: Set<KClass<out GestureAction>>,
|
||||||
|
shortcutOptions: List<SavableSearchable>,
|
||||||
|
widgetOptions: List<WidgetPageOption>,
|
||||||
|
) {
|
||||||
|
var showSheet by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
val value = if (value is GestureAction.Widgets && widgetOptions.none { it.id == value.target }) {
|
||||||
|
GestureAction.NoAction
|
||||||
|
} else {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
val iconService: IconService = koinInject()
|
||||||
|
val iconSize = 24.dp.toPixels().toInt()
|
||||||
|
|
||||||
|
val shortcut = remember(shortcutOptions, value) {
|
||||||
|
if (value !is GestureAction.Launch) {
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
shortcutOptions.find { value.key == it.key }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val icons by remember(shortcutOptions) {
|
||||||
|
combine(shortcutOptions.map {
|
||||||
|
iconService.getIcon(it, iconSize)
|
||||||
|
}) { it.toList() }
|
||||||
|
}.collectAsStateWithLifecycle(emptyList())
|
||||||
|
|
||||||
|
Preference(
|
||||||
|
title = title,
|
||||||
|
summary = getActionLabel(LocalResources.current, value, shortcutOptions),
|
||||||
|
icon = icon,
|
||||||
|
onClick = {
|
||||||
|
showSheet = true
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
DismissableBottomSheet(
|
||||||
|
expanded = showSheet,
|
||||||
|
onDismissRequest = { showSheet = false }
|
||||||
|
) {
|
||||||
|
val contentPadding = PaddingValues(
|
||||||
|
start = 16.dp,
|
||||||
|
end = 16.dp,
|
||||||
|
bottom = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
|
||||||
|
)
|
||||||
|
var showShortcutPicker by remember { mutableStateOf(false) }
|
||||||
|
AnimatedContent(
|
||||||
|
showShortcutPicker
|
||||||
|
) { shortcutPicker ->
|
||||||
|
if (shortcutPicker) {
|
||||||
|
SearchablePicker(
|
||||||
|
value = shortcut,
|
||||||
|
onValueChanged = {
|
||||||
|
onValueChanged(GestureAction.Launch(it?.key), it)
|
||||||
|
showSheet = false
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentPadding = contentPadding,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
contentPadding = contentPadding,
|
||||||
|
) {
|
||||||
|
item {
|
||||||
|
PreferenceCategory {
|
||||||
|
GestureItem(
|
||||||
|
title = stringResource(R.string.gesture_action_none),
|
||||||
|
icon = R.drawable.close_24px,
|
||||||
|
selected = value is GestureAction.NoAction,
|
||||||
|
onClick = {
|
||||||
|
onValueChanged(GestureAction.NoAction, null)
|
||||||
|
showSheet = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
PreferenceCategory(
|
||||||
|
title = stringResource(R.string.gesture_action_category_launcher)
|
||||||
|
) {
|
||||||
|
if (options.contains(GestureAction.Search::class)) {
|
||||||
|
GestureItem(
|
||||||
|
title = stringResource(R.string.gesture_action_open_search),
|
||||||
|
icon = R.drawable.search_24px,
|
||||||
|
selected = value is GestureAction.Search,
|
||||||
|
onClick = {
|
||||||
|
onValueChanged(GestureAction.Search, null)
|
||||||
|
showSheet = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (options.contains(GestureAction.Feed::class)) {
|
||||||
|
GestureItem(
|
||||||
|
title = stringResource(R.string.gesture_action_feed),
|
||||||
|
icon = R.drawable.news_24px,
|
||||||
|
selected = value is GestureAction.Feed,
|
||||||
|
onClick = {
|
||||||
|
onValueChanged(GestureAction.Feed, null)
|
||||||
|
showSheet = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (options.contains(GestureAction.Widgets::class)) {
|
||||||
|
for (widget in widgetOptions) {
|
||||||
|
GestureItem(
|
||||||
|
title = if (widget.isNewPage) {
|
||||||
|
stringResource(R.string.gesture_action_widgets_new)
|
||||||
|
} else {
|
||||||
|
getActionLabel(
|
||||||
|
LocalResources.current,
|
||||||
|
GestureAction.Widgets(widget.id),
|
||||||
|
shortcutOptions
|
||||||
|
)
|
||||||
|
},
|
||||||
|
icon = if (widget.isNewPage) R.drawable.widgets_add_24px else R.drawable.widgets_24px,
|
||||||
|
selected = value is GestureAction.Widgets && value.target == widget.id,
|
||||||
|
onClick = {
|
||||||
|
onValueChanged(GestureAction.Widgets(widget.id), null)
|
||||||
|
showSheet = false
|
||||||
|
},
|
||||||
|
summary = if (widget.isNewPage) {
|
||||||
|
null
|
||||||
|
} else if (widget.widgets.isEmpty()) {
|
||||||
|
stringResource(R.string.gesture_action_widgets_empty)
|
||||||
|
} else {
|
||||||
|
ListFormatter.getInstance().format(
|
||||||
|
widget.widgets.map { it.getLabel(LocalContext.current) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
PreferenceCategory(
|
||||||
|
title = stringResource(R.string.gesture_action_category_system)
|
||||||
|
) {
|
||||||
|
if (options.contains(GestureAction.Notifications::class)) {
|
||||||
|
GestureItem(
|
||||||
|
title = stringResource(R.string.gesture_action_notifications),
|
||||||
|
icon = R.drawable.notifications_24px,
|
||||||
|
selected = value is GestureAction.Notifications,
|
||||||
|
onClick = {
|
||||||
|
onValueChanged(GestureAction.Notifications, null)
|
||||||
|
showSheet = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (options.contains(GestureAction.QuickSettings::class)) {
|
||||||
|
GestureItem(
|
||||||
|
title = stringResource(R.string.gesture_action_quick_settings),
|
||||||
|
icon = R.drawable.settings_24px,
|
||||||
|
selected = value is GestureAction.QuickSettings,
|
||||||
|
onClick = {
|
||||||
|
onValueChanged(GestureAction.QuickSettings, null)
|
||||||
|
showSheet = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (options.contains(GestureAction.ScreenLock::class)) {
|
||||||
|
GestureItem(
|
||||||
|
title = stringResource(R.string.gesture_action_lock_screen),
|
||||||
|
icon = R.drawable.lock_24px,
|
||||||
|
selected = value is GestureAction.ScreenLock,
|
||||||
|
onClick = {
|
||||||
|
onValueChanged(GestureAction.ScreenLock, null)
|
||||||
|
showSheet = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (options.contains(GestureAction.PowerMenu::class)) {
|
||||||
|
GestureItem(
|
||||||
|
title = stringResource(R.string.gesture_action_power_menu),
|
||||||
|
icon = R.drawable.power_settings_new_24px,
|
||||||
|
selected = value is GestureAction.PowerMenu,
|
||||||
|
onClick = {
|
||||||
|
onValueChanged(GestureAction.PowerMenu, null)
|
||||||
|
showSheet = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (options.contains(GestureAction.Recents::class)) {
|
||||||
|
GestureItem(
|
||||||
|
title = stringResource(R.string.gesture_action_recents),
|
||||||
|
icon = R.drawable.amp_stories_24px,
|
||||||
|
selected = value is GestureAction.Recents,
|
||||||
|
onClick = {
|
||||||
|
onValueChanged(GestureAction.Recents, null)
|
||||||
|
showSheet = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
PreferenceCategory(
|
||||||
|
title = stringResource(R.string.gesture_action_category_apps)
|
||||||
|
) {
|
||||||
|
for ((i, shortcut) in shortcutOptions.withIndex()) {
|
||||||
|
GestureItem(
|
||||||
|
title = shortcut.labelOverride ?: shortcut.label,
|
||||||
|
icon = R.drawable.android_24px,
|
||||||
|
shortcutIcon = icons.getOrNull(i),
|
||||||
|
selected = value is GestureAction.Launch && value.key == shortcut.key,
|
||||||
|
onClick = {
|
||||||
|
onValueChanged(GestureAction.Launch(shortcut.key), shortcut)
|
||||||
|
showSheet = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
GestureItem(
|
||||||
|
title = stringResource(R.string.gesture_action_launch_select_app),
|
||||||
|
icon = R.drawable.action_key_24px,
|
||||||
|
selected = false,
|
||||||
|
onClick = {
|
||||||
|
showShortcutPicker = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun GestureItem(
|
||||||
|
title: String,
|
||||||
|
summary: String? = null,
|
||||||
|
@DrawableRes icon: Int,
|
||||||
|
shortcutIcon: LauncherIcon? = null,
|
||||||
|
selected: Boolean,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
) {
|
||||||
|
val transition = updateTransition(selected)
|
||||||
|
val backgroundColor by transition.animateColor {
|
||||||
|
if (it) MaterialTheme.colorScheme.secondaryContainer
|
||||||
|
else MaterialTheme.colorScheme.surfaceBright
|
||||||
|
}
|
||||||
|
val textColor by transition.animateColor {
|
||||||
|
if (it) MaterialTheme.colorScheme.onSecondaryContainer
|
||||||
|
else MaterialTheme.colorScheme.onSurface
|
||||||
|
}
|
||||||
|
val iconColor by transition.animateColor {
|
||||||
|
if (it) MaterialTheme.colorScheme.onSecondaryContainer
|
||||||
|
else MaterialTheme.colorScheme.primary
|
||||||
|
}
|
||||||
|
val shape by animateShapeAsState(
|
||||||
|
if (selected) MaterialTheme.shapes.medium else MaterialTheme.shapes.extraSmall
|
||||||
|
)
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clip(shape)
|
||||||
|
.clickable(onClick = onClick)
|
||||||
|
.background(backgroundColor)
|
||||||
|
.padding(
|
||||||
|
start = 8.dp,
|
||||||
|
end = if (selected) 8.dp else 16.dp,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(56.dp)
|
||||||
|
.padding(end = 8.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
if (shortcutIcon != null) {
|
||||||
|
ShapedLauncherIcon(
|
||||||
|
size = 36.dp,
|
||||||
|
icon = { shortcutIcon },
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Icon(
|
||||||
|
painterResource(icon),
|
||||||
|
contentDescription = null,
|
||||||
|
tint = iconColor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(vertical = 16.dp),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
color = textColor,
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
)
|
||||||
|
if (summary != null) {
|
||||||
|
Text(
|
||||||
|
text = summary,
|
||||||
|
color = textColor,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (selected) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(56.dp)
|
||||||
|
.padding(end = 8.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painterResource(R.drawable.check_24px),
|
||||||
|
contentDescription = null,
|
||||||
|
tint = iconColor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getActionLabel(
|
||||||
|
resources: Resources,
|
||||||
|
action: GestureAction?,
|
||||||
|
shortcutOptions: List<SavableSearchable>
|
||||||
|
): String {
|
||||||
|
return when (action) {
|
||||||
|
GestureAction.Feed -> resources.getString(R.string.gesture_action_feed)
|
||||||
|
is GestureAction.Launch -> {
|
||||||
|
shortcutOptions.find { it.key == action.key }
|
||||||
|
?.let { it.labelOverride ?: it.label }
|
||||||
|
?: resources.getString(R.string.gesture_action_launch_app)
|
||||||
|
}
|
||||||
|
|
||||||
|
GestureAction.Notifications -> resources.getString(R.string.gesture_action_notifications)
|
||||||
|
GestureAction.PowerMenu -> resources.getString(R.string.gesture_action_power_menu)
|
||||||
|
GestureAction.QuickSettings -> resources.getString(R.string.gesture_action_quick_settings)
|
||||||
|
GestureAction.Recents -> resources.getString(R.string.gesture_action_recents)
|
||||||
|
GestureAction.ScreenLock -> resources.getString(R.string.gesture_action_lock_screen)
|
||||||
|
GestureAction.Search -> resources.getString(R.string.gesture_action_open_search)
|
||||||
|
is GestureAction.Widgets -> {
|
||||||
|
when (action.target) {
|
||||||
|
WidgetScreenTarget.Widgets1 -> resources.getString(R.string.gesture_action_widgets)
|
||||||
|
WidgetScreenTarget.Widgets2 -> resources.getString(
|
||||||
|
R.string.gesture_action_widgets_indexed,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
|
||||||
|
WidgetScreenTarget.Widgets3 -> resources.getString(
|
||||||
|
R.string.gesture_action_widgets_indexed,
|
||||||
|
3
|
||||||
|
)
|
||||||
|
|
||||||
|
WidgetScreenTarget.Widgets4 -> resources.getString(
|
||||||
|
R.string.gesture_action_widgets_indexed,
|
||||||
|
4
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> resources.getString(R.string.gesture_action_none)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,7 +9,10 @@ import androidx.compose.foundation.layout.Row
|
|||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.LocalContentColor
|
import androidx.compose.material3.LocalContentColor
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -20,6 +23,7 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
@@ -49,51 +53,33 @@ fun GestureSettingsScreen() {
|
|||||||
val viewModel: GestureSettingsScreenVM = viewModel()
|
val viewModel: GestureSettingsScreenVM = viewModel()
|
||||||
|
|
||||||
val hasPermission by viewModel.hasPermission.collectAsStateWithLifecycle(null)
|
val hasPermission by viewModel.hasPermission.collectAsStateWithLifecycle(null)
|
||||||
val allowWidgetGesture by viewModel.allowWidgetGesture.collectAsStateWithLifecycle(null)
|
|
||||||
val widgetScreenCount by viewModel.widgetScreenCount.collectAsStateWithLifecycle(1)
|
|
||||||
|
|
||||||
val options = buildList {
|
val options = buildSet {
|
||||||
add(stringResource(R.string.gesture_action_none) to GestureAction.NoAction)
|
add(GestureAction.NoAction::class)
|
||||||
add(stringResource(R.string.gesture_action_notifications) to GestureAction.Notifications)
|
add(GestureAction.Notifications::class)
|
||||||
add(stringResource(R.string.gesture_action_quick_settings) to GestureAction.QuickSettings)
|
add(GestureAction.QuickSettings::class)
|
||||||
if (isAtLeastApiLevel(28)) add(stringResource(R.string.gesture_action_lock_screen) to GestureAction.ScreenLock)
|
if (isAtLeastApiLevel(28)) add(GestureAction.ScreenLock::class)
|
||||||
add(stringResource(R.string.gesture_action_recents) to GestureAction.Recents)
|
add(GestureAction.Recents::class)
|
||||||
add(stringResource(R.string.gesture_action_power_menu) to GestureAction.PowerMenu)
|
add(GestureAction.PowerMenu::class)
|
||||||
add(stringResource(R.string.gesture_action_open_search) to GestureAction.Search)
|
add(GestureAction.Search::class)
|
||||||
if (allowWidgetGesture == true) {
|
add(GestureAction.Widgets::class)
|
||||||
// Dynamically add widget screen targets based on user configuration
|
add(GestureAction.Launch::class)
|
||||||
WidgetScreenTarget.getAvailableTargets(widgetScreenCount)
|
|
||||||
.forEachIndexed { index, target ->
|
|
||||||
|
|
||||||
val label =
|
|
||||||
if (widgetScreenCount == 1) {
|
|
||||||
stringResource(R.string.gesture_action_widgets)
|
|
||||||
} else {
|
|
||||||
stringResource(R.string.gesture_action_widgets_indexed, index + 1)
|
|
||||||
}
|
|
||||||
add(label to GestureAction.Widgets(target))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
add(stringResource(R.string.gesture_action_launch_app) to GestureAction.Launch(null))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val shortcutOptions by viewModel.shortcutOptions.collectAsStateWithLifecycle(emptyList())
|
||||||
|
val widgetOptions by viewModel.widgetOptions.collectAsStateWithLifecycle(emptyList())
|
||||||
|
|
||||||
val optionsWithFeed =
|
val optionsWithFeed =
|
||||||
if (FeatureFlags.feed) {
|
if (FeatureFlags.feed) {
|
||||||
options + (stringResource(R.string.gesture_action_feed) to GestureAction.Feed)
|
options + GestureAction.Feed::class
|
||||||
} else options
|
} else options
|
||||||
|
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
PreferenceScreen(title = stringResource(R.string.preference_screen_gestures)) {
|
PreferenceScreen(title = stringResource(R.string.preference_screen_gestures)) {
|
||||||
item {
|
item {
|
||||||
val appIconSize = 32.dp.toPixels()
|
|
||||||
PreferenceCategory {
|
PreferenceCategory {
|
||||||
val swipeDown by viewModel.swipeDown.collectAsStateWithLifecycle(null)
|
val swipeDown by viewModel.swipeDown.collectAsStateWithLifecycle(null)
|
||||||
val swipeDownApp by viewModel.swipeDownApp.collectAsState(null)
|
|
||||||
val swipeDownAppIcon by remember(swipeDownApp?.key) {
|
|
||||||
viewModel.getIcon(swipeDownApp, appIconSize.toInt())
|
|
||||||
}.collectAsState(null)
|
|
||||||
|
|
||||||
GuardedPreference(
|
GuardedPreference(
|
||||||
locked = hasPermission == false && requiresAccessibilityService(swipeDown),
|
locked = hasPermission == false && requiresAccessibilityService(swipeDown),
|
||||||
description = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
description = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
||||||
@@ -103,19 +89,14 @@ fun GestureSettingsScreen() {
|
|||||||
title = stringResource(R.string.preference_gesture_swipe_down),
|
title = stringResource(R.string.preference_gesture_swipe_down),
|
||||||
icon = R.drawable.swipe_down_alt_24px,
|
icon = R.drawable.swipe_down_alt_24px,
|
||||||
value = swipeDown,
|
value = swipeDown,
|
||||||
onValueChanged = { viewModel.setSwipeDown(it) },
|
onValueChanged = viewModel::setSwipeDown,
|
||||||
options = options,
|
options = options,
|
||||||
app = swipeDownApp,
|
shortcutOptions = shortcutOptions,
|
||||||
appIcon = swipeDownAppIcon,
|
widgetOptions = widgetOptions,
|
||||||
onAppChanged = { viewModel.setSwipeDownApp(it) },
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val swipeLeft by viewModel.swipeLeft.collectAsStateWithLifecycle(null)
|
val swipeLeft by viewModel.swipeLeft.collectAsStateWithLifecycle(null)
|
||||||
val swipeLeftApp by viewModel.swipeLeftApp.collectAsState(null)
|
|
||||||
val swipeLeftAppIcon by remember(swipeLeftApp?.key) {
|
|
||||||
viewModel.getIcon(swipeLeftApp, appIconSize.toInt())
|
|
||||||
}.collectAsState(null)
|
|
||||||
GuardedPreference(
|
GuardedPreference(
|
||||||
locked = hasPermission == false && requiresAccessibilityService(swipeLeft),
|
locked = hasPermission == false && requiresAccessibilityService(swipeLeft),
|
||||||
description = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
description = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
||||||
@@ -125,19 +106,14 @@ fun GestureSettingsScreen() {
|
|||||||
title = stringResource(R.string.preference_gesture_swipe_left),
|
title = stringResource(R.string.preference_gesture_swipe_left),
|
||||||
icon = R.drawable.swipe_left_alt_24px,
|
icon = R.drawable.swipe_left_alt_24px,
|
||||||
value = swipeLeft,
|
value = swipeLeft,
|
||||||
onValueChanged = { viewModel.setSwipeLeft(it) },
|
onValueChanged = viewModel::setSwipeLeft,
|
||||||
options = options,
|
options = options,
|
||||||
app = swipeLeftApp,
|
shortcutOptions = shortcutOptions,
|
||||||
appIcon = swipeLeftAppIcon,
|
widgetOptions = widgetOptions,
|
||||||
onAppChanged = { viewModel.setSwipeLeftApp(it) },
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val swipeRight by viewModel.swipeRight.collectAsStateWithLifecycle(null)
|
val swipeRight by viewModel.swipeRight.collectAsStateWithLifecycle(null)
|
||||||
val swipeRightApp by viewModel.swipeRightApp.collectAsState(null)
|
|
||||||
val swipeRightAppIcon by remember(swipeRightApp?.key) {
|
|
||||||
viewModel.getIcon(swipeRightApp, appIconSize.toInt())
|
|
||||||
}.collectAsState(null)
|
|
||||||
GuardedPreference(
|
GuardedPreference(
|
||||||
locked = hasPermission == false && requiresAccessibilityService(swipeRight),
|
locked = hasPermission == false && requiresAccessibilityService(swipeRight),
|
||||||
description = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
description = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
||||||
@@ -147,19 +123,14 @@ fun GestureSettingsScreen() {
|
|||||||
title = stringResource(R.string.preference_gesture_swipe_right),
|
title = stringResource(R.string.preference_gesture_swipe_right),
|
||||||
icon = R.drawable.swipe_right_alt_24px,
|
icon = R.drawable.swipe_right_alt_24px,
|
||||||
value = swipeRight,
|
value = swipeRight,
|
||||||
onValueChanged = { viewModel.setSwipeRight(it) },
|
onValueChanged = viewModel::setSwipeRight,
|
||||||
options = optionsWithFeed,
|
options = optionsWithFeed,
|
||||||
app = swipeRightApp,
|
shortcutOptions = shortcutOptions,
|
||||||
appIcon = swipeRightAppIcon,
|
widgetOptions = widgetOptions,
|
||||||
onAppChanged = { viewModel.setSwipeRightApp(it) },
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val swipeUp by viewModel.swipeUp.collectAsStateWithLifecycle(null)
|
val swipeUp by viewModel.swipeUp.collectAsStateWithLifecycle(null)
|
||||||
val swipeUpApp by viewModel.swipeUpApp.collectAsState(null)
|
|
||||||
val swipeUpAppIcon by remember(swipeUpApp?.key) {
|
|
||||||
viewModel.getIcon(swipeUpApp, appIconSize.toInt())
|
|
||||||
}.collectAsState(null)
|
|
||||||
GuardedPreference(
|
GuardedPreference(
|
||||||
locked = hasPermission == false && requiresAccessibilityService(swipeUp),
|
locked = hasPermission == false && requiresAccessibilityService(swipeUp),
|
||||||
description = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
description = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
||||||
@@ -169,19 +140,14 @@ fun GestureSettingsScreen() {
|
|||||||
title = stringResource(R.string.preference_gesture_swipe_up),
|
title = stringResource(R.string.preference_gesture_swipe_up),
|
||||||
icon = R.drawable.swipe_up_alt_24px,
|
icon = R.drawable.swipe_up_alt_24px,
|
||||||
value = swipeUp,
|
value = swipeUp,
|
||||||
onValueChanged = { viewModel.setSwipeUp(it) },
|
onValueChanged = viewModel::setSwipeUp,
|
||||||
options = options,
|
options = options,
|
||||||
app = swipeUpApp,
|
shortcutOptions = shortcutOptions,
|
||||||
appIcon = swipeUpAppIcon,
|
widgetOptions = widgetOptions,
|
||||||
onAppChanged = { viewModel.setSwipeUpApp(it) },
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val doubleTap by viewModel.doubleTap.collectAsStateWithLifecycle(null)
|
val doubleTap by viewModel.doubleTap.collectAsStateWithLifecycle(null)
|
||||||
val doubleTapApp by viewModel.doubleTapApp.collectAsState(null)
|
|
||||||
val doubleTapAppIcon by remember(doubleTapApp?.key) {
|
|
||||||
viewModel.getIcon(doubleTapApp, appIconSize.toInt())
|
|
||||||
}.collectAsState(null)
|
|
||||||
GuardedPreference(
|
GuardedPreference(
|
||||||
locked = hasPermission == false && requiresAccessibilityService(doubleTap),
|
locked = hasPermission == false && requiresAccessibilityService(doubleTap),
|
||||||
description = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
description = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
||||||
@@ -191,19 +157,14 @@ fun GestureSettingsScreen() {
|
|||||||
title = stringResource(R.string.preference_gesture_double_tap),
|
title = stringResource(R.string.preference_gesture_double_tap),
|
||||||
icon = R.drawable.adjust_24px,
|
icon = R.drawable.adjust_24px,
|
||||||
value = doubleTap,
|
value = doubleTap,
|
||||||
onValueChanged = { viewModel.setDoubleTap(it) },
|
onValueChanged = viewModel::setDoubleTap,
|
||||||
options = options,
|
options = options,
|
||||||
app = doubleTapApp,
|
shortcutOptions = shortcutOptions,
|
||||||
appIcon = doubleTapAppIcon,
|
widgetOptions = widgetOptions,
|
||||||
onAppChanged = { viewModel.setDoubleTapApp(it) },
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val longPress by viewModel.longPress.collectAsStateWithLifecycle(null)
|
val longPress by viewModel.longPress.collectAsStateWithLifecycle(null)
|
||||||
val longPressApp by viewModel.longPressApp.collectAsState(null)
|
|
||||||
val longPressAppIcon by remember(longPressApp?.key) {
|
|
||||||
viewModel.getIcon(longPressApp, appIconSize.toInt())
|
|
||||||
}.collectAsState(null)
|
|
||||||
GuardedPreference(
|
GuardedPreference(
|
||||||
locked = hasPermission == false && requiresAccessibilityService(longPress),
|
locked = hasPermission == false && requiresAccessibilityService(longPress),
|
||||||
description = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
description = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
||||||
@@ -213,18 +174,13 @@ fun GestureSettingsScreen() {
|
|||||||
title = stringResource(R.string.preference_gesture_long_press),
|
title = stringResource(R.string.preference_gesture_long_press),
|
||||||
icon = R.drawable.circle_24px,
|
icon = R.drawable.circle_24px,
|
||||||
value = longPress,
|
value = longPress,
|
||||||
onValueChanged = { viewModel.setLongPress(it) },
|
onValueChanged = viewModel::setLongPress,
|
||||||
options = options,
|
options = options,
|
||||||
app = longPressApp,
|
shortcutOptions = shortcutOptions,
|
||||||
appIcon = longPressAppIcon,
|
widgetOptions = widgetOptions,
|
||||||
onAppChanged = { viewModel.setLongPressApp(it) },
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val homeButton by viewModel.homeButton.collectAsStateWithLifecycle(null)
|
val homeButton by viewModel.homeButton.collectAsStateWithLifecycle(null)
|
||||||
val homeButtonApp by viewModel.homeButtonApp.collectAsState(null)
|
|
||||||
val homeButtonAppIcon by remember(homeButtonApp?.key) {
|
|
||||||
viewModel.getIcon(homeButtonApp, appIconSize.toInt())
|
|
||||||
}.collectAsState(null)
|
|
||||||
GuardedPreference(
|
GuardedPreference(
|
||||||
locked = hasPermission == false && requiresAccessibilityService(homeButton),
|
locked = hasPermission == false && requiresAccessibilityService(homeButton),
|
||||||
description = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
description = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
||||||
@@ -234,11 +190,10 @@ fun GestureSettingsScreen() {
|
|||||||
title = stringResource(R.string.preference_gesture_home_button),
|
title = stringResource(R.string.preference_gesture_home_button),
|
||||||
icon = R.drawable.home_24px,
|
icon = R.drawable.home_24px,
|
||||||
value = homeButton,
|
value = homeButton,
|
||||||
onValueChanged = { viewModel.setHomeButton(it) },
|
onValueChanged = viewModel::setHomeButton,
|
||||||
options = options,
|
options = options,
|
||||||
app = homeButtonApp,
|
shortcutOptions = shortcutOptions,
|
||||||
appIcon = homeButtonAppIcon,
|
widgetOptions = widgetOptions,
|
||||||
onAppChanged = { viewModel.setHomeButtonApp(it) },
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -257,75 +212,88 @@ fun requiresAccessibilityService(action: GestureAction?): Boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
//@Composable
|
||||||
fun GesturePreference(
|
//fun GesturePreference(
|
||||||
title: String,
|
// title: String,
|
||||||
@DrawableRes icon: Int,
|
// @DrawableRes icon: Int,
|
||||||
value: GestureAction?,
|
// value: GestureAction?,
|
||||||
onValueChanged: (GestureAction) -> Unit,
|
// onValueChanged: (GestureAction) -> Unit,
|
||||||
options: List<Pair<String, GestureAction>>,
|
// options: List<Pair<String, GestureAction>>,
|
||||||
app: SavableSearchable?,
|
// app: SavableSearchable?,
|
||||||
appIcon: LauncherIcon?,
|
// appIcon: LauncherIcon?,
|
||||||
onAppChanged: (SavableSearchable?) -> Unit,
|
// onAppChanged: (SavableSearchable?) -> Unit,
|
||||||
) {
|
//) {
|
||||||
var showAppPicker by remember { mutableStateOf(false) }
|
// var showAppPicker by remember { mutableStateOf(false) }
|
||||||
Row(
|
// Row(
|
||||||
verticalAlignment = (Alignment.CenterVertically),
|
// verticalAlignment = (Alignment.CenterVertically),
|
||||||
) {
|
// ) {
|
||||||
Box(
|
// Box(
|
||||||
modifier = Modifier.weight(1f),
|
// modifier = Modifier.weight(1f),
|
||||||
) {
|
// ) {
|
||||||
ListPreference(
|
// ListPreference(
|
||||||
title = title,
|
// title = title,
|
||||||
icon = icon,
|
// icon = icon,
|
||||||
items = options,
|
// items = options,
|
||||||
value = value,
|
// value = value,
|
||||||
summary = options.find { option ->
|
// summary = options.find { option ->
|
||||||
when {
|
// when {
|
||||||
value is GestureAction.Widgets && option.second is GestureAction.Widgets -> {
|
// value is GestureAction.Widgets && option.second is GestureAction.Widgets -> {
|
||||||
val valueTarget = value.target
|
// val valueTarget = value.target
|
||||||
val optionTarget = (option.second as GestureAction.Widgets).target
|
// val optionTarget = (option.second as GestureAction.Widgets).target
|
||||||
valueTarget == optionTarget
|
// valueTarget == optionTarget
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
else -> value?.javaClass == option.second.javaClass
|
// else -> value?.javaClass == option.second.javaClass
|
||||||
}
|
// }
|
||||||
}?.first ?: stringResource(R.string.gesture_action_none),
|
// }?.first ?: stringResource(R.string.gesture_action_none),
|
||||||
onValueChanged = { if (it != null) onValueChanged(it) },
|
// onValueChanged = { if (it != null) onValueChanged(it) },
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
if (value is GestureAction.Launch) {
|
// if (value is GestureAction.Launch) {
|
||||||
Box(
|
// Box(
|
||||||
modifier = Modifier
|
// modifier = Modifier
|
||||||
.height(36.dp)
|
// .height(36.dp)
|
||||||
.width(1.dp)
|
// .width(1.dp)
|
||||||
.alpha(0.38f)
|
// .background(MaterialTheme.colorScheme.outlineVariant),
|
||||||
.background(LocalContentColor.current),
|
// )
|
||||||
)
|
// Box(
|
||||||
Box(
|
// modifier = Modifier
|
||||||
modifier = Modifier
|
// .clickable { showAppPicker = true }
|
||||||
.clickable { showAppPicker = true }
|
// .padding(12.dp),
|
||||||
.padding(12.dp),
|
// ) {
|
||||||
) {
|
// ShapedLauncherIcon(size = 32.dp, icon = { appIcon })
|
||||||
ShapedLauncherIcon(size = 32.dp, icon = { appIcon })
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// if (value is GestureAction.Widgets) {
|
||||||
}
|
// Box(
|
||||||
|
// modifier = Modifier
|
||||||
if (value is GestureAction.Launch) {
|
// .height(36.dp)
|
||||||
SearchablePicker(
|
// .width(1.dp)
|
||||||
expanded = (showAppPicker || app == null),
|
// .background(MaterialTheme.colorScheme.outlineVariant),
|
||||||
onDismissRequest = {
|
// )
|
||||||
showAppPicker = false
|
// IconButton(
|
||||||
if (app == null) onValueChanged(GestureAction.NoAction)
|
// modifier = Modifier.padding(4.dp),
|
||||||
},
|
// onClick = {}
|
||||||
value = app,
|
// ) {
|
||||||
onValueChanged = {
|
// Icon(painterResource(R.drawable.tune_24px), "")
|
||||||
showAppPicker = false
|
// }
|
||||||
onAppChanged(it)
|
// }
|
||||||
},
|
// }
|
||||||
)
|
//
|
||||||
}
|
// if (value is GestureAction.Launch) {
|
||||||
|
// SearchablePicker(
|
||||||
}
|
// expanded = (showAppPicker || app == null),
|
||||||
|
// onDismissRequest = {
|
||||||
|
// showAppPicker = false
|
||||||
|
// if (app == null) onValueChanged(GestureAction.NoAction)
|
||||||
|
// },
|
||||||
|
// value = app,
|
||||||
|
// onValueChanged = {
|
||||||
|
// showAppPicker = false
|
||||||
|
// onAppChanged(it)
|
||||||
|
// },
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
|||||||
@@ -7,39 +7,36 @@ import de.mm20.launcher2.icons.IconService
|
|||||||
import de.mm20.launcher2.icons.LauncherIcon
|
import de.mm20.launcher2.icons.LauncherIcon
|
||||||
import de.mm20.launcher2.permissions.PermissionGroup
|
import de.mm20.launcher2.permissions.PermissionGroup
|
||||||
import de.mm20.launcher2.permissions.PermissionsManager
|
import de.mm20.launcher2.permissions.PermissionsManager
|
||||||
import de.mm20.launcher2.preferences.BaseLayout
|
|
||||||
import de.mm20.launcher2.preferences.GestureAction
|
import de.mm20.launcher2.preferences.GestureAction
|
||||||
|
import de.mm20.launcher2.preferences.WidgetScreenTarget
|
||||||
import de.mm20.launcher2.preferences.ui.GestureSettings
|
import de.mm20.launcher2.preferences.ui.GestureSettings
|
||||||
import de.mm20.launcher2.preferences.ui.UiSettings
|
import de.mm20.launcher2.preferences.ui.UiSettings
|
||||||
import de.mm20.launcher2.search.SavableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.searchable.SavableSearchableRepository
|
import de.mm20.launcher2.searchable.SavableSearchableRepository
|
||||||
|
import de.mm20.launcher2.widgets.Widget
|
||||||
|
import de.mm20.launcher2.widgets.WidgetRepository
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.emptyFlow
|
import kotlinx.coroutines.flow.emptyFlow
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.flatMapLatest
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
import kotlinx.coroutines.flow.flowOf
|
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
|
|
||||||
class GestureSettingsScreenVM : ViewModel(), KoinComponent {
|
internal class GestureSettingsScreenVM : ViewModel(), KoinComponent {
|
||||||
private val gestureSettings: GestureSettings by inject()
|
private val gestureSettings: GestureSettings by inject()
|
||||||
private val uiSettings: UiSettings by inject()
|
private val uiSettings: UiSettings by inject()
|
||||||
private val permissionsManager: PermissionsManager by inject()
|
private val permissionsManager: PermissionsManager by inject()
|
||||||
private val searchableRepository: SavableSearchableRepository by inject()
|
private val searchableRepository: SavableSearchableRepository by inject()
|
||||||
private val iconService: IconService by inject()
|
private val iconService: IconService by inject()
|
||||||
|
private val widgetRepository: WidgetRepository by inject()
|
||||||
|
|
||||||
val hasPermission = permissionsManager.hasPermission(PermissionGroup.Accessibility)
|
val hasPermission = permissionsManager.hasPermission(PermissionGroup.Accessibility)
|
||||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||||
|
|
||||||
val allowWidgetGesture = uiSettings.homeScreenWidgets.map { it == false }
|
|
||||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
|
||||||
|
|
||||||
val widgetScreenCount = uiSettings.widgetScreenCount
|
|
||||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 1)
|
|
||||||
|
|
||||||
val swipeDown = gestureSettings.swipeDown
|
val swipeDown = gestureSettings.swipeDown
|
||||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||||
val swipeLeft = gestureSettings.swipeLeft
|
val swipeLeft = gestureSettings.swipeLeft
|
||||||
@@ -55,131 +52,101 @@ class GestureSettingsScreenVM : ViewModel(), KoinComponent {
|
|||||||
val homeButton = gestureSettings.homeButton
|
val homeButton = gestureSettings.homeButton
|
||||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)
|
||||||
|
|
||||||
fun setSwipeDown(action: GestureAction) {
|
fun setSwipeDown(action: GestureAction, searchable: SavableSearchable?) {
|
||||||
|
saveShortcut(searchable)
|
||||||
gestureSettings.setSwipeDown(action)
|
gestureSettings.setSwipeDown(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSwipeLeft(action: GestureAction) {
|
fun setSwipeLeft(action: GestureAction, searchable: SavableSearchable?) {
|
||||||
|
saveShortcut(searchable)
|
||||||
gestureSettings.setSwipeLeft(action)
|
gestureSettings.setSwipeLeft(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSwipeRight(action: GestureAction) {
|
fun setSwipeRight(action: GestureAction, searchable: SavableSearchable?) {
|
||||||
|
saveShortcut(searchable)
|
||||||
gestureSettings.setSwipeRight(action)
|
gestureSettings.setSwipeRight(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSwipeUp(action: GestureAction) {
|
fun setSwipeUp(action: GestureAction, searchable: SavableSearchable?) {
|
||||||
|
saveShortcut(searchable)
|
||||||
gestureSettings.setSwipeUp(action)
|
gestureSettings.setSwipeUp(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setDoubleTap(action: GestureAction) {
|
fun setDoubleTap(action: GestureAction, searchable: SavableSearchable?) {
|
||||||
|
saveShortcut(searchable)
|
||||||
gestureSettings.setDoubleTap(action)
|
gestureSettings.setDoubleTap(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setLongPress(action: GestureAction) {
|
fun setLongPress(action: GestureAction, searchable: SavableSearchable?) {
|
||||||
|
saveShortcut(searchable)
|
||||||
gestureSettings.setLongPress(action)
|
gestureSettings.setLongPress(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setHomeButton(action: GestureAction) {
|
fun setHomeButton(action: GestureAction, searchable: SavableSearchable?) {
|
||||||
|
saveShortcut(searchable)
|
||||||
gestureSettings.setHomeButton(action)
|
gestureSettings.setHomeButton(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
val swipeLeftApp: Flow<SavableSearchable?> = swipeLeft
|
private fun saveShortcut(searchable: SavableSearchable?) {
|
||||||
.flatMapLatest {
|
searchable?.let { searchableRepository.insert(it) }
|
||||||
if (it !is GestureAction.Launch || it.key == null) flowOf(null)
|
|
||||||
else searchableRepository.getByKeys(listOf(it.key!!)).map {
|
|
||||||
it.firstOrNull()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null)
|
|
||||||
|
|
||||||
fun setSwipeLeftApp(searchable: SavableSearchable?) {
|
|
||||||
searchable?.let { searchableRepository.insert(it) } ?: return
|
|
||||||
setSwipeLeft(GestureAction.Launch(searchable.key))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val swipeRightApp: Flow<SavableSearchable?> = swipeRight
|
val shortcutOptions: Flow<List<SavableSearchable>> = gestureSettings.flatMapLatest {
|
||||||
.flatMapLatest {
|
val keys = listOfNotNull(
|
||||||
if (it !is GestureAction.Launch || it.key == null) flowOf(null)
|
(it.swipeUp as? GestureAction.Launch)?.key,
|
||||||
else searchableRepository.getByKeys(listOf(it.key!!)).map {
|
(it.swipeLeft as? GestureAction.Launch)?.key,
|
||||||
it.firstOrNull()
|
(it.swipeRight as? GestureAction.Launch)?.key,
|
||||||
}
|
(it.swipeDown as? GestureAction.Launch)?.key,
|
||||||
}
|
(it.doubleTap as? GestureAction.Launch)?.key,
|
||||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null)
|
(it.homeButton as? GestureAction.Launch)?.key,
|
||||||
|
(it.longPress as? GestureAction.Launch)?.key,
|
||||||
fun setSwipeRightApp(searchable: SavableSearchable?) {
|
)
|
||||||
searchable?.let { searchableRepository.insert(it) } ?: return
|
searchableRepository.getByKeys(keys)
|
||||||
setSwipeRight(GestureAction.Launch(searchable.key))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val swipeDownApp: Flow<SavableSearchable?> = swipeDown
|
val widgetOptions: Flow<List<WidgetPageOption>> =
|
||||||
.flatMapLatest {
|
combine(uiSettings.homeScreenWidgets, gestureSettings) { home, gestures ->
|
||||||
if (it !is GestureAction.Launch || it.key == null) flowOf(null)
|
val options = mutableListOf<WidgetPageOption>()
|
||||||
else searchableRepository.getByKeys(listOf(it.key!!)).map {
|
val usedTargets = setOfNotNull(
|
||||||
it.firstOrNull()
|
(gestures.swipeUp as? GestureAction.Widgets)?.target,
|
||||||
|
(gestures.swipeRight as? GestureAction.Widgets)?.target,
|
||||||
|
(gestures.swipeLeft as? GestureAction.Widgets)?.target,
|
||||||
|
(gestures.swipeDown as? GestureAction.Widgets)?.target,
|
||||||
|
(gestures.doubleTap as? GestureAction.Widgets)?.target,
|
||||||
|
(gestures.longPress as? GestureAction.Widgets)?.target,
|
||||||
|
(gestures.homeButton as? GestureAction.Widgets)?.target,
|
||||||
|
)
|
||||||
|
// We want to provide at most one "new screen" option
|
||||||
|
var newScreenOptionAdded = false
|
||||||
|
for (target in WidgetScreenTarget.entries) {
|
||||||
|
// Never offer the default screen as an option when widgets on home is enabled
|
||||||
|
if (home && target == WidgetScreenTarget.Default) continue
|
||||||
|
val widgets = widgetRepository.get(
|
||||||
|
parent = target.id,
|
||||||
|
).first()
|
||||||
|
|
||||||
|
if (widgets.isNotEmpty() || usedTargets.contains(target)) {
|
||||||
|
options += WidgetPageOption(
|
||||||
|
widgets = widgets,
|
||||||
|
id = target,
|
||||||
|
isNewPage = false,
|
||||||
|
)
|
||||||
|
} else if (!newScreenOptionAdded) {
|
||||||
|
options += WidgetPageOption(
|
||||||
|
id = target,
|
||||||
|
widgets = emptyList(),
|
||||||
|
isNewPage = true,
|
||||||
|
)
|
||||||
|
newScreenOptionAdded = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
options.sortedWith { a, b ->
|
||||||
|
if (a.isNewPage) return@sortedWith 1
|
||||||
|
if (b.isNewPage) return@sortedWith -1
|
||||||
|
a.id.compareTo(b.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null)
|
|
||||||
|
|
||||||
fun setSwipeDownApp(searchable: SavableSearchable?) {
|
|
||||||
searchable?.let { searchableRepository.insert(it) } ?: return
|
|
||||||
setSwipeDown(GestureAction.Launch(searchable.key))
|
|
||||||
}
|
|
||||||
|
|
||||||
val swipeUpApp: Flow<SavableSearchable?> = swipeUp
|
|
||||||
.flatMapLatest {
|
|
||||||
if (it !is GestureAction.Launch || it.key == null) flowOf(null)
|
|
||||||
else searchableRepository.getByKeys(listOf(it.key!!)).map {
|
|
||||||
it.firstOrNull()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null)
|
|
||||||
|
|
||||||
fun setSwipeUpApp(searchable: SavableSearchable?) {
|
|
||||||
searchable?.let { searchableRepository.insert(it) } ?: return
|
|
||||||
setSwipeUp(GestureAction.Launch(searchable.key))
|
|
||||||
}
|
|
||||||
|
|
||||||
val longPressApp: Flow<SavableSearchable?> = longPress
|
|
||||||
.flatMapLatest {
|
|
||||||
if (it !is GestureAction.Launch || it.key == null) flowOf(null)
|
|
||||||
else searchableRepository.getByKeys(listOf(it.key!!)).map {
|
|
||||||
it.firstOrNull()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null)
|
|
||||||
|
|
||||||
fun setLongPressApp(searchable: SavableSearchable?) {
|
|
||||||
searchable?.let { searchableRepository.insert(it) } ?: return
|
|
||||||
setLongPress(GestureAction.Launch(searchable.key))
|
|
||||||
}
|
|
||||||
|
|
||||||
val doubleTapApp: Flow<SavableSearchable?> = doubleTap
|
|
||||||
.flatMapLatest {
|
|
||||||
if (it !is GestureAction.Launch || it.key == null) flowOf(null)
|
|
||||||
else searchableRepository.getByKeys(listOf(it.key!!)).map {
|
|
||||||
it.firstOrNull()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null)
|
|
||||||
|
|
||||||
fun setDoubleTapApp(searchable: SavableSearchable?) {
|
|
||||||
searchable?.let { searchableRepository.insert(it) } ?: return
|
|
||||||
setDoubleTap(GestureAction.Launch(searchable.key))
|
|
||||||
}
|
|
||||||
|
|
||||||
val homeButtonApp: Flow<SavableSearchable?> = homeButton
|
|
||||||
.flatMapLatest {
|
|
||||||
if (it !is GestureAction.Launch || it.key == null) flowOf(null)
|
|
||||||
else searchableRepository.getByKeys(listOf(it.key!!)).map {
|
|
||||||
it.firstOrNull()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(stopTimeoutMillis = 10000), null)
|
|
||||||
|
|
||||||
fun setHomeButtonApp(searchable: SavableSearchable?) {
|
|
||||||
searchable?.let { searchableRepository.insert(it) } ?: return
|
|
||||||
setHomeButton(GestureAction.Launch(searchable.key))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun requestPermission(context: AppCompatActivity) {
|
fun requestPermission(context: AppCompatActivity) {
|
||||||
@@ -191,3 +158,9 @@ class GestureSettingsScreenVM : ViewModel(), KoinComponent {
|
|||||||
return iconService.getIcon(searchable, size)
|
return iconService.getIcon(searchable, size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal data class WidgetPageOption(
|
||||||
|
val id: WidgetScreenTarget,
|
||||||
|
val widgets: List<Widget>,
|
||||||
|
val isNewPage: Boolean,
|
||||||
|
)
|
||||||
@@ -135,28 +135,6 @@ fun HomescreenSettingsScreen() {
|
|||||||
onValueChanged = {
|
onValueChanged = {
|
||||||
viewModel.setWidgetsOnHomeScreen(it)
|
viewModel.setWidgetsOnHomeScreen(it)
|
||||||
})
|
})
|
||||||
AnimatedVisibility(widgetsOnHomeScreen == false) {
|
|
||||||
val widgetScreenCount by viewModel.widgetScreenCount.collectAsStateWithLifecycle(1)
|
|
||||||
|
|
||||||
Column {
|
|
||||||
SliderPreference(
|
|
||||||
title = stringResource(R.string.preference_widget_screen_count),
|
|
||||||
value = widgetScreenCount,
|
|
||||||
min = 1,
|
|
||||||
max = 4,
|
|
||||||
onValueChanged = {
|
|
||||||
viewModel.setWidgetScreenCount(it)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.preference_widget_screen_count_info),
|
|
||||||
style = MaterialTheme.typography.bodySmall,
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SwitchPreference(
|
SwitchPreference(
|
||||||
title = stringResource(id = R.string.preference_edit_button),
|
title = stringResource(id = R.string.preference_edit_button),
|
||||||
summary = stringResource(id = R.string.preference_widgets_edit_button_summary),
|
summary = stringResource(id = R.string.preference_widgets_edit_button_summary),
|
||||||
|
|||||||
@@ -166,13 +166,6 @@ class HomescreenSettingsScreenVM(
|
|||||||
uiSettings.setHomeScreenWidgets(widgetsOnHomeScreen)
|
uiSettings.setHomeScreenWidgets(widgetsOnHomeScreen)
|
||||||
}
|
}
|
||||||
|
|
||||||
val widgetScreenCount = uiSettings.widgetScreenCount
|
|
||||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), 1)
|
|
||||||
|
|
||||||
fun setWidgetScreenCount(count: Int) {
|
|
||||||
uiSettings.setWidgetScreenCount(count)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object : KoinComponent {
|
companion object : KoinComponent {
|
||||||
val Factory = viewModelFactory {
|
val Factory = viewModelFactory {
|
||||||
initializer {
|
initializer {
|
||||||
|
|||||||
10
core/base/src/main/res/drawable/action_key_24px.xml
Normal file
10
core/base/src/main/res/drawable/action_key_24px.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M660,820Q594,820 547,773Q500,726 500,660Q500,594 547,547Q594,500 660,500Q726,500 773,547Q820,594 820,660Q820,683 814,703.5Q808,724 797,742L892,836Q898,841 901,848.5Q904,856 904,864Q904,872 901,879Q898,886 892,892Q887,897 879.5,900Q872,903 864,903Q856,903 849,900Q842,897 836,892L741,798Q723,809 702.5,814.5Q682,820 660,820ZM220,820Q154,820 107,773Q60,726 60,660Q60,594 107,547Q154,500 220,500Q286,500 333,547Q380,594 380,660Q380,726 333,773Q286,820 220,820ZM220,740Q253,740 276.5,716.5Q300,693 300,660Q300,627 276.5,603.5Q253,580 220,580Q187,580 163.5,603.5Q140,627 140,660Q140,693 163.5,716.5Q187,740 220,740ZM660,740Q693,740 716.5,716.5Q740,693 740,660Q740,627 716.5,603.5Q693,580 660,580Q627,580 603.5,603.5Q580,627 580,660Q580,693 603.5,716.5Q627,740 660,740ZM220,380Q154,380 107,333Q60,286 60,220Q60,154 107,107Q154,60 220,60Q286,60 333,107Q380,154 380,220Q380,286 333,333Q286,380 220,380ZM660,380Q594,380 547,333Q500,286 500,220Q500,154 547,107Q594,60 660,60Q726,60 773,107Q820,154 820,220Q820,286 773,333Q726,380 660,380ZM220,300Q253,300 276.5,276.5Q300,253 300,220Q300,187 276.5,163.5Q253,140 220,140Q187,140 163.5,163.5Q140,187 140,220Q140,253 163.5,276.5Q187,300 220,300ZM660,300Q693,300 716.5,276.5Q740,253 740,220Q740,187 716.5,163.5Q693,140 660,140Q627,140 603.5,163.5Q580,187 580,220Q580,253 603.5,276.5Q627,300 660,300ZM220,660Q220,660 220,660Q220,660 220,660Q220,660 220,660Q220,660 220,660Q220,660 220,660Q220,660 220,660Q220,660 220,660Q220,660 220,660ZM220,220Q220,220 220,220Q220,220 220,220Q220,220 220,220Q220,220 220,220Q220,220 220,220Q220,220 220,220Q220,220 220,220Q220,220 220,220ZM660,220Q660,220 660,220Q660,220 660,220Q660,220 660,220Q660,220 660,220Q660,220 660,220Q660,220 660,220Q660,220 660,220Q660,220 660,220Z"/>
|
||||||
|
</vector>
|
||||||
10
core/base/src/main/res/drawable/amp_stories_24px.xml
Normal file
10
core/base/src/main/res/drawable/amp_stories_24px.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M320,800Q303,800 291.5,788.5Q280,777 280,760L280,200Q280,183 291.5,171.5Q303,160 320,160L640,160Q657,160 668.5,171.5Q680,183 680,200L680,760Q680,777 668.5,788.5Q657,800 640,800L320,800ZM120,680L120,279Q120,262 131.5,251Q143,240 160,240Q177,240 188.5,251.5Q200,263 200,280L200,681Q200,698 188.5,709Q177,720 160,720Q143,720 131.5,708.5Q120,697 120,680ZM760,680L760,279Q760,262 771.5,251Q783,240 800,240Q817,240 828.5,251.5Q840,263 840,280L840,681Q840,698 828.5,709Q817,720 800,720Q783,720 771.5,708.5Q760,697 760,680ZM360,720L600,720L600,240L360,240L360,720ZM360,720L360,240L360,240L360,720Z"/>
|
||||||
|
</vector>
|
||||||
10
core/base/src/main/res/drawable/widgets_add_24px.xml
Normal file
10
core/base/src/main/res/drawable/widgets_add_24px.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M638,492L468,322Q462,316 459.5,309Q457,302 457,294Q457,286 459.5,279Q462,272 468,266L638,96Q644,90 651,87.5Q658,85 666,85Q674,85 681,87.5Q688,90 694,96L864,266Q870,272 872.5,279Q875,286 875,294Q875,302 872.5,309Q870,316 864,322L694,492Q688,498 681,500.5Q674,503 666,503Q658,503 651,500.5Q644,498 638,492ZM120,400L120,160Q120,143 131.5,131.5Q143,120 160,120L400,120Q417,120 428.5,131.5Q440,143 440,160L440,400Q440,417 428.5,428.5Q417,440 400,440L160,440Q143,440 131.5,428.5Q120,417 120,400ZM120,800L120,560Q120,543 131.5,531.5Q143,520 160,520L400,520Q417,520 428.5,531.5Q440,543 440,560L440,800Q440,817 428.5,828.5Q417,840 400,840L160,840Q143,840 131.5,828.5Q120,817 120,800ZM200,360L360,360L360,200L200,200L200,360ZM667,408L780,295L667,182L554,295L667,408ZM200,760L360,760L360,600L200,600L200,760ZM360,360L360,360L360,360L360,360ZM554,295L554,295L554,295L554,295ZM360,600L360,600L360,600L360,600ZM600,600L600,600L600,600L600,600Z M680,840Q663,840 651.5,828.5Q640,817 640,800L640,720L559,720Q542,720 531,708.5Q520,697 520,680Q520,663 531.5,651.5Q543,640 560,640L640,640L640,559Q640,542 651.5,531Q663,520 680,520Q697,520 708.5,531.5Q720,543 720,560L720,640L801,640Q818,640 829,651.5Q840,663 840,680Q840,697 828.5,708.5Q817,720 800,720L720,720L720,801Q720,818 708.5,829Q697,840 680,840Z"/>
|
||||||
|
</vector>
|
||||||
@@ -812,15 +812,21 @@
|
|||||||
<string name="preference_gesture_long_press">Long press</string>
|
<string name="preference_gesture_long_press">Long press</string>
|
||||||
<string name="preference_gesture_home_button">Home button/gesture</string>
|
<string name="preference_gesture_home_button">Home button/gesture</string>
|
||||||
<string name="gesture_action_none">Do nothing</string>
|
<string name="gesture_action_none">Do nothing</string>
|
||||||
<string name="gesture_action_open_search">Open search</string>
|
<string name="gesture_action_category_launcher">Launcher pages</string>
|
||||||
|
<string name="gesture_action_category_system">System actions</string>
|
||||||
|
<string name="gesture_action_category_apps">Apps and shortcuts</string>
|
||||||
|
<string name="gesture_action_open_search">Search / app drawer</string>
|
||||||
<string name="gesture_action_widgets">Widgets</string>
|
<string name="gesture_action_widgets">Widgets</string>
|
||||||
<string name="gesture_action_widgets_indexed">Widgets %1$d</string>
|
<string name="gesture_action_widgets_new">New widgets page</string>
|
||||||
|
<string name="gesture_action_widgets_empty">Empty</string>
|
||||||
|
<string name="gesture_action_widgets_indexed">Widgets (%1$d)</string>
|
||||||
<string name="gesture_action_launch_app">Launch app</string>
|
<string name="gesture_action_launch_app">Launch app</string>
|
||||||
<string name="gesture_action_notifications">Open notification drawer</string>
|
<string name="gesture_action_launch_select_app">Select app or shortcut</string>
|
||||||
|
<string name="gesture_action_notifications">Notifications</string>
|
||||||
<string name="gesture_action_lock_screen">Turn off screen</string>
|
<string name="gesture_action_lock_screen">Turn off screen</string>
|
||||||
<string name="gesture_action_quick_settings">Open quick settings</string>
|
<string name="gesture_action_quick_settings">Quick settings</string>
|
||||||
<string name="gesture_action_power_menu">Show power menu</string>
|
<string name="gesture_action_power_menu">Power menu</string>
|
||||||
<string name="gesture_action_recents">Open recent apps</string>
|
<string name="gesture_action_recents">Recent apps</string>
|
||||||
<string name="gesture_action_feed">Feed</string>
|
<string name="gesture_action_feed">Feed</string>
|
||||||
<string name="gesture_failed_message">You have performed a \"%1$s\" gesture. This gesture is currently set to trigger a \"%2$s\" action. However, the action could not be performed for the following reason:</string>
|
<string name="gesture_failed_message">You have performed a \"%1$s\" gesture. This gesture is currently set to trigger a \"%2$s\" action. However, the action could not be performed for the following reason:</string>
|
||||||
<string name="missing_permission_accessibility_gesture_failed">The launcher\'s accessibility service needs to be enabled to perform this action.</string>
|
<string name="missing_permission_accessibility_gesture_failed">The launcher\'s accessibility service needs to be enabled to perform this action.</string>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ enum class WidgetScreenTarget {
|
|||||||
* Returns the UUID used as parent identifier for widget repository operations.
|
* Returns the UUID used as parent identifier for widget repository operations.
|
||||||
* These are deterministic UUIDs generated using UUID v5 (name-based).
|
* These are deterministic UUIDs generated using UUID v5 (name-based).
|
||||||
*/
|
*/
|
||||||
val scopeId: UUID
|
val id: UUID
|
||||||
get() = when (this) {
|
get() = when (this) {
|
||||||
Widgets1 -> UUID.fromString("00000000-0000-0000-0000-000000000001")
|
Widgets1 -> UUID.fromString("00000000-0000-0000-0000-000000000001")
|
||||||
Widgets2 -> UUID.fromString("00000000-0000-0000-0000-000000000002")
|
Widgets2 -> UUID.fromString("00000000-0000-0000-0000-000000000002")
|
||||||
@@ -31,15 +31,5 @@ enum class WidgetScreenTarget {
|
|||||||
* The default widget screen target
|
* The default widget screen target
|
||||||
*/
|
*/
|
||||||
val Default = Widgets1
|
val Default = Widgets1
|
||||||
|
|
||||||
/**
|
|
||||||
* Get list of available widget screen targets based on configured count.
|
|
||||||
* @param count Number of widget screens to make available (1-4)
|
|
||||||
* @return List of WidgetScreenTarget enum values
|
|
||||||
*/
|
|
||||||
fun getAvailableTargets(count: Int): List<WidgetScreenTarget> {
|
|
||||||
val all = listOf(Widgets1, Widgets2, Widgets3, Widgets4)
|
|
||||||
return all.take(count.coerceIn(1, 4))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -379,44 +379,4 @@ class UiSettings internal constructor(
|
|||||||
it.copy(widgetsEditButton = editButton)
|
it.copy(widgetsEditButton = editButton)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val widgetScreenCount
|
|
||||||
get() = launcherDataStore.data.map {
|
|
||||||
it.widgetScreenCount.coerceIn(1, 4)
|
|
||||||
}.distinctUntilChanged()
|
|
||||||
|
|
||||||
fun setWidgetScreenCount(count: Int) {
|
|
||||||
val validCount = count.coerceIn(1, 4)
|
|
||||||
launcherDataStore.update { data ->
|
|
||||||
var updatedData = data.copy(widgetScreenCount = validCount)
|
|
||||||
|
|
||||||
// Auto-reset gestures that point to invalid widget screens
|
|
||||||
val resetGesture: (GestureAction) -> GestureAction = { action ->
|
|
||||||
if (action is GestureAction.Widgets) {
|
|
||||||
val targetIndex = when (action.target) {
|
|
||||||
WidgetScreenTarget.Widgets1 -> 1
|
|
||||||
WidgetScreenTarget.Widgets2 -> 2
|
|
||||||
WidgetScreenTarget.Widgets3 -> 3
|
|
||||||
WidgetScreenTarget.Widgets4 -> 4
|
|
||||||
else -> 0
|
|
||||||
}
|
|
||||||
if (targetIndex > validCount) GestureAction.NoAction else action
|
|
||||||
} else {
|
|
||||||
action
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updatedData = updatedData.copy(
|
|
||||||
gesturesSwipeUp = resetGesture(updatedData.gesturesSwipeUp),
|
|
||||||
gesturesSwipeDown = resetGesture(updatedData.gesturesSwipeDown),
|
|
||||||
gesturesSwipeLeft = resetGesture(updatedData.gesturesSwipeLeft),
|
|
||||||
gesturesSwipeRight = resetGesture(updatedData.gesturesSwipeRight),
|
|
||||||
gesturesDoubleTap = resetGesture(updatedData.gesturesDoubleTap),
|
|
||||||
gesturesLongPress = resetGesture(updatedData.gesturesLongPress),
|
|
||||||
gesturesHomeButton = resetGesture(updatedData.gesturesHomeButton),
|
|
||||||
)
|
|
||||||
|
|
||||||
updatedData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val defaultParentId = WidgetScreenTarget.Default.scopeId
|
val defaultParentId = WidgetScreenTarget.Default.id
|
||||||
db.execSQL(
|
db.execSQL(
|
||||||
"INSERT INTO Widget (`type`, `position`, `id`, `parentId`) VALUES " +
|
"INSERT INTO Widget (`type`, `position`, `id`, `parentId`) VALUES " +
|
||||||
"('weather', 0, ?, ?)," +
|
"('weather', 0, ?, ?)," +
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class Migration_30_31 : Migration(30, 31) {
|
|||||||
SET parentId = ?
|
SET parentId = ?
|
||||||
WHERE parentId IS NULL
|
WHERE parentId IS NULL
|
||||||
""".trimIndent(),
|
""".trimIndent(),
|
||||||
arrayOf(WidgetScreenTarget.Default.scopeId.toBytes()),
|
arrayOf(WidgetScreenTarget.Default.id.toBytes()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user