✨ feat(ui, accessibility): add double tap to lock screen feature with accessibility service support

This commit is contained in:
sankalp committed 2025-06-30 13:34:31 +05:45
1 parent 93f37ba201
commit 5cea05828d
13 files changed
+671 -5

No files matched your search

+15 -1
View File
@@ -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;
@@ -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"
+8
View File
@@ -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" />