From b95c9c218b3cee2a6e9946524797558d4c5dcecf Mon Sep 17 00:00:00 2001 From: Aryan <143314529+Aryan0577@users.noreply.github.com> Date: Wed, 28 Jan 2026 22:09:25 +0530 Subject: [PATCH] feat: Remove Accessibility requirement for Notification and Quick Settings gestures (#1784) * Replace Accessibility Service with reflection for Notification and Quick Settings actions * move reflection logic to GlobalActionsService with fallback --- app/ui/src/main/AndroidManifest.xml | 1 + .../scaffold/NotificationsComponent.kt | 30 ++------------- .../scaffold/QuickSettingsComponent.kt | 37 +++++-------------- .../gestures/GestureSettingsScreen.kt | 7 ++-- .../globalactions/GlobalActionsService.kt | 30 +++++++++++++-- .../de/mm20/launcher2/globalactions/Module.kt | 3 +- 6 files changed, 45 insertions(+), 63 deletions(-) diff --git a/app/ui/src/main/AndroidManifest.xml b/app/ui/src/main/AndroidManifest.xml index d70660ba4..4eac699dc 100644 --- a/app/ui/src/main/AndroidManifest.xml +++ b/app/ui/src/main/AndroidManifest.xml @@ -5,6 +5,7 @@ + diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/scaffold/NotificationsComponent.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/scaffold/NotificationsComponent.kt index fa64d48d8..eb947ebce 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/scaffold/NotificationsComponent.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/scaffold/NotificationsComponent.kt @@ -15,7 +15,6 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -28,21 +27,15 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex import de.mm20.launcher2.globalactions.GlobalActionsService -import de.mm20.launcher2.permissions.PermissionGroup -import de.mm20.launcher2.permissions.PermissionsManager -import de.mm20.launcher2.preferences.GestureAction import de.mm20.launcher2.ui.R -import de.mm20.launcher2.ui.launcher.sheets.LocalBottomSheetManager import org.koin.core.component.KoinComponent import org.koin.core.component.inject internal object NotificationsComponent : ScaffoldComponent(), KoinComponent { - private val permissionsManager: PermissionsManager by inject() private val globalActionService: GlobalActionsService by inject() - override val permanent: Boolean - get() = !permissionsManager.checkPermissionOnce(PermissionGroup.Accessibility) + override val permanent: Boolean = false override val showSearchBar: Boolean = false @@ -56,19 +49,6 @@ internal object NotificationsComponent : ScaffoldComponent(), KoinComponent { insets: PaddingValues, state: LauncherScaffoldState ) { - if (isActive) { - val bottomSheetManager = LocalBottomSheetManager.current - LaunchedEffect(Unit) { - val gesture = state.currentGesture ?: return@LaunchedEffect - if (!permissionsManager.checkPermissionOnce(PermissionGroup.Accessibility)) { - bottomSheetManager.showFailedGestureSheet( - gesture = gesture, - action = GestureAction.Notifications, - ) - } - } - } - val scale by animateFloatAsState( if (state.currentProgress >= 0.5f) 1.2f else 1f ) @@ -105,16 +85,12 @@ internal object NotificationsComponent : ScaffoldComponent(), KoinComponent { override suspend fun onPreActivate(state: LauncherScaffoldState) { super.onPreActivate(state) - if (permissionsManager.checkPermissionOnce(PermissionGroup.Accessibility)) { - globalActionService.openNotificationDrawer() - } + globalActionService.openNotificationDrawer() } override suspend fun onActivate(state: LauncherScaffoldState) { super.onActivate(state) - if (!permissionsManager.checkPermissionOnce(PermissionGroup.Accessibility)) { - state.navigateBack(true) - } + state.navigateBack(false) } @SuppressLint("ModifierFactoryExtensionFunction") diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/scaffold/QuickSettingsComponent.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/scaffold/QuickSettingsComponent.kt index be5666727..20703897a 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/scaffold/QuickSettingsComponent.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/scaffold/QuickSettingsComponent.kt @@ -15,7 +15,6 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -28,21 +27,15 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex import de.mm20.launcher2.globalactions.GlobalActionsService -import de.mm20.launcher2.permissions.PermissionGroup -import de.mm20.launcher2.permissions.PermissionsManager -import de.mm20.launcher2.preferences.GestureAction import de.mm20.launcher2.ui.R -import de.mm20.launcher2.ui.launcher.sheets.LocalBottomSheetManager import org.koin.core.component.KoinComponent import org.koin.core.component.inject internal object QuickSettingsComponent : ScaffoldComponent(), KoinComponent { - private val permissionsManager: PermissionsManager by inject() private val globalActionService: GlobalActionsService by inject() - override val permanent: Boolean - get() = !permissionsManager.checkPermissionOnce(PermissionGroup.Accessibility) + override val permanent: Boolean = false override val showSearchBar: Boolean = false @@ -56,19 +49,6 @@ internal object QuickSettingsComponent : ScaffoldComponent(), KoinComponent { insets: PaddingValues, state: LauncherScaffoldState ) { - if (isActive) { - val bottomSheetManager = LocalBottomSheetManager.current - LaunchedEffect(Unit) { - val gesture = state.currentGesture ?: return@LaunchedEffect - if (!permissionsManager.checkPermissionOnce(PermissionGroup.Accessibility)) { - bottomSheetManager.showFailedGestureSheet( - gesture = gesture, - action = GestureAction.QuickSettings, - ) - } - } - } - val scale by animateFloatAsState( if (state.currentProgress >= 0.5f) 1.2f else 1f ) @@ -85,7 +65,10 @@ internal object QuickSettingsComponent : ScaffoldComponent(), KoinComponent { .systemBarsPadding() .padding(16.dp) .size(64.dp) - .offset(y = -134.dp * interpolator.getInterpolation(1f - state.currentProgress * 2f).coerceAtLeast(0f)) + .offset( + y = -134.dp * interpolator.getInterpolation(1f - state.currentProgress * 2f) + .coerceAtLeast(0f) + ) .scale(scale) .shadow(4.dp, CircleShape) .background(MaterialTheme.colorScheme.secondaryContainer, CircleShape), @@ -102,16 +85,14 @@ internal object QuickSettingsComponent : ScaffoldComponent(), KoinComponent { override suspend fun onPreActivate(state: LauncherScaffoldState) { super.onPreActivate(state) - if (permissionsManager.checkPermissionOnce(PermissionGroup.Accessibility)) { - globalActionService.openQuickSettings() - } + + globalActionService.openQuickSettings() + } override suspend fun onActivate(state: LauncherScaffoldState) { super.onActivate(state) - if (!permissionsManager.checkPermissionOnce(PermissionGroup.Accessibility)) { - state.navigateBack(true) - } + state.navigateBack(false) } @SuppressLint("ModifierFactoryExtensionFunction") diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/gestures/GestureSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/gestures/GestureSettingsScreen.kt index bff5b5944..69b90494e 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/gestures/GestureSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/gestures/GestureSettingsScreen.kt @@ -40,7 +40,7 @@ import de.mm20.launcher2.ui.ktx.toPixels import kotlinx.serialization.Serializable @Serializable -data object GesturesSettingsRoute: NavKey +data object GesturesSettingsRoute : NavKey @Composable fun GestureSettingsScreen() { @@ -61,7 +61,8 @@ fun GestureSettingsScreen() { add(stringResource(R.string.gesture_action_launch_app) to GestureAction.Launch(null)) } - val optionsWithFeed = options + (stringResource(R.string.gesture_action_feed) to GestureAction.Feed) + val optionsWithFeed = + options + (stringResource(R.string.gesture_action_feed) to GestureAction.Feed) val context = LocalContext.current @@ -229,9 +230,7 @@ fun GestureSettingsScreen() { fun requiresAccessibilityService(action: GestureAction?): Boolean { return when (action) { - is GestureAction.Notifications, is GestureAction.ScreenLock, - is GestureAction.QuickSettings, is GestureAction.Recents, is GestureAction.PowerMenu -> true diff --git a/services/global-actions/src/main/java/de/mm20/launcher2/globalactions/GlobalActionsService.kt b/services/global-actions/src/main/java/de/mm20/launcher2/globalactions/GlobalActionsService.kt index 0a1b04428..0c8a7ad02 100644 --- a/services/global-actions/src/main/java/de/mm20/launcher2/globalactions/GlobalActionsService.kt +++ b/services/global-actions/src/main/java/de/mm20/launcher2/globalactions/GlobalActionsService.kt @@ -1,10 +1,15 @@ package de.mm20.launcher2.globalactions import android.accessibilityservice.AccessibilityService +import android.content.Context -class GlobalActionsService { +class GlobalActionsService(private val context: Context) { fun openNotificationDrawer() { - LauncherAccessibilityService.getInstance()?.performGlobalAction(AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS) + try { + expandNotificationPanel() + } catch (e: Exception) { + LauncherAccessibilityService.getInstance()?.performGlobalAction(AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS) + } } fun lockScreen() { @@ -12,7 +17,11 @@ class GlobalActionsService { } fun openQuickSettings() { - LauncherAccessibilityService.getInstance()?.performGlobalAction(AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS) + try { + expandQuickSettings() + } catch (e: Exception) { + LauncherAccessibilityService.getInstance()?.performGlobalAction(AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS) + } } fun openPowerDialog() { @@ -22,4 +31,19 @@ class GlobalActionsService { fun openRecents() { LauncherAccessibilityService.getInstance()?.performGlobalAction(AccessibilityService.GLOBAL_ACTION_RECENTS) } + + private fun expandNotificationPanel() { + val statusBarService = context.getSystemService("statusbar") + val statusBarManager = Class.forName("android.app.StatusBarManager") + val method = statusBarManager.getMethod("expandNotificationsPanel") + method.invoke(statusBarService) + } + + private fun expandQuickSettings() { + val statusBarService = context.getSystemService("statusbar") + val statusBarManager = Class.forName("android.app.StatusBarManager") + val method = statusBarManager.getMethod("expandSettingsPanel") + method.invoke(statusBarService) + } + } \ No newline at end of file diff --git a/services/global-actions/src/main/java/de/mm20/launcher2/globalactions/Module.kt b/services/global-actions/src/main/java/de/mm20/launcher2/globalactions/Module.kt index 4219d0619..86de12c37 100644 --- a/services/global-actions/src/main/java/de/mm20/launcher2/globalactions/Module.kt +++ b/services/global-actions/src/main/java/de/mm20/launcher2/globalactions/Module.kt @@ -1,7 +1,8 @@ package de.mm20.launcher2.globalactions +import org.koin.android.ext.koin.androidContext import org.koin.dsl.module val globalActionsModule = module { - single { GlobalActionsService() } + single { GlobalActionsService(androidContext()) } } \ No newline at end of file