Compare commits

...

6 commits

Author SHA1 Message Date
653d16b269
new action: launch other launchers
Some checks failed
Android CI / build (push) Has been cancelled
2025-03-29 21:09:15 +01:00
5d695ec0ea
fix #135 2025-03-29 18:45:53 +01:00
b4608ef153
add new action: show recent apps
Some checks failed
Android CI / build (push) Has been cancelled
2025-03-24 13:21:58 +01:00
8e140e2e69
rename tab "Apps" to "Actions" and "Volume Up/Down" to "Volume Up/Down Key"
Some checks failed
Android CI / build (push) Has been cancelled
2025-03-20 16:23:01 +01:00
7fc58fe384
0.1.3 2025-03-20 15:52:12 +01:00
54409b6312
fix #133 2025-03-20 14:55:22 +01:00
14 changed files with 130 additions and 40 deletions

View file

@ -23,8 +23,8 @@ android {
minSdkVersion 21
targetSdkVersion 35
compileSdk 35
versionCode 42
versionName "0.1.2"
versionCode 43
versionName "0.1.3"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

View file

@ -85,7 +85,7 @@
<service
android:name=".actions.lock.LauncherAccessibilityService"
android:exported="true"
android:label="@string/accessibility_service_name"
android:label="@string/app_name"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
@ -97,4 +97,4 @@
</service>
</application>
</manifest>
</manifest>

View file

@ -38,6 +38,8 @@ import androidx.core.net.toUri
const val LOG_TAG = "Launcher"
const val REQUEST_SET_DEFAULT_HOME = 42
fun isDefaultHomeScreen(context: Context): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val roleManager = context.getSystemService(RoleManager::class.java)
@ -59,11 +61,12 @@ fun setDefaultHomeScreen(context: Context, checkDefault: Boolean = false) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
&& context is Activity
&& !isDefault // using role manager only works when µLauncher is not already the default.
&& checkDefault // using role manager only works when µLauncher is not already the default.
) {
val roleManager = context.getSystemService(RoleManager::class.java)
context.startActivity(
roleManager.createRequestRoleIntent(RoleManager.ROLE_HOME)
context.startActivityForResult(
roleManager.createRequestRoleIntent(RoleManager.ROLE_HOME),
REQUEST_SET_DEFAULT_HOME
)
return
}

View file

@ -11,7 +11,9 @@ import android.view.KeyEvent
import android.widget.Toast
import androidx.appcompat.content.res.AppCompatResources
import de.jrpie.android.launcher.Application
import de.jrpie.android.launcher.BuildConfig
import de.jrpie.android.launcher.R
import de.jrpie.android.launcher.actions.lock.LauncherAccessibilityService
import de.jrpie.android.launcher.apps.AppFilter
import de.jrpie.android.launcher.apps.hidePrivateSpaceWhenLocked
import de.jrpie.android.launcher.apps.isPrivateSpaceSupported
@ -132,6 +134,14 @@ enum class LauncherAction(
R.drawable.baseline_settings_applications_24,
::expandSettingsPanel
),
RECENT_APPS(
"recent_apps",
R.string.list_other_recent_apps,
R.drawable.baseline_apps_24,
LauncherAccessibilityService::openRecentApps,
false,
{ _ -> BuildConfig.USE_ACCESSIBILITY_SERVICE }
),
LOCK_SCREEN(
"lock_screen",
R.string.list_other_lock_screen,
@ -142,7 +152,13 @@ enum class LauncherAction(
"toggle_torch",
R.string.list_other_torch,
R.drawable.baseline_flashlight_on_24,
::toggleTorch
::toggleTorch,
),
LAUNCH_OTHER_LAUNCHER(
"launcher_other_launcher",
R.string.list_other_launch_other_launcher,
R.drawable.baseline_home_24,
::launchOtherLauncher
),
NOP("nop", R.string.list_other_nop, R.drawable.baseline_not_interested_24, {});
@ -248,6 +264,15 @@ private fun expandSettingsPanel(context: Context) {
}
}
private fun launchOtherLauncher(context: Context) {
context.startActivity(
Intent.createChooser(
Intent(Intent.ACTION_MAIN).also { it.addCategory(Intent.CATEGORY_HOME) },
context.getString(R.string.list_other_launch_other_launcher)
)
)
}
private fun openSettings(context: Context) {
context.startActivity(Intent(context, SettingsActivity::class.java))
}

View file

@ -22,26 +22,44 @@ class LauncherAccessibilityService : AccessibilityService() {
companion object {
private const val TAG = "Launcher Accessibility"
private const val ACTION_REQUEST_ENABLE = "ACTION_REQUEST_ENABLE"
const val ACTION_LOCK_SCREEN = "ACTION_LOCK_SCREEN"
const val ACTION_RECENT_APPS = "ACTION_RECENT_APPS"
fun lockScreen(context: Context) {
private fun invoke(context: Context, action: String, failureMessageRes: Int) {
try {
context.startService(
Intent(
context,
LauncherAccessibilityService::class.java
).apply {
action = ACTION_LOCK_SCREEN
this.action = action
})
} catch (e: Exception) {
} catch (_: Exception) {
Toast.makeText(
context,
context.getString(R.string.alert_lock_screen_failed),
context.getString(failureMessageRes),
Toast.LENGTH_LONG
).show()
}
}
fun lockScreen(context: Context) {
if (!isEnabled(context)) {
showEnableDialog(context)
} else {
invoke(context, ACTION_LOCK_SCREEN, R.string.alert_lock_screen_failed)
}
}
fun openRecentApps(context: Context) {
if (!isEnabled(context)) {
showEnableDialog(context)
} else {
invoke(context, ACTION_RECENT_APPS, R.string.alert_recent_apps_failed)
}
}
fun isEnabled(context: Context): Boolean {
val enabledServices = Settings.Secure.getString(
context.contentResolver,
@ -58,7 +76,7 @@ class LauncherAccessibilityService : AccessibilityService() {
setView(R.layout.dialog_consent_accessibility)
setTitle(R.string.dialog_consent_accessibility_title)
setPositiveButton(R.string.dialog_consent_accessibility_ok) { _, _ ->
lockScreen(context)
invoke(context, ACTION_REQUEST_ENABLE, R.string.alert_enable_accessibility_failed)
}
setNegativeButton(R.string.dialog_cancel) { _, _ -> }
}.create().also { it.show() }.apply {
@ -94,7 +112,9 @@ class LauncherAccessibilityService : AccessibilityService() {
}
when (action) {
ACTION_REQUEST_ENABLE -> {} // do nothing
ACTION_LOCK_SCREEN -> handleLockScreen()
ACTION_RECENT_APPS -> performGlobalAction(GLOBAL_ACTION_RECENTS)
}
}
return super.onStartCommand(intent, flags, startId)

View file

@ -6,6 +6,7 @@ import android.widget.Button
import androidx.appcompat.app.AlertDialog
import de.jrpie.android.launcher.BuildConfig
import de.jrpie.android.launcher.R
import de.jrpie.android.launcher.actions.lock.LauncherAccessibilityService
import de.jrpie.android.launcher.preferences.LauncherPreferences

View file

@ -2,10 +2,10 @@ package de.jrpie.android.launcher.ui
import android.annotation.SuppressLint
import android.content.SharedPreferences
import android.content.res.Configuration
import android.content.res.Resources
import android.os.Build
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
@ -56,22 +56,11 @@ class HomeActivity : UIObject, AppCompatActivity() {
super<AppCompatActivity>.onCreate(savedInstanceState)
super<UIObject>.onCreate()
val displayMetrics = DisplayMetrics()
@Suppress("deprecation") // required to support API < 30
windowManager.defaultDisplay.getMetrics(displayMetrics)
val width = displayMetrics.widthPixels
val height = displayMetrics.heightPixels
touchGestureDetector = TouchGestureDetector(
this,
width,
height,
this, 0, 0,
LauncherPreferences.enabled_gestures().edgeSwipeEdgeWidth() / 100f
)
touchGestureDetector.updateScreenSize(windowManager)
// Initialise layout
binding = HomeBinding.inflate(layoutInflater)
@ -103,6 +92,11 @@ class HomeActivity : UIObject, AppCompatActivity() {
}
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
touchGestureDetector.updateScreenSize(windowManager)
}
override fun onStart() {
super<AppCompatActivity>.onStart()

View file

@ -5,8 +5,10 @@ import android.graphics.Insets
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.DisplayMetrics
import android.view.MotionEvent
import android.view.ViewConfiguration
import android.view.WindowManager
import androidx.annotation.RequiresApi
import de.jrpie.android.launcher.actions.Gesture
import de.jrpie.android.launcher.preferences.LauncherPreferences
@ -17,8 +19,8 @@ import kotlin.math.tan
class TouchGestureDetector(
private val context: Context,
val width: Int,
val height: Int,
var width: Int,
var height: Int,
var edgeWidth: Float
) {
private val ANGULAR_THRESHOLD = tan(Math.PI / 6)
@ -319,6 +321,14 @@ class TouchGestureDetector(
}
}
fun updateScreenSize(windowManager: WindowManager) {
val displayMetrics = DisplayMetrics()
@Suppress("deprecation") // required to support API < 30
windowManager.defaultDisplay.getMetrics(displayMetrics)
width = displayMetrics.widthPixels
height = displayMetrics.heightPixels
}
@RequiresApi(Build.VERSION_CODES.Q)
fun setSystemGestureInsets(insets: Insets) {
systemGestureInsetTop = insets.top

View file

@ -109,7 +109,7 @@ class SettingsActivity : AppCompatActivity(), UIObject {
}
private val TAB_TITLES = arrayOf(
R.string.settings_tab_app,
R.string.settings_tab_actions,
R.string.settings_tab_launcher,
R.string.settings_tab_meta
)

View file

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?android:textColor"
android:pathData="M4,8h4L8,4L4,4v4zM10,20h4v-4h-4v4zM4,20h4v-4L4,16v4zM4,14h4v-4L4,10v4zM10,14h4v-4h-4v4zM16,4v4h4L20,4h-4zM10,8h4L14,4h-4v4zM16,14h4v-4h-4v4zM16,20h4v-4h-4v4z" />
</vector>

View file

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?android:textColor"
android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z" />
</vector>

View file

@ -14,7 +14,7 @@
-
-->
<string name="settings_title">Einstellungen</string>
<string name="settings_tab_app">Apps</string>
<string name="settings_tab_actions">Aktionen</string>
<string name="settings_tab_launcher">Launcher</string>
<string name="settings_tab_meta">Meta</string>
<!--
@ -54,9 +54,9 @@
<string name="settings_gesture_description_down_left_edge">An der linken Kante nach unten wischen</string>
<string name="settings_gesture_down_right_edge">Abwärts (rechts)</string>
<string name="settings_gesture_description_down_right_edge">An der rechten Kanten nach unten wischen</string>
<string name="settings_gesture_vol_up">Lautstärke +</string>
<string name="settings_gesture_vol_up">Lauter-Taste</string>
<string name="settings_gesture_description_vol_up">Die Taste \"Lauter\" drücken</string>
<string name="settings_gesture_vol_down">Lautstärke -</string>
<string name="settings_gesture_vol_down">Leiser-Taste</string>
<string name="settings_gesture_description_vol_down">Die Taste \"Leiser\" drücken</string>
<string name="settings_gesture_double_click">Doppelklick</string>
<string name="settings_gesture_description_double_click">In einem leeren Bereich doppelt klicken</string>

View file

@ -16,7 +16,7 @@
-->
<string name="settings_title">Settings</string>
<string name="settings_tab_app">Apps</string>
<string name="settings_tab_actions">Actions</string>
<string name="settings_tab_launcher">Launcher</string>
<string name="settings_tab_meta">Meta</string>
@ -85,9 +85,9 @@
<string name="settings_gesture_swipe_lambda_reverse">Λ (Reverse)</string>
<string name="settings_gesture_description_swipe_lambda_reverse">Bottom right -> top mid -> bottom left</string>
<string name="settings_gesture_vol_up">Volume Up</string>
<string name="settings_gesture_vol_up">Volume Up Key</string>
<string name="settings_gesture_description_vol_up">Press the volume up button</string>
<string name="settings_gesture_vol_down">Volume Down</string>
<string name="settings_gesture_vol_down">Volume Down Key</string>
<string name="settings_gesture_description_vol_down">Press the volume down button</string>
<string name="settings_gesture_double_click">Double Click</string>
<string name="settings_gesture_description_double_click">Double click an empty area</string>
@ -252,9 +252,11 @@
<string name="list_other_track_previous">Music: Previous</string>
<string name="list_other_track_play_pause">Music: Play / Pause</string>
<string name="list_other_expand_notifications_panel">Expand notifications panel</string>
<string name="list_other_recent_apps">Recent Apps</string>
<string name="list_other_nop">Do nothing</string>
<string name="list_other_lock_screen">Lock Screen</string>
<string name="list_other_torch">Toggle Torch</string>
<string name="list_other_launch_other_launcher">Launch other Home Screen</string>
<!-- Pin shortcuts -->
<string name="pin_shortcut_title">Add Shortcut</string>
@ -307,6 +309,8 @@
<string name="alert_no_torch_found">No camera with torch detected.</string>
<string name="alert_torch_access_exception">Error: Can\'t access torch.</string>
<string name="alert_lock_screen_failed">Error: Failed to lock screen. (If you just upgraded the app, try to disable and re-enable the accessibility service in phone settings)</string>
<string name="alert_recent_apps_failed">Error: Failed to show recent apps. (If you just upgraded the app, try to disable and re-enable the accessibility service in phone settings)</string>
<string name="alert_enable_accessibility_failed">Error: Failed to enable the accessibility service.</string>
<string name="toast_accessibility_service_not_enabled">μLauncher\'s accessibility service is not enabled. Please enable it in settings</string>
<string name="toast_private_space_locked">Private space locked</string>
<string name="toast_private_space_unlocked">Private space unlocked</string>
@ -315,12 +319,17 @@
<string name="tooltip_lock_private_space">Lock private space</string>
<string name="tooltip_unlock_private_space">Unlock private space</string>
<string name="toast_lock_screen_not_supported">Error: Locking the screen using accessibility is not supported on this device. Please use device admin instead.</string>
<string name="accessibility_service_name">μLauncher - lock screen</string>
<string name="accessibility_service_name">μLauncher</string>
<string name="accessibility_service_description">
Setting μLauncher as an accessibility service allows it to lock the screen.
Setting μLauncher as an accessibility service allows it to lock the screen and open the recent apps menu.
Note that excessive permissions are required. You should never grant such permissions lightly to any app.
μLauncher will use the accessibility service only for locking the screen. You can check the source code to make sure.
μLauncher will use the accessibility service only for performing the following actions when requested by the user:
* lock screen
* open recent apps
μLauncher will never use the accessibility service to collect data. You can check the source code to make sure.
Note that locking the screen can also be accomplished by granting μLauncher device administrator permissions. However that method doesn\'t work with fingerprint and face unlock.
</string>
@ -365,7 +374,12 @@
<string name="dialog_consent_accessibility_other_options">I am aware that other options exist (using device administrator privileges or the power button).</string>
<string name="dialog_consent_accessibility_consent">I consent to μLauncher using the accessibility service to provide functionality unrelated to accessibility.</string>
<string name="dialog_consent_accessibility_data_collection">I consent to μLauncher not collecting any data.</string>
<string name="dialog_consent_accessibility_text"><![CDATA[You are about to activate the accessibility service. This will grant <strong>far-reaching privileges</strong> to μLauncher.<br/>μLauncher will use these privileges <strong>only to lock the screen</strong>. μLauncher <strong>will never collect any data</strong>. In particular, μLauncher does not use the accessibility service to collect any data.]]></string>
<string name="dialog_consent_accessibility_text"><![CDATA[You are about to activate the accessibility service. This will grant <strong>far-reaching privileges</strong> to μLauncher.<br/>μLauncher will use these privileges <strong>only</strong> to perform the following actions:
<ul>
<li>Lock Screen</li>
<li>Recent Apps</li>
</ul>
μLauncher <strong>will never collect any data</strong>. In particular, μLauncher does not use the accessibility service to collect any data.]]></string>
<string name="dialog_consent_accessibility_title">Activating the Accessibility Service</string>
<string name="dialog_consent_accessibility_ok">Activate Accessibility Service</string>
<string name="dialog_cancel">Cancel</string>

View file

@ -0,0 +1 @@
* Fixed gesture detection in landscape orientation