From 62c1a4293fe1bbd8c5f5bb1e8a44000caa040162 Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Fri, 23 Jun 2023 18:12:05 +0200 Subject: [PATCH] Add home button gesture trigger --- .../ui/assistant/AssistantScaffold.kt | 2 ++ .../de/mm20/launcher2/ui/gestures/Gesture.kt | 1 + .../launcher2/ui/gestures/GestureDetector.kt | 5 ++++ .../launcher2/ui/gestures/GestureHandler.kt | 5 ++++ .../launcher2/ui/launcher/LauncherActivity.kt | 11 +++++++-- .../ui/launcher/LauncherScaffoldVM.kt | 23 ++++++++++++++----- .../launcher2/ui/launcher/PagerScaffold.kt | 16 +++++++++++-- .../launcher2/ui/launcher/PullDownScaffold.kt | 14 ++++++++++- .../ui/launcher/SharedLauncherActivity.kt | 7 +++--- .../gestures/LauncherGestureHandler.kt | 15 +++++++++++- .../ui/launcher/sheets/FailedGestureSheet.kt | 1 + .../launcher/sheets/FailedGestureSheetVM.kt | 1 + 12 files changed, 85 insertions(+), 16 deletions(-) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/assistant/AssistantScaffold.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/assistant/AssistantScaffold.kt index 8770169a..17637bd8 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/assistant/AssistantScaffold.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/assistant/AssistantScaffold.kt @@ -23,6 +23,7 @@ import de.mm20.launcher2.preferences.Settings import de.mm20.launcher2.searchactions.actions.SearchAction import de.mm20.launcher2.ui.component.SearchBarLevel import de.mm20.launcher2.ui.launcher.LauncherScaffoldVM +import de.mm20.launcher2.ui.launcher.gestures.LauncherGestureHandler import de.mm20.launcher2.ui.launcher.helper.WallpaperBlur import de.mm20.launcher2.ui.launcher.search.SearchColumn import de.mm20.launcher2.ui.launcher.search.SearchVM @@ -215,4 +216,5 @@ fun AssistantScaffold( } else null ) } + LauncherGestureHandler() } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/gestures/Gesture.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/gestures/Gesture.kt index dfedd6d5..dee5073b 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/gestures/Gesture.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/gestures/Gesture.kt @@ -6,4 +6,5 @@ enum class Gesture { SwipeDown, SwipeLeft, SwipeRight, + HomeButton, } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/gestures/GestureDetector.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/gestures/GestureDetector.kt index c34f3cae..b06f01f4 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/gestures/GestureDetector.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/gestures/GestureDetector.kt @@ -41,6 +41,9 @@ class GestureDetector { gestureListener?.onDragEnd() } + fun dispatchHomeButtonPress() { + gestureListener?.onHomeButtonPress() + } interface OnGestureListener { fun onTap(position: Offset) {} @@ -54,6 +57,8 @@ class GestureDetector { fun onDrag(offset: Offset): Boolean = false fun onDragEnd() {} + + fun onHomeButtonPress() {} } } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/gestures/GestureHandler.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/gestures/GestureHandler.kt index 2c7de12d..b34ce185 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/gestures/GestureHandler.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/gestures/GestureHandler.kt @@ -12,6 +12,7 @@ fun GestureHandler( onDoubleTap: (Offset) -> Unit = {}, onDrag: (Offset) -> Boolean = { false }, onDragEnd: () -> Unit = {}, + onHomeButtonPress: () -> Unit = {}, ) { DisposableEffect(detector) { detector.gestureListener = object : GestureDetector.OnGestureListener { @@ -34,6 +35,10 @@ fun GestureHandler( override fun onDragEnd() { onDragEnd() } + + override fun onHomeButtonPress() { + onHomeButtonPress() + } } onDispose { detector.gestureListener = null diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherActivity.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherActivity.kt index d2b1ea9d..5cff5c00 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherActivity.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherActivity.kt @@ -1,6 +1,7 @@ package de.mm20.launcher2.ui.launcher import android.content.Intent +import android.util.Log import com.android.launcher3.GestureNavContract @@ -10,14 +11,20 @@ class LauncherActivity: SharedLauncherActivity(LauncherActivityMode.Launcher) { val navContract = intent?.let { GestureNavContract.fromIntent(it) } if (navContract != null) { enterHomeTransitionManager.resolve(navContract, window) - } else { - onBackPressed() + } else if (System.currentTimeMillis() - pausedAt < 50) { + // If the onPause was called less than 50ms ago, we assume that the app was already + // in the foreground when the user pressed the home button. In this case, we dispatch + // the home button press event to the gesture detector. + gestureDetector.dispatchHomeButtonPress() } } + private var pausedAt: Long = 0 + override fun onPause() { super.onPause() enterHomeTransitionManager.clear() + pausedAt = System.currentTimeMillis() } override fun onBackPressed() { diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherScaffoldVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherScaffoldVM.kt index cb4d68a7..925944b3 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherScaffoldVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/LauncherScaffoldVM.kt @@ -94,7 +94,7 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent { } fun closeSearch() { - if (isSearchOpen.value == false) return + if (!isSearchOpen.value) return isSearchOpen.value = false setSearchbarFocus(false) } @@ -130,6 +130,8 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent { settings?.swipeDown?.takeIf { layout != Layout.PullDown } ?: GestureAction.None val longPressAction = settings?.longPress ?: GestureAction.None val doubleTapAction = settings?.doubleTap ?: GestureAction.None + val homeButtonAction = settings?.homeButton ?: GestureAction.None + val swipeLeftAppKey = if (swipeLeftAction == GestureAction.LaunchApp) settings.swipeLeftApp else null val swipeRightAppKey = @@ -140,12 +142,15 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent { if (longPressAction == GestureAction.LaunchApp) settings.longPressApp else null val doubleTapAppKey = if (doubleTapAction == GestureAction.LaunchApp) settings.doubleTapApp else null + val homeButtonAppKey = + if (homeButtonAction == GestureAction.LaunchApp) settings.homeButtonApp else null val apps = listOfNotNull( swipeLeftAppKey, swipeRightAppKey, swipeDownAppKey, longPressAppKey, - doubleTapAppKey + doubleTapAppKey, + homeButtonAppKey, ).let { searchableRepository.getByKeys(it) } GestureState( @@ -154,11 +159,13 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent { swipeDownAction = swipeDownAction, longPressAction = longPressAction, doubleTapAction = doubleTapAction, + homeButtonAction = homeButtonAction, swipeLeftApp = apps.firstOrNull { it.key == swipeLeftAppKey }, swipeRightApp = apps.firstOrNull { it.key == swipeRightAppKey }, swipeDownApp = apps.firstOrNull { it.key == swipeDownAppKey }, longPressApp = apps.firstOrNull { it.key == longPressAppKey }, - doubleTapApp = apps.firstOrNull { it.key == doubleTapAppKey } + doubleTapApp = apps.firstOrNull { it.key == doubleTapAppKey }, + homeButtonApp = apps.firstOrNull { it.key == homeButtonAppKey }, ) }.stateIn(viewModelScope, SharingStarted.Eagerly, GestureState()) @@ -167,9 +174,10 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent { val action = when (gesture) { Gesture.DoubleTap -> gestureState.value.doubleTapAction Gesture.LongPress -> gestureState.value.longPressAction - Gesture.SwipeDown -> gestureState.value.swipeDownAction.takeIf { baseLayout.value != Settings.LayoutSettings.Layout.PullDown } - Gesture.SwipeLeft -> gestureState.value.swipeLeftAction.takeIf { baseLayout.value != Settings.LayoutSettings.Layout.Pager } - Gesture.SwipeRight -> gestureState.value.swipeRightAction.takeIf { baseLayout.value != Settings.LayoutSettings.Layout.PagerReversed } + Gesture.SwipeDown -> gestureState.value.swipeDownAction.takeIf { baseLayout.value != Layout.PullDown } + Gesture.SwipeLeft -> gestureState.value.swipeLeftAction.takeIf { baseLayout.value != Layout.Pager } + Gesture.SwipeRight -> gestureState.value.swipeRightAction.takeIf { baseLayout.value != Layout.PagerReversed } + Gesture.HomeButton -> gestureState.value.homeButtonAction } val requiresAccessibilityService = action == GestureAction.OpenRecents @@ -233,6 +241,7 @@ class LauncherScaffoldVM : ViewModel(), KoinComponent { Gesture.SwipeDown -> gestureState.value.swipeDownApp Gesture.LongPress -> gestureState.value.longPressApp Gesture.DoubleTap -> gestureState.value.doubleTapApp + Gesture.HomeButton -> gestureState.value.homeButtonApp }?.launch(context, options.toBundle()) true } @@ -252,11 +261,13 @@ data class GestureState( val swipeDownAction: GestureAction = GestureAction.None, val longPressAction: GestureAction = GestureAction.None, val doubleTapAction: GestureAction = GestureAction.None, + val homeButtonAction: GestureAction = GestureAction.None, val swipeLeftApp: SavableSearchable? = null, val swipeRightApp: SavableSearchable? = null, val swipeDownApp: SavableSearchable? = null, val longPressApp: SavableSearchable? = null, val doubleTapApp: SavableSearchable? = null, + val homeButtonApp: SavableSearchable? = null, ) data class FailedGesture(val gesture: Gesture, val action: GestureAction) \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt index b29da9d0..0b13bea3 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PagerScaffold.kt @@ -69,12 +69,12 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.google.accompanist.systemuicontroller.rememberSystemUiController import de.mm20.launcher2.preferences.Settings.SearchBarSettings.SearchBarColors -import de.mm20.launcher2.preferences.Settings.SearchBarSettings.SearchBarStyle import de.mm20.launcher2.searchactions.actions.SearchAction import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.component.SearchBarLevel import de.mm20.launcher2.ui.gestures.LocalGestureDetector import de.mm20.launcher2.ui.ktx.animateTo +import de.mm20.launcher2.ui.launcher.gestures.LauncherGestureHandler import de.mm20.launcher2.ui.launcher.helper.WallpaperBlur import de.mm20.launcher2.ui.launcher.search.SearchColumn import de.mm20.launcher2.ui.launcher.search.SearchVM @@ -227,15 +227,18 @@ fun PagerScaffold( val searchBarOffset = remember { mutableStateOf(0f) } val scope = rememberCoroutineScope() - BackHandler { + + val `handleBackOrHomeEvent` = { when { isSearchOpen -> { viewModel.closeSearch() searchVM.search("") + true } isWidgetEditMode -> { viewModel.setWidgetEditMode(false) + true } widgetsScrollState.value != 0 -> { @@ -245,10 +248,16 @@ fun PagerScaffold( scope.launch { searchBarOffset.animateTo(0f) } + true } + else -> false } } + BackHandler { + `handleBackOrHomeEvent`() + } + val keyboardController = LocalSoftwareKeyboardController.current val gestureManager = LocalGestureDetector.current @@ -542,6 +551,9 @@ fun PagerScaffold( } else null ) } + LauncherGestureHandler( + onHomeButtonPress = handleBackOrHomeEvent, + ) } private enum class Page { diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt index 3428aef7..2ac0c867 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/PullDownScaffold.kt @@ -72,6 +72,7 @@ import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.component.SearchBarLevel import de.mm20.launcher2.ui.gestures.LocalGestureDetector import de.mm20.launcher2.ui.ktx.animateTo +import de.mm20.launcher2.ui.launcher.gestures.LauncherGestureHandler import de.mm20.launcher2.ui.launcher.helper.WallpaperBlur import de.mm20.launcher2.ui.launcher.search.SearchColumn import de.mm20.launcher2.ui.launcher.search.SearchVM @@ -247,15 +248,17 @@ fun PullDownScaffold( if (!isWidgetEditMode) searchBarOffset.value = 0f } - BackHandler { + val handleBackOrHomeEvent = { when { isSearchOpen -> { viewModel.closeSearch() searchVM.search("") + true } isWidgetEditMode -> { viewModel.setWidgetEditMode(false) + true } widgetsScrollState.value != 0 -> { @@ -265,10 +268,16 @@ fun PullDownScaffold( scope.launch { searchBarOffset.animateTo(0f) } + true } + else -> false } } + BackHandler { + handleBackOrHomeEvent() + } + val keyboardController = LocalSoftwareKeyboardController.current val gestureManager = LocalGestureDetector.current @@ -571,4 +580,7 @@ fun PullDownScaffold( ) } + LauncherGestureHandler( + onHomeButtonPress = handleBackOrHomeEvent, + ) } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/SharedLauncherActivity.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/SharedLauncherActivity.kt index 0163936c..2ab81226 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/SharedLauncherActivity.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/SharedLauncherActivity.kt @@ -44,10 +44,9 @@ import de.mm20.launcher2.ui.component.NavBarEffects import de.mm20.launcher2.ui.gestures.GestureDetector import de.mm20.launcher2.ui.gestures.LocalGestureDetector import de.mm20.launcher2.ui.ktx.animateTo -import de.mm20.launcher2.ui.launcher.gestures.LauncherGestureHandler import de.mm20.launcher2.ui.launcher.search.SearchVM -import de.mm20.launcher2.ui.launcher.sheets.LauncherBottomSheets import de.mm20.launcher2.ui.launcher.sheets.LauncherBottomSheetManager +import de.mm20.launcher2.ui.launcher.sheets.LauncherBottomSheets import de.mm20.launcher2.ui.launcher.sheets.LocalBottomSheetManager import de.mm20.launcher2.ui.launcher.transitions.EnterHomeTransition import de.mm20.launcher2.ui.launcher.transitions.EnterHomeTransitionManager @@ -70,6 +69,8 @@ abstract class SharedLauncherActivity( internal val enterHomeTransitionManager = EnterHomeTransitionManager() + internal val gestureDetector = GestureDetector() + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -84,7 +85,6 @@ abstract class SharedLauncherActivity( viewModel.setSystemInDarkMode(resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES) val bottomSheetManager = LauncherBottomSheetManager(this) - val gestureDetector = GestureDetector() setContent { val snackbarHostState = remember { SnackbarHostState() } @@ -258,7 +258,6 @@ abstract class SharedLauncherActivity( } LauncherBottomSheets() } - LauncherGestureHandler() } } } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/gestures/LauncherGestureHandler.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/gestures/LauncherGestureHandler.kt index a024a87f..c582988c 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/gestures/LauncherGestureHandler.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/gestures/LauncherGestureHandler.kt @@ -34,8 +34,15 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlin.math.absoluteValue +/** + * Handles gestures on the launcher according to the user's settings. + * @param onHomeButtonPress Called when the home button is pressed. Allows the caller to intercept the event. + * If the function returns true, the event is considered handled and the default action is not performed. + */ @Composable -fun LauncherGestureHandler() { +fun LauncherGestureHandler( + onHomeButtonPress: () -> Boolean = { false } +) { val context = LocalContext.current val wallpaperManager = remember { WallpaperManager.getInstance(context) } val gestureDetector = LocalGestureDetector.current @@ -69,6 +76,12 @@ fun LauncherGestureHandler() { onLongPress = { viewModel.handleGesture(context, Gesture.LongPress) }, + onHomeButtonPress = { + if (onHomeButtonPress()) { + return@GestureHandler + } + viewModel.handleGesture(context, Gesture.HomeButton) + }, onDrag = { when { gestureState.swipeRightApp != null && it.x > swipeStartThreshold && ( diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/FailedGestureSheet.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/FailedGestureSheet.kt index 5412d52b..ed720fbb 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/FailedGestureSheet.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/FailedGestureSheet.kt @@ -43,6 +43,7 @@ fun FailedGestureSheet( Gesture.SwipeDown -> R.string.preference_gesture_swipe_down Gesture.SwipeLeft -> R.string.preference_gesture_swipe_left Gesture.SwipeRight -> R.string.preference_gesture_swipe_right + Gesture.HomeButton -> R.string.preference_gesture_home_button }) BottomSheetDialog( diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/FailedGestureSheetVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/FailedGestureSheetVM.kt index 4c82228d..ecaa6db0 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/FailedGestureSheetVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/FailedGestureSheetVM.kt @@ -31,6 +31,7 @@ class FailedGestureSheetVM : ViewModel(), KoinComponent { Gesture.SwipeRight -> swipeRight = GestureAction.None Gesture.DoubleTap -> doubleTap = GestureAction.None Gesture.LongPress -> longPress = GestureAction.None + Gesture.HomeButton -> homeButton = GestureAction.None } }.build() ).build()