services: Introduce Shake Gestures [1/2]

Co-authored-by: AmeChanRain <downloadbot007@gmail.com>
Change-Id: If2a3c094c2f30e3eb7bf1df811edb482554749bb
Signed-off-by: Alvin Francis <nivlafx@gmail.com>
Signed-off-by: minaripenguin <minaripenguin@users.noreply.github.com>
Signed-off-by: MOVZX <movzx@yahoo.com>
This commit is contained in:
minaripenguin
2024-10-10 11:00:37 +08:00
committed by MOVZX
parent 046539ce51
commit 3f38c67f1c
3 changed files with 261 additions and 0 deletions

View File

@@ -284,6 +284,8 @@ import lineageos.providers.LineageSettings;
import org.lineageos.internal.buttons.LineageButtons;
import org.lineageos.internal.util.ActionUtils;
import org.rising.server.ShakeGestureService;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
@@ -731,6 +733,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
boolean mPendingMetaAction;
boolean mPendingCapsLockToggle;
private ShakeGestureService mShakeGestures;
// Tracks user-customisable behavior for certain key events
private Action mBackLongPressAction;
private Action mHomeLongPressAction;
@@ -743,6 +747,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private Action mAppSwitchLongPressAction;
private Action mEdgeLongSwipeAction;
private Action mThreeFingersSwipeAction;
private Action mShakeGestureAction;
// support for activating the lock screen while the screen is on
private HashSet<Integer> mAllowLockscreenWhenOnDisplays = new HashSet<>();
@@ -1181,6 +1186,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
resolver.registerContentObserver(Settings.Secure.getUriFor(
"nothing_three_finger_screenshot"), false, this,
UserHandle.USER_ALL);
resolver.registerContentObserver(LineageSettings.System.getUriFor(
LineageSettings.System.KEY_SHAKE_GESTURE_ACTION), false, this,
UserHandle.USER_ALL);
updateSettings();
}
@@ -3505,6 +3513,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
resolver, "nothing_three_finger_screenshot",
0, UserHandle.USER_CURRENT));
mShakeGestureAction = Action.fromSettings(resolver,
LineageSettings.System.KEY_SHAKE_GESTURE_ACTION,
Action.NOTHING);
mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_NOTHING;
if (mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE;
@@ -7663,6 +7675,21 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (mVrManagerInternal != null) {
mVrManagerInternal.addPersistentVrModeStateListener(mPersistentVrModeListener);
}
mShakeGestures = ShakeGestureService.getInstance(mContext, new ShakeGestureService.ShakeGesturesCallbacks() {
@Override
public void onShake() {
if (mShakeGestureAction == Action.NOTHING)
return;
long now = SystemClock.uptimeMillis();
KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_SYSRQ, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_FROM_SYSTEM, InputDevice.SOURCE_TOUCHSCREEN);
performKeyAction(mShakeGestureAction, event);
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, "Shake Gesture");
}
});
mShakeGestures.onStart();
mDockObserverInternal = LocalServices.getService(DockObserverInternal.class);
if (mDockObserverInternal != null) {

View File

@@ -0,0 +1,106 @@
/*
* Copyright (C) 2023-2024 The RisingOS Android 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.
*/
package org.rising.server;
import android.content.Context;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.provider.Settings;
public final class ShakeGestureService {
private static final String TAG = "ShakeGestureService";
private static final String SHAKE_GESTURES_ENABLED = "shake_gestures_enabled";
private static final String SHAKE_GESTURES_ACTION = "shake_gestures_action";
private static final int USER_ALL = UserHandle.USER_ALL;
private final Context mContext;
private ShakeGestureUtils mShakeGestureUtils;
private static volatile ShakeGestureService instance;
private final ShakeGesturesCallbacks mShakeCallbacks;
private final SettingsObserver mSettingsObserver;
private boolean mShakeServiceEnabled = false;
private ShakeGestureUtils.OnShakeListener mShakeListener;
public interface ShakeGesturesCallbacks {
void onShake();
}
private ShakeGestureService(Context context, ShakeGesturesCallbacks callback) {
mContext = context;
mShakeCallbacks = callback;
mShakeListener = () -> {
if (mShakeServiceEnabled && mShakeCallbacks != null) {
mShakeCallbacks.onShake();
}
};
mSettingsObserver = new SettingsObserver(null);
}
public static synchronized ShakeGestureService getInstance(Context context, ShakeGesturesCallbacks callback) {
if (instance == null) {
synchronized (ShakeGestureService.class) {
if (instance == null) {
instance = new ShakeGestureService(context, callback);
}
}
}
return instance;
}
public void onStart() {
if (mShakeGestureUtils == null) {
mShakeGestureUtils = new ShakeGestureUtils(mContext);
}
updateSettings();
mSettingsObserver.observe();
if (mShakeServiceEnabled) {
mShakeGestureUtils.registerListener(mShakeListener);
}
}
private void updateSettings() {
boolean wasShakeServiceEnabled = mShakeServiceEnabled;
mShakeServiceEnabled = Settings.System.getInt(mContext.getContentResolver(),
SHAKE_GESTURES_ENABLED, 0) == 1;
if (mShakeServiceEnabled && !wasShakeServiceEnabled) {
mShakeGestureUtils.registerListener(mShakeListener);
} else if (!mShakeServiceEnabled && wasShakeServiceEnabled) {
mShakeGestureUtils.unregisterListener(mShakeListener);
}
}
class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler) {
super(handler);
}
void observe() {
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(SHAKE_GESTURES_ENABLED), false, this, USER_ALL);
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(SHAKE_GESTURES_ACTION), false, this, USER_ALL);
}
@Override
public void onChange(boolean selfChange) {
updateSettings();
}
}
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright (C) 2023-2024 The RisingOS Android 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.
*/
package org.rising.server;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.SystemClock;
import android.provider.Settings;
import java.util.ArrayList;
public class ShakeGestureUtils implements SensorEventListener {
private static final String TAG = "ShakeGestureUtils";
private static final String SHAKE_GESTURES_SHAKE_INTENSITY = "shake_gestures_intensity";
private Context mContext;
private SensorManager mSensorManager;
private Sensor mAccelerometer;
private ArrayList<OnShakeListener> mListeners = new ArrayList<>();
private long mLastShakeTime = 0L;
private long mLastUpdateTime = 0L;
private int mShakeCount = 0;
private float mLastX = 0f;
private float mLastY = 0f;
private float mLastZ = 0f;
public ShakeGestureUtils(Context context) {
mContext = context;
mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
if (mSensorManager != null) {
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}
}
public interface OnShakeListener {
void onShake();
}
public void registerListener(OnShakeListener listener) {
if (!mListeners.contains(listener)) {
mListeners.add(listener);
startListening();
}
}
public void unregisterListener(OnShakeListener listener) {
mListeners.remove(listener);
if (mListeners.isEmpty()) {
stopListening();
}
}
private void startListening() {
if (mAccelerometer != null) {
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_UI);
}
}
private void stopListening() {
mSensorManager.unregisterListener(this);
}
private int getShakeIntensity() {
return Settings.System.getInt(mContext.getContentResolver(),
SHAKE_GESTURES_SHAKE_INTENSITY, 3);
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event == null) {
return;
}
long curUpdateTime = System.currentTimeMillis();
long timeInterval = curUpdateTime - mLastUpdateTime;
if (timeInterval < (getShakeIntensity() * 14f)) {
return;
}
if (event.values.length < 3) {
return;
}
mLastUpdateTime = curUpdateTime;
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
float deltaX = x - mLastX;
float deltaY = y - mLastY;
float deltaZ = z - mLastZ;
mLastX = x;
mLastY = y;
mLastZ = z;
double speed = Math.sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ) * 1000.0 / timeInterval;
if (speed >= getShakeIntensity() * 100f) {
notifyShakeListeners();
}
}
private void notifyShakeListeners() {
if (SystemClock.elapsedRealtime() - mLastShakeTime < 1000) {
return;
}
for (OnShakeListener listener : mListeners) {
listener.onShake();
}
mLastShakeTime = SystemClock.elapsedRealtime();
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {}
}