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:
@@ -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) {
|
||||
|
||||
106
services/core/java/org/rising/server/ShakeGestureService.java
Normal file
106
services/core/java/org/rising/server/ShakeGestureService.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
128
services/core/java/org/rising/server/ShakeGestureUtils.java
Normal file
128
services/core/java/org/rising/server/ShakeGestureUtils.java
Normal 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) {}
|
||||
}
|
||||
Reference in New Issue
Block a user