diff --git a/README.md b/README.md index 75105371..37e2ce51 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,30 @@ AF stands for Ad-Free ## +### Accessibility Intents + +Olauncher exposes two intents that allow external apps (such as physical key mappers or accessibility services) to trigger launcher actions without touch input. + +| Intent Action | Description | +|---|---| +| `app.olauncher.ACTION_OPEN_APP_DRAWER` | Opens the app drawer from the home screen | +| `app.olauncher.ACTION_DISMISS_KEYGUARD` | Shows the PIN/pattern/password entry to unlock the device (Android 8.0+) | + +**ADB example:** +```bash +adb shell am start -a app.olauncher.ACTION_OPEN_APP_DRAWER -n app.olauncher/.MainActivity +adb shell am start -a app.olauncher.ACTION_DISMISS_KEYGUARD -n app.olauncher/.MainActivity +``` + +**Android example (Kotlin):** +```kotlin +val intent = Intent("app.olauncher.ACTION_OPEN_APP_DRAWER") +intent.setPackage("app.olauncher") +startActivity(intent) +``` + +## + License: [GNU GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html) Dev: [X/twitter](https://x.com/tanujnotes) • [Bluesky](https://bsky.app/profile/tanujnotes.bsky.social) diff --git a/app/build.gradle b/app/build.gradle index 027a501f..dd3efe9e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -34,6 +34,7 @@ android { debuggable true minifyEnabled false applicationIdSuffix ".debug" + resValue "string", "app_name", "Olauncher (Debug)" proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 91aa7afe..c04d1f75 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -45,6 +45,14 @@ + + + + + + + + = Build.VERSION_CODES.VANILLA_ICE_CREAM) { profileReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { @@ -147,6 +150,11 @@ class MainActivity : AppCompatActivity() { } override fun onNewIntent(intent: Intent?) { + if (handleIntentAction(intent)) { + super.onNewIntent(intent) + return + } + val alreadyHome = navController.currentDestination?.id == R.id.mainFragment backToHomeScreen() if (alreadyHome && isResumed && prefs.homeButtonShowRecents) @@ -332,6 +340,29 @@ class MainActivity : AppCompatActivity() { navController.popBackStack(R.id.mainFragment, false) } + private fun dismissKeyguard() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager + keyguardManager.requestDismissKeyguard(this, null) + } + } + + private fun handleIntentAction(intent: Intent?): Boolean { + return when (intent?.action) { + Constants.ACTION_OPEN_APP_DRAWER -> { + viewModel.showAppDrawer.call() + true + } + + Constants.ACTION_DISMISS_KEYGUARD -> { + dismissKeyguard() + true + } + + else -> false + } + } + private fun setPlainWallpaper() { if (this.isDarkThemeOn()) setPlainWallpaper(this, android.R.color.black) diff --git a/app/src/main/java/app/olauncher/MainViewModel.kt b/app/src/main/java/app/olauncher/MainViewModel.kt index 7196a7ed..6e599989 100644 --- a/app/src/main/java/app/olauncher/MainViewModel.kt +++ b/app/src/main/java/app/olauncher/MainViewModel.kt @@ -63,6 +63,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { val checkForMessages = SingleLiveEvent() val resetLauncherLiveData = SingleLiveEvent() val showRecentApps = SingleLiveEvent() + val showAppDrawer = SingleLiveEvent() fun selectedApp(appModel: AppModel, flag: Int) { if (appModel is AppModel.PrivateSpaceHeader) return diff --git a/app/src/main/java/app/olauncher/data/Constants.kt b/app/src/main/java/app/olauncher/data/Constants.kt index 48bd1480..4aca2619 100644 --- a/app/src/main/java/app/olauncher/data/Constants.kt +++ b/app/src/main/java/app/olauncher/data/Constants.kt @@ -111,6 +111,9 @@ object Constants { const val URL_DUCK_SEARCH = "https://duck.co/?q=" const val URL_DIGITAL_WELLBEING_LEARN_MORE = "https://tanujnotes.substack.com/p/digital-wellbeing-app-on-android?utm_source=olauncher" + const val ACTION_OPEN_APP_DRAWER = "app.olauncher.ACTION_OPEN_APP_DRAWER" + const val ACTION_DISMISS_KEYGUARD = "app.olauncher.ACTION_DISMISS_KEYGUARD" + const val DIGITAL_WELLBEING_PACKAGE_NAME = "com.google.android.apps.wellbeing" const val DIGITAL_WELLBEING_ACTIVITY = "com.google.android.apps.wellbeing.settings.TopLevelSettingsActivity" const val DIGITAL_WELLBEING_SAMSUNG_PACKAGE_NAME = "com.samsung.android.forest" diff --git a/app/src/main/java/app/olauncher/ui/HomeFragment.kt b/app/src/main/java/app/olauncher/ui/HomeFragment.kt index ef7620bd..ec87efa7 100644 --- a/app/src/main/java/app/olauncher/ui/HomeFragment.kt +++ b/app/src/main/java/app/olauncher/ui/HomeFragment.kt @@ -207,6 +207,9 @@ class HomeFragment : Fragment(), View.OnClickListener, View.OnLongClickListener viewModel.showRecentApps.observe(viewLifecycleOwner) { binding.recents.performClick() } + viewModel.showAppDrawer.observe(viewLifecycleOwner) { + showAppList(Constants.FLAG_LAUNCH_APP) + } } private fun initSwipeTouchListener() {