From f61f86195083a466374a2ffdc82090d7edf03b37 Mon Sep 17 00:00:00 2001 From: Josia Pietsch Date: Sat, 9 Nov 2024 00:36:07 +0100 Subject: [PATCH] add accessibility service as second method for locking the screen (cf. #65) --- app/src/main/AndroidManifest.xml | 4 +- .../launcher/actions/LauncherAction.kt | 4 +- .../LauncherAccessibilityService.kt | 50 +++++++++---- .../actions/{ => lock}/LauncherDeviceAdmin.kt | 16 ++-- .../launcher/actions/lock/LockMethod.kt | 74 +++++++++++++++++++ .../LauncherPreferences$Config.java | 4 + .../launcher/SettingsFragmentLauncher.kt | 10 +++ .../android/launcher/ui/util/HtmlTextView.kt | 18 +++++ .../res/layout/dialog_select_lock_method.xml | 14 ++++ app/src/main/res/values/donottranslate.xml | 2 + app/src/main/res/values/strings.xml | 33 ++++++++- app/src/main/res/xml/preferences.xml | 4 + 12 files changed, 206 insertions(+), 27 deletions(-) rename app/src/main/java/de/jrpie/android/launcher/actions/{ => lock}/LauncherAccessibilityService.kt (55%) rename app/src/main/java/de/jrpie/android/launcher/actions/{ => lock}/LauncherDeviceAdmin.kt (80%) create mode 100644 app/src/main/java/de/jrpie/android/launcher/actions/lock/LockMethod.kt create mode 100644 app/src/main/java/de/jrpie/android/launcher/ui/util/HtmlTextView.kt create mode 100644 app/src/main/res/layout/dialog_select_lock_method.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a4e5122..d1c53c7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -53,7 +53,7 @@ - - diff --git a/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt b/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt index 8848e3a..6c23bf0 100644 --- a/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt +++ b/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAction.kt @@ -12,9 +12,9 @@ import android.view.KeyEvent import android.widget.Toast import de.jrpie.android.launcher.Application import de.jrpie.android.launcher.R -import de.jrpie.android.launcher.actions.LauncherAccessibilityService.Companion.ACTION_LOCK_SCREEN import de.jrpie.android.launcher.apps.AppFilter import de.jrpie.android.launcher.apps.AppInfo.Companion.INVALID_USER +import de.jrpie.android.launcher.preferences.LauncherPreferences import de.jrpie.android.launcher.ui.list.ListActivity import de.jrpie.android.launcher.ui.settings.SettingsActivity @@ -78,7 +78,7 @@ enum class LauncherAction( "launcher:lockScreen", R.string.list_other_lock_screen, R.drawable.baseline_lock_24px, - LauncherDeviceAdmin::lockScreen + { c -> LauncherPreferences.actions().lockMethod().lockOrEnable(c) } ), TORCH( "launcher:toggleTorch", diff --git a/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAccessibilityService.kt b/app/src/main/java/de/jrpie/android/launcher/actions/lock/LauncherAccessibilityService.kt similarity index 55% rename from app/src/main/java/de/jrpie/android/launcher/actions/LauncherAccessibilityService.kt rename to app/src/main/java/de/jrpie/android/launcher/actions/lock/LauncherAccessibilityService.kt index bd46dab..969b493 100644 --- a/app/src/main/java/de/jrpie/android/launcher/actions/LauncherAccessibilityService.kt +++ b/app/src/main/java/de/jrpie/android/launcher/actions/lock/LauncherAccessibilityService.kt @@ -1,19 +1,19 @@ -package de.jrpie.android.launcher.actions +package de.jrpie.android.launcher.actions.lock import android.accessibilityservice.AccessibilityService import android.accessibilityservice.AccessibilityServiceInfo import android.content.Context import android.content.Intent import android.os.Build -import android.util.Log import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityManager import android.widget.Toast import androidx.core.content.getSystemService +import de.jrpie.android.launcher.BuildConfig import de.jrpie.android.launcher.R class LauncherAccessibilityService : AccessibilityService() { - override fun onInterrupt() { } + override fun onInterrupt() {} override fun onAccessibilityEvent(event: AccessibilityEvent?) { // Intentionally left blank, we are not interested in any AccessibilityEvents. @@ -23,11 +23,15 @@ class LauncherAccessibilityService : AccessibilityService() { companion object { const val ACTION_LOCK_SCREEN = "ACTION_LOCK_SCREEN" - private fun lockScreen(context: Context){ + fun lockScreen(context: Context) { try { - context.startService(Intent(context, LauncherAccessibilityService::class.java).apply { - action = ACTION_LOCK_SCREEN - }) + context.startService( + Intent( + context, + LauncherAccessibilityService::class.java + ).apply { + action = ACTION_LOCK_SCREEN + }) } catch (e: Exception) { Toast.makeText( context, @@ -36,19 +40,33 @@ class LauncherAccessibilityService : AccessibilityService() { ).show() } } + + fun isEnabled(context: Context): Boolean { + val accessibilityManager = + context.getSystemService() ?: return false + val enabledServices = + accessibilityManager.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK) + return enabledServices.any { + it.id.startsWith(BuildConfig.APPLICATION_ID) + } + } + } - private fun isServiceEnabled(): Boolean { - val accessibilityManager = getSystemService() ?: return false - val enabledServices = accessibilityManager.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK) - return enabledServices.any { it.id.contains(packageName) } - } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { intent?.action?.let { action -> - if (!isServiceEnabled()) { - Toast.makeText(this, getString(R.string.toast_accessibility_service_not_enabled), Toast.LENGTH_LONG).show() - startActivity(Intent(android.provider.Settings.ACTION_ACCESSIBILITY_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)) + if (!isEnabled(this)) { + Toast.makeText( + this, + getString(R.string.toast_accessibility_service_not_enabled), + Toast.LENGTH_LONG + ).show() + startActivity( + Intent(android.provider.Settings.ACTION_ACCESSIBILITY_SETTINGS).addFlags( + Intent.FLAG_ACTIVITY_NEW_TASK + ) + ) return START_NOT_STICKY } @@ -59,7 +77,7 @@ class LauncherAccessibilityService : AccessibilityService() { return super.onStartCommand(intent, flags, startId) } - private fun handleLockScreen(){ + private fun handleLockScreen() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { Toast.makeText( this, diff --git a/app/src/main/java/de/jrpie/android/launcher/actions/LauncherDeviceAdmin.kt b/app/src/main/java/de/jrpie/android/launcher/actions/lock/LauncherDeviceAdmin.kt similarity index 80% rename from app/src/main/java/de/jrpie/android/launcher/actions/LauncherDeviceAdmin.kt rename to app/src/main/java/de/jrpie/android/launcher/actions/lock/LauncherDeviceAdmin.kt index d6aa3a2..47c57d2 100644 --- a/app/src/main/java/de/jrpie/android/launcher/actions/LauncherDeviceAdmin.kt +++ b/app/src/main/java/de/jrpie/android/launcher/actions/lock/LauncherDeviceAdmin.kt @@ -1,4 +1,4 @@ -package de.jrpie.android.launcher.actions +package de.jrpie.android.launcher.actions.lock import android.app.admin.DeviceAdminReceiver import android.app.admin.DevicePolicyManager @@ -18,11 +18,17 @@ class LauncherDeviceAdmin : DeviceAdminReceiver() { val intent = Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN).apply { putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, getComponentName(context)) - putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, - context.getString(R.string.device_admin_explanation)) + putExtra( + DevicePolicyManager.EXTRA_ADD_EXPLANATION, + context.getString(R.string.device_admin_explanation) + ) } context.startActivity(intent) + } + fun isDeviceAdmin(context: Context): Boolean { + val dpm = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager + return dpm.isAdminActive(getComponentName(context)) } private fun assertDeviceAdmin(context: Context): Boolean { @@ -36,17 +42,15 @@ class LauncherDeviceAdmin : DeviceAdminReceiver() { requestDeviceAdmin(context) return false } - return true } fun lockScreen(context: Context) { + assertDeviceAdmin(context) || return val dpm = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager dpm.lockNow() } } - - } \ No newline at end of file diff --git a/app/src/main/java/de/jrpie/android/launcher/actions/lock/LockMethod.kt b/app/src/main/java/de/jrpie/android/launcher/actions/lock/LockMethod.kt new file mode 100644 index 0000000..4e1fb15 --- /dev/null +++ b/app/src/main/java/de/jrpie/android/launcher/actions/lock/LockMethod.kt @@ -0,0 +1,74 @@ +package de.jrpie.android.launcher.actions.lock + +import android.content.Context +import android.os.Build +import android.view.LayoutInflater +import androidx.appcompat.app.AlertDialog +import de.jrpie.android.launcher.R +import de.jrpie.android.launcher.preferences.LauncherPreferences + + +@Suppress("unused") +enum class LockMethod( + private val lock: (Context) -> Unit, + private val isEnabled: (Context) -> Boolean, + private val enable: (Context) -> Unit +) { + DEVICE_ADMIN( + LauncherDeviceAdmin::lockScreen, + LauncherDeviceAdmin::isDeviceAdmin, + LauncherDeviceAdmin::lockScreen + ), + ACCESSIBILITY_SERVICE( + LauncherAccessibilityService::lockScreen, + LauncherAccessibilityService::isEnabled, + LauncherAccessibilityService::lockScreen + ), + ; + + fun lockOrEnable(context: Context) { + if (!this.isEnabled(context)) { + chooseMethod(context) + return + } + this.lock(context) + } + + companion object { + fun chooseMethod(context: Context) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { + // only device admin is available + setMethod(context, DEVICE_ADMIN) + return + } + val builder = AlertDialog.Builder(context, R.style.AlertDialogCustom) + builder.setNegativeButton("cancel") { _, _ -> } + builder.setCustomTitle( + LayoutInflater.from(context).inflate(R.layout.dialog_select_lock_method, null) + ) + + builder.setItems( + arrayOf( + context.getString(R.string.screen_lock_method_use_accessibility), + context.getString(R.string.screen_lock_method_use_device_admin) + ) + ) { _, i -> + val method = when (i) { + 0 -> ACCESSIBILITY_SERVICE + 1 -> DEVICE_ADMIN + else -> return@setItems + } + setMethod(context, method) + } + builder.show() + + return + } + + private fun setMethod(context: Context, m: LockMethod) { + LauncherPreferences.actions().lockMethod(m) + if (!m.isEnabled(context)) + m.enable(context) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/de/jrpie/android/launcher/preferences/LauncherPreferences$Config.java b/app/src/main/java/de/jrpie/android/launcher/preferences/LauncherPreferences$Config.java index 904893b..c876fc3 100644 --- a/app/src/main/java/de/jrpie/android/launcher/preferences/LauncherPreferences$Config.java +++ b/app/src/main/java/de/jrpie/android/launcher/preferences/LauncherPreferences$Config.java @@ -5,6 +5,7 @@ import java.util.Set; import de.jrpie.android.launcher.R; import de.jrpie.android.launcher.apps.AppInfo; +import de.jrpie.android.launcher.actions.lock.LockMethod; import de.jrpie.android.launcher.preferences.theme.Background; import de.jrpie.android.launcher.preferences.theme.ColorTheme; import de.jrpie.android.launcher.preferences.theme.Font; @@ -62,6 +63,9 @@ import eu.jonahbauer.android.preference.annotations.serializer.PreferenceSeriali @Preference(name = "edge_swipe", type = boolean.class, defaultValue = "true"), @Preference(name = "edge_swipe_edge_width", type = int.class, defaultValue = "15"), }), + @PreferenceGroup(name = "actions", prefix = "settings_actions_", suffix = "_key", value = { + @Preference(name = "lock_method", type = LockMethod.class, defaultValue = "DEVICE_ADMIN"), + }), }) public final class LauncherPreferences$Config { public static class AppInfoSetSerializer implements PreferenceSerializer, Set> { diff --git a/app/src/main/java/de/jrpie/android/launcher/ui/settings/launcher/SettingsFragmentLauncher.kt b/app/src/main/java/de/jrpie/android/launcher/ui/settings/launcher/SettingsFragmentLauncher.kt index 1c6e0df..f945b5a 100644 --- a/app/src/main/java/de/jrpie/android/launcher/ui/settings/launcher/SettingsFragmentLauncher.kt +++ b/app/src/main/java/de/jrpie/android/launcher/ui/settings/launcher/SettingsFragmentLauncher.kt @@ -7,6 +7,7 @@ import androidx.preference.PreferenceFragmentCompat import de.jrpie.android.launcher.R import de.jrpie.android.launcher.actions.openAppsList import de.jrpie.android.launcher.preferences.LauncherPreferences +import de.jrpie.android.launcher.actions.lock.LockMethod import de.jrpie.android.launcher.setDefaultHomeScreen @@ -74,6 +75,15 @@ class SettingsFragmentLauncher : PreferenceFragmentCompat() { openAppsList(requireContext(), favorite = false, hidden = true) true } + + val lockMethod = findPreference( + LauncherPreferences.actions().keys().lockMethod() + ) + + lockMethod?.setOnPreferenceClickListener { + LockMethod.chooseMethod(requireContext()) + true + } updateVisibility() } } diff --git a/app/src/main/java/de/jrpie/android/launcher/ui/util/HtmlTextView.kt b/app/src/main/java/de/jrpie/android/launcher/ui/util/HtmlTextView.kt new file mode 100644 index 0000000..549f10f --- /dev/null +++ b/app/src/main/java/de/jrpie/android/launcher/ui/util/HtmlTextView.kt @@ -0,0 +1,18 @@ +package de.jrpie.android.launcher.ui.util + +import android.content.Context +import android.text.Html +import android.text.method.LinkMovementMethod +import android.util.AttributeSet +import androidx.appcompat.widget.AppCompatTextView + +class HtmlTextView(context: Context, attr: AttributeSet?, int: Int) : + AppCompatTextView(context, attr, int) { + constructor(context: Context, attr: AttributeSet?) : this(context, attr, 0) + constructor(context: Context) : this(context, null, 0) + + init { + text = Html.fromHtml(text.toString()) + movementMethod = LinkMovementMethod.getInstance() + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_select_lock_method.xml b/app/src/main/res/layout/dialog_select_lock_method.xml new file mode 100644 index 0000000..8f19e45 --- /dev/null +++ b/app/src/main/res/layout/dialog_select_lock_method.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index f81eca0..d3cac4b 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -102,6 +102,8 @@ functionality.search_auto_keyboard + settings_action_lock_method +