/* * Copyright (C) 2015 The CyanogenMod 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.cyanogenmod.settings.device; import android.app.ActivityManagerNative; import android.app.KeyguardManager; import android.content.ActivityNotFoundException; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Resources; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.camera2.CameraManager; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraAccessException; import android.media.session.MediaSessionLegacyHelper; import android.net.Uri; import android.os.Handler; 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.Vibrator; import android.provider.MediaStore; import android.provider.Settings; import android.provider.Settings.Global; import android.util.Log; import android.view.KeyEvent; import android.view.WindowManagerGlobal; import cyanogenmod.hardware.CMHardwareManager; import cyanogenmod.providers.CMSettings; import com.android.internal.os.DeviceKeyHandler; import com.android.internal.util.ArrayUtils; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.File; import java.io.FileReader; import java.io.IOException; import android.app.Instrumentation; import android.content.SharedPreferences; import android.content.ContextWrapper; public class KeyHandler implements DeviceKeyHandler { private static final String TAG = KeyHandler.class.getSimpleName(); private static final int GESTURE_REQUEST = 1; private static final String MEIZU_GESTURE_DATA = "/sys/devices/platform/mx-gs/gesture_data"; private static final String KEY_GESTURE_HAPTIC_FEEDBACK = "touchscreen_gesture_haptic_feedback"; private static final String TOUCHSCREEN_GESTURE_HAPTIC_FEEDBACK = "touchscreen_gesture_haptic_feedback"; private static final String ACTION_DISMISS_KEYGUARD = "com.android.keyguard.action.DISMISS_KEYGUARD_SECURELY"; // M2Note gesture codes public static final int DOUBLE_TAP = 0xA0; //160 public static final int SWIPE_X_LEFT = 0xB0; //176 public static final int SWIPE_X_RIGHT = 0xB1; public static final int SWIPE_Y_UP = 0xB2; public static final int SWIPE_Y_DOWN = 0xB3; public static final int UNICODE_E = 0xC0; // 192 public static final int UNICODE_C = 0xC1; public static final int UNICODE_W = 0xC2; public static final int UNICODE_M = 0xC3; public static final int UNICODE_O = 0xC4; public static final int UNICODE_S = 0xC5; public static final int UNICODE_V_UP = 0xC6; public static final int UNICODE_V_DOWN = 0xC7; public static final int UNICODE_V_L = 0xC8; public static final int UNICODE_V_R = 0xC9; public static final int UNICODE_Z = 0xCA; private static final int GESTURE_WAKELOCK_DURATION = 3000; private final Context mContext; private final PowerManager mPowerManager; private KeyguardManager mKeyguardManager; private EventHandler mEventHandler; private SensorManager mSensorManager; private PackageManager mPackageManager; private CameraManager mCameraManager; private String mRearCameraId; private boolean mTorchEnabled; private Sensor mProximitySensor; private Vibrator mVibrator; private WakeLock mProximityWakeLock; private WakeLock mGestureWakeLock; private int mProximityTimeOut; private boolean mProximityWakeSupported; private Instrumentation m_Instrumentation; private Context cmaContext = null; private boolean isLastPressHomeButton = false; private boolean mNotificationSliderVibrate; public KeyHandler(Context context) { mContext = context; mPackageManager = context.getPackageManager(); mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); mEventHandler = new EventHandler(); m_Instrumentation = new Instrumentation(); mGestureWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "GestureWakeLock"); try { cmaContext = mContext.createPackageContext("com.cyanogenmod.settings.device", Context.CONTEXT_RESTRICTED); } catch (Exception e) { e.printStackTrace(); } final Resources resources = mContext.getResources(); mProximityTimeOut = resources.getInteger( org.cyanogenmod.platform.internal.R.integer.config_proximityCheckTimeout); mProximityWakeSupported = resources.getBoolean( org.cyanogenmod.platform.internal.R.bool.config_proximityCheckOnWake); if (mProximityWakeSupported) { mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); mProximityWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ProximityWakeLock"); } 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(), mEventHandler); } 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; } } 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 void ensureKeyguardManager() { if (mKeyguardManager == null) { mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); } } private class EventHandler extends Handler { @Override public void handleMessage(Message msg) { try { boolean gestureHandled = true; int gestureData = (int) msg.obj; String prefValue = getCMAStringPref(String.valueOf(gestureData) + "_action"); Log.i(TAG, "Handling gesture: " + gestureData + " with action: " + prefValue); switch (prefValue) { case "wakeUp": mPowerManager.wakeUp(SystemClock.uptimeMillis()); break; case "unlock": ensureKeyguardManager(); mGestureWakeLock.acquire(GESTURE_WAKELOCK_DURATION); mContext.sendBroadcastAsUser(new Intent(ACTION_DISMISS_KEYGUARD), UserHandle.CURRENT); mPowerManager.wakeUp(SystemClock.uptimeMillis()); break; case "camera": ensureKeyguardManager(); final String action; mGestureWakeLock.acquire(GESTURE_WAKELOCK_DURATION); if (mKeyguardManager.isKeyguardSecure() && mKeyguardManager.isKeyguardLocked()) { action = MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE; } else { mContext.sendBroadcastAsUser(new Intent(ACTION_DISMISS_KEYGUARD), UserHandle.CURRENT); action = MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA; } mPowerManager.wakeUp(SystemClock.uptimeMillis()); Intent intent = new Intent(action, null); startActivitySafely(intent); break; case "play": dispatchMediaKeyWithWakeLockToMediaSession(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE); break; case "flashlight": String rearCameraId = getRearCameraId(); if (rearCameraId != null) { mGestureWakeLock.acquire(GESTURE_WAKELOCK_DURATION); try { mCameraManager.setTorchMode(rearCameraId, !mTorchEnabled); mTorchEnabled = !mTorchEnabled; } catch (CameraAccessException e) { // Ignore } } break; case "prev": dispatchMediaKeyWithWakeLockToMediaSession(KeyEvent.KEYCODE_MEDIA_PREVIOUS); break; case "next": dispatchMediaKeyWithWakeLockToMediaSession(KeyEvent.KEYCODE_MEDIA_NEXT); break; case "doNotDisturb": Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, Global.ZEN_MODE_NO_INTERRUPTIONS); break; case "normal": Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, Global.ZEN_MODE_OFF); break; case "mute": Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS); break; default: if (prefValue.startsWith("launch$")) { String packageName = prefValue.replace("launch$", ""); ensureKeyguardManager(); mGestureWakeLock.acquire(GESTURE_WAKELOCK_DURATION); if (!mKeyguardManager.isKeyguardSecure() && mKeyguardManager.isKeyguardLocked()) { mContext.sendBroadcastAsUser(new Intent(ACTION_DISMISS_KEYGUARD), UserHandle.CURRENT); } mPowerManager.wakeUp(SystemClock.uptimeMillis()); Intent appIntent = mPackageManager.getLaunchIntentForPackage(packageName); startActivitySafely(appIntent); } else { gestureHandled = false; } break; } if (gestureHandled) doHapticFeedback(); } catch (Exception e) { Log.e(TAG, "Gesture EventHandler", e); } } } public KeyEvent handleKeyEvent(KeyEvent event) { KeyEvent isHandled = event; CMHardwareManager hardware = CMHardwareManager.getInstance(mContext); boolean virtualKeysEnabled = hardware.get(CMHardwareManager.FEATURE_KEY_DISABLE); switch (event.getScanCode()) { case 102: // Home button event if (event.getAction() == KeyEvent.ACTION_DOWN) { if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && isLastPressHomeButton) { isHandled = null; } isLastPressHomeButton = mPowerManager.isInteractive() && event.getKeyCode() == KeyEvent.KEYCODE_HOME; } break; case 195: // Gesture event isHandled = null; if (event.getAction() == KeyEvent.ACTION_DOWN) { try { int gestureData = readGestureData(); if (gestureData != 0 && !mEventHandler.hasMessages(GESTURE_REQUEST)) { Message msg = getGestureMessage(gestureData); boolean defaultProximity = mContext.getResources().getBoolean( org.cyanogenmod.platform.internal.R.bool.config_proximityCheckOnWakeEnabledByDefault); boolean proximityWakeCheckEnabled = CMSettings.System.getInt(mContext.getContentResolver(), CMSettings.System.PROXIMITY_ON_WAKE, defaultProximity ? 1 : 0) == 1; if (mProximityWakeSupported && proximityWakeCheckEnabled && mProximitySensor != null) { mEventHandler.sendMessageDelayed(msg, mProximityTimeOut); processEvent(event); } else { mEventHandler.sendMessage(msg); } } } catch (Exception e) { Log.e(TAG, "Gesture handling failed.", e); isHandled = event; } } } if (event.getScanCode() == 102) { return virtualKeysEnabled ? null : isHandled; } return isHandled; } private Message getGestureMessage(int gestureData) { Message msg = mEventHandler.obtainMessage(GESTURE_REQUEST); msg.obj = gestureData; return msg; } private Message getMessageForKeyEvent(KeyEvent keyEvent) { Message msg = mEventHandler.obtainMessage(GESTURE_REQUEST); msg.obj = keyEvent; return msg; } private void processEvent(final KeyEvent keyEvent) { mProximityWakeLock.acquire(); mSensorManager.registerListener(new SensorEventListener() { @Override public void onSensorChanged(SensorEvent event) { mProximityWakeLock.release(); mSensorManager.unregisterListener(this); if (!mEventHandler.hasMessages(GESTURE_REQUEST)) { // The sensor took to long, ignoring. return; } mEventHandler.removeMessages(GESTURE_REQUEST); if (event.values[0] == mProximitySensor.getMaximumRange()) { Message msg = getMessageForKeyEvent(keyEvent); mEventHandler.sendMessage(msg); } } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) {} }, mProximitySensor, SensorManager.SENSOR_DELAY_FASTEST); } private void dispatchMediaKeyWithWakeLockToMediaSession(int keycode) { MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext); if (helper != null) { KeyEvent event = new KeyEvent(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), KeyEvent.ACTION_DOWN, keycode, 0); helper.sendMediaButtonEvent(event, true); event = KeyEvent.changeAction(event, KeyEvent.ACTION_UP); helper.sendMediaButtonEvent(event, true); } else { Log.w(TAG, "Unable to send media key event"); } } 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 } } // TODO implement it more graceful way private boolean getCMApref(String prefKey, boolean defVal) { SharedPreferences cmaPrefs = null; cmaPrefs = cmaContext.getSharedPreferences("com.cyanogenmod.settings.device_preferences", Context.MODE_MULTI_PROCESS); return cmaPrefs.getBoolean(prefKey, defVal); } private String getCMAStringPref(String prefKey) { SharedPreferences cmaPrefs = null; cmaPrefs = cmaContext.getSharedPreferences("com.cyanogenmod.settings.device_preferences", Context.MODE_MULTI_PROCESS); return cmaPrefs.getString(prefKey, "disabled"); } private void doHapticFeedback() { if (mVibrator == null) { return; } boolean enabled = getCMApref(TOUCHSCREEN_GESTURE_HAPTIC_FEEDBACK, false); if (enabled) { mVibrator.vibrate(50); } } public String readSingleLine(File f) throws IOException { BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(f)); String line = reader.readLine(); reader.close(); return line; } finally { if (reader != null) { try { reader.close(); } catch (IOException ignored) { } } } } private int readGestureData() throws Exception { File f = new File(MEIZU_GESTURE_DATA); return Integer.valueOf(readSingleLine(f)); } }