Revert "sanders: drop device keyhandler for now"
This reverts commit e3c7463566.
* Also remove proximity check from keyhandler
This commit is contained in:
756
MotoActions/src/com/moto/actions/KeyHandler.java
Normal file
756
MotoActions/src/com/moto/actions/KeyHandler.java
Normal file
@@ -0,0 +1,756 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The CyanogenMod Project
|
||||
* Copyright (C) 2017 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.
|
||||
*/
|
||||
|
||||
package com.moto.actions;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManagerNative;
|
||||
import android.app.ISearchManager;
|
||||
import android.app.KeyguardManager;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.camera2.CameraAccessException;
|
||||
import android.hardware.camera2.CameraCharacteristics;
|
||||
import android.hardware.camera2.CameraManager;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.session.MediaSessionLegacyHelper;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.PowerManager;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.os.VibrationEffect;
|
||||
import android.os.Vibrator;
|
||||
import android.provider.MediaStore;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyCharacterMap;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import com.android.internal.os.DeviceKeyHandler;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
|
||||
import com.moto.actions.util.FileUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.moto.actions.actions.Constants.*;
|
||||
|
||||
public class KeyHandler implements DeviceKeyHandler {
|
||||
|
||||
private static final String TAG = KeyHandler.class.getSimpleName();
|
||||
|
||||
private static final int GESTURE_REQUEST = 1;
|
||||
private static final int FP_ACTION_REQUEST = 2;
|
||||
|
||||
private static final String ACTION_DISMISS_KEYGUARD =
|
||||
"com.android.keyguard.action.DISMISS_KEYGUARD_SECURELY";
|
||||
|
||||
private static final String GESTURE_WAKEUP_REASON = "keyhandler-gesture-wakeup";
|
||||
private static final int GESTURE_WAKELOCK_DURATION = 3000;
|
||||
private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
|
||||
.build();
|
||||
private final Context mContext;
|
||||
private final PowerManager mPowerManager;
|
||||
WakeLock mGestureWakeLock;
|
||||
private KeyguardManager mKeyguardManager;
|
||||
private FPScreenOffGesturesHandler mFPScreenOffGesturesHandler;
|
||||
private CameraManager mCameraManager;
|
||||
private String mRearCameraId;
|
||||
private boolean mTorchEnabled;
|
||||
private Vibrator mVibrator;
|
||||
private ISearchManager mSearchManagerService;
|
||||
private Handler mHandler;
|
||||
private int fpTapCounts = 0;
|
||||
private boolean fpTapPending = false;
|
||||
private boolean fpGesturePending = false;
|
||||
private Runnable doubleTapRunnable = new Runnable() {
|
||||
public void run() {
|
||||
int action = 0;
|
||||
if (fpTapCounts > 1) {
|
||||
action = str2int(FileUtils.readOneLine(getFPNodeBasedOnScreenState(FP_KEY_DBLTAP_NODE)));
|
||||
} else {
|
||||
if (isSingleTapEnabledOnFP()) {
|
||||
action = str2int(FileUtils.readOneLine(getFPNodeBasedOnScreenState(FP_KEYS_NODE)));
|
||||
}
|
||||
}
|
||||
|
||||
if (action != 0) {
|
||||
boolean isActionSupported = ArrayUtils.contains(mPowerManager.isScreenOn() ? sFPSupportedActions : sFPSupportedActionsScreenOff, action);
|
||||
if (isActionSupported) {
|
||||
fireFPAction(action, true);
|
||||
}
|
||||
}
|
||||
resetDoubleTapOnFP();
|
||||
}
|
||||
};
|
||||
private Runnable fpGestureRunnable = new Runnable() {
|
||||
public void run() {
|
||||
resetFPGestureDelay();
|
||||
}
|
||||
};
|
||||
|
||||
public KeyHandler(Context context) {
|
||||
mContext = context;
|
||||
|
||||
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||
mFPScreenOffGesturesHandler = new FPScreenOffGesturesHandler();
|
||||
|
||||
mGestureWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
|
||||
"GestureWakeLock");
|
||||
|
||||
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
|
||||
if (mVibrator == null || !mVibrator.hasVibrator()) {
|
||||
mVibrator = null;
|
||||
}
|
||||
|
||||
mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
|
||||
mCameraManager.registerTorchCallback(new MyTorchCallback(), null);
|
||||
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
}
|
||||
|
||||
static long[] getLongIntArray(Resources r, int resid) {
|
||||
int[] ar = r.getIntArray(resid);
|
||||
if (ar == null) {
|
||||
return null;
|
||||
}
|
||||
long[] out = new long[ar.length];
|
||||
for (int i = 0; i < ar.length; i++) {
|
||||
out[i] = ar[i];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private static ActivityInfo getRunningActivityInfo(Context context) {
|
||||
final ActivityManager am = (ActivityManager) context
|
||||
.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
final PackageManager pm = context.getPackageManager();
|
||||
|
||||
List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
|
||||
if (tasks != null && !tasks.isEmpty()) {
|
||||
ActivityManager.RunningTaskInfo top = tasks.get(0);
|
||||
try {
|
||||
return pm.getActivityInfo(top.topActivity, 0);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void dispatchMediaKeyWithWakeLock(int keycode, Context context) {
|
||||
if (ActivityManagerNative.isSystemReady()) {
|
||||
KeyEvent event = new KeyEvent(SystemClock.uptimeMillis(),
|
||||
SystemClock.uptimeMillis(), KeyEvent.ACTION_DOWN, keycode, 0);
|
||||
MediaSessionLegacyHelper.getHelper(context).sendMediaButtonEvent(event, true);
|
||||
event = KeyEvent.changeAction(event, KeyEvent.ACTION_UP);
|
||||
MediaSessionLegacyHelper.getHelper(context).sendMediaButtonEvent(event, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void switchToLastApp(Context context) {
|
||||
final ActivityManager am =
|
||||
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
ActivityManager.RunningTaskInfo lastTask = getLastTask(context, am);
|
||||
|
||||
if (lastTask != null) {
|
||||
am.moveTaskToFront(lastTask.id, ActivityManager.MOVE_TASK_NO_USER_ACTION);
|
||||
}
|
||||
}
|
||||
|
||||
private static ActivityManager.RunningTaskInfo getLastTask(Context context,
|
||||
final ActivityManager am) {
|
||||
final String defaultHomePackage = resolveCurrentLauncherPackage(context);
|
||||
List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(5);
|
||||
|
||||
for (int i = 1; i < tasks.size(); i++) {
|
||||
String packageName = tasks.get(i).topActivity.getPackageName();
|
||||
if (!packageName.equals(defaultHomePackage)
|
||||
&& !packageName.equals(context.getPackageName())
|
||||
&& !packageName.equals("com.android.systemui")) {
|
||||
return tasks.get(i);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String resolveCurrentLauncherPackage(Context context) {
|
||||
final Intent launcherIntent = new Intent(Intent.ACTION_MAIN)
|
||||
.addCategory(Intent.CATEGORY_HOME);
|
||||
final PackageManager pm = context.getPackageManager();
|
||||
final ResolveInfo launcherInfo = pm.resolveActivity(launcherIntent, 0);
|
||||
return launcherInfo.activityInfo.packageName;
|
||||
}
|
||||
|
||||
private String getRearCameraId() {
|
||||
if (mRearCameraId == null) {
|
||||
try {
|
||||
for (final String cameraId : mCameraManager.getCameraIdList()) {
|
||||
CameraCharacteristics characteristics =
|
||||
mCameraManager.getCameraCharacteristics(cameraId);
|
||||
int cOrientation = characteristics.get(CameraCharacteristics.LENS_FACING);
|
||||
if (cOrientation == CameraCharacteristics.LENS_FACING_BACK) {
|
||||
mRearCameraId = cameraId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (CameraAccessException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
return mRearCameraId;
|
||||
}
|
||||
|
||||
private Intent getLaunchableIntent(Intent intent) {
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
List<ResolveInfo> resInfo = pm.queryIntentActivities(intent, 0);
|
||||
if (resInfo.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return pm.getLaunchIntentForPackage(resInfo.get(0).activityInfo.packageName);
|
||||
}
|
||||
|
||||
private void triggerCameraAction() {
|
||||
ensureKeyguardManager();
|
||||
WakeLock wl = mPowerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, "GestureWakeLock");
|
||||
wl.acquire(500);
|
||||
if (mKeyguardManager.inKeyguardRestrictedInputMode()) {
|
||||
launchSecureCamera();
|
||||
} else {
|
||||
launchCamera();
|
||||
}
|
||||
}
|
||||
|
||||
private void launchCamera() {
|
||||
Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.addFlags(Intent.FLAG_FROM_BACKGROUND);
|
||||
if (getBestActivityInfo(intent) != null) {
|
||||
// Only launch if we can succeed, but let the user pick the action
|
||||
mContext.startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
private void launchSecureCamera() {
|
||||
// Keyguard won't allow a picker, try to pick the secure intent in the package
|
||||
// that would be the one used for a default action of launching the camera
|
||||
Intent normalIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
|
||||
normalIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
normalIntent.addFlags(Intent.FLAG_FROM_BACKGROUND);
|
||||
|
||||
Intent secureIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE);
|
||||
secureIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
secureIntent.addFlags(Intent.FLAG_FROM_BACKGROUND);
|
||||
|
||||
ActivityInfo normalActivity = getBestActivityInfo(normalIntent);
|
||||
ActivityInfo secureActivity = getBestActivityInfo(secureIntent, normalActivity);
|
||||
if (secureActivity != null) {
|
||||
secureIntent.setComponent(new ComponentName(secureActivity.applicationInfo.packageName, secureActivity.name));
|
||||
mContext.startActivity(secureIntent);
|
||||
}
|
||||
}
|
||||
|
||||
private ActivityInfo getBestActivityInfo(Intent intent) {
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
|
||||
if (resolveInfo != null) {
|
||||
return resolveInfo.activityInfo;
|
||||
} else {
|
||||
// If the resolving failed, just find our own best match
|
||||
return getBestActivityInfo(intent, null);
|
||||
}
|
||||
}
|
||||
|
||||
private ActivityInfo getBestActivityInfo(Intent intent, ActivityInfo match) {
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
List<ResolveInfo> activities = pm.queryIntentActivities(intent, 0);
|
||||
ActivityInfo best = null;
|
||||
if (activities.size() > 0) {
|
||||
best = activities.get(0).activityInfo;
|
||||
if (match != null) {
|
||||
String packageName = match.applicationInfo.packageName;
|
||||
for (int i = activities.size() - 1; i >= 0; i--) {
|
||||
ActivityInfo activityInfo = activities.get(i).activityInfo;
|
||||
if (packageName.equals(activityInfo.applicationInfo.packageName)) {
|
||||
best = activityInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
private void openBrowser() {
|
||||
mGestureWakeLock.acquire(GESTURE_WAKELOCK_DURATION);
|
||||
mPowerManager.wakeUp(SystemClock.uptimeMillis(), GESTURE_WAKEUP_REASON);
|
||||
final Intent intent = getLaunchableIntent(
|
||||
new Intent(Intent.ACTION_VIEW, Uri.parse("http:")));
|
||||
startActivitySafely(intent);
|
||||
}
|
||||
|
||||
private void openDialer() {
|
||||
mGestureWakeLock.acquire(GESTURE_WAKELOCK_DURATION);
|
||||
mPowerManager.wakeUp(SystemClock.uptimeMillis(), GESTURE_WAKEUP_REASON);
|
||||
final Intent intent = new Intent(Intent.ACTION_DIAL, null);
|
||||
startActivitySafely(intent);
|
||||
}
|
||||
|
||||
private void openEmail() {
|
||||
mGestureWakeLock.acquire(GESTURE_WAKELOCK_DURATION);
|
||||
mPowerManager.wakeUp(SystemClock.uptimeMillis(), GESTURE_WAKEUP_REASON);
|
||||
final Intent intent = getLaunchableIntent(
|
||||
new Intent(Intent.ACTION_VIEW, Uri.parse("mailto:")));
|
||||
startActivitySafely(intent);
|
||||
}
|
||||
|
||||
private void openMessages() {
|
||||
mGestureWakeLock.acquire(GESTURE_WAKELOCK_DURATION);
|
||||
mPowerManager.wakeUp(SystemClock.uptimeMillis(), GESTURE_WAKEUP_REASON);
|
||||
final String defaultApplication = Settings.Secure.getString(
|
||||
mContext.getContentResolver(), "sms_default_application");
|
||||
final PackageManager pm = mContext.getPackageManager();
|
||||
final Intent intent = pm.getLaunchIntentForPackage(defaultApplication);
|
||||
if (intent != null) {
|
||||
startActivitySafely(intent);
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleFlashlight() {
|
||||
String rearCameraId = getRearCameraId();
|
||||
if (rearCameraId != null) {
|
||||
mGestureWakeLock.acquire(GESTURE_WAKELOCK_DURATION);
|
||||
try {
|
||||
mCameraManager.setTorchMode(rearCameraId, !mTorchEnabled);
|
||||
mTorchEnabled = !mTorchEnabled;
|
||||
} catch (CameraAccessException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureKeyguardManager() {
|
||||
if (mKeyguardManager == null) {
|
||||
mKeyguardManager =
|
||||
(KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetDoubleTapOnFP() {
|
||||
fpTapCounts = 0;
|
||||
fpTapPending = false;
|
||||
mHandler.removeCallbacks(doubleTapRunnable);
|
||||
}
|
||||
|
||||
private void detectDoubleTapOnFP() {
|
||||
fpTapCounts++;
|
||||
if (fpTapCounts == 1 || fpTapCounts == 2) {
|
||||
doHapticFeedbackFP(false);
|
||||
}
|
||||
if (!fpTapPending) {
|
||||
fpTapPending = true;
|
||||
mHandler.postDelayed(doubleTapRunnable, ViewConfiguration.getDoubleTapTimeout());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSingleTapEnabledOnFP() {
|
||||
return !FileUtils.readOneLine(getFPNodeBasedOnScreenState(FP_KEYS_NODE)).equals("0");
|
||||
}
|
||||
|
||||
private boolean isDoubleTapEnabledOnFP() {
|
||||
return !FileUtils.readOneLine(getFPNodeBasedOnScreenState(FP_KEY_DBLTAP_NODE)).equals("0");
|
||||
}
|
||||
|
||||
private boolean isHapticFeedbackEnabledOnFP() {
|
||||
return !FileUtils.readOneLine(getFPNodeBasedOnScreenState(FP_HAPTIC_NODE)).equals("0");
|
||||
}
|
||||
|
||||
private String getFPNodeBasedOnScreenState(String node) {
|
||||
if (mPowerManager.isScreenOn()) {
|
||||
return node;
|
||||
}
|
||||
switch (node) {
|
||||
case FP_KEYS_NODE:
|
||||
return FP_KEYS_SCREENOFF_NODE;
|
||||
case FP_HAPTIC_NODE:
|
||||
return FP_HAPTIC_SCREENOFF_NODE;
|
||||
case FP_KEY_DBLTAP_NODE:
|
||||
return FP_KEY_SCREENOFF_DBLTAP_NODE;
|
||||
case FP_KEY_HOLD_NODE:
|
||||
return FP_KEY_SCREENOFF_HOLD_NODE;
|
||||
case FP_KEY_RIGHT_NODE:
|
||||
return FP_KEY_SCREENOFF_RIGHT_NODE;
|
||||
case FP_KEY_LEFT_NODE:
|
||||
return FP_KEY_SCREENOFF_LEFT_NODE;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
public boolean handleKeyEvent(KeyEvent event) {
|
||||
int scanCode = event.getScanCode();
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "DEBUG: action=" + event.getAction()
|
||||
+ ", flags=" + event.getFlags()
|
||||
+ ", keyCode=" + event.getKeyCode()
|
||||
+ ", scanCode=" + event.getScanCode()
|
||||
+ ", metaState=" + event.getMetaState()
|
||||
+ ", repeatCount=" + event.getRepeatCount());
|
||||
}
|
||||
|
||||
boolean isFPScanCode = ArrayUtils.contains(sSupportedFPGestures, scanCode);
|
||||
if (!isFPScanCode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isFPGestureEnabled = FileUtils.readOneLine(FP_HOME_NODE).equals("1");
|
||||
boolean isFPGestureEnabledOnScreenOff = FileUtils.readOneLine(FP_HOME_OFF_NODE).equals("1");
|
||||
|
||||
boolean isScreenOn = mPowerManager.isScreenOn();
|
||||
|
||||
// We only want ACTION_UP event
|
||||
if (event.getAction() != KeyEvent.ACTION_UP) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isFPScanCode){
|
||||
if (fpGesturePending) {
|
||||
return false;
|
||||
} else {
|
||||
resetFPGestureDelay();
|
||||
fpGesturePending = true;
|
||||
mHandler.postDelayed(fpGestureRunnable, 10);
|
||||
}
|
||||
}
|
||||
|
||||
if (scanCode != FP_TAP_SCANCODE) {
|
||||
resetDoubleTapOnFP();
|
||||
}
|
||||
|
||||
if (isFPScanCode) {
|
||||
if ((!isFPGestureEnabled) || (!isScreenOn && !isFPGestureEnabledOnScreenOff)) {
|
||||
resetDoubleTapOnFP();
|
||||
return false;
|
||||
}
|
||||
if (!isScreenOn && isFPGestureEnabledOnScreenOff) {
|
||||
processFPScreenOffScancode(scanCode);
|
||||
} else {
|
||||
processFPScancode(scanCode);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void processFPScancode(int scanCode) {
|
||||
int action = 0;
|
||||
boolean isScreenOn = mPowerManager.isScreenOn();
|
||||
switch (scanCode) {
|
||||
case FP_TAP_SCANCODE:
|
||||
if (isDoubleTapEnabledOnFP()) {
|
||||
detectDoubleTapOnFP();
|
||||
return;
|
||||
} else {
|
||||
resetDoubleTapOnFP();
|
||||
action = str2int(FileUtils.readOneLine(getFPNodeBasedOnScreenState(FP_KEYS_NODE)));
|
||||
}
|
||||
break;
|
||||
case FP_HOLD_SCANCODE:
|
||||
action = str2int(FileUtils.readOneLine(getFPNodeBasedOnScreenState(FP_KEY_HOLD_NODE)));
|
||||
break;
|
||||
case FP_RIGHT_SCANCODE:
|
||||
action = str2int(FileUtils.readOneLine(getFPNodeBasedOnScreenState(FP_KEY_RIGHT_NODE)));
|
||||
break;
|
||||
case FP_LEFT_SCANCODE:
|
||||
action = str2int(FileUtils.readOneLine(getFPNodeBasedOnScreenState(FP_KEY_LEFT_NODE)));
|
||||
break;
|
||||
}
|
||||
boolean isActionSupported = ArrayUtils.contains(isScreenOn ? sFPSupportedActions : sFPSupportedActionsScreenOff, action);
|
||||
if (isActionSupported) {
|
||||
fireFPAction(action, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void fireFPAction(int action, boolean isDoubleTap) {
|
||||
ensureKeyguardManager();
|
||||
boolean isHapticFeedbackEnabledOnFP = isHapticFeedbackEnabledOnFP();
|
||||
if (isDoubleTap && action != ACTION_CAMERA && action != ACTION_FLASHLIGHT) {
|
||||
isHapticFeedbackEnabledOnFP = false;
|
||||
}
|
||||
if (isHapticFeedbackEnabledOnFP){
|
||||
if (action == ACTION_CAMERA || action == ACTION_FLASHLIGHT) {
|
||||
vibrate(action == ACTION_CAMERA ? 500 : 250);
|
||||
}else if (action != ACTION_VOICE_ASSISTANT) {
|
||||
doHapticFeedbackFP(false);
|
||||
}
|
||||
}
|
||||
switch (action) {
|
||||
case ACTION_HOME:
|
||||
if (!mKeyguardManager.inKeyguardRestrictedInputMode()) {
|
||||
triggerVirtualKeypress(mHandler, KeyEvent.KEYCODE_HOME);
|
||||
}
|
||||
break;
|
||||
case ACTION_POWER:
|
||||
toggleScreenState();
|
||||
break;
|
||||
case ACTION_BACK:
|
||||
triggerVirtualKeypress(mHandler, KeyEvent.KEYCODE_BACK);
|
||||
break;
|
||||
case ACTION_RECENTS:
|
||||
if (!mKeyguardManager.inKeyguardRestrictedInputMode()) {
|
||||
triggerVirtualKeypress(mHandler, KeyEvent.KEYCODE_APP_SWITCH);
|
||||
}
|
||||
break;
|
||||
case ACTION_VOLUME_UP:
|
||||
triggerVirtualKeypress(mHandler, KeyEvent.KEYCODE_VOLUME_UP);
|
||||
break;
|
||||
case ACTION_VOLUME_DOWN:
|
||||
triggerVirtualKeypress(mHandler, KeyEvent.KEYCODE_VOLUME_DOWN);
|
||||
break;
|
||||
case ACTION_VOICE_ASSISTANT:
|
||||
if (!mKeyguardManager.inKeyguardRestrictedInputMode()) {
|
||||
fireGoogleNowOnTap();
|
||||
}
|
||||
return;
|
||||
case ACTION_PLAY_PAUSE:
|
||||
dispatchMediaKeyWithWakeLock(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, mContext);
|
||||
break;
|
||||
case ACTION_PREVIOUS_TRACK:
|
||||
dispatchMediaKeyWithWakeLock(KeyEvent.KEYCODE_MEDIA_PREVIOUS, mContext);
|
||||
break;
|
||||
case ACTION_NEXT_TRACK:
|
||||
dispatchMediaKeyWithWakeLock(KeyEvent.KEYCODE_MEDIA_NEXT, mContext);
|
||||
break;
|
||||
case ACTION_FLASHLIGHT:
|
||||
toggleFlashlight();
|
||||
break;
|
||||
case ACTION_CAMERA:
|
||||
triggerCameraAction();
|
||||
break;
|
||||
case ACTION_SCREENSHOT:
|
||||
triggerVirtualKeypress(mHandler, KeyEvent.KEYCODE_SYSRQ);
|
||||
break;
|
||||
case ACTION_LAST_APP:
|
||||
if (!mKeyguardManager.inKeyguardRestrictedInputMode()) {
|
||||
switchToLastApp(mContext);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void vibrate(int intensity) {
|
||||
if (mVibrator == null) {
|
||||
return;
|
||||
}
|
||||
mVibrator.vibrate(intensity);
|
||||
}
|
||||
|
||||
private void toggleScreenState() {
|
||||
if (mPowerManager.isScreenOn()) {
|
||||
mPowerManager.goToSleep(SystemClock.uptimeMillis());
|
||||
} else {
|
||||
mPowerManager.wakeUp(SystemClock.uptimeMillis());
|
||||
}
|
||||
}
|
||||
|
||||
private void triggerVirtualKeypress(final Handler handler, final int keyCode) {
|
||||
final InputManager im = InputManager.getInstance();
|
||||
long now = SystemClock.uptimeMillis();
|
||||
|
||||
final KeyEvent downEvent = new KeyEvent(now, now, KeyEvent.ACTION_DOWN,
|
||||
keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
|
||||
KeyEvent.FLAG_FROM_SYSTEM, InputDevice.SOURCE_CLASS_BUTTON);
|
||||
final KeyEvent upEvent = KeyEvent.changeAction(downEvent,
|
||||
KeyEvent.ACTION_UP);
|
||||
|
||||
// add a small delay to make sure everything behind got focus
|
||||
handler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
im.injectInputEvent(downEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
|
||||
}
|
||||
}, 10);
|
||||
|
||||
handler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
im.injectInputEvent(upEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
|
||||
}
|
||||
}, 20);
|
||||
}
|
||||
|
||||
private void fireGoogleNowOnTap() {
|
||||
doHapticFeedbackFP(true);
|
||||
mSearchManagerService = ISearchManager.Stub.asInterface(ServiceManager.getService(Context.SEARCH_SERVICE));
|
||||
if (mSearchManagerService != null) {
|
||||
try {
|
||||
mSearchManagerService.launchAssist(new Bundle());
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int str2int(String str) {
|
||||
if (str == null || str.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
return Integer.valueOf(str);
|
||||
} catch (Exception e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void processFPScreenOffScancode(int scanCode) {
|
||||
if (!mFPScreenOffGesturesHandler.hasMessages(FP_ACTION_REQUEST)) {
|
||||
Message msg = mFPScreenOffGesturesHandler.obtainMessage(FP_ACTION_REQUEST);
|
||||
msg.arg1 = scanCode;
|
||||
mFPScreenOffGesturesHandler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetFPGestureDelay() {
|
||||
fpGesturePending = false;
|
||||
mHandler.removeCallbacks(fpGestureRunnable);
|
||||
}
|
||||
|
||||
private void fireScreenOffAction(int action) {
|
||||
boolean haptic = Settings.System.getInt(mContext.getContentResolver(), KEY_GESTURE_ENABLE_HAPTIC_FEEDBACK, 1) != 0;
|
||||
if (haptic && (action == ACTION_CAMERA || action == ACTION_FLASHLIGHT)) {
|
||||
vibrate(action == ACTION_CAMERA ? 500 : 250);
|
||||
}
|
||||
if (haptic && action == ACTION_POWER){
|
||||
doHapticFeedbackScreenOff();
|
||||
}
|
||||
switch (action) {
|
||||
case ACTION_POWER:
|
||||
toggleScreenState();
|
||||
break;
|
||||
case ACTION_PLAY_PAUSE:
|
||||
dispatchMediaKeyWithWakeLock(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, mContext);
|
||||
break;
|
||||
case ACTION_PREVIOUS_TRACK:
|
||||
dispatchMediaKeyWithWakeLock(KeyEvent.KEYCODE_MEDIA_PREVIOUS, mContext);
|
||||
break;
|
||||
case ACTION_NEXT_TRACK:
|
||||
dispatchMediaKeyWithWakeLock(KeyEvent.KEYCODE_MEDIA_NEXT, mContext);
|
||||
break;
|
||||
case ACTION_FLASHLIGHT:
|
||||
toggleFlashlight();
|
||||
break;
|
||||
case ACTION_CAMERA:
|
||||
triggerCameraAction();
|
||||
break;
|
||||
case ACTION_BROWSER:
|
||||
openBrowser();
|
||||
break;
|
||||
case ACTION_DIALER:
|
||||
openDialer();
|
||||
break;
|
||||
case ACTION_EMAIL:
|
||||
openEmail();
|
||||
break;
|
||||
case ACTION_MESSAGES:
|
||||
openMessages();
|
||||
break;
|
||||
}
|
||||
if (action != ACTION_FLASHLIGHT && action != ACTION_CAMERA && action != ACTION_POWER) {
|
||||
doHapticFeedbackScreenOff();
|
||||
}
|
||||
}
|
||||
|
||||
private void startActivitySafely(Intent intent) {
|
||||
intent.addFlags(
|
||||
Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
| Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
try {
|
||||
UserHandle user = new UserHandle(UserHandle.USER_CURRENT);
|
||||
mContext.startActivityAsUser(intent, null, user);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
private void doHapticFeedbackScreenOff() {
|
||||
if (mVibrator == null) {
|
||||
return;
|
||||
}
|
||||
boolean enabled = Settings.System.getInt(mContext.getContentResolver(), KEY_GESTURE_ENABLE_HAPTIC_FEEDBACK, 1) != 0;
|
||||
if (enabled) {
|
||||
mVibrator.vibrate(50);
|
||||
}
|
||||
}
|
||||
|
||||
private void doHapticFeedbackFP(boolean longpress) {
|
||||
if (mVibrator == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isHapticFeedbackEnabledOnFP()) {
|
||||
mHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
int owningUid;
|
||||
String owningPackage;
|
||||
owningUid = android.os.Process.myUid();
|
||||
owningPackage = mContext.getOpPackageName();
|
||||
VibrationEffect effect = VibrationEffect.createOneShot(longpress ? 50 : 40, VibrationEffect.DEFAULT_AMPLITUDE);
|
||||
mVibrator.vibrate(owningUid, owningPackage, effect, VIBRATION_ATTRIBUTES);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private class FPScreenOffGesturesHandler extends Handler {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
processFPScancode(msg.arg1);
|
||||
}
|
||||
}
|
||||
|
||||
private class MyTorchCallback extends CameraManager.TorchCallback {
|
||||
@Override
|
||||
public void onTorchModeChanged(String cameraId, boolean enabled) {
|
||||
if (!cameraId.equals(mRearCameraId))
|
||||
return;
|
||||
mTorchEnabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTorchModeUnavailable(String cameraId) {
|
||||
if (!cameraId.equals(mRearCameraId))
|
||||
return;
|
||||
mTorchEnabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -319,6 +319,12 @@
|
||||
<!-- Whether device supports double tap to wake -->
|
||||
<bool name="config_supportDoubleTapWake">false</bool>
|
||||
|
||||
<!-- Paths to the libraries that contain device specific key handlers -->
|
||||
<string name="config_deviceKeyHandlerLib" translatable="false">/system/priv-app/MotoActions/MotoActions.apk</string>
|
||||
|
||||
<!-- Names of the key handler classes -->
|
||||
<string name="config_deviceKeyHandlerClass" translatable="false">com.moto.actions.KeyHandler</string>
|
||||
|
||||
<!-- Default value for proximity check on screen wake
|
||||
NOTE ! - Enable for devices that have a fast response proximity sensor (ideally < 300ms)-->
|
||||
<!--<bool name="config_proximityCheckOnWake">true</bool>
|
||||
|
||||
Reference in New Issue
Block a user