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() {