Gestures
This commit is contained in:
@@ -143,4 +143,5 @@ dependencies {
|
||||
implementation(project(":services:accounts"))
|
||||
implementation(project(":services:backup"))
|
||||
implementation(project(":data:search-actions"))
|
||||
implementation(project(":services:global-actions"))
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package de.mm20.launcher2.ui.gestures
|
||||
|
||||
enum class Gesture {
|
||||
DoubleTap,
|
||||
LongPress,
|
||||
SwipeDown,
|
||||
SwipeLeft,
|
||||
SwipeRight,
|
||||
}
|
||||
@@ -1,12 +1,23 @@
|
||||
package de.mm20.launcher2.ui.launcher
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.asLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.globalactions.GlobalActionsService
|
||||
import de.mm20.launcher2.permissions.PermissionGroup
|
||||
import de.mm20.launcher2.permissions.PermissionsManager
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.Settings.GestureSettings
|
||||
import de.mm20.launcher2.preferences.Settings.GestureSettings.GestureAction
|
||||
import de.mm20.launcher2.ui.gestures.Gesture
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
@@ -17,6 +28,19 @@ import org.koin.core.component.inject
|
||||
class LauncherScaffoldVM : ViewModel(), KoinComponent {
|
||||
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
private val globalActionsService: GlobalActionsService by inject()
|
||||
private val permissionsManager: PermissionsManager by inject()
|
||||
|
||||
private var gestureSettings : GestureSettings? = null
|
||||
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
dataStore.data.map { it.gestures }.collectLatest {
|
||||
gestureSettings = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var isSystemInDarkMode = MutableStateFlow(false)
|
||||
|
||||
@@ -84,4 +108,68 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent {
|
||||
val fillClockHeight = dataStore.data.map { it.clockWidget.fillHeight }.asLiveData()
|
||||
val searchBarColor = dataStore.data.map { it.searchBar.color }.asLiveData()
|
||||
val searchBarStyle = dataStore.data.map { it.searchBar.searchBarStyle }.asLiveData()
|
||||
}
|
||||
|
||||
|
||||
var failedGestureState by mutableStateOf<FailedGesture?>(null)
|
||||
fun handleGesture(gesture: Gesture): Boolean {
|
||||
val action = when (gesture) {
|
||||
Gesture.DoubleTap -> gestureSettings?.doubleTap
|
||||
Gesture.LongPress -> gestureSettings?.longPress
|
||||
Gesture.SwipeDown -> gestureSettings?.swipeDown?.takeIf { baseLayout.value != Settings.LayoutSettings.Layout.PullDown }
|
||||
Gesture.SwipeLeft -> gestureSettings?.swipeLeft?.takeIf { baseLayout.value != Settings.LayoutSettings.Layout.Pager }
|
||||
Gesture.SwipeRight -> gestureSettings?.swipeRight?.takeIf { baseLayout.value != Settings.LayoutSettings.Layout.PagerReversed }
|
||||
}
|
||||
val requiresAccessibilityService =
|
||||
action == GestureAction.OpenRecents
|
||||
|| action == GestureAction.OpenPowerDialog
|
||||
|| action == GestureAction.OpenQuickSettings
|
||||
|| action == GestureAction.OpenNotificationDrawer
|
||||
|| action == GestureAction.LockScreen
|
||||
|
||||
if (action != null && requiresAccessibilityService && !permissionsManager.checkPermissionOnce(PermissionGroup.Accessibility)) {
|
||||
failedGestureState = FailedGesture(gesture, action)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
return when (action) {
|
||||
GestureAction.OpenSearch -> {
|
||||
openSearch()
|
||||
true
|
||||
}
|
||||
|
||||
GestureAction.OpenNotificationDrawer -> {
|
||||
globalActionsService.openNotificationDrawer()
|
||||
true
|
||||
}
|
||||
|
||||
GestureAction.OpenQuickSettings -> {
|
||||
globalActionsService.openQuickSettings()
|
||||
true
|
||||
}
|
||||
|
||||
GestureAction.LockScreen -> {
|
||||
globalActionsService.lockScreen()
|
||||
true
|
||||
}
|
||||
|
||||
GestureAction.OpenPowerDialog -> {
|
||||
globalActionsService.openPowerDialog()
|
||||
true
|
||||
}
|
||||
|
||||
GestureAction.OpenRecents -> {
|
||||
globalActionsService.openRecents()
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun dismissGestureFailedSheet() {
|
||||
failedGestureState = null
|
||||
}
|
||||
}
|
||||
|
||||
data class FailedGesture(val gesture: Gesture, val action: GestureAction)
|
||||
@@ -4,7 +4,6 @@ import android.app.WallpaperManager
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.foundation.background
|
||||
@@ -37,6 +36,7 @@ import androidx.core.view.WindowInsetsControllerCompat
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.flowWithLifecycle
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import de.mm20.launcher2.globalactions.GlobalActionsService
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.preferences.Settings.SystemBarsSettings.SystemBarColors
|
||||
import de.mm20.launcher2.ui.assistant.AssistantScaffold
|
||||
@@ -44,13 +44,14 @@ import de.mm20.launcher2.ui.base.BaseActivity
|
||||
import de.mm20.launcher2.ui.base.ProvideCurrentTime
|
||||
import de.mm20.launcher2.ui.base.ProvideSettings
|
||||
import de.mm20.launcher2.ui.component.NavBarEffects
|
||||
import de.mm20.launcher2.ui.gestures.Gesture
|
||||
import de.mm20.launcher2.ui.gestures.GestureDetector
|
||||
import de.mm20.launcher2.ui.gestures.GestureHandler
|
||||
import de.mm20.launcher2.ui.gestures.LocalGestureDetector
|
||||
import de.mm20.launcher2.ui.ktx.animateTo
|
||||
import de.mm20.launcher2.ui.ktx.toPixels
|
||||
import de.mm20.launcher2.ui.launcher.sheets.CustomizeSearchableSheet
|
||||
import de.mm20.launcher2.ui.launcher.sheets.EditFavoritesSheet
|
||||
import de.mm20.launcher2.ui.launcher.sheets.FailedGestureSheet
|
||||
import de.mm20.launcher2.ui.launcher.sheets.LauncherBottomSheets
|
||||
import de.mm20.launcher2.ui.launcher.sheets.LauncherBottomSheetManager
|
||||
import de.mm20.launcher2.ui.launcher.sheets.LocalBottomSheetManager
|
||||
import de.mm20.launcher2.ui.launcher.transitions.HomeTransition
|
||||
@@ -62,6 +63,7 @@ import de.mm20.launcher2.ui.locals.LocalWallpaperColors
|
||||
import de.mm20.launcher2.ui.locals.LocalWindowSize
|
||||
import de.mm20.launcher2.ui.theme.LauncherTheme
|
||||
import de.mm20.launcher2.ui.theme.wallpaperColorsAsState
|
||||
import org.koin.android.ext.android.inject
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.pow
|
||||
|
||||
@@ -70,7 +72,9 @@ abstract class SharedLauncherActivity(
|
||||
private val mode: LauncherActivityMode
|
||||
) : BaseActivity() {
|
||||
|
||||
private val viewModel: LauncherActivityVM by viewModels()
|
||||
private val viewModel: LauncherScaffoldVM by viewModels()
|
||||
|
||||
private val globalActionsService: GlobalActionsService by inject()
|
||||
|
||||
internal val homeTransitionManager = HomeTransitionManager()
|
||||
|
||||
@@ -256,52 +260,43 @@ abstract class SharedLauncherActivity(
|
||||
) { enterTransitionProgress.value }
|
||||
}
|
||||
}
|
||||
|
||||
bottomSheetManager.customizeSearchableSheetShown.value?.let {
|
||||
CustomizeSearchableSheet(
|
||||
searchable = it,
|
||||
onDismiss = { bottomSheetManager.dismissCustomizeSearchableModal() })
|
||||
}
|
||||
if (bottomSheetManager.editFavoritesSheetShown.value) {
|
||||
EditFavoritesSheet(onDismiss = { bottomSheetManager.dismissEditFavoritesSheet() })
|
||||
}
|
||||
LauncherBottomSheets()
|
||||
}
|
||||
|
||||
val swipeThreshold = 150.dp.toPixels()
|
||||
GestureHandler(
|
||||
detector = gestureDetector,
|
||||
onDoubleTap = {
|
||||
Log.d("MM20", "Double tap")
|
||||
viewModel.handleGesture(Gesture.DoubleTap)
|
||||
},
|
||||
onLongPress = {
|
||||
Log.d("MM20", "Long press")
|
||||
viewModel.handleGesture(Gesture.LongPress)
|
||||
},
|
||||
onDrag = {
|
||||
return@GestureHandler when {
|
||||
it.x > swipeThreshold && it.x.absoluteValue > it.y.absoluteValue * 2f -> {
|
||||
Log.d("MM20", "Swipe right")
|
||||
true
|
||||
viewModel.handleGesture(Gesture.SwipeRight)
|
||||
}
|
||||
|
||||
it.x < -swipeThreshold && it.x.absoluteValue > it.y.absoluteValue * 2f -> {
|
||||
Log.d("MM20", "Swipe left")
|
||||
true
|
||||
viewModel.handleGesture(Gesture.SwipeLeft)
|
||||
}
|
||||
|
||||
it.y > swipeThreshold && it.y.absoluteValue > it.x.absoluteValue * 2f -> {
|
||||
Log.d("MM20", "Swipe down")
|
||||
true
|
||||
viewModel.handleGesture(Gesture.SwipeDown)
|
||||
}
|
||||
|
||||
it.y < -swipeThreshold && it.y.absoluteValue > it.x.absoluteValue * 2f -> {
|
||||
Log.d("MM20", "Swipe up")
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
)
|
||||
if (viewModel.failedGestureState != null) {
|
||||
FailedGestureSheet(
|
||||
failedGesture = viewModel.failedGestureState!!,
|
||||
onDismiss = {
|
||||
viewModel.dismissGestureFailedSheet()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package de.mm20.launcher2.ui.launcher.sheets
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.preferences.Settings
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.BottomSheetDialog
|
||||
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
||||
import de.mm20.launcher2.ui.gestures.Gesture
|
||||
import de.mm20.launcher2.ui.launcher.FailedGesture
|
||||
|
||||
@Composable
|
||||
fun FailedGestureSheet(
|
||||
failedGesture: FailedGesture,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
val viewModel: FailedGestureSheetVM = viewModel()
|
||||
|
||||
val actionName = stringResource(when(failedGesture.action) {
|
||||
Settings.GestureSettings.GestureAction.OpenSearch -> R.string.gesture_action_open_search
|
||||
Settings.GestureSettings.GestureAction.OpenNotificationDrawer -> R.string.gesture_action_notifications
|
||||
Settings.GestureSettings.GestureAction.LockScreen -> R.string.gesture_action_lock_screen
|
||||
Settings.GestureSettings.GestureAction.OpenQuickSettings -> R.string.gesture_action_quick_settings
|
||||
Settings.GestureSettings.GestureAction.OpenRecents -> R.string.gesture_action_recents
|
||||
Settings.GestureSettings.GestureAction.OpenPowerDialog -> R.string.gesture_action_power_menu
|
||||
else -> R.string.gesture_action_none
|
||||
})
|
||||
val gestureName = stringResource(when(failedGesture.gesture) {
|
||||
Gesture.DoubleTap -> R.string.preference_gesture_double_tap
|
||||
Gesture.LongPress -> R.string.preference_gesture_long_press
|
||||
Gesture.SwipeDown -> R.string.preference_gesture_swipe_down
|
||||
Gesture.SwipeLeft -> R.string.preference_gesture_swipe_left
|
||||
Gesture.SwipeRight -> R.string.preference_gesture_swipe_right
|
||||
})
|
||||
|
||||
BottomSheetDialog(
|
||||
title = { Text(actionName) },
|
||||
onDismissRequest = onDismiss,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(it)
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.gesture_failed_message, gestureName, actionName),
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
val context = LocalLifecycleOwner.current
|
||||
MissingPermissionBanner(
|
||||
modifier = Modifier.padding(vertical = 16.dp),
|
||||
text = stringResource(id = R.string.missing_permission_accessibility_gesture_failed),
|
||||
secondaryAction = {
|
||||
OutlinedButton(onClick = {
|
||||
viewModel.disableGesture(failedGesture.gesture)
|
||||
onDismiss()
|
||||
}) {
|
||||
Text(stringResource(R.string.turn_off))
|
||||
}
|
||||
},
|
||||
onClick = {
|
||||
viewModel.requestPermission(context as AppCompatActivity)
|
||||
onDismiss()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package de.mm20.launcher2.ui.launcher.sheets
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.permissions.PermissionGroup
|
||||
import de.mm20.launcher2.permissions.PermissionsManager
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.Settings.GestureSettings.GestureAction
|
||||
import de.mm20.launcher2.ui.gestures.Gesture
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class FailedGestureSheetVM : ViewModel(), KoinComponent {
|
||||
private val permissionsManager: PermissionsManager by inject()
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
|
||||
fun requestPermission(context: AppCompatActivity) {
|
||||
permissionsManager.requestPermission(context, PermissionGroup.Accessibility)
|
||||
}
|
||||
|
||||
fun disableGesture(gesture: Gesture) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setGestures(
|
||||
it.gestures.toBuilder().apply {
|
||||
when (gesture) {
|
||||
Gesture.SwipeDown -> swipeDown = GestureAction.None
|
||||
Gesture.SwipeLeft -> swipeLeft = GestureAction.None
|
||||
Gesture.SwipeRight -> swipeRight = GestureAction.None
|
||||
Gesture.DoubleTap -> doubleTap = GestureAction.None
|
||||
Gesture.LongPress -> longPress = GestureAction.None
|
||||
}
|
||||
}.build()
|
||||
).build()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package de.mm20.launcher2.ui.launcher.sheets
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
@Composable
|
||||
fun LauncherBottomSheets() {
|
||||
val bottomSheetManager = LocalBottomSheetManager.current
|
||||
bottomSheetManager.customizeSearchableSheetShown.value?.let {
|
||||
CustomizeSearchableSheet(
|
||||
searchable = it,
|
||||
onDismiss = { bottomSheetManager.dismissCustomizeSearchableModal() })
|
||||
}
|
||||
if (bottomSheetManager.editFavoritesSheetShown.value) {
|
||||
EditFavoritesSheet(onDismiss = { bottomSheetManager.dismissEditFavoritesSheet() })
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,7 @@ import de.mm20.launcher2.ui.settings.debug.DebugSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.easteregg.EasterEggSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.favorites.FavoritesSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.filesearch.FileSearchSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.gestures.GestureSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.hiddenitems.HiddenItemsSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.layout.LayoutSettingsScreen
|
||||
import de.mm20.launcher2.ui.settings.license.LicenseScreen
|
||||
@@ -115,6 +116,9 @@ class SettingsActivity : BaseActivity() {
|
||||
composable("settings/search") {
|
||||
SearchSettingsScreen()
|
||||
}
|
||||
composable("settings/gestures") {
|
||||
GestureSettingsScreen()
|
||||
}
|
||||
composable("settings/search/unitconverter") {
|
||||
UnitConverterSettingsScreen()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
package de.mm20.launcher2.ui.settings.gestures
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||
import de.mm20.launcher2.preferences.Settings.GestureSettings.GestureAction
|
||||
import de.mm20.launcher2.preferences.Settings.LayoutSettings.Layout
|
||||
import de.mm20.launcher2.ui.R
|
||||
import de.mm20.launcher2.ui.component.MissingPermissionBanner
|
||||
import de.mm20.launcher2.ui.component.preferences.ListPreference
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
|
||||
import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
|
||||
|
||||
@Composable
|
||||
fun GestureSettingsScreen() {
|
||||
val viewModel: GestureSettingsScreenVM = viewModel()
|
||||
|
||||
val layout by viewModel.layout.observeAsState()
|
||||
val hasPermission by viewModel.hasPermission.observeAsState()
|
||||
|
||||
val options = buildList {
|
||||
add(stringResource(R.string.gesture_action_none) to GestureAction.None)
|
||||
add(stringResource(R.string.gesture_action_notifications) to GestureAction.OpenNotificationDrawer)
|
||||
add(stringResource(R.string.gesture_action_quick_settings) to GestureAction.OpenQuickSettings)
|
||||
if (isAtLeastApiLevel(28)) add(stringResource(R.string.gesture_action_lock_screen) to GestureAction.LockScreen)
|
||||
add(stringResource(R.string.gesture_action_recents) to GestureAction.OpenRecents)
|
||||
add(stringResource(R.string.gesture_action_power_menu) to GestureAction.OpenPowerDialog)
|
||||
add(stringResource(R.string.gesture_action_open_search) to GestureAction.OpenSearch)
|
||||
}
|
||||
|
||||
val context = LocalContext.current
|
||||
PreferenceScreen(title = stringResource(R.string.preference_screen_gestures)) {
|
||||
item {
|
||||
PreferenceCategory {
|
||||
val doubleTap by viewModel.doubleTap.observeAsState()
|
||||
AnimatedVisibility(hasPermission == false && requiresAccessibilityService(doubleTap)) {
|
||||
MissingPermissionBanner(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
text = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
||||
onClick = { viewModel.requestPermission(context as AppCompatActivity) }
|
||||
)
|
||||
}
|
||||
ListPreference(
|
||||
title = stringResource(R.string.preference_gesture_double_tap),
|
||||
items = options,
|
||||
value = doubleTap,
|
||||
onValueChanged = { if (it != null) viewModel.setDoubleTap(it) }
|
||||
)
|
||||
val longPress by viewModel.longPress.observeAsState()
|
||||
AnimatedVisibility(hasPermission == false && requiresAccessibilityService(longPress)) {
|
||||
MissingPermissionBanner(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
text = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
||||
onClick = { viewModel.requestPermission(context as AppCompatActivity) }
|
||||
)
|
||||
}
|
||||
ListPreference(
|
||||
title = stringResource(R.string.preference_gesture_long_press),
|
||||
items = options,
|
||||
value = longPress,
|
||||
onValueChanged = { if (it != null) viewModel.setLongPress(it) }
|
||||
)
|
||||
val swipeDown by viewModel.swipeDown.observeAsState()
|
||||
AnimatedVisibility(hasPermission == false && requiresAccessibilityService(swipeDown)) {
|
||||
MissingPermissionBanner(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
text = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
||||
onClick = { viewModel.requestPermission(context as AppCompatActivity) }
|
||||
)
|
||||
}
|
||||
ListPreference(
|
||||
title = stringResource(R.string.preference_gesture_swipe_down),
|
||||
enabled = layout != Layout.PullDown,
|
||||
items = options,
|
||||
value = if (layout == Layout.PullDown) GestureAction.OpenSearch else swipeDown,
|
||||
onValueChanged = { if (it != null) viewModel.setSwipeDown(it) }
|
||||
)
|
||||
val swipeLeft by viewModel.swipeLeft.observeAsState()
|
||||
AnimatedVisibility(hasPermission == false && requiresAccessibilityService(swipeLeft)) {
|
||||
MissingPermissionBanner(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
text = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
||||
onClick = { viewModel.requestPermission(context as AppCompatActivity) }
|
||||
)
|
||||
}
|
||||
ListPreference(
|
||||
title = stringResource(R.string.preference_gesture_swipe_left),
|
||||
enabled = layout != Layout.Pager,
|
||||
items = options,
|
||||
value = if (layout == Layout.Pager) GestureAction.OpenSearch else swipeLeft,
|
||||
onValueChanged = { if (it != null) viewModel.setSwipeLeft(it) }
|
||||
)
|
||||
val swipeRight by viewModel.swipeRight.observeAsState()
|
||||
AnimatedVisibility(hasPermission == false && requiresAccessibilityService(swipeRight)) {
|
||||
MissingPermissionBanner(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
text = stringResource(R.string.missing_permission_accessibility_gesture_settings),
|
||||
onClick = { viewModel.requestPermission(context as AppCompatActivity) }
|
||||
)
|
||||
}
|
||||
ListPreference(
|
||||
title = stringResource(R.string.preference_gesture_swipe_right),
|
||||
enabled = layout != Layout.PagerReversed,
|
||||
items = options,
|
||||
value = if (layout == Layout.PagerReversed) GestureAction.OpenSearch else swipeRight,
|
||||
onValueChanged = { if (it != null) viewModel.setSwipeRight(it) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun requiresAccessibilityService(action: GestureAction?) : Boolean{
|
||||
return when(action) {
|
||||
GestureAction.OpenNotificationDrawer,
|
||||
GestureAction.LockScreen,
|
||||
GestureAction.OpenQuickSettings,
|
||||
GestureAction.OpenRecents,
|
||||
GestureAction.OpenPowerDialog -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
package de.mm20.launcher2.ui.settings.gestures
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.asLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import de.mm20.launcher2.permissions.PermissionGroup
|
||||
import de.mm20.launcher2.permissions.PermissionsManager
|
||||
import de.mm20.launcher2.preferences.LauncherDataStore
|
||||
import de.mm20.launcher2.preferences.Settings.GestureSettings.GestureAction
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
|
||||
class GestureSettingsScreenVM : ViewModel(), KoinComponent {
|
||||
private val dataStore: LauncherDataStore by inject()
|
||||
private val permissionsManager: PermissionsManager by inject()
|
||||
|
||||
val hasPermission = permissionsManager.hasPermission(PermissionGroup.Accessibility).asLiveData()
|
||||
|
||||
val layout = dataStore.data.map { it.layout.baseLayout }.asLiveData()
|
||||
|
||||
val swipeDown = dataStore.data.map { it.gestures.swipeDown }.asLiveData()
|
||||
val swipeLeft = dataStore.data.map { it.gestures.swipeLeft }.asLiveData()
|
||||
val swipeRight = dataStore.data.map { it.gestures.swipeRight }.asLiveData()
|
||||
val doubleTap = dataStore.data.map { it.gestures.doubleTap }.asLiveData()
|
||||
val longPress = dataStore.data.map { it.gestures.longPress }.asLiveData()
|
||||
|
||||
fun setSwipeDown(action: GestureAction) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setGestures(it.gestures.toBuilder().setSwipeDown(action).build())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setSwipeLeft(action: GestureAction) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setGestures(it.gestures.toBuilder().setSwipeLeft(action).build())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setSwipeRight(action: GestureAction) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setGestures(it.gestures.toBuilder().setSwipeRight(action).build())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setDoubleTap(action: GestureAction) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setGestures(it.gestures.toBuilder().setDoubleTap(action).build())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setLongPress(action: GestureAction) {
|
||||
viewModelScope.launch {
|
||||
dataStore.updateData {
|
||||
it.toBuilder().setGestures(it.gestures.toBuilder().setLongPress(action).build())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun requestPermission(context: AppCompatActivity) {
|
||||
permissionsManager.requestPermission(context, PermissionGroup.Accessibility)
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,14 @@ fun MainSettingsScreen() {
|
||||
navController?.navigate("settings/widgets")
|
||||
}
|
||||
)
|
||||
Preference(
|
||||
icon = Icons.Rounded.Gesture,
|
||||
title = stringResource(id = R.string.preference_screen_gestures),
|
||||
summary = stringResource(id = R.string.preference_screen_gestures_summary),
|
||||
onClick = {
|
||||
navController?.navigate("settings/gestures")
|
||||
}
|
||||
)
|
||||
Preference(
|
||||
icon = Icons.Rounded.NotificationBadge,
|
||||
title = stringResource(id = R.string.preference_screen_badges),
|
||||
|
||||
Reference in New Issue
Block a user