pipa: peripheralmanager: Revert many keyboard/pen changes
* will be reimplemented later.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (C) 2023-2025 The LineageOS Project
|
||||
// Copyright (C) 2023 The LineageOS Project
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
@@ -46,10 +46,4 @@ cc_binary {
|
||||
"liblog",
|
||||
"libsensorndkbridge",
|
||||
],
|
||||
|
||||
arch: {
|
||||
arm: {
|
||||
cflags: ["-mfpu=neon", "-mfloat-abi=softfp"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2023-2025 The LineageOS Project
|
||||
Copyright (C) 2023 The LineageOS Project
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
@@ -43,31 +43,5 @@
|
||||
android:resource="@string/stylus_summary" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".KeyboardSettingsActivity"
|
||||
android:label="@string/keyboard_title"
|
||||
android:theme="@style/Theme.SubSettingsBase">
|
||||
<intent-filter>
|
||||
<action android:name="com.android.settings.action.IA_SETTINGS" />
|
||||
</intent-filter>
|
||||
<meta-data android:name="com.android.settings.category"
|
||||
android:value="com.android.settings.category.ia.connect" />
|
||||
<meta-data android:name="com.android.settings.summary"
|
||||
android:resource="@string/keyboard_summary" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".LidSettingsActivity"
|
||||
android:label="@string/lid_title"
|
||||
android:theme="@style/Theme.SubSettingsBase">
|
||||
<intent-filter>
|
||||
<action android:name="com.android.settings.action.IA_SETTINGS" />
|
||||
</intent-filter>
|
||||
<meta-data android:name="com.android.settings.category"
|
||||
android:value="com.android.settings.category.ia.connect" />
|
||||
<meta-data android:name="com.android.settings.summary"
|
||||
android:resource="@string/lid_summary" />
|
||||
</activity>
|
||||
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -14,16 +14,4 @@
|
||||
<string name="stylus_switch_title">Force recognize stylus</string>
|
||||
<string name="stylus_switch_summary">Enable this settings to allow using third party styluses</string>
|
||||
|
||||
<!-- Xiaomi Keyboard -->
|
||||
<string name="keyboard_title">Keyboard</string>
|
||||
<string name="keyboard_summary">Keyboard Settings</string>
|
||||
<string name="keyboard_switch_title">Toggle angle detection</string>
|
||||
<string name="keyboard_switch_summary">This toggles the angle detection of the keyboard</string>
|
||||
|
||||
<!-- Smart Cover -->
|
||||
<string name="lid_title">Smart Cover</string>
|
||||
<string name="lid_summary">Smart Cover Settings</string>
|
||||
<string name="lid_switch_title">Toggle Smart Cover behaivor</string>
|
||||
<string name="lid_switch_summary">This toggles the behaivor of the smart lid</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2018 The LineageOS Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:title="@string/stylus_title">
|
||||
|
||||
<SwitchPreference
|
||||
android:key="keyboard_switch_key"
|
||||
android:defaultValue="false"
|
||||
android:title="@string/keyboard_switch_title"
|
||||
android:summary="@string/keyboard_switch_summary"/>
|
||||
|
||||
</PreferenceScreen>
|
||||
@@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2018 The LineageOS Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:title="@string/stylus_title">
|
||||
|
||||
<SwitchPreference
|
||||
android:key="lid_switch_key"
|
||||
android:defaultValue="true"
|
||||
android:title="@string/lid_switch_title"
|
||||
android:summary="@string/lid_switch_summary"/>
|
||||
|
||||
</PreferenceScreen>
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2025 The LineageOS Project
|
||||
* Copyright (C) 2023 The LineageOS Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -9,57 +9,20 @@ package org.lineageos.xiaomiperipheralmanager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.SystemProperties;
|
||||
import android.util.Log;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* BroadcastReceiver that initializes peripheral managers on boot
|
||||
*/
|
||||
public class BootCompletedReceiver extends BroadcastReceiver {
|
||||
|
||||
private static final String TAG = "XiaomiPeripheralManager";
|
||||
private static final boolean DEBUG = SystemProperties.getBoolean("persist.xiaomi.peripherals.debug", false);
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
@Override
|
||||
public void onReceive(final Context context, Intent intent) {
|
||||
if (!intent.getAction().equals(Intent.ACTION_LOCKED_BOOT_COMPLETED)) {
|
||||
return;
|
||||
}
|
||||
|
||||
logInfo("Device boot completed, initializing peripheral services");
|
||||
|
||||
try {
|
||||
KeyboardUtils.setup(context);
|
||||
logInfo("Keyboard service initialized");
|
||||
} catch (Exception e) {
|
||||
logError("Failed to initialize keyboard service: " + e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
PenUtils.setup(context);
|
||||
logInfo("Pen service initialized");
|
||||
} catch (Exception e) {
|
||||
logError("Failed to initialize pen service: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void logDebug(String message) {
|
||||
if (DEBUG) Log.d(TAG, getTimestamp() + message);
|
||||
}
|
||||
|
||||
private void logInfo(String message) {
|
||||
Log.i(TAG, getTimestamp() + message);
|
||||
}
|
||||
|
||||
private void logError(String message) {
|
||||
Log.e(TAG, getTimestamp() + message);
|
||||
}
|
||||
|
||||
private String getTimestamp() {
|
||||
return "[" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US).format(new Date()) + "] ";
|
||||
if (DEBUG) Log.d(TAG, "Received boot completed intent");
|
||||
KeyboardUtils.setup(context);
|
||||
PenUtils.setup(context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2025 The LineageOS Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.lineageos.xiaomiperipheralmanager;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
|
||||
|
||||
/**
|
||||
* Settings activity for stylus/pen configuration
|
||||
* Hosts the StylusSettingsFragment for user configuration
|
||||
*/
|
||||
public class KeyboardSettingsActivity extends CollapsingToolbarBaseActivity {
|
||||
|
||||
private static final String TAG = "XiaomiKeyboardSettings";
|
||||
private static final String TAG_KEYBOARD = "keyboard";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Log.i(TAG, "Opening keyboard settings");
|
||||
|
||||
getFragmentManager().beginTransaction().replace(
|
||||
com.android.settingslib.collapsingtoolbar.R.id.content_frame,
|
||||
new KeyboardSettingsFragment(), TAG_KEYBOARD).commit();
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2025 The LineageOS Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.lineageos.xiaomiperipheralmanager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import android.preference.PreferenceManager;
|
||||
import androidx.preference.PreferenceFragment;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class KeyboardSettingsFragment extends PreferenceFragment implements
|
||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
private static final String TAG = "XiaomiKeyboardSettings";
|
||||
private static final String KEYBOARD_KEY = "keyboard_switch_key";
|
||||
private static final boolean DEBUG = true;
|
||||
private static final String CONF_LOCATION = "/data/misc/xiaomi_keyboard.conf";
|
||||
|
||||
private SharedPreferences mKeyboardPreference;
|
||||
|
||||
private void saveAngleDetectionPreference(boolean enabled) {
|
||||
try {
|
||||
File file = new File(CONF_LOCATION);
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
fos.write((enabled ? "1" : "0").getBytes());
|
||||
fos.close();
|
||||
logInfo("Angle detection preference saved: " + enabled);
|
||||
} catch (IOException e) {
|
||||
logError("Failed to save angle detection preference: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
try {
|
||||
addPreferencesFromResource(R.xml.keyboard_settings);
|
||||
|
||||
mKeyboardPreference = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
SwitchPreference switchPreference = (SwitchPreference) findPreference(KEYBOARD_KEY);
|
||||
|
||||
if (switchPreference != null) {
|
||||
switchPreference.setChecked(mKeyboardPreference.getBoolean(KEYBOARD_KEY, false));
|
||||
switchPreference.setEnabled(true);
|
||||
} else {
|
||||
logError("Could not find keyboard switch preference");
|
||||
}
|
||||
|
||||
logInfo("Keyboard settings fragment created");
|
||||
} catch (Exception e) {
|
||||
logError("Error creating keyboard settings: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
try {
|
||||
mKeyboardPreference.registerOnSharedPreferenceChangeListener(this);
|
||||
logDebug("Registered preference change listener");
|
||||
|
||||
} catch (Exception e) {
|
||||
logError("Error in onResume: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
try {
|
||||
mKeyboardPreference.unregisterOnSharedPreferenceChangeListener(this);
|
||||
logDebug("Unregistered preference change listener");
|
||||
|
||||
} catch (Exception e) {
|
||||
logError("Error in onPause: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreference, String key) {
|
||||
if (KEYBOARD_KEY.equals(key)) {
|
||||
try {
|
||||
boolean newStatus = mKeyboardPreference.getBoolean(key, false);
|
||||
saveAngleDetectionPreference(newStatus);
|
||||
logInfo("Keyboard status changed: " + newStatus);
|
||||
} catch (Exception e) {
|
||||
logError("Error handling preference change: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enhanced logging helpers to match other classes
|
||||
private void logDebug(String message) {
|
||||
if (DEBUG) Log.d(TAG, getTimestamp() + message);
|
||||
}
|
||||
|
||||
private void logInfo(String message) {
|
||||
Log.i(TAG, getTimestamp() + message);
|
||||
}
|
||||
|
||||
private void logError(String message) {
|
||||
Log.e(TAG, getTimestamp() + message);
|
||||
}
|
||||
|
||||
private String getTimestamp() {
|
||||
return "[" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US).format(new Date()) + "] ";
|
||||
}
|
||||
}
|
||||
@@ -1,338 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2025 The LineageOS Project
|
||||
* Copyright (C) 2023 The LineageOS Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.lineageos.xiaomiperipheralmanager;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.os.FileUtils;
|
||||
import android.os.SystemProperties;
|
||||
import android.util.Log;
|
||||
import android.view.InputDevice;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Utility class for handling Xiaomi keyboard operations at the framework level.
|
||||
* Works in conjunction with the native xiaomi-keyboard service.
|
||||
*/
|
||||
public class KeyboardUtils {
|
||||
|
||||
private static final String TAG = "XiaomiKeyboard";
|
||||
private static boolean DEBUG = SystemProperties.getBoolean("persist.xiaomi.keyboard.debug", false);
|
||||
private static final String TAG = "XiaomiPeripheralManagerKeyboardUtils";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
// Xiaomi keyboard identifiers
|
||||
private static final int KEYBOARD_VENDOR_ID = 5593;
|
||||
private static final int KEYBOARD_PRODUCT_ID = 163;
|
||||
|
||||
// Communication with native service
|
||||
private static final String NANODEV_PATH = "/dev/nanodev0";
|
||||
private static final byte MSG_TYPE_LOCK = 41;
|
||||
private static final byte MSG_TYPE_UNLOCK = 42;
|
||||
private static final byte MSG_HEADER_1 = 0x31;
|
||||
private static final byte MSG_HEADER_2 = 0x38;
|
||||
private static final int kbVendorId = 5593;
|
||||
private static final int kbProductId = 163;
|
||||
|
||||
private static InputManager mInputManager;
|
||||
private static boolean mLastEnabledState = false;
|
||||
private static boolean mIsDeviceLocked = false;
|
||||
private static Context mContext = null;
|
||||
private static ScreenStateReceiver mScreenStateReceiver = null;
|
||||
|
||||
// Add watchdog monitor and recovery
|
||||
private static final long WATCHDOG_TIMEOUT_MS = 10000; // 10 seconds
|
||||
private static Thread mWatchdogThread = null;
|
||||
private static volatile boolean mWatchdogRunning = false;
|
||||
private static volatile long mLastWatchdogCheck = 0;
|
||||
|
||||
/**
|
||||
* Initialize the keyboard utilities and set initial state
|
||||
* @param context Application context
|
||||
*/
|
||||
public static void setup(Context context) {
|
||||
logInfo("Initializing Xiaomi keyboard framework integration");
|
||||
mContext = context.getApplicationContext();
|
||||
|
||||
try {
|
||||
if (mInputManager == null) {
|
||||
mInputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
|
||||
}
|
||||
|
||||
// Force disable keyboard at startup for safety
|
||||
setKeyboardEnabled(false);
|
||||
|
||||
// Register broadcast receiver for screen state changes
|
||||
registerScreenStateReceiver(context);
|
||||
|
||||
// Start watchdog thread
|
||||
startWatchdogThread();
|
||||
|
||||
} catch (Exception e) {
|
||||
logError("Error setting up keyboard utils: " + e.getMessage());
|
||||
if (mInputManager == null) {
|
||||
mInputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a BroadcastReceiver to handle screen state changes
|
||||
*/
|
||||
private static void registerScreenStateReceiver(Context context) {
|
||||
if (mScreenStateReceiver != null) {
|
||||
return; // Already registered
|
||||
}
|
||||
|
||||
logInfo("Registering screen state receiver");
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_SCREEN_OFF);
|
||||
filter.addAction(Intent.ACTION_USER_PRESENT);
|
||||
filter.addAction(Intent.ACTION_SCREEN_ON);
|
||||
|
||||
mScreenStateReceiver = new ScreenStateReceiver();
|
||||
context.registerReceiver(mScreenStateReceiver, filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start watchdog thread to monitor and recover from stuck conditions
|
||||
*/
|
||||
private static void startWatchdogThread() {
|
||||
if (mWatchdogThread != null && mWatchdogThread.isAlive()) {
|
||||
return; // Thread already running
|
||||
}
|
||||
|
||||
mWatchdogRunning = true;
|
||||
mLastWatchdogCheck = System.currentTimeMillis();
|
||||
|
||||
mWatchdogThread = new Thread(() -> {
|
||||
logInfo("Keyboard watchdog thread started");
|
||||
while (mWatchdogRunning) {
|
||||
try {
|
||||
// Update watchdog timestamp
|
||||
mLastWatchdogCheck = System.currentTimeMillis();
|
||||
|
||||
// Safety check: ensure keyboard is disabled in lock screen
|
||||
if (mIsDeviceLocked && mLastEnabledState) {
|
||||
logError("Watchdog detected keyboard enabled while locked! Forcing disable");
|
||||
setKeyboardEnabled(false);
|
||||
}
|
||||
|
||||
Thread.sleep(5000); // Check every 5 seconds
|
||||
} catch (InterruptedException e) {
|
||||
// Thread interrupted, continue loop
|
||||
} catch (Exception e) {
|
||||
logError("Watchdog thread error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
logInfo("Keyboard watchdog thread stopped");
|
||||
});
|
||||
|
||||
mWatchdogThread.setDaemon(true);
|
||||
mWatchdogThread.setName("KeyboardWatchdog");
|
||||
mWatchdogThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the watchdog thread during cleanup
|
||||
*/
|
||||
private static void stopWatchdogThread() {
|
||||
mWatchdogRunning = false;
|
||||
if (mWatchdogThread != null) {
|
||||
mWatchdogThread.interrupt();
|
||||
mWatchdogThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the screen state receiver when service is destroyed
|
||||
*/
|
||||
public static void cleanup(Context context) {
|
||||
if (mScreenStateReceiver != null) {
|
||||
try {
|
||||
context.unregisterReceiver(mScreenStateReceiver);
|
||||
mScreenStateReceiver = null;
|
||||
} catch (Exception e) {
|
||||
logError("Error unregistering receiver: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Stop watchdog thread
|
||||
stopWatchdogThread();
|
||||
|
||||
// Force disable keyboard on cleanup
|
||||
setKeyboardEnabled(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable the Xiaomi keyboard input device
|
||||
* @param enabled Whether the keyboard should be enabled
|
||||
* @return true if operation was successful, false otherwise
|
||||
*/
|
||||
public static boolean setKeyboardEnabled(boolean enabled) {
|
||||
// If the device is locked, never enable the keyboard
|
||||
if (enabled && mIsDeviceLocked) {
|
||||
logDebug("Not enabling keyboard because device is locked");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update watchdog timestamp to show activity
|
||||
mLastWatchdogCheck = System.currentTimeMillis();
|
||||
|
||||
if (enabled == mLastEnabledState) {
|
||||
logDebug("Keyboard already in requested state: " + enabled);
|
||||
return true;
|
||||
}
|
||||
|
||||
logInfo("Setting keyboard enabled: " + enabled);
|
||||
boolean success = false;
|
||||
boolean deviceFound = false;
|
||||
|
||||
try {
|
||||
if (mInputManager == null) {
|
||||
logError("InputManager not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int id : mInputManager.getInputDeviceIds()) {
|
||||
if (isDeviceXiaomiKeyboard(id)) {
|
||||
deviceFound = true;
|
||||
logDebug("Found Xiaomi Keyboard with id: " + id);
|
||||
|
||||
// Apply enable/disable with timeout protection
|
||||
try {
|
||||
if (enabled) {
|
||||
mInputManager.enableInputDevice(id);
|
||||
} else {
|
||||
mInputManager.disableInputDevice(id);
|
||||
}
|
||||
mLastEnabledState = enabled;
|
||||
success = true;
|
||||
} catch (Exception e) {
|
||||
logError("Failed to change keyboard state: " + e.getMessage());
|
||||
}
|
||||
public static void setKeyboardEnabled(boolean enabled) {
|
||||
if (DEBUG) Log.d(TAG, "setKeyboardEnabled: " + enabled);
|
||||
for (int id : mInputManager.getInputDeviceIds()) {
|
||||
if (isDeviceXiaomiKeyboard(id)) {
|
||||
if (DEBUG) Log.d(TAG, "setKeyboardEnabled: Found Xiaomi Keyboard with id: " + id);
|
||||
if (enabled) {
|
||||
if (DEBUG) Log.d(TAG, "setKeyboardEnabled: Enabling Xiaomi Keyboard");
|
||||
mInputManager.enableInputDevice(id);
|
||||
} else {
|
||||
if (DEBUG) Log.d(TAG, "setKeyboardEnabled: Disabling Xiaomi Keyboard");
|
||||
mInputManager.disableInputDevice(id);
|
||||
}
|
||||
}
|
||||
|
||||
if (!deviceFound) {
|
||||
logInfo("Xiaomi keyboard not found in input devices");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logError("Error changing keyboard state: " + e.getMessage());
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set device lock state and notify native service
|
||||
* @param isLocked Whether the device is locked
|
||||
*/
|
||||
public static void setDeviceLockState(boolean isLocked) {
|
||||
mIsDeviceLocked = isLocked;
|
||||
logInfo("Device lock state changed: " + (isLocked ? "LOCKED" : "UNLOCKED"));
|
||||
|
||||
// If locked, force disable the keyboard with higher priority
|
||||
if (isLocked) {
|
||||
setKeyboardEnabled(false);
|
||||
} else if (!mLastEnabledState) {
|
||||
// Re-enable keyboard if unlocked and currently disabled
|
||||
setKeyboardEnabled(true);
|
||||
}
|
||||
|
||||
// Notify native service about lock state change
|
||||
sendLockStateToNativeService(isLocked);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send lock state message to native service
|
||||
* @param isLocked Whether device is locked
|
||||
*/
|
||||
private static void sendLockStateToNativeService(boolean isLocked) {
|
||||
try {
|
||||
byte messageType = isLocked ? MSG_TYPE_LOCK : MSG_TYPE_UNLOCK;
|
||||
|
||||
// Protocol: [0x??][Header1][Header2][0][MessageType][1][1]
|
||||
byte[] message = new byte[7];
|
||||
message[0] = 0; // First byte can be anything
|
||||
message[1] = MSG_HEADER_1;
|
||||
message[2] = MSG_HEADER_2;
|
||||
message[3] = 0;
|
||||
message[4] = messageType;
|
||||
message[5] = 1;
|
||||
message[6] = 1;
|
||||
|
||||
// Write to nanodev device
|
||||
File nanodev = new File(NANODEV_PATH);
|
||||
if (!nanodev.exists() || !nanodev.canWrite()) {
|
||||
logError("Cannot write to nanodev: " + NANODEV_PATH);
|
||||
return;
|
||||
}
|
||||
|
||||
FileOutputStream fos = new FileOutputStream(NANODEV_PATH);
|
||||
fos.write(message);
|
||||
fos.close();
|
||||
|
||||
logDebug("Sent lock state " + (isLocked ? "LOCK" : "UNLOCK") + " to native service");
|
||||
} catch (IOException e) {
|
||||
logError("Failed to send lock state to native service: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an input device is the Xiaomi keyboard
|
||||
* @param id Device ID to check
|
||||
* @return true if the device is the Xiaomi keyboard
|
||||
*/
|
||||
private static boolean isDeviceXiaomiKeyboard(int id) {
|
||||
try {
|
||||
InputDevice inputDevice = mInputManager.getInputDevice(id);
|
||||
if (inputDevice == null) return false;
|
||||
|
||||
return inputDevice.getVendorId() == KEYBOARD_VENDOR_ID &&
|
||||
inputDevice.getProductId() == KEYBOARD_PRODUCT_ID;
|
||||
} catch (Exception e) {
|
||||
logError("Error checking device: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* BroadcastReceiver to detect screen state changes
|
||||
*/
|
||||
private static class ScreenStateReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
|
||||
if (Intent.ACTION_SCREEN_OFF.equals(action)) {
|
||||
setDeviceLockState(true);
|
||||
} else if (Intent.ACTION_USER_PRESENT.equals(action)) {
|
||||
setDeviceLockState(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enhanced logging helpers to match C++ style
|
||||
private static void logDebug(String message) {
|
||||
if (DEBUG) Log.d(TAG, getTimestamp() + message);
|
||||
}
|
||||
|
||||
private static void logInfo(String message) {
|
||||
Log.i(TAG, getTimestamp() + message);
|
||||
}
|
||||
|
||||
private static void logError(String message) {
|
||||
Log.e(TAG, getTimestamp() + message);
|
||||
}
|
||||
|
||||
private static String getTimestamp() {
|
||||
return "[" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US).format(new Date()) + "] ";
|
||||
InputDevice inputDevice = mInputManager.getInputDevice(id);
|
||||
return inputDevice.getVendorId() == kbVendorId && inputDevice.getProductId() == kbProductId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2025 The LineageOS Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.lineageos.xiaomiperipheralmanager;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
|
||||
|
||||
/**
|
||||
* Settings activity for stylus/pen configuration
|
||||
* Hosts the StylusSettingsFragment for user configuration
|
||||
*/
|
||||
public class LidSettingsActivity extends CollapsingToolbarBaseActivity {
|
||||
|
||||
private static final String TAG = "XiaomiLidSettings";
|
||||
private static final String TAG_LID = "lid";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Log.i(TAG, "Opening lid settings");
|
||||
|
||||
getFragmentManager().beginTransaction().replace(
|
||||
com.android.settingslib.collapsingtoolbar.R.id.content_frame,
|
||||
new LidSettingsFragment(), TAG_LID).commit();
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2025 The LineageOS Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.lineageos.xiaomiperipheralmanager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemProperties;
|
||||
import android.util.Log;
|
||||
|
||||
import android.preference.PreferenceManager;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.Preference.OnPreferenceChangeListener;
|
||||
import androidx.preference.PreferenceFragment;
|
||||
import androidx.preference.SwitchPreference;
|
||||
import com.android.settingslib.widget.MainSwitchPreference;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import android.provider.Settings;
|
||||
|
||||
/**
|
||||
* Settings fragment for lid configuration
|
||||
* Allows users to manually enable/disable the smart cover
|
||||
*/
|
||||
public class LidSettingsFragment extends PreferenceFragment implements
|
||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
private static final String TAG = "XiaomiLidSettings";
|
||||
private static final String LID_KEY = "lid_switch_key";
|
||||
|
||||
private SharedPreferences mLidPreference;
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
try {
|
||||
addPreferencesFromResource(R.xml.lid_settings);
|
||||
|
||||
mLidPreference = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
SwitchPreference switchPreference = (SwitchPreference) findPreference(LID_KEY);
|
||||
|
||||
if (switchPreference != null) {
|
||||
switchPreference.setChecked(mLidPreference.getBoolean(LID_KEY, false));
|
||||
switchPreference.setEnabled(true);
|
||||
} else {
|
||||
logError("Could not find lid switch preference");
|
||||
}
|
||||
|
||||
logInfo("Lid settings fragment created");
|
||||
} catch (Exception e) {
|
||||
logError("Error creating lid settings: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
try {
|
||||
mLidPreference.registerOnSharedPreferenceChangeListener(this);
|
||||
} catch (Exception e) {
|
||||
logError("Error in onResume: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
try {
|
||||
mLidPreference.unregisterOnSharedPreferenceChangeListener(this);
|
||||
} catch (Exception e) {
|
||||
logError("Error in onPause: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreference, String key) {
|
||||
if (LID_KEY.equals(key)) {
|
||||
try {
|
||||
boolean newStatus = mLidPreference.getBoolean(key, false);
|
||||
logInfo("Lid preference changed to: " + newStatus);
|
||||
Settings.Global.putInt(getActivity().getContentResolver(),
|
||||
"lid_behavior", newStatus ? 1 : 0);
|
||||
} catch (Exception e) {
|
||||
logError("Error handling preference change: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void logInfo(String message) {
|
||||
Log.i(TAG, getTimestamp() + message);
|
||||
}
|
||||
|
||||
private void logError(String message) {
|
||||
Log.e(TAG, getTimestamp() + message);
|
||||
}
|
||||
|
||||
private String getTimestamp() {
|
||||
return "[" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US).format(new Date()) + "] ";
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2025 The LineageOS Project
|
||||
* Copyright (C) 2023 The LineageOS Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -16,151 +16,73 @@ import android.view.InputDevice;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Utility class for handling Xiaomi pen operations and mode switching
|
||||
*/
|
||||
public class PenUtils {
|
||||
|
||||
private static final String TAG = "XiaomiPen";
|
||||
private static boolean DEBUG = SystemProperties.getBoolean("persist.xiaomi.pen.debug", false);
|
||||
private static final String TAG = "XiaomiPeripheralManagerPenUtils";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
// Xiaomi pen identifiers
|
||||
private static final int PEN_VENDOR_ID = 6421;
|
||||
private static final int PEN_PRODUCT_ID = 19841;
|
||||
private static final int penVendorId = 6421;
|
||||
private static final int penProductId = 19841;
|
||||
|
||||
private static InputManager mInputManager;
|
||||
private static SharedPreferences mPreferences;
|
||||
|
||||
private static final String STYLUS_KEY = "stylus_switch_key";
|
||||
|
||||
/**
|
||||
* Initialize the pen utilities and register for input device events
|
||||
* @param context Application context
|
||||
*/
|
||||
private static SharedPreferences preferences;
|
||||
private static RefreshUtils mRefreshUtils;
|
||||
|
||||
public static void setup(Context context) {
|
||||
logInfo("Initializing Xiaomi pen framework integration");
|
||||
try {
|
||||
mInputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
|
||||
mInputManager.registerInputDeviceListener(mInputDeviceListener, null);
|
||||
mPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
refreshPenMode();
|
||||
} catch (Exception e) {
|
||||
logError("Error setting up pen utils: " + e.getMessage());
|
||||
}
|
||||
mInputManager = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
|
||||
mInputManager.registerInputDeviceListener(mInputDeviceListener, null);
|
||||
preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
mRefreshUtils = new RefreshUtils(context);
|
||||
refreshPenMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable pen mode by setting system property
|
||||
*/
|
||||
public static void enablePenMode() {
|
||||
logInfo("Enabling pen mode");
|
||||
try {
|
||||
SystemProperties.set("persist.vendor.parts.pen", "18");
|
||||
} catch (Exception e) {
|
||||
logError("Failed to enable pen mode: " + e.getMessage());
|
||||
}
|
||||
Log.d(TAG, "enablePenMode: Enable Pen Mode");
|
||||
SystemProperties.set("persist.vendor.parts.pen", "18");
|
||||
Log.d(TAG, "enablePenMode: Setting Refresh Rates for Pen");
|
||||
mRefreshUtils.setPenRefreshRate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable pen mode by setting system property
|
||||
*/
|
||||
public static void disablePenMode() {
|
||||
logInfo("Disabling pen mode");
|
||||
try {
|
||||
SystemProperties.set("persist.vendor.parts.pen", "2");
|
||||
} catch (Exception e) {
|
||||
logError("Failed to disable pen mode: " + e.getMessage());
|
||||
}
|
||||
Log.d(TAG, "disablePenMode: Disable Pen Mode");
|
||||
SystemProperties.set("persist.vendor.parts.pen", "2");
|
||||
Log.d(TAG, "disablePenMode: Resetting Refresh Rate Values");
|
||||
mRefreshUtils.setDefaultRefreshRate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for pen presence and update mode accordingly
|
||||
*/
|
||||
private static void refreshPenMode() {
|
||||
try {
|
||||
boolean penFound = false;
|
||||
|
||||
for (int id : mInputManager.getInputDeviceIds()) {
|
||||
if (isDeviceXiaomiPen(id)) {
|
||||
logDebug("Found Xiaomi Pen with id: " + id);
|
||||
penFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Also check preference override
|
||||
boolean preferenceEnabled = mPreferences.getBoolean(STYLUS_KEY, false);
|
||||
|
||||
if (penFound || preferenceEnabled) {
|
||||
logInfo("Pen detected or enabled in preferences");
|
||||
for (int id : mInputManager.getInputDeviceIds()) {
|
||||
if (isDeviceXiaomiPen(id) || preferences.getBoolean(STYLUS_KEY, false)) {
|
||||
if (DEBUG) Log.d(TAG, "refreshPenMode: Found Xiaomi Pen");
|
||||
enablePenMode();
|
||||
} else {
|
||||
logInfo("No pen detected and not enabled in preferences");
|
||||
disablePenMode();
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logError("Error refreshing pen mode: " + e.getMessage());
|
||||
}
|
||||
if (DEBUG) Log.d(TAG, "refreshPenMode: No Xiaomi Pen found");
|
||||
disablePenMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an input device is the Xiaomi pen
|
||||
* @param id Device ID to check
|
||||
* @return true if the device is the Xiaomi pen
|
||||
*/
|
||||
private static boolean isDeviceXiaomiPen(int id) {
|
||||
try {
|
||||
InputDevice inputDevice = mInputManager.getInputDevice(id);
|
||||
if (inputDevice == null) return false;
|
||||
|
||||
return inputDevice.getVendorId() == PEN_VENDOR_ID &&
|
||||
inputDevice.getProductId() == PEN_PRODUCT_ID;
|
||||
} catch (Exception e) {
|
||||
logError("Error checking pen device: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
InputDevice inputDevice = mInputManager.getInputDevice(id);
|
||||
return inputDevice.getVendorId() == penVendorId &&
|
||||
inputDevice.getProductId() == penProductId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Input device listener for pen connection/disconnection events
|
||||
*/
|
||||
private static InputDeviceListener mInputDeviceListener = new InputDeviceListener() {
|
||||
@Override
|
||||
public void onInputDeviceAdded(int id) {
|
||||
logDebug("Input device added: " + id);
|
||||
refreshPenMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputDeviceRemoved(int id) {
|
||||
logDebug("Input device removed: " + id);
|
||||
refreshPenMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputDeviceChanged(int id) {
|
||||
logDebug("Input device changed: " + id);
|
||||
refreshPenMode();
|
||||
}
|
||||
};
|
||||
|
||||
// Enhanced logging helpers to match other classes
|
||||
private static void logDebug(String message) {
|
||||
if (DEBUG) Log.d(TAG, getTimestamp() + message);
|
||||
}
|
||||
|
||||
private static void logInfo(String message) {
|
||||
Log.i(TAG, getTimestamp() + message);
|
||||
}
|
||||
|
||||
private static void logError(String message) {
|
||||
Log.e(TAG, getTimestamp() + message);
|
||||
}
|
||||
|
||||
private static String getTimestamp() {
|
||||
return "[" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US).format(new Date()) + "] ";
|
||||
}
|
||||
@Override
|
||||
public void onInputDeviceAdded(int id) {
|
||||
refreshPenMode();
|
||||
}
|
||||
@Override
|
||||
public void onInputDeviceRemoved(int id) {
|
||||
refreshPenMode();
|
||||
}
|
||||
@Override
|
||||
public void onInputDeviceChanged(int id) {
|
||||
refreshPenMode();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package org.lineageos.xiaomiperipheralmanager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.provider.Settings;
|
||||
|
||||
public final class RefreshUtils {
|
||||
private static final String KEY_PEAK_REFRESH_RATE = "peak_refresh_rate";
|
||||
private static final String KEY_MIN_REFRESH_RATE = "min_refresh_rate";
|
||||
private static final String KEY_PEN_MODE = "pen_mode";
|
||||
private static final String PREF_FILE_NAME = "pen_refresh_prefs";
|
||||
|
||||
private Context mContext;
|
||||
private SharedPreferences mSharedPrefs;
|
||||
|
||||
protected RefreshUtils(Context context) {
|
||||
mContext = context;
|
||||
mSharedPrefs = context.getSharedPreferences(PREF_FILE_NAME, Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
protected void setPenRefreshRate() {
|
||||
boolean penMode = mSharedPrefs.getBoolean(KEY_PEN_MODE, false);
|
||||
|
||||
if (!penMode) {
|
||||
float maxRate = Settings.System.getFloat(mContext.getContentResolver(), KEY_PEAK_REFRESH_RATE, 144f);
|
||||
float minRate = Settings.System.getFloat(mContext.getContentResolver(), KEY_MIN_REFRESH_RATE, 144f);
|
||||
|
||||
// Update default values in SharedPreferences
|
||||
mSharedPrefs.edit()
|
||||
.putFloat(KEY_MIN_REFRESH_RATE, minRate)
|
||||
.putFloat(KEY_PEAK_REFRESH_RATE, maxRate)
|
||||
.putBoolean(KEY_PEN_MODE, true)
|
||||
.apply();
|
||||
|
||||
// Ensure valid values for maxRate and minRate
|
||||
maxRate = (maxRate != 60) ? 120 : maxRate;
|
||||
minRate = (minRate <= 60) ? 60 : 120;
|
||||
|
||||
// Set the values in the Settings.System
|
||||
Settings.System.putFloat(mContext.getContentResolver(), KEY_MIN_REFRESH_RATE, minRate);
|
||||
Settings.System.putFloat(mContext.getContentResolver(), KEY_PEAK_REFRESH_RATE, maxRate);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setDefaultRefreshRate() {
|
||||
float defaultMinRate = mSharedPrefs.getFloat(KEY_MIN_REFRESH_RATE, 144f);
|
||||
float defaultMaxRate = mSharedPrefs.getFloat(KEY_PEAK_REFRESH_RATE, 144f);
|
||||
|
||||
mSharedPrefs.edit().putBoolean(KEY_PEN_MODE, false).apply();
|
||||
|
||||
// Set the values in the Settings.System directly
|
||||
Settings.System.putFloat(mContext.getContentResolver(), KEY_MIN_REFRESH_RATE, defaultMinRate);
|
||||
Settings.System.putFloat(mContext.getContentResolver(), KEY_PEAK_REFRESH_RATE, defaultMaxRate);
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2025 The LineageOS Project
|
||||
* Copyright (C) 2023 The LineageOS Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.lineageos.xiaomiperipheralmanager;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
|
||||
|
||||
/**
|
||||
* Settings activity for stylus/pen configuration
|
||||
* Hosts the StylusSettingsFragment for user configuration
|
||||
*/
|
||||
public class StylusSettingsActivity extends CollapsingToolbarBaseActivity {
|
||||
|
||||
private static final String TAG = "XiaomiPenSettings";
|
||||
private static final String TAG_STYLUS = "stylus";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Log.i(TAG, "Opening stylus settings");
|
||||
|
||||
|
||||
getFragmentManager().beginTransaction().replace(
|
||||
com.android.settingslib.collapsingtoolbar.R.id.content_frame,
|
||||
new StylusSettingsFragment(), TAG_STYLUS).commit();
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2025 The LineageOS Project
|
||||
* Copyright (C) 2023 The LineageOS Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.lineageos.xiaomiperipheralmanager;
|
||||
@@ -9,7 +19,7 @@ package org.lineageos.xiaomiperipheralmanager;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemProperties;
|
||||
import android.widget.Switch;
|
||||
import android.util.Log;
|
||||
|
||||
import android.preference.PreferenceManager;
|
||||
@@ -20,108 +30,53 @@ import androidx.preference.PreferenceFragment;
|
||||
import androidx.preference.SwitchPreference;
|
||||
import com.android.settingslib.widget.MainSwitchPreference;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import org.lineageos.xiaomiperipheralmanager.PenUtils;
|
||||
import org.lineageos.xiaomiperipheralmanager.R;
|
||||
|
||||
/**
|
||||
* Settings fragment for stylus/pen configuration
|
||||
* Allows users to manually enable/disable the pen mode
|
||||
*/
|
||||
public class StylusSettingsFragment extends PreferenceFragment implements
|
||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
private static final String TAG = "XiaomiPenSettings";
|
||||
private static boolean DEBUG = SystemProperties.getBoolean("persist.xiaomi.pen.debug", false);
|
||||
private static final String TAG = "XiaomiPeripheralManagerPenUtils";
|
||||
private static final String STYLUS_KEY = "stylus_switch_key";
|
||||
|
||||
private SharedPreferences mStylusPreference;
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
try {
|
||||
addPreferencesFromResource(R.xml.stylus_settings);
|
||||
addPreferencesFromResource(R.xml.stylus_settings);
|
||||
|
||||
mStylusPreference = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
SwitchPreference switchPreference = (SwitchPreference) findPreference(STYLUS_KEY);
|
||||
mStylusPreference = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
SwitchPreference switchPreference = (SwitchPreference) findPreference(STYLUS_KEY);
|
||||
|
||||
if (switchPreference != null) {
|
||||
switchPreference.setChecked(mStylusPreference.getBoolean(STYLUS_KEY, false));
|
||||
switchPreference.setEnabled(true);
|
||||
} else {
|
||||
logError("Could not find stylus switch preference");
|
||||
}
|
||||
|
||||
logInfo("Stylus settings fragment created");
|
||||
} catch (Exception e) {
|
||||
logError("Error creating stylus settings: " + e.getMessage());
|
||||
}
|
||||
switchPreference.setChecked(mStylusPreference.getBoolean(STYLUS_KEY, false));
|
||||
switchPreference.setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
try {
|
||||
mStylusPreference.registerOnSharedPreferenceChangeListener(this);
|
||||
logDebug("Registered preference change listener");
|
||||
} catch (Exception e) {
|
||||
logError("Error in onResume: " + e.getMessage());
|
||||
}
|
||||
mStylusPreference.registerOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
try {
|
||||
mStylusPreference.unregisterOnSharedPreferenceChangeListener(this);
|
||||
logDebug("Unregistered preference change listener");
|
||||
} catch (Exception e) {
|
||||
logError("Error in onPause: " + e.getMessage());
|
||||
}
|
||||
mStylusPreference.unregisterOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreference, String key) {
|
||||
if (STYLUS_KEY.equals(key)) {
|
||||
try {
|
||||
boolean newStatus = mStylusPreference.getBoolean(key, false);
|
||||
logInfo("Stylus preference changed to: " + newStatus);
|
||||
forceStylus(newStatus);
|
||||
} catch (Exception e) {
|
||||
logError("Error handling preference change: " + e.getMessage());
|
||||
}
|
||||
forceStylus(mStylusPreference.getBoolean(key, false));
|
||||
}
|
||||
}
|
||||
|
||||
private void forceStylus(boolean status) {
|
||||
try {
|
||||
mStylusPreference.edit().putBoolean(STYLUS_KEY, status).apply();
|
||||
logInfo("Setting stylus mode: " + (status ? "enabled" : "disabled"));
|
||||
|
||||
if (status) {
|
||||
PenUtils.enablePenMode();
|
||||
} else {
|
||||
PenUtils.disablePenMode();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logError("Error setting stylus mode: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Enhanced logging helpers to match other classes
|
||||
private void logDebug(String message) {
|
||||
if (DEBUG) Log.d(TAG, getTimestamp() + message);
|
||||
}
|
||||
|
||||
private void logInfo(String message) {
|
||||
Log.i(TAG, getTimestamp() + message);
|
||||
}
|
||||
|
||||
private void logError(String message) {
|
||||
Log.e(TAG, getTimestamp() + message);
|
||||
}
|
||||
|
||||
private String getTimestamp() {
|
||||
return "[" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US).format(new Date()) + "] ";
|
||||
mStylusPreference.edit().putBoolean(STYLUS_KEY, status).apply();
|
||||
|
||||
if (status)
|
||||
PenUtils.enablePenMode();
|
||||
else
|
||||
PenUtils.disablePenMode();
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2025 The LineageOS Project
|
||||
* Copyright (C) 2023 The LineageOS Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -8,12 +8,7 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#define VERSION_STRING "1.0.0"
|
||||
|
||||
// Device control definitions
|
||||
#define SET_CUR_VALUE 0
|
||||
#define TOUCH_PEN_MODE 20
|
||||
#define TOUCH_MAGIC 't'
|
||||
@@ -21,41 +16,12 @@
|
||||
#define TOUCH_DEV_PATH "/dev/xiaomi-touch"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// Validate arguments
|
||||
if(argc != 2) {
|
||||
fprintf(stderr, "Xiaomi pen utility v%s\n", VERSION_STRING);
|
||||
fprintf(stderr, "Usage: %s <value>\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Open the touch device
|
||||
int fd = open(TOUCH_DEV_PATH, O_RDWR);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Error opening device %s: %s\n",
|
||||
TOUCH_DEV_PATH, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Parse the input value
|
||||
int value = atoi(argv[1]);
|
||||
if (value < 0 || value > 20) {
|
||||
fprintf(stderr, "Warning: Value %d outside normal range (0-20)\n", value);
|
||||
}
|
||||
fprintf(stdout, "Setting pen mode to: %d\n", value);
|
||||
|
||||
// Prepare and send the command
|
||||
int arg[2] = {TOUCH_PEN_MODE, value};
|
||||
int result = ioctl(fd, TOUCH_IOC_SETMODE, &arg);
|
||||
|
||||
// Check for errors
|
||||
if (result < 0) {
|
||||
fprintf(stderr, "Error setting pen mode: %s\n", strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Clean up
|
||||
int arg[2] = {TOUCH_PEN_MODE, atoi(argv[1])};
|
||||
ioctl(fd, TOUCH_IOC_SETMODE, &arg);
|
||||
close(fd);
|
||||
fprintf(stdout, "Pen mode set successfully\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,3 @@ service xiaomi-keyboard /vendor/bin/xiaomi-keyboard
|
||||
|
||||
on property:persist.vendor.parts.pen=*
|
||||
start xiaomi-pen
|
||||
|
||||
on post-data-fs
|
||||
exec - system system -- /system/bin/touch /data/misc/xiaomi_keyboard.conf
|
||||
exec - system system -- /system/bin/restorecon /data/misc/xiaomi_keyboard.conf
|
||||
|
||||
1
sepolicy/vendor/file.te
vendored
1
sepolicy/vendor/file.te
vendored
@@ -1 +0,0 @@
|
||||
type xiaomi_keyboard_conf_file, file_type;
|
||||
2
sepolicy/vendor/file_contexts
vendored
2
sepolicy/vendor/file_contexts
vendored
@@ -9,5 +9,3 @@
|
||||
|
||||
# Xiaomi Keyboard
|
||||
/dev/nanodev0 u:object_r:xiaomi_keyboard_device:s0
|
||||
|
||||
/data/vendor/xiaomi_keyboard.conf u:object_r:xiaomi_keyboard_conf_file:s0
|
||||
|
||||
1
sepolicy/vendor/servicemanager.te
vendored
1
sepolicy/vendor/servicemanager.te
vendored
@@ -1 +0,0 @@
|
||||
allow servicemanager xiaomi_keyboard:binder call;
|
||||
1
sepolicy/vendor/system_app.te
vendored
1
sepolicy/vendor/system_app.te
vendored
@@ -1,2 +1 @@
|
||||
set_prop(system_app, vendor_pen_prop)
|
||||
allow system_app xiaomi_keyboard_conf_file:file { read write open getattr };
|
||||
16
sepolicy/vendor/xiaomi_keyboard.te
vendored
16
sepolicy/vendor/xiaomi_keyboard.te
vendored
@@ -9,20 +9,8 @@ get_prop(xiaomi_keyboard, hwservicemanager_prop)
|
||||
binder_call(xiaomi_keyboard, hwservicemanager)
|
||||
binder_call(xiaomi_keyboard, system_server)
|
||||
|
||||
# Enhanced permissions for keyboard device
|
||||
allow xiaomi_keyboard xiaomi_keyboard_device:chr_file { getattr open read write ioctl };
|
||||
allow xiaomi_keyboard xiaomi_keyboard_device:chr_file { open read write };
|
||||
allow xiaomi_keyboard fwk_sensor_hwservice:hwservice_manager find;
|
||||
allow xiaomi_keyboard fwk_sensor_service:service_manager find;
|
||||
|
||||
# Enhanced input device permissions
|
||||
allow xiaomi_keyboard input_device:dir { read search open };
|
||||
allow xiaomi_keyboard input_device:file { read open getattr };
|
||||
allow xiaomi_keyboard input_device:dir search;
|
||||
allow xiaomi_keyboard servicemanager:binder { call transfer };
|
||||
|
||||
# Additional diagnostic permissions
|
||||
allow xiaomi_keyboard sysfs:dir { read open };
|
||||
allow xiaomi_keyboard sysfs_devices_system_cpu:file { read open };
|
||||
|
||||
allow xiaomi_keyboard xiaomi_keyboard_conf_file:file { read open };
|
||||
|
||||
typeattribute xiaomi_keyboard_conf_file data_file_type;
|
||||
|
||||
Reference in New Issue
Block a user