✨ feat(ui, accessibility): add double tap to lock screen feature with accessibility service support
This commit is contained in:
13 files changed
+671
-5
No files matched your search
@@ -15,6 +15,9 @@
|
||||
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"
|
||||
tools:ignore="ProtectedPermissions" />
|
||||
|
||||
<!-- Accessibility service permission for lock screen functionality -->
|
||||
<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:clearTaskOnLaunch="true"
|
||||
@@ -40,7 +43,18 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
|
||||
<!-- Accessibility service for lock screen functionality -->
|
||||
<service
|
||||
android:name=".services.LockScreenAccessibilityService"
|
||||
android:exported="false"
|
||||
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
|
||||
<intent-filter>
|
||||
<action android:name="android.accessibilityservice.AccessibilityService" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.accessibilityservice"
|
||||
android:resource="@xml/accessibility_service_config" />
|
||||
</service>
|
||||
|
||||
</application>
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ import java.util.Map;
|
||||
|
||||
import io.github.sankalp.lunox.dialogs.AppSettingsPopup;
|
||||
import io.github.sankalp.lunox.dialogs.ColorSizeDialog;
|
||||
import io.github.sankalp.lunox.dialogs.DoubleTapLockDialog;
|
||||
import io.github.sankalp.lunox.dialogs.FrozenAppsDialogs;
|
||||
import io.github.sankalp.lunox.dialogs.GlobalColorSizeDialog;
|
||||
import io.github.sankalp.lunox.dialogs.GlobalSettingsDialog;
|
||||
@@ -94,6 +95,7 @@ import io.github.sankalp.lunox.model.Shortcut;
|
||||
import io.github.sankalp.lunox.utils.CrashUtils;
|
||||
import io.github.sankalp.lunox.utils.DbUtils;
|
||||
|
||||
import io.github.sankalp.lunox.utils.AccessibilityUtils;
|
||||
import io.github.sankalp.lunox.utils.Gestures;
|
||||
import io.github.sankalp.lunox.utils.NotificationPanelManager;
|
||||
import io.github.sankalp.lunox.utils.ShortcutUtils;
|
||||
@@ -140,7 +142,8 @@ import static io.github.sankalp.lunox.utils.Constants.SORT_BY_UPDATE_TIME;
|
||||
@SuppressLint("NonConstantResourceId")
|
||||
public class LauncherActivity extends Activity implements View.OnClickListener,
|
||||
View.OnLongClickListener,
|
||||
Gestures.OnSwipeListener {
|
||||
Gestures.OnSwipeListener,
|
||||
Gestures.OnDoubleTapListener {
|
||||
|
||||
|
||||
//region Field declarations
|
||||
@@ -283,7 +286,7 @@ public class LauncherActivity extends Activity implements View.OnClickListener,
|
||||
//set padding ..
|
||||
mHomeLayout.setPadding(DbUtils.getPaddingLeft(), DbUtils.getPaddingTop(), DbUtils.getPaddingRight(), DbUtils.getPaddingBottom());
|
||||
|
||||
detector = new Gestures(this, this);
|
||||
detector = new Gestures(this, this, this);
|
||||
|
||||
// Initialize long press detector for empty areas
|
||||
longPressDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
|
||||
@@ -1211,6 +1214,27 @@ public class LauncherActivity extends Activity implements View.OnClickListener,
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDoubleTap(MotionEvent e) {
|
||||
// Check if double tap to lock is enabled
|
||||
if (!DbUtils.isDoubleTapToLockEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the double tap is on an empty area (not on an app)
|
||||
View touchedView = findViewAt(e.getX(), e.getY());
|
||||
if (!(touchedView instanceof AppTextView)) {
|
||||
// Attempt to lock screen using accessibility service
|
||||
boolean success = AccessibilityUtils.requestLockScreen(this);
|
||||
if (!success) {
|
||||
// Show message if accessibility service is not enabled
|
||||
if (!AccessibilityUtils.isAccessibilityServiceEnabled(this)) {
|
||||
showAccessibilityServiceDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1322,5 +1346,33 @@ public class LauncherActivity extends Activity implements View.OnClickListener,
|
||||
permissionDialog.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show dialog to enable accessibility service for double tap to lock
|
||||
*/
|
||||
private void showAccessibilityServiceDialog() {
|
||||
Context ctx;
|
||||
if (DbUtils.getTheme() == R.style.Wallpaper)
|
||||
ctx = new ContextThemeWrapper(this, R.style.AppTheme);
|
||||
else
|
||||
ctx = new ContextThemeWrapper(this, DbUtils.getTheme());
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
|
||||
builder.setTitle("Enable Accessibility Service");
|
||||
builder.setMessage("To use double tap to lock screen, please enable the Lunox Accessibility Service in Settings.");
|
||||
builder.setPositiveButton("Open Settings", (dialog, which) -> {
|
||||
AccessibilityUtils.openAccessibilitySettings(this);
|
||||
});
|
||||
builder.setNegativeButton("Cancel", null);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show detailed double tap to lock dialog
|
||||
*/
|
||||
private void showDoubleTapLockDialog() {
|
||||
dialogs = new DoubleTapLockDialog(this, this);
|
||||
dialogs.show();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Lunox Launcher
|
||||
* Copyright (C) 2024 Sankalp
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.sankalp.lunox.dialogs;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.widget.TextView;
|
||||
|
||||
import io.github.sankalp.lunox.LauncherActivity;
|
||||
import io.github.sankalp.lunox.R;
|
||||
import io.github.sankalp.lunox.utils.AccessibilityUtils;
|
||||
import io.github.sankalp.lunox.utils.DbUtils;
|
||||
|
||||
/**
|
||||
* Dialog for configuring double tap to lock screen functionality
|
||||
*/
|
||||
public class DoubleTapLockDialog extends Dialog implements View.OnClickListener {
|
||||
|
||||
private final LauncherActivity launcherActivity;
|
||||
private final Context context;
|
||||
private TextView enableButton;
|
||||
private TextView closeButton;
|
||||
private TextView mainDescription;
|
||||
private TextView additionalInfo;
|
||||
|
||||
public DoubleTapLockDialog(Context context, LauncherActivity launcherActivity) {
|
||||
super(context);
|
||||
this.context = context;
|
||||
this.launcherActivity = launcherActivity;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
setContentView(R.layout.dialog_double_tap_lock);
|
||||
|
||||
// Make background transparent for rounded corners
|
||||
if (getWindow() != null) {
|
||||
getWindow().setBackgroundDrawableResource(android.R.color.transparent);
|
||||
}
|
||||
|
||||
// Initialize views
|
||||
enableButton = findViewById(R.id.btn_enable);
|
||||
closeButton = findViewById(R.id.btn_close);
|
||||
mainDescription = findViewById(R.id.tv_main_description);
|
||||
additionalInfo = findViewById(R.id.tv_additional_info);
|
||||
|
||||
// Set click listeners
|
||||
enableButton.setOnClickListener(this);
|
||||
closeButton.setOnClickListener(this);
|
||||
|
||||
// Update content based on current state
|
||||
updateDialogContent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
if (hasFocus) {
|
||||
// Refresh dialog content when user returns from settings
|
||||
updateDialogContent();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
int id = view.getId();
|
||||
|
||||
if (id == R.id.btn_enable) {
|
||||
handleEnableButton();
|
||||
} else if (id == R.id.btn_close) {
|
||||
dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleEnableButton() {
|
||||
boolean isCurrentlyEnabled = DbUtils.isDoubleTapToLockEnabled();
|
||||
|
||||
if (isCurrentlyEnabled) {
|
||||
// Disable the feature
|
||||
DbUtils.setDoubleTapToLock(false);
|
||||
updateDialogContent();
|
||||
dismiss();
|
||||
} else {
|
||||
// Check if accessibility service is enabled before enabling the feature
|
||||
if (AccessibilityUtils.isAccessibilityServiceEnabled(context)) {
|
||||
// Service is enabled, so we can enable the feature
|
||||
DbUtils.setDoubleTapToLock(true);
|
||||
updateDialogContent();
|
||||
dismiss();
|
||||
} else {
|
||||
// Service is not enabled, open accessibility settings but don't enable the feature yet
|
||||
AccessibilityUtils.openAccessibilitySettings(context);
|
||||
// Keep the dialog open so user can try again after enabling the service
|
||||
updateDialogContent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDialogContent() {
|
||||
boolean isEnabled = DbUtils.isDoubleTapToLockEnabled();
|
||||
boolean isServiceEnabled = AccessibilityUtils.isAccessibilityServiceEnabled(context);
|
||||
|
||||
// Update button text based on current state
|
||||
if (isEnabled) {
|
||||
enableButton.setText("Disable");
|
||||
} else if (isServiceEnabled) {
|
||||
enableButton.setText("Enable");
|
||||
} else {
|
||||
enableButton.setText("Open Settings");
|
||||
}
|
||||
|
||||
// Update description based on current state
|
||||
if (isEnabled && isServiceEnabled) {
|
||||
mainDescription.setText("Double tap to lock is currently enabled and working. You can disable it below if you no longer want this feature.");
|
||||
additionalInfo.setText("The accessibility service is active and allows you to lock your screen by double tapping on empty areas of the launcher.");
|
||||
} else if (isEnabled && !isServiceEnabled) {
|
||||
mainDescription.setText("Double tap to lock is enabled but the accessibility service is not active. Please enable Lunox's accessibility service in your device's settings.");
|
||||
additionalInfo.setText("The accessibility service is required for the double tap to lock feature to work. It only locks your screen and does not collect any data.");
|
||||
} else if (!isEnabled && isServiceEnabled) {
|
||||
mainDescription.setText("The accessibility service is enabled. You can now enable double tap to lock to quickly lock your screen by double tapping on empty areas.");
|
||||
additionalInfo.setText("This feature will use the accessibility service to lock your screen when you double tap on empty areas of the launcher.");
|
||||
} else {
|
||||
mainDescription.setText("To enable double tap to lock, you first need to enable Lunox's accessibility service in your device's settings.");
|
||||
additionalInfo.setText("The accessibility service is required for this feature to work. It only locks your screen and does not collect or share any data. Click Enable to open the settings.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
package io.github.sankalp.lunox.dialogs;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -38,7 +39,9 @@ import java.util.Locale;
|
||||
import io.github.sankalp.lunox.BuildConfig;
|
||||
import io.github.sankalp.lunox.LauncherActivity;
|
||||
import io.github.sankalp.lunox.R;
|
||||
import io.github.sankalp.lunox.dialogs.DoubleTapLockDialog;
|
||||
import io.github.sankalp.lunox.model.Apps;
|
||||
import io.github.sankalp.lunox.utils.AccessibilityUtils;
|
||||
import io.github.sankalp.lunox.utils.Constants;
|
||||
import io.github.sankalp.lunox.utils.DbUtils;
|
||||
import io.github.sankalp.lunox.utils.Utils;
|
||||
@@ -57,6 +60,7 @@ public class GlobalSettingsDialog extends Dialog implements View.OnClickListener
|
||||
private final LauncherActivity launcherActivity;
|
||||
private final Context context;
|
||||
private TextView freezeSize;
|
||||
private TextView doubleTapLockButton;
|
||||
|
||||
public GlobalSettingsDialog(Context context, LauncherActivity launcherActivity) {
|
||||
super(context);
|
||||
@@ -102,6 +106,10 @@ public class GlobalSettingsDialog extends Dialog implements View.OnClickListener
|
||||
findViewById(R.id.settings_frozen_apps).setOnClickListener(this);
|
||||
findViewById(R.id.settings_hidden_apps).setOnClickListener(this);
|
||||
|
||||
// Double tap to lock setting
|
||||
doubleTapLockButton = findViewById(R.id.settings_double_tap_lock);
|
||||
doubleTapLockButton.setOnClickListener(this);
|
||||
|
||||
|
||||
//reflect the DB value
|
||||
if (DbUtils.isSizeFrozen()) {
|
||||
@@ -109,6 +117,9 @@ public class GlobalSettingsDialog extends Dialog implements View.OnClickListener
|
||||
} else
|
||||
freezeSize.setText(R.string.freeze_apps_size);
|
||||
|
||||
// Set double tap to lock button text
|
||||
updateDoubleTapLockButtonText();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -145,6 +156,8 @@ public class GlobalSettingsDialog extends Dialog implements View.OnClickListener
|
||||
cancel();
|
||||
} else if (id == R.id.settings_restart_launcher) {
|
||||
launcherActivity.recreate();
|
||||
} else if (id == R.id.settings_double_tap_lock) {
|
||||
toggleDoubleTapToLock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,6 +291,48 @@ public class GlobalSettingsDialog extends Dialog implements View.OnClickListener
|
||||
|
||||
}
|
||||
|
||||
private void toggleDoubleTapToLock() {
|
||||
boolean currentState = DbUtils.isDoubleTapToLockEnabled();
|
||||
|
||||
if (currentState) {
|
||||
// If currently enabled, just disable it directly
|
||||
DbUtils.setDoubleTapToLock(false);
|
||||
updateDoubleTapLockButtonText();
|
||||
} else {
|
||||
// If currently disabled, show the detailed dialog to explain and enable the feature
|
||||
DoubleTapLockDialog doubleTapDialog = new DoubleTapLockDialog(context, launcherActivity);
|
||||
doubleTapDialog.show();
|
||||
|
||||
// Update button text when dialog is dismissed
|
||||
doubleTapDialog.setOnDismissListener(dialog -> updateDoubleTapLockButtonText());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDoubleTapLockButtonText() {
|
||||
if (DbUtils.isDoubleTapToLockEnabled()) {
|
||||
doubleTapLockButton.setText(R.string.double_tap_to_lock_enabled);
|
||||
} else {
|
||||
doubleTapLockButton.setText(R.string.double_tap_to_lock_disabled);
|
||||
}
|
||||
}
|
||||
|
||||
private void showAccessibilityServiceDialog() {
|
||||
Context ctx;
|
||||
if (DbUtils.getTheme() == R.style.Wallpaper)
|
||||
ctx = new ContextThemeWrapper(getContext(), R.style.AppTheme);
|
||||
else
|
||||
ctx = new ContextThemeWrapper(getContext(), DbUtils.getTheme());
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
|
||||
builder.setTitle("Enable Accessibility Service");
|
||||
builder.setMessage("To use double tap to lock screen, please enable the Lunox Accessibility Service in Settings.");
|
||||
builder.setPositiveButton("Open Settings", (dialog, which) -> {
|
||||
AccessibilityUtils.openAccessibilitySettings(context);
|
||||
});
|
||||
builder.setNegativeButton("Cancel", null);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void backup() {
|
||||
cancel();
|
||||
Intent intentBackupFiles;
|
||||
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Lunox Launcher
|
||||
* Copyright (C) 2024 Sankalp
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.sankalp.lunox.services;
|
||||
|
||||
import android.accessibilityservice.AccessibilityService;
|
||||
import android.accessibilityservice.GestureDescription;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Path;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
/**
|
||||
* Accessibility service for lock screen functionality
|
||||
* This service can perform global actions like simulating power key press
|
||||
*/
|
||||
public class LockScreenAccessibilityService extends AccessibilityService {
|
||||
|
||||
private static final String TAG = "LockScreenAccessibilityService";
|
||||
private static LockScreenAccessibilityService instance;
|
||||
|
||||
public static final String ACTION_LOCK_SCREEN = "io.github.sankalp.lunox.ACTION_LOCK_SCREEN";
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
instance = this;
|
||||
Log.d(TAG, "Accessibility service created");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
instance = null;
|
||||
Log.d(TAG, "Accessibility service destroyed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccessibilityEvent(AccessibilityEvent event) {
|
||||
// We don't need to handle accessibility events for this use case
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInterrupt() {
|
||||
Log.d(TAG, "Accessibility service interrupted");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onKeyEvent(KeyEvent event) {
|
||||
return super.onKeyEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if (intent != null && ACTION_LOCK_SCREEN.equals(intent.getAction())) {
|
||||
lockScreen();
|
||||
}
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock the screen using accessibility service global action
|
||||
*/
|
||||
public void lockScreen() {
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
// Android 9+ supports GLOBAL_ACTION_LOCK_SCREEN
|
||||
boolean success = performGlobalAction(GLOBAL_ACTION_LOCK_SCREEN);
|
||||
Log.d(TAG, "Lock screen action performed: " + success);
|
||||
if (!success) {
|
||||
// Fallback to power button simulation
|
||||
simulatePowerButton();
|
||||
}
|
||||
} else {
|
||||
// For older versions, simulate power button press
|
||||
simulatePowerButton();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to lock screen", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate power button press using accessibility service
|
||||
*/
|
||||
private void simulatePowerButton() {
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
// Use gesture simulation to simulate power button
|
||||
// This is a workaround since we can't directly simulate hardware keys
|
||||
// We'll use the global action for power dialog which can lock the screen
|
||||
boolean success = performGlobalAction(GLOBAL_ACTION_POWER_DIALOG);
|
||||
Log.d(TAG, "Power dialog action performed: " + success);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to simulate power button", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current instance of the accessibility service
|
||||
*/
|
||||
public static LockScreenAccessibilityService getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the accessibility service is running
|
||||
*/
|
||||
public static boolean isServiceRunning() {
|
||||
return instance != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Lunox Launcher
|
||||
* Copyright (C) 2024 Sankalp
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.github.sankalp.lunox.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import io.github.sankalp.lunox.services.LockScreenAccessibilityService;
|
||||
|
||||
/**
|
||||
* Utility class for accessibility service operations
|
||||
*/
|
||||
public class AccessibilityUtils {
|
||||
|
||||
private static final String TAG = "AccessibilityUtils";
|
||||
|
||||
/**
|
||||
* Check if our accessibility service is enabled
|
||||
*/
|
||||
public static boolean isAccessibilityServiceEnabled(Context context) {
|
||||
AccessibilityManager am = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
|
||||
if (am == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if accessibility is enabled and our service is in the enabled services list
|
||||
String enabledServices = Settings.Secure.getString(
|
||||
context.getContentResolver(),
|
||||
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
|
||||
);
|
||||
|
||||
if (enabledServices == null || enabledServices.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String packageName = context.getPackageName();
|
||||
String serviceName = LockScreenAccessibilityService.class.getName();
|
||||
String fullServiceName = packageName + "/" + serviceName;
|
||||
|
||||
// Check if our service is in the enabled services list
|
||||
return enabledServices.contains(fullServiceName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open accessibility settings to enable our service
|
||||
*/
|
||||
public static void openAccessibilitySettings(Context context) {
|
||||
try {
|
||||
Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to open accessibility settings", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request lock screen action through accessibility service
|
||||
*/
|
||||
public static boolean requestLockScreen(Context context) {
|
||||
if (!isAccessibilityServiceEnabled(context)) {
|
||||
Log.w(TAG, "Accessibility service not enabled");
|
||||
return false;
|
||||
}
|
||||
|
||||
LockScreenAccessibilityService service = LockScreenAccessibilityService.getInstance();
|
||||
if (service != null) {
|
||||
service.lockScreen();
|
||||
return true;
|
||||
} else {
|
||||
// Try to start the service with intent
|
||||
try {
|
||||
Intent intent = new Intent(context, LockScreenAccessibilityService.class);
|
||||
intent.setAction(LockScreenAccessibilityService.ACTION_LOCK_SCREEN);
|
||||
context.startService(intent);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to start accessibility service", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if accessibility service is running
|
||||
*/
|
||||
public static boolean isServiceRunning() {
|
||||
return LockScreenAccessibilityService.isServiceRunning();
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,7 @@ public class DbUtils {
|
||||
private static final String APPS_COLORS_DEFAULT = "apps_color_default";
|
||||
private static final String APPS_SORTS_TYPE = "apps_sorts_types";
|
||||
private static final String PERMISSION_DIALOG_SHOWN = "permission_dialog_shown";
|
||||
private static final String DOUBLE_TAP_TO_LOCK = "double_tap_to_lock";
|
||||
|
||||
|
||||
public static void init(Context context) {
|
||||
@@ -392,6 +393,20 @@ public class DbUtils {
|
||||
SpUtils.getInstance().putBoolean(PERMISSION_DIALOG_SHOWN, shown);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if double tap to lock is enabled
|
||||
*/
|
||||
public static boolean isDoubleTapToLockEnabled() {
|
||||
return SpUtils.getInstance().getBoolean(DOUBLE_TAP_TO_LOCK, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set double tap to lock preference
|
||||
*/
|
||||
public static void setDoubleTapToLock(boolean enabled) {
|
||||
SpUtils.getInstance().putBoolean(DOUBLE_TAP_TO_LOCK, enabled);
|
||||
}
|
||||
|
||||
// a simple ciphered counter: "opening counter" is a private thing
|
||||
// rest is on device security
|
||||
private static String codeCount(int count) {
|
||||
|
||||
@@ -30,12 +30,20 @@ public class Gestures extends GestureDetector.SimpleOnGestureListener {
|
||||
|
||||
private final GestureDetector detector;
|
||||
private final OnSwipeListener listener;
|
||||
private final OnDoubleTapListener doubleTapListener;
|
||||
|
||||
public Gestures(Activity context,
|
||||
OnSwipeListener onSwipeListener) {
|
||||
this(context, onSwipeListener, null);
|
||||
}
|
||||
|
||||
public Gestures(Activity context,
|
||||
OnSwipeListener onSwipeListener,
|
||||
OnDoubleTapListener onDoubleTapListener) {
|
||||
|
||||
this.detector = new GestureDetector(context, this);
|
||||
this.listener = onSwipeListener;
|
||||
this.doubleTapListener = onDoubleTapListener;
|
||||
}
|
||||
|
||||
public void onTouchEvent(MotionEvent event) {
|
||||
@@ -73,8 +81,14 @@ public class Gestures extends GestureDetector.SimpleOnGestureListener {
|
||||
return consumed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onDoubleTap(MotionEvent e) {
|
||||
if (doubleTapListener != null) {
|
||||
doubleTapListener.onDoubleTap(e);
|
||||
return true;
|
||||
}
|
||||
return super.onDoubleTap(e);
|
||||
}
|
||||
|
||||
public enum Direction {
|
||||
SWIPE_UP, SWIPE_DOWN, SWIPE_LEFT, SWIPE_RIGHT
|
||||
@@ -84,4 +98,8 @@ public class Gestures extends GestureDetector.SimpleOnGestureListener {
|
||||
void onSwipe(Direction direction);
|
||||
}
|
||||
|
||||
public interface OnDoubleTapListener {
|
||||
void onDoubleTap(MotionEvent e);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#DD000000" />
|
||||
<corners android:radius="16dp" />
|
||||
</shape>
|
||||
@@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:background="@drawable/dialog_background"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<!-- Title -->
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:gravity="start"
|
||||
android:text="Double tap to lock"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<!-- Main description -->
|
||||
<TextView
|
||||
android:id="@+id/tv_main_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:lineSpacingExtra="4dp"
|
||||
android:text="Please turn on or off Lunox's accessibility service in your device's settings to enable or disable this feature."
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<!-- Additional information -->
|
||||
<TextView
|
||||
android:id="@+id/tv_additional_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:lineSpacingExtra="4dp"
|
||||
android:text="Please note that this service is only used to let you turn off your device's screen using the double tap gesture. Our accessibility service does not collect or share any data."
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<!-- Buttons container -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<!-- Close button -->
|
||||
<TextView
|
||||
android:id="@+id/btn_close"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:padding="12dp"
|
||||
android:text="Close"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<!-- Enable/Disable button -->
|
||||
<TextView
|
||||
android:id="@+id/btn_enable"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:padding="12dp"
|
||||
android:text="Enable"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -208,6 +208,23 @@
|
||||
android:background="#DEDEDE"
|
||||
android:foregroundGravity="center_vertical|center_horizontal" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/settings_double_tap_lock"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="5dp"
|
||||
android:text="@string/double_tap_to_lock"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<View
|
||||
android:layout_width="170dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_gravity="center|center_horizontal|center_vertical"
|
||||
android:background="#DEDEDE"
|
||||
android:foregroundGravity="center_vertical|center_horizontal" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/settings_backup"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -104,4 +104,12 @@
|
||||
<string name="from_colorsniffer_app">From ColorSniffer App</string>
|
||||
<!-- Size setting for individual app entry -->
|
||||
<string name="size_for_this_entry">Size for this entry</string>
|
||||
|
||||
<!-- Double tap to lock screen -->
|
||||
<string name="double_tap_to_lock">Double Tap Lock</string>
|
||||
<string name="double_tap_to_lock_enabled">Double Tap Lock:On</string>
|
||||
<string name="double_tap_to_lock_disabled">Double Tap Lock:Off</string>
|
||||
|
||||
<!-- Accessibility service description -->
|
||||
<string name="accessibility_service_description">Lunox Accessibility Service for lock screen functionality. This service allows double tap to lock the screen.</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accessibilityEventTypes="typeAllMask"
|
||||
android:accessibilityFeedbackType="feedbackGeneric"
|
||||
android:accessibilityFlags="flagDefault"
|
||||
android:canPerformGestures="true"
|
||||
android:canRetrieveWindowContent="false"
|
||||
android:description="@string/accessibility_service_description"
|
||||
android:notificationTimeout="100"
|
||||
android:packageNames="io.github.sankalp.lunox"
|
||||
android:settingsActivity="io.github.sankalp.lunox.LauncherActivity" />
|
||||
Reference in new issue
Block a user