diff --git a/cmactions/Android.mk b/cmactions/Android.mk
new file mode 100644
index 0000000..747a317
--- /dev/null
+++ b/cmactions/Android.mk
@@ -0,0 +1,40 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CMActions
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-v14-preference \
+ android-support-v7-appcompat \
+ android-support-v7-preference \
+ android-support-v7-recyclerview
+
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+
+LOCAL_RESOURCE_DIR := \
+ $(LOCAL_PATH)/res \
+ $(LOCAL_PATH)/../../../../packages/resources/devicesettings/res \
+ frameworks/support/v14/preference/res \
+ frameworks/support/v7/appcompat/res \
+ frameworks/support/v7/preference/res \
+ frameworks/support/v7/recyclerview/res
+
+LOCAL_AAPT_FLAGS := --auto-add-overlay \
+ --extra-packages android.support.v14.preference:android.support.v7.appcompat:android.support.v7.preference:android.support.v7.recyclerview
+
+ifneq ($(INCREMENTAL_BUILDS),)
+ LOCAL_PROGUARD_ENABLED := disabled
+ LOCAL_JACK_ENABLED := incremental
+endif
+
+include frameworks/base/packages/SettingsLib/common.mk
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/cmactions/AndroidManifest.xml b/cmactions/AndroidManifest.xml
new file mode 100644
index 0000000..c42bfca
--- /dev/null
+++ b/cmactions/AndroidManifest.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cmactions/proguard.flags b/cmactions/proguard.flags
new file mode 100644
index 0000000..32daf9b
--- /dev/null
+++ b/cmactions/proguard.flags
@@ -0,0 +1,9 @@
+-keep class org.cyanogenmod.CMActions.* {
+ *;
+}
+
+-keepclasseswithmembers class * {
+ public (android.content.Context, android.util.AttributeSet);
+}
+
+-keep class ** extends android.support.v14.preference.PreferenceFragment
diff --git a/cmactions/res/drawable/ic_settings_gestures.xml b/cmactions/res/drawable/ic_settings_gestures.xml
new file mode 100644
index 0000000..95cdede
--- /dev/null
+++ b/cmactions/res/drawable/ic_settings_gestures.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
diff --git a/cmactions/res/values/array.xml b/cmactions/res/values/array.xml
new file mode 100644
index 0000000..eb7e711
--- /dev/null
+++ b/cmactions/res/values/array.xml
@@ -0,0 +1,44 @@
+
+
+
+
+ - None
+ - Home
+ - Power
+ - Back
+ - Recents
+ - Volume Up
+ - Volume Down
+ - Voice Assistant
+ - Play/Pause
+ - Previous Track
+ - Next Track
+
+
+ - 0
+ - 102
+ - 116
+ - 158
+ - 580
+ - 115
+ - 114
+ - 582
+ - 164
+ - 165
+ - 163
+
+
diff --git a/cmactions/res/values/strings.xml b/cmactions/res/values/strings.xml
new file mode 100644
index 0000000..6e07fe2
--- /dev/null
+++ b/cmactions/res/values/strings.xml
@@ -0,0 +1,21 @@
+
+
+
+ Moto Actions
+ Manage custom actions and fingerprint gestures
+
diff --git a/cmactions/res/values/styles.xml b/cmactions/res/values/styles.xml
new file mode 100644
index 0000000..98927a1
--- /dev/null
+++ b/cmactions/res/values/styles.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
diff --git a/cmactions/res/xml/doze_panel.xml b/cmactions/res/xml/doze_panel.xml
new file mode 100644
index 0000000..c260b94
--- /dev/null
+++ b/cmactions/res/xml/doze_panel.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cmactions/res/xml/gesture_panel.xml b/cmactions/res/xml/gesture_panel.xml
new file mode 100644
index 0000000..11f6c53
--- /dev/null
+++ b/cmactions/res/xml/gesture_panel.xml
@@ -0,0 +1,172 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cmactions/src/com/cyanogenmod/settings/device/BootCompletedReceiver.java b/cmactions/src/com/cyanogenmod/settings/device/BootCompletedReceiver.java
new file mode 100644
index 0000000..a198e3b
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/BootCompletedReceiver.java
@@ -0,0 +1,61 @@
+/*
+ * 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.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.cyanogenmod.settings.device.ServiceWrapper.LocalBinder;
+
+public class BootCompletedReceiver extends BroadcastReceiver {
+ static final String TAG = "CMActions";
+ final String NAVBAR_SHOWN = "navbar_shown";
+ private ServiceWrapper mServiceWrapper;
+
+ @Override
+ public void onReceive(final Context context, Intent intent) {
+ Log.i(TAG, "Booting");
+
+ // Restore nodes to saved preference values
+ for (String pref : Constants.sButtonPrefKeys) {
+ Constants.writePreference(context, pref);
+ }
+
+ context.startService(new Intent(context, ServiceWrapper.class));
+ }
+
+ private ServiceConnection serviceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ LocalBinder binder = (LocalBinder) service;
+ mServiceWrapper = binder.getService();
+ mServiceWrapper.start();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName className) {
+ mServiceWrapper = null;
+ }
+ };
+}
diff --git a/cmactions/src/com/cyanogenmod/settings/device/CMActionsService.java b/cmactions/src/com/cyanogenmod/settings/device/CMActionsService.java
new file mode 100644
index 0000000..0311cff
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/CMActionsService.java
@@ -0,0 +1,109 @@
+/*
+ * 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.IntentService;
+import android.content.Context;
+import android.content.Intent;
+import android.os.PowerManager;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.List;
+import java.util.LinkedList;
+
+public class CMActionsService extends IntentService implements ScreenStateNotifier,
+ UpdatedStateNotifier {
+ private static final String TAG = "CMActions";
+
+ private final Context mContext;
+
+ private final DozePulseAction mDozePulseAction;
+ private final PowerManager mPowerManager;
+ private final PowerManager.WakeLock mWakeLock;
+ private final ScreenReceiver mScreenReceiver;
+ private final SensorHelper mSensorHelper;
+
+ private final List mScreenStateNotifiers = new LinkedList();
+ private final List mUpdatedStateNotifiers =
+ new LinkedList();
+
+ public CMActionsService(Context context) {
+ super("CMActionService");
+ mContext = context;
+
+ Log.d(TAG, "Starting");
+
+ CMActionsSettings cmActionsSettings = new CMActionsSettings(context, this);
+ mSensorHelper = new SensorHelper(context);
+ mScreenReceiver = new ScreenReceiver(context, this);
+
+ mDozePulseAction = new DozePulseAction(context);
+ mScreenStateNotifiers.add(mDozePulseAction);
+
+ // Actionable sensors get screen on/off notifications
+ mScreenStateNotifiers.add(new GlanceSensor(cmActionsSettings, mSensorHelper, mDozePulseAction));
+ mScreenStateNotifiers.add(new ProximitySensor(cmActionsSettings, mSensorHelper, mDozePulseAction));
+ mScreenStateNotifiers.add(new StowSensor(cmActionsSettings, mSensorHelper, mDozePulseAction));
+
+ // Other actions that are always enabled
+ mUpdatedStateNotifiers.add(new CameraActivationSensor(cmActionsSettings, mSensorHelper));
+ mUpdatedStateNotifiers.add(new ChopChopSensor(cmActionsSettings, mSensorHelper));
+ mUpdatedStateNotifiers.add(new ProximitySilencer(cmActionsSettings, context, mSensorHelper));
+ mUpdatedStateNotifiers.add(new FlipToMute(cmActionsSettings, context, mSensorHelper));
+ mUpdatedStateNotifiers.add(new LiftToSilence(cmActionsSettings, context, mSensorHelper));
+
+ mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "CMActionsWakeLock");
+ updateState();
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ }
+
+ @Override
+ public void screenTurnedOn() {
+ if (!mWakeLock.isHeld()) {
+ mWakeLock.acquire();
+ }
+ for (ScreenStateNotifier screenStateNotifier : mScreenStateNotifiers) {
+ screenStateNotifier.screenTurnedOn();
+ }
+ }
+
+ @Override
+ public void screenTurnedOff() {
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ for (ScreenStateNotifier screenStateNotifier : mScreenStateNotifiers) {
+ screenStateNotifier.screenTurnedOff();
+ }
+ }
+
+ public void updateState() {
+ if (mPowerManager.isInteractive()) {
+ screenTurnedOn();
+ } else {
+ screenTurnedOff();
+ }
+ for (UpdatedStateNotifier notifier : mUpdatedStateNotifiers) {
+ notifier.updateState();
+ }
+ }
+}
diff --git a/cmactions/src/com/cyanogenmod/settings/device/CMActionsSettings.java b/cmactions/src/com/cyanogenmod/settings/device/CMActionsSettings.java
new file mode 100644
index 0000000..a322fad
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/CMActionsSettings.java
@@ -0,0 +1,143 @@
+/*
+ * 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.content.ContentResolver;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+
+import android.util.Log;
+
+public class CMActionsSettings {
+ private static final String TAG = "CMActions";
+
+ private static final String GESTURE_CAMERA_ACTION_KEY = "gesture_camera_action";
+ private static final String GESTURE_CHOP_CHOP_KEY = "gesture_chop_chop";
+ private static final String GESTURE_PICK_UP_KEY = "gesture_pick_up";
+ private static final String GESTURE_IR_WAKEUP_KEY = "gesture_hand_wave";
+ private static final String GESTURE_IR_SILENCER_KEY = "gesture_ir_silencer";
+ private static final String GESTURE_FLIP_TO_MUTE_KEY = "gesture_flip_to_mute";
+ private static final String GESTURE_LIFT_TO_SILENCE_KEY = "gesture_lift_to_silence";
+
+ private final Context mContext;
+ private final UpdatedStateNotifier mUpdatedStateNotifier;
+
+ private boolean mCameraGestureEnabled;
+ private boolean mChopChopEnabled;
+ private boolean mPickUpGestureEnabled;
+ private boolean mIrWakeUpEnabled;
+ private boolean mIrSilencerEnabled;
+ private boolean mFlipToMuteEnabled;
+ private boolean mLiftToSilenceEnabled;
+
+ public CMActionsSettings(Context context, UpdatedStateNotifier updatedStateNotifier) {
+ SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
+ loadPreferences(sharedPrefs);
+ sharedPrefs.registerOnSharedPreferenceChangeListener(mPrefListener);
+ mContext = context;
+ mUpdatedStateNotifier = updatedStateNotifier;
+ }
+
+ public boolean isCameraGestureEnabled() {
+ return mCameraGestureEnabled;
+ }
+
+ public boolean isChopChopGestureEnabled() {
+ return mChopChopEnabled;
+ }
+
+ public static boolean isDozeEnabled(ContentResolver contentResolver) {
+ return (Settings.Secure.getInt(contentResolver, Settings.Secure.DOZE_ENABLED, 1) != 0);
+ }
+
+ public boolean isDozeEnabled() {
+ return isDozeEnabled(mContext.getContentResolver());
+ }
+
+ public boolean isIrWakeupEnabled() {
+ return isDozeEnabled() && mIrWakeUpEnabled;
+ }
+
+ public boolean isPickUpEnabled() {
+ return isDozeEnabled() && mPickUpGestureEnabled;
+ }
+
+ public boolean isIrSilencerEnabled() {
+ return mIrSilencerEnabled;
+ }
+
+ public boolean isFlipToMuteEnabled() {
+ return mFlipToMuteEnabled;
+ }
+
+ public boolean isLiftToSilenceEnabled() {
+ return mLiftToSilenceEnabled;
+ }
+
+ public void cameraAction() {
+ new CameraActivationAction(mContext).action();
+ }
+
+ public void chopChopAction() {
+ new TorchAction(mContext).action();
+ }
+
+ private void loadPreferences(SharedPreferences sharedPreferences) {
+ mCameraGestureEnabled = sharedPreferences.getBoolean(GESTURE_CAMERA_ACTION_KEY, true);
+ mChopChopEnabled = sharedPreferences.getBoolean(GESTURE_CHOP_CHOP_KEY, true);
+ mIrWakeUpEnabled = sharedPreferences.getBoolean(GESTURE_IR_WAKEUP_KEY, true);
+ mPickUpGestureEnabled = sharedPreferences.getBoolean(GESTURE_PICK_UP_KEY, true);
+ mIrSilencerEnabled = sharedPreferences.getBoolean(GESTURE_IR_SILENCER_KEY, false);
+ mFlipToMuteEnabled = sharedPreferences.getBoolean(GESTURE_FLIP_TO_MUTE_KEY, false);
+ mLiftToSilenceEnabled = sharedPreferences.getBoolean(GESTURE_LIFT_TO_SILENCE_KEY, false);
+ }
+
+ private SharedPreferences.OnSharedPreferenceChangeListener mPrefListener =
+ new SharedPreferences.OnSharedPreferenceChangeListener() {
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ boolean updated = true;
+
+ if (GESTURE_CAMERA_ACTION_KEY.equals(key)) {
+ mCameraGestureEnabled = sharedPreferences.getBoolean(GESTURE_CAMERA_ACTION_KEY, true);
+ } else if (GESTURE_CHOP_CHOP_KEY.equals(key)) {
+ mChopChopEnabled = sharedPreferences.getBoolean(GESTURE_CHOP_CHOP_KEY, true);
+ } else if (GESTURE_IR_WAKEUP_KEY.equals(key)) {
+ mIrWakeUpEnabled = sharedPreferences.getBoolean(GESTURE_IR_WAKEUP_KEY, true);
+ } else if (GESTURE_PICK_UP_KEY.equals(key)) {
+ mPickUpGestureEnabled = sharedPreferences.getBoolean(GESTURE_PICK_UP_KEY, true);
+ } else if (GESTURE_IR_SILENCER_KEY.equals(key)) {
+ mIrSilencerEnabled = sharedPreferences.getBoolean(GESTURE_IR_SILENCER_KEY, false);
+ } else if (GESTURE_FLIP_TO_MUTE_KEY.equals(key)) {
+ mFlipToMuteEnabled = sharedPreferences.getBoolean(GESTURE_FLIP_TO_MUTE_KEY, false);
+ } else if (GESTURE_LIFT_TO_SILENCE_KEY.equals(key)) {
+ mLiftToSilenceEnabled = sharedPreferences.getBoolean(GESTURE_LIFT_TO_SILENCE_KEY, false);
+ } else if (Constants.FP_HOME_KEY.equals(key) || Constants.FP_HAPTIC_KEY.equals(key) || Constants.FP_KEYS.equals(key) || Constants.FP_KEY_HOLD.equals(key) || Constants.FP_KEY_LEFT.equals(key) || Constants.FP_KEY_RIGHT.equals(key)) {
+ Constants.writePreference(mContext, key);
+ updated = false;
+ } else {
+ updated = false;
+ }
+
+ if (updated) {
+ mUpdatedStateNotifier.updateState();
+ }
+ }
+ };
+}
diff --git a/cmactions/src/com/cyanogenmod/settings/device/CameraActivationAction.java b/cmactions/src/com/cyanogenmod/settings/device/CameraActivationAction.java
new file mode 100644
index 0000000..45992e9
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/CameraActivationAction.java
@@ -0,0 +1,132 @@
+/*
+ * 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 java.util.List;
+
+import android.app.KeyguardManager;
+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.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.Vibrator;
+import android.provider.MediaStore;
+import android.util.Log;
+
+public class CameraActivationAction implements SensorAction {
+ private static final String TAG = "CMActions";
+
+ private static final int TURN_SCREEN_ON_WAKE_LOCK_MS = 500;
+
+ private final Context mContext;
+ private final KeyguardManager mKeyguardManager;
+ private final PackageManager mPackageManager;
+ private final PowerManager mPowerManager;
+
+ public CameraActivationAction(Context context) {
+ mContext = context;
+ mKeyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
+ mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ mPackageManager = context.getPackageManager();
+ }
+
+ @Override
+ public void action() {
+ vibrate();
+ turnScreenOn();
+ if (mKeyguardManager.inKeyguardRestrictedInputMode()) {
+ launchSecureCamera();
+ } else {
+ launchCamera();
+ }
+ }
+
+ private void vibrate() {
+ Vibrator v = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
+ v.vibrate(500);
+ }
+
+ private void turnScreenOn() {
+ PowerManager.WakeLock wl = mPowerManager.newWakeLock(
+ PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, TAG);
+ wl.acquire(TURN_SCREEN_ON_WAKE_LOCK_MS);
+ }
+
+ private void launchCamera() {
+ Intent intent = createIntent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
+ 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 = createIntent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
+ Intent secureIntent = createIntent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE);
+ ActivityInfo normalActivity = getBestActivityInfo(normalIntent);
+ ActivityInfo secureActivity = getBestActivityInfo(secureIntent, normalActivity);
+ if (secureActivity != null) {
+ secureIntent.setComponent(componentName(secureActivity));
+ mContext.startActivity(secureIntent);
+ }
+ }
+
+ private Intent createIntent(String intentName) {
+ Intent intent = new Intent(intentName);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.addFlags(Intent.FLAG_FROM_BACKGROUND);
+ return intent;
+ }
+
+ private ActivityInfo getBestActivityInfo(Intent intent) {
+ ResolveInfo resolveInfo = mPackageManager.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) {
+ List activities = mPackageManager.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 ComponentName componentName(ActivityInfo activity) {
+ return new ComponentName(activity.applicationInfo.packageName, activity.name);
+ }
+}
diff --git a/cmactions/src/com/cyanogenmod/settings/device/CameraActivationSensor.java b/cmactions/src/com/cyanogenmod/settings/device/CameraActivationSensor.java
new file mode 100644
index 0000000..0128ec4
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/CameraActivationSensor.java
@@ -0,0 +1,66 @@
+/*
+ * 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 java.util.List;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.util.Log;
+
+public class CameraActivationSensor implements SensorEventListener, UpdatedStateNotifier {
+ private static final String TAG = "CMActions-CameraSensor";
+
+ private static final int TURN_SCREEN_ON_WAKE_LOCK_MS = 500;
+
+ private final CMActionsSettings mCMActionsSettings;
+ private final SensorHelper mSensorHelper;
+
+ private final Sensor mSensor;
+
+ private boolean mIsEnabled;
+
+ public CameraActivationSensor(CMActionsSettings cmActionsSettings, SensorHelper sensorHelper) {
+ mCMActionsSettings = cmActionsSettings;
+ mSensorHelper = sensorHelper;
+ mSensor = sensorHelper.getCameraActivationSensor();
+ mSensorHelper.registerListener(mSensor, this);
+ }
+
+ @Override
+ public synchronized void updateState() {
+ if (mCMActionsSettings.isCameraGestureEnabled() && !mIsEnabled) {
+ Log.d(TAG, "Enabling");
+ mIsEnabled = true;
+ } else if (! mCMActionsSettings.isCameraGestureEnabled() && mIsEnabled) {
+ Log.d(TAG, "Disabling");
+ mIsEnabled = false;
+ }
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ Log.d(TAG, "activate camera");
+ if (mIsEnabled) mCMActionsSettings.cameraAction();
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+}
diff --git a/cmactions/src/com/cyanogenmod/settings/device/ChopChopSensor.java b/cmactions/src/com/cyanogenmod/settings/device/ChopChopSensor.java
new file mode 100644
index 0000000..ac9ace3
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/ChopChopSensor.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2015-2016 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 java.util.List;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.util.Log;
+
+public class ChopChopSensor implements SensorEventListener, UpdatedStateNotifier {
+ private static final String TAG = "CMActions-ChopChopSensor";
+
+ private static final int TURN_SCREEN_ON_WAKE_LOCK_MS = 500;
+
+ private final CMActionsSettings mCMActionsSettings;
+ private final SensorHelper mSensorHelper;
+ private final Sensor mSensor;
+ private final Sensor mProx;
+
+ private boolean mIsEnabled;
+ private boolean mProxIsCovered;
+
+ public ChopChopSensor(CMActionsSettings cmActionsSettings, SensorHelper sensorHelper) {
+ mCMActionsSettings = cmActionsSettings;
+ mSensorHelper = sensorHelper;
+ mSensor = sensorHelper.getChopChopSensor();
+ mProx = sensorHelper.getProximitySensor();
+ }
+
+ @Override
+ public synchronized void updateState() {
+ if (mCMActionsSettings.isChopChopGestureEnabled() && !mIsEnabled) {
+ Log.d(TAG, "Enabling");
+ mSensorHelper.registerListener(mSensor, this);
+ mSensorHelper.registerListener(mProx, mProxListener);
+ mIsEnabled = true;
+ } else if (! mCMActionsSettings.isChopChopGestureEnabled() && mIsEnabled) {
+ Log.d(TAG, "Disabling");
+ mSensorHelper.unregisterListener(this);
+ mSensorHelper.unregisterListener(mProxListener);
+ mIsEnabled = false;
+ }
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ Log.d(TAG, "chop chop triggered");
+ if (mProxIsCovered) {
+ Log.d(TAG, "proximity sensor covered, ignoring chop-chop");
+ return;
+ }
+ mCMActionsSettings.chopChopAction();
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+
+ private SensorEventListener mProxListener = new SensorEventListener() {
+ @Override
+ public synchronized void onSensorChanged(SensorEvent event) {
+ mProxIsCovered = event.values[0] < mProx.getMaximumRange();
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor mSensor, int accuracy) {
+ }
+ };
+
+}
diff --git a/cmactions/src/com/cyanogenmod/settings/device/Constants.java b/cmactions/src/com/cyanogenmod/settings/device/Constants.java
new file mode 100644
index 0000000..aefab87
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/Constants.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2016 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 java.util.HashMap;
+import java.util.Map;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import android.util.Log;
+
+import org.cyanogenmod.internal.util.FileUtils;
+
+public class Constants {
+
+ private static final String TAG = "CMActions";
+
+ // Swap keys
+ public static final String FP_HOME_KEY = "fp_home";
+ public static final String FP_HOME_KEY_OFF = "fp_home_scr_off";
+
+ // Swap nodes
+ public static final String FP_HOME_NODE = "/sys/homebutton/enable";
+ public static final String FP_HOME_OFF_NODE = "/sys/homebutton/enable_off";
+
+ // Haptic node
+ public static final String FP_HAPTIC_NODE = "/sys/homebutton/haptic";
+ public static final String FP_HAPTIC_KEY = "fp_haptic";
+
+ // List of keys
+ public static final String FP_KEYS = "fp_keys";
+ public static final String FP_KEY_HOLD = "fp_key_hold";
+ public static final String FP_KEY_LEFT = "fp_key_left";
+ public static final String FP_KEY_RIGHT = "fp_key_right";
+
+ public static final String FP_KEYS_OFF = "fp_keys_off";
+ public static final String FP_KEY_HOLD_OFF = "fp_key_hold_off";
+ public static final String FP_KEY_LEFT_OFF = "fp_key_left_off";
+ public static final String FP_KEY_RIGHT_OFF = "fp_key_right_off";
+
+ // Keys nodes
+ public static final String FP_KEYS_NODE = "/sys/homebutton/key";
+ public static final String FP_KEY_HOLD_NODE = "/sys/homebutton/key_hold";
+ public static final String FP_KEY_LEFT_NODE = "/sys/homebutton/key_left";
+ public static final String FP_KEY_RIGHT_NODE = "/sys/homebutton/key_right";
+
+ // Holds -> mapping
+ public static final Map sBooleanNodePreferenceMap = new HashMap<>();
+
+ // Holds -> mapping
+ public static final Map sNodeDefaultMap = new HashMap<>();
+
+ public static final String[] sButtonPrefKeys = {
+ FP_HOME_KEY,
+ FP_HOME_KEY_OFF,
+ FP_KEYS,
+ FP_KEY_HOLD,
+ FP_KEY_RIGHT,
+ FP_KEY_LEFT,
+ FP_HAPTIC_KEY,
+ };
+
+ public static final String[] sButtonScreenOffPrefKeys = {
+ FP_KEYS_OFF,
+ FP_KEY_HOLD_OFF,
+ FP_KEY_RIGHT_OFF,
+ FP_KEY_LEFT_OFF,
+ FP_HOME_KEY_OFF,
+ };
+
+ static {
+ sBooleanNodePreferenceMap.put(FP_HOME_KEY, FP_HOME_NODE);
+ sBooleanNodePreferenceMap.put(FP_HOME_KEY_OFF, FP_HOME_OFF_NODE);
+ sBooleanNodePreferenceMap.put(FP_KEYS, FP_KEYS_NODE);
+ sBooleanNodePreferenceMap.put(FP_KEY_HOLD, FP_KEY_HOLD_NODE);
+ sBooleanNodePreferenceMap.put(FP_KEY_LEFT, FP_KEY_LEFT_NODE);
+ sBooleanNodePreferenceMap.put(FP_KEY_RIGHT, FP_KEY_RIGHT_NODE);
+ sBooleanNodePreferenceMap.put(FP_HAPTIC_KEY, FP_HAPTIC_NODE);
+ sBooleanNodePreferenceMap.put(FP_KEYS_OFF, FP_KEYS_NODE);
+ sBooleanNodePreferenceMap.put(FP_KEY_HOLD_OFF, FP_KEY_HOLD_NODE);
+ sBooleanNodePreferenceMap.put(FP_KEY_LEFT_OFF, FP_KEY_LEFT_NODE);
+ sBooleanNodePreferenceMap.put(FP_KEY_RIGHT_OFF, FP_KEY_RIGHT_NODE);
+ sNodeDefaultMap.put(FP_HOME_KEY, false);
+ sNodeDefaultMap.put(FP_HOME_KEY_OFF, false);
+ sNodeDefaultMap.put(FP_KEYS, "0");
+ sNodeDefaultMap.put(FP_KEY_HOLD, "0");
+ sNodeDefaultMap.put(FP_KEY_LEFT, "0");
+ sNodeDefaultMap.put(FP_KEY_RIGHT, "0");
+ sNodeDefaultMap.put(FP_KEYS_OFF, "0");
+ sNodeDefaultMap.put(FP_KEY_HOLD_OFF, "0");
+ sNodeDefaultMap.put(FP_KEY_LEFT_OFF, "0");
+ sNodeDefaultMap.put(FP_KEY_RIGHT_OFF, "0");
+ sNodeDefaultMap.put(FP_HAPTIC_KEY, false);
+ }
+
+ public static boolean isPreferenceEnabled(Context context, String key) {
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
+ return preferences.getBoolean(key, (Boolean) sNodeDefaultMap.get(key));
+ }
+
+ public static String GetPreference(Context context, String key) {
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
+ return preferences.getString(key, (String) sNodeDefaultMap.get(key));
+ }
+
+ public static void writePreference(Context context, String pref) {
+
+ String value = "1";
+ Log.e(TAG, "Write Pref: " + pref);
+ if (!pref.equals(FP_KEYS) && !pref.equals(FP_KEY_HOLD) && !pref.equals(FP_KEY_LEFT) && !pref.equals(FP_KEY_RIGHT) &&
+ !pref.equals(FP_KEYS_OFF) && !pref.equals(FP_KEY_HOLD_OFF) && !pref.equals(FP_KEY_LEFT_OFF) && !pref.equals(FP_KEY_RIGHT_OFF))
+ value = isPreferenceEnabled(context, pref) ? "1" : "0";
+ else
+ value = GetPreference(context, pref);
+
+ String node = sBooleanNodePreferenceMap.get(pref);
+ Log.e(TAG, "Write " + value + " to node " + node);
+
+ if (!FileUtils.writeLine(node, value)) {
+ Log.w(TAG, "Write " + value + " to node " + node +
+ "failed while restoring saved preference values");
+ }
+ }
+}
diff --git a/cmactions/src/com/cyanogenmod/settings/device/DozePreferenceActivity.java b/cmactions/src/com/cyanogenmod/settings/device/DozePreferenceActivity.java
new file mode 100644
index 0000000..118c658
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/DozePreferenceActivity.java
@@ -0,0 +1,73 @@
+/*
+ * 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.cyanogenmod.settings.device;
+
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceCategory;
+import android.support.v14.preference.PreferenceFragment;
+import android.support.v14.preference.SwitchPreference;
+import android.view.MenuItem;
+
+public class DozePreferenceActivity extends PreferenceActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getActionBar().setDisplayHomeAsUpEnabled(true);
+ getFragmentManager().beginTransaction()
+ .replace(android.R.id.content, new DozePreferenceFragment()).commit();
+ }
+
+ public class DozePreferenceFragment extends PreferenceFragment {
+ private static final String KEY_AMBIENT_DISPLAY_ENABLE = "doze_enabled";
+
+ private SwitchPreference mAmbientDisplayPreference;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.doze_panel);
+ boolean dozeEnabled = CMActionsSettings.isDozeEnabled(getActivity().getContentResolver());
+ mAmbientDisplayPreference = (SwitchPreference) findPreference(KEY_AMBIENT_DISPLAY_ENABLE);
+ // Read from DOZE_ENABLED secure setting
+ mAmbientDisplayPreference.setChecked(dozeEnabled);
+ mAmbientDisplayPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ boolean enable = (boolean) newValue;
+ return enableDoze(enable);
+ }
+ });
+ }
+
+ private boolean enableDoze(boolean enable) {
+ return Settings.Secure.putInt(getContext().getContentResolver(),
+ Settings.Secure.DOZE_ENABLED, enable ? 1 : 0);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/cmactions/src/com/cyanogenmod/settings/device/DozePulseAction.java b/cmactions/src/com/cyanogenmod/settings/device/DozePulseAction.java
new file mode 100644
index 0000000..75ef9c3
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/DozePulseAction.java
@@ -0,0 +1,63 @@
+/*
+ * 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.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class DozePulseAction implements SensorAction, ScreenStateNotifier {
+ private static final String TAG = "CMActions";
+
+ private static final int DELAY_BETWEEN_DOZES_IN_MS = 1500;
+
+ private final Context mContext;
+
+ private long mLastDoze;
+
+ public DozePulseAction(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public void screenTurnedOn() {
+ }
+
+ @Override
+ public void screenTurnedOff() {
+ mLastDoze = System.currentTimeMillis();
+ }
+
+ public void action() {
+ if (mayDoze()) {
+ Log.d(TAG, "Sending doze.pulse intent");
+ mContext.sendBroadcast(new Intent("com.android.systemui.doze.pulse"));
+ }
+ }
+
+ public synchronized boolean mayDoze() {
+ long now = System.currentTimeMillis();
+ if (now - mLastDoze > DELAY_BETWEEN_DOZES_IN_MS) {
+ Log.d(TAG, "Allowing doze");
+ mLastDoze = now;
+ return true;
+ } else {
+ Log.d(TAG, "Denying doze");
+ return false;
+ }
+ }
+}
diff --git a/cmactions/src/com/cyanogenmod/settings/device/FlipToMute.java b/cmactions/src/com/cyanogenmod/settings/device/FlipToMute.java
new file mode 100644
index 0000000..d5d5bf9
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/FlipToMute.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2016 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.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.util.Log;
+
+public class FlipToMute implements UpdatedStateNotifier {
+ private static final String TAG = "CMActions-FlipToMute";
+
+ private final NotificationManager mNotificationManager;
+ private final CMActionsSettings mCMActionsSettings;
+ private final SensorHelper mSensorHelper;
+ private final Sensor mFlatDown;
+ private final Sensor mStow;
+
+ private boolean mIsEnabled;
+ private boolean mIsFlatDown;
+ private boolean mIsStowed;
+ private int mFilter;
+ private Context mContext;
+ private Receiver mReceiver;
+
+ public FlipToMute(CMActionsSettings cmActionsSettings, Context context,
+ SensorHelper sensorHelper) {
+ mCMActionsSettings = cmActionsSettings;
+ mContext = context;
+ mSensorHelper = sensorHelper;
+ mFlatDown = sensorHelper.getFlatDownSensor();
+ mStow = sensorHelper.getStowSensor();
+ mNotificationManager =
+ (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ mFilter = mNotificationManager.getCurrentInterruptionFilter();
+ mReceiver = new Receiver();
+ }
+
+ @Override
+ public void updateState() {
+ if (mCMActionsSettings.isFlipToMuteEnabled() && !mIsEnabled) {
+ Log.d(TAG, "Enabling");
+ mSensorHelper.registerListener(mFlatDown, mFlatDownListener);
+ mSensorHelper.registerListener(mStow, mStowListener);
+ mContext.registerReceiver(mReceiver,
+ new IntentFilter(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED));
+ mIsEnabled = true;
+ } else if (!mCMActionsSettings.isFlipToMuteEnabled() && mIsEnabled) {
+ Log.d(TAG, "Disabling");
+ mSensorHelper.unregisterListener(mFlatDownListener);
+ mSensorHelper.unregisterListener(mStowListener);
+ mContext.unregisterReceiver(mReceiver);
+ mIsEnabled = false;
+ }
+ }
+
+ private SensorEventListener mFlatDownListener = new SensorEventListener() {
+ @Override
+ public synchronized void onSensorChanged(SensorEvent event) {
+ mIsFlatDown = (event.values[0] != 0);
+ sensorChange();
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor mSensor, int accuracy) {
+ }
+ };
+
+ private SensorEventListener mStowListener = new SensorEventListener() {
+ @Override
+ public synchronized void onSensorChanged(SensorEvent event) {
+ mIsStowed = (event.values[0] != 0);
+ sensorChange();
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor mSensor, int accuracy) {
+ }
+ };
+
+ private void sensorChange() {
+
+ Log.d(TAG, "event: " + mIsFlatDown + " mIsStowed=" + mIsStowed);
+
+ if (mIsFlatDown && mIsStowed) {
+ mNotificationManager.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY);
+ Log.d(TAG, "Interrupt filter: Allow priority");
+ } else if (!mIsFlatDown) {
+ mNotificationManager.setInterruptionFilter(mFilter);
+ Log.d(TAG, "Interrupt filter: Restore");
+ }
+ }
+
+ public class Receiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!mIsFlatDown && !mIsStowed) {
+ mFilter = mNotificationManager.getCurrentInterruptionFilter();
+ Log.d(TAG, "Interrupt filter: Backup");
+ }
+ }
+ }
+}
diff --git a/cmactions/src/com/cyanogenmod/settings/device/GlanceSensor.java b/cmactions/src/com/cyanogenmod/settings/device/GlanceSensor.java
new file mode 100644
index 0000000..4d83c81
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/GlanceSensor.java
@@ -0,0 +1,69 @@
+/*
+ * 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.cyanogenmod.settings.device;
+
+import android.hardware.Sensor;
+import android.hardware.TriggerEvent;
+import android.hardware.TriggerEventListener;
+import android.util.Log;
+
+public class GlanceSensor implements ScreenStateNotifier {
+ private static final String TAG = "CMActions-GlanceSensor";
+
+ private final CMActionsSettings mCMActionsSettings;
+ private final SensorHelper mSensorHelper;
+ private final SensorAction mSensorAction;
+ private final Sensor mSensor;
+
+ private boolean mEnabled;
+
+ public GlanceSensor(CMActionsSettings cmActionsSettings, SensorHelper sensorHelper,
+ SensorAction action) {
+ mCMActionsSettings = cmActionsSettings;
+ mSensorHelper = sensorHelper;
+ mSensorAction = action;
+
+ mSensor = sensorHelper.getGlanceSensor();
+ }
+
+ @Override
+ public void screenTurnedOn() {
+ if (mEnabled) {
+ Log.d(TAG, "Disabling");
+ mSensorHelper.cancelTriggerSensor(mSensor, mGlanceListener);
+ mEnabled = false;
+ }
+ }
+
+ @Override
+ public void screenTurnedOff() {
+ if (mCMActionsSettings.isPickUpEnabled() && !mEnabled) {
+ Log.d(TAG, "Enabling");
+ mSensorHelper.requestTriggerSensor(mSensor, mGlanceListener);
+ mEnabled = true;
+ }
+ }
+
+ private TriggerEventListener mGlanceListener = new TriggerEventListener() {
+ @Override
+ public void onTrigger(TriggerEvent event) {
+ Log.d(TAG, "triggered");
+ mSensorAction.action();
+ mSensorHelper.requestTriggerSensor(mSensor, mGlanceListener);
+ }
+ };
+}
diff --git a/cmactions/src/com/cyanogenmod/settings/device/LiftToSilence.java b/cmactions/src/com/cyanogenmod/settings/device/LiftToSilence.java
new file mode 100644
index 0000000..a5286c3
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/LiftToSilence.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2016 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.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.telephony.PhoneStateListener;
+import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+public class LiftToSilence extends PhoneStateListener implements SensorEventListener, UpdatedStateNotifier {
+ private static final String TAG = "CMActions-LiftToSilence";
+
+ private final CMActionsSettings mCMActionsSettings;
+ private final SensorHelper mSensorHelper;
+ private final Sensor mFlatUpSensor;
+ private final Sensor mStowSensor;
+
+ private final TelecomManager mTelecomManager;
+ private final TelephonyManager mTelephonyManager;
+
+ private boolean mIsRinging;
+ private boolean mIsStowed;
+ private boolean mLastFlatUp;
+
+ public LiftToSilence(CMActionsSettings cmActionsSettings, Context context,
+ SensorHelper sensorHelper) {
+ mCMActionsSettings = cmActionsSettings;
+ mSensorHelper = sensorHelper;
+ mFlatUpSensor = sensorHelper.getFlatUpSensor();
+ mStowSensor = sensorHelper.getStowSensor();
+ mTelecomManager = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
+ mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ }
+
+ @Override
+ public void updateState() {
+ if (mCMActionsSettings.isLiftToSilenceEnabled()) {
+ mTelephonyManager.listen(this, LISTEN_CALL_STATE);
+ } else {
+ mTelephonyManager.listen(this, 0);
+ }
+ }
+
+ @Override
+ public synchronized void onCallStateChanged(int state, String incomingNumber) {
+ if (state == TelephonyManager.CALL_STATE_RINGING && !mIsRinging) {
+ Log.d(TAG, "Ringing started");
+ mSensorHelper.registerListener(mFlatUpSensor, this);
+ mSensorHelper.registerListener(mStowSensor, mStowListener);
+ mIsRinging = true;
+ } else if (state != TelephonyManager.CALL_STATE_RINGING && mIsRinging) {
+ Log.d(TAG, "Ringing stopped");
+ mSensorHelper.unregisterListener(this);
+ mSensorHelper.unregisterListener(mStowListener);
+ mIsRinging = false;
+ }
+ }
+
+
+ @Override
+ public synchronized void onSensorChanged(SensorEvent event) {
+ boolean thisFlatUp = (event.values[0] != 0);
+
+ Log.d(TAG, "event: " + thisFlatUp + " mLastFlatUp=" + mLastFlatUp + " mIsStowed=" +
+ mIsStowed);
+
+ if (mLastFlatUp && !thisFlatUp && !mIsStowed) {
+ mTelecomManager.silenceRinger();
+ }
+ mLastFlatUp = thisFlatUp;
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor mSensor, int accuracy) {
+ }
+
+ private SensorEventListener mStowListener = new SensorEventListener() {
+ @Override
+ public synchronized void onSensorChanged(SensorEvent event) {
+ mIsStowed = (event.values[0] != 0);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor mSensor, int accuracy) {
+ }
+ };
+}
diff --git a/cmactions/src/com/cyanogenmod/settings/device/ProximitySensor.java b/cmactions/src/com/cyanogenmod/settings/device/ProximitySensor.java
new file mode 100644
index 0000000..7c4af6c
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/ProximitySensor.java
@@ -0,0 +1,76 @@
+/*
+ * 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.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.util.Log;
+
+public class ProximitySensor implements ScreenStateNotifier, SensorEventListener {
+ private static final String TAG = "CMActions-ProximitySensor";
+
+ private final CMActionsSettings mCMActionsSettings;
+ private final SensorHelper mSensorHelper;
+ private final SensorAction mSensorAction;
+ private final Sensor mSensor;
+
+ private boolean mEnabled;
+
+ private boolean mSawNear = false;
+
+ public ProximitySensor(CMActionsSettings cmActionsSettings, SensorHelper sensorHelper,
+ SensorAction action) {
+ mCMActionsSettings = cmActionsSettings;
+ mSensorHelper = sensorHelper;
+ mSensorAction = action;
+
+ mSensor = sensorHelper.getProximitySensor();
+ }
+
+ @Override
+ public void screenTurnedOn() {
+ if (mEnabled) {
+ Log.d(TAG, "Disabling");
+ mSensorHelper.unregisterListener(this);
+ mEnabled = false;
+ }
+ }
+
+ @Override
+ public void screenTurnedOff() {
+ if (mCMActionsSettings.isIrWakeupEnabled() && !mEnabled) {
+ Log.d(TAG, "Enabling");
+ mSensorHelper.registerListener(mSensor, this);
+ mEnabled = true;
+ }
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ boolean isNear = event.values[0] < mSensor.getMaximumRange();
+ if (mSawNear && !isNear) {
+ Log.d(TAG, "wave triggered");
+ mSensorAction.action();
+ }
+ mSawNear = isNear;
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor mSensor, int accuracy) {
+ }
+}
diff --git a/cmactions/src/com/cyanogenmod/settings/device/ProximitySilencer.java b/cmactions/src/com/cyanogenmod/settings/device/ProximitySilencer.java
new file mode 100644
index 0000000..3975776
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/ProximitySilencer.java
@@ -0,0 +1,110 @@
+/*
+ * 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.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.telephony.PhoneStateListener;
+import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import static android.telephony.TelephonyManager.*;
+
+public class ProximitySilencer extends PhoneStateListener implements SensorEventListener, UpdatedStateNotifier {
+ private static final String TAG = "CMActions-ProximitySilencer";
+
+ private static final int SILENCE_DELAY_MS = 500;
+
+ private final TelecomManager mTelecomManager;
+ private final TelephonyManager mTelephonyManager;
+ private final CMActionsSettings mCMActionsSettings;
+ private final SensorHelper mSensorHelper;
+ private final Sensor mSensor;
+ private boolean mIsRinging;
+ private long mRingStartedMs;
+ private boolean mCoveredRinging;
+
+ public ProximitySilencer(CMActionsSettings cmActionsSettings, Context context,
+ SensorHelper sensorHelper) {
+ mTelecomManager = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
+ mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+
+ mCMActionsSettings = cmActionsSettings;
+ mSensorHelper = sensorHelper;
+ mSensor = sensorHelper.getProximitySensor();
+ mCoveredRinging = false;
+ mIsRinging = false;
+ }
+
+ @Override
+ public void updateState() {
+ if (mCMActionsSettings.isIrSilencerEnabled()) {
+ mTelephonyManager.listen(this, LISTEN_CALL_STATE);
+ } else {
+ mTelephonyManager.listen(this, 0);
+ }
+ }
+
+ @Override
+ public synchronized void onSensorChanged(SensorEvent event) {
+ boolean isNear = event.values[0] < mSensor.getMaximumRange();
+ long now = System.currentTimeMillis();
+
+ if (isNear){
+ if (mIsRinging && (now - mRingStartedMs >= SILENCE_DELAY_MS)){
+ mCoveredRinging = true;
+ } else {
+ mCoveredRinging = false;
+ }
+ return;
+ }
+
+ if (!isNear && mIsRinging) {
+ Log.d(TAG, "event: [" + event.values.length + "]: " + event.values[0] + ", " +
+ event.values[1] + ", " + event.values[2] + " covered " + Boolean.toString(mCoveredRinging));
+ if (mCoveredRinging) {
+ Log.d(TAG, "Silencing ringer");
+ mTelecomManager.silenceRinger();
+ } else {
+ Log.d(TAG, "Ignoring silence gesture: " + now + " is too close to " +
+ mRingStartedMs + ", delay=" + SILENCE_DELAY_MS + " or covered " + Boolean.toString(mCoveredRinging));
+ }
+ mCoveredRinging = false;
+ }
+ }
+
+ @Override
+ public synchronized void onCallStateChanged(int state, String incomingNumber) {
+ if (state == CALL_STATE_RINGING && !mIsRinging) {
+ Log.d(TAG, "Ringing started");
+ mSensorHelper.registerListener(mSensor, this);
+ mIsRinging = true;
+ mRingStartedMs = System.currentTimeMillis();
+ } else if (state != CALL_STATE_RINGING && mIsRinging) {
+ Log.d(TAG, "Ringing stopped");
+ mSensorHelper.unregisterListener(this);
+ mIsRinging = false;
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor mSensor, int accuracy) {
+ }
+}
diff --git a/cmactions/src/com/cyanogenmod/settings/device/ScreenReceiver.java b/cmactions/src/com/cyanogenmod/settings/device/ScreenReceiver.java
new file mode 100644
index 0000000..fd39852
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/ScreenReceiver.java
@@ -0,0 +1,53 @@
+/*
+ * 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 java.util.List;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+public class ScreenReceiver extends BroadcastReceiver {
+ private final ScreenStateNotifier mNotifier;
+
+ public ScreenReceiver(Context context, ScreenStateNotifier notifier) {
+ mNotifier = notifier;
+
+ IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ context.registerReceiver(this, filter);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
+ mNotifier.screenTurnedOff();
+
+ for (String pref : Constants.sButtonScreenOffPrefKeys) {
+ Constants.writePreference(context, pref);
+ }
+ } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
+ mNotifier.screenTurnedOn();
+
+ for (String pref : Constants.sButtonPrefKeys) {
+ Constants.writePreference(context, pref);
+ }
+ }
+ }
+}
diff --git a/cmactions/src/com/cyanogenmod/settings/device/ScreenStateNotifier.java b/cmactions/src/com/cyanogenmod/settings/device/ScreenStateNotifier.java
new file mode 100644
index 0000000..1ffd844
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/ScreenStateNotifier.java
@@ -0,0 +1,22 @@
+/*
+ * 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;
+
+interface ScreenStateNotifier {
+ public void screenTurnedOn();
+ public void screenTurnedOff();
+}
diff --git a/cmactions/src/com/cyanogenmod/settings/device/SensorAction.java b/cmactions/src/com/cyanogenmod/settings/device/SensorAction.java
new file mode 100644
index 0000000..ed590b2
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/SensorAction.java
@@ -0,0 +1,21 @@
+/*
+ * 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;
+
+interface SensorAction {
+ public void action();
+}
diff --git a/cmactions/src/com/cyanogenmod/settings/device/SensorHelper.java b/cmactions/src/com/cyanogenmod/settings/device/SensorHelper.java
new file mode 100644
index 0000000..c27fa1d
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/SensorHelper.java
@@ -0,0 +1,116 @@
+/*
+ * 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 java.util.List;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.TriggerEventListener;
+import android.util.Log;
+
+public class SensorHelper {
+ private static final String TAG = "CMActions";
+
+ private static final int SENSOR_TYPE_MMI_CAMERA_ACTIVATION = 65540;
+ private static final int SENSOR_TYPE_MMI_CHOP_CHOP = 65546;
+ private static final int SENSOR_TYPE_MMI_FLAT_UP = 65537;
+ private static final int SENSOR_TYPE_MMI_FLAT_DOWN = 65538;
+ private static final int SENSOR_TYPE_MMI_STOW = 65539;
+
+ private static final int BATCH_LATENCY_IN_MS = 100;
+
+ private final Context mContext;
+ private final SensorManager mSensorManager;
+
+ public SensorHelper(Context context) {
+ mContext = context;
+ mSensorManager = (SensorManager) mContext .getSystemService(Context.SENSOR_SERVICE);
+ dumpSensorsList();
+ }
+
+ private void dumpSensorsList() {
+ try {
+ FileOutputStream out = mContext.openFileOutput("sensors.txt", Context.MODE_PRIVATE);
+ OutputStreamWriter writer = new OutputStreamWriter(out);
+
+ List sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+ for (Sensor sensor : sensorList) {
+ writer.write("sensor " + sensor.getType() + " = " + sensor.getName()
+ + " max batch: " + sensor.getFifoMaxEventCount() + " isWakeUp: " + sensor.isWakeUpSensor() + "\n");
+ }
+ writer.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public Sensor getCameraActivationSensor() {
+ return mSensorManager.getDefaultSensor(SENSOR_TYPE_MMI_CAMERA_ACTIVATION, true);
+ }
+
+ public Sensor getChopChopSensor() {
+ return mSensorManager.getDefaultSensor(SENSOR_TYPE_MMI_CHOP_CHOP, true);
+ }
+
+ public Sensor getFlatUpSensor() {
+ return mSensorManager.getDefaultSensor(SENSOR_TYPE_MMI_FLAT_UP, true);
+ }
+
+ public Sensor getFlatDownSensor() {
+ return mSensorManager.getDefaultSensor(SENSOR_TYPE_MMI_FLAT_DOWN, true);
+ }
+
+ public Sensor getGlanceSensor() {
+ return mSensorManager.getDefaultSensor(Sensor.TYPE_GLANCE_GESTURE, true);
+ }
+
+ public Sensor getProximitySensor() {
+ return mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY, true);
+ }
+
+ public Sensor getStowSensor() {
+ return mSensorManager.getDefaultSensor(SENSOR_TYPE_MMI_STOW, true);
+ }
+
+ public void registerListener(Sensor sensor, SensorEventListener listener) {
+ if (!mSensorManager.registerListener(listener, sensor,
+ SensorManager.SENSOR_DELAY_NORMAL, BATCH_LATENCY_IN_MS * 1000)) {
+ throw new RuntimeException("Failed to registerListener for sensor " + sensor);
+ }
+ }
+
+ public void unregisterListener(SensorEventListener listener) {
+ mSensorManager.unregisterListener(listener);
+ }
+
+ /* TriggerSensor */
+ public void requestTriggerSensor(Sensor sensor, TriggerEventListener listener) {
+ if (!mSensorManager.requestTriggerSensor(listener, sensor)) {
+ throw new RuntimeException("Failed to requestTriggerSensor for sensor " + sensor);
+ }
+ }
+
+ public void cancelTriggerSensor(Sensor sensor, TriggerEventListener listener) {
+ mSensorManager.cancelTriggerSensor(listener, sensor);
+ }
+}
diff --git a/cmactions/src/com/cyanogenmod/settings/device/ServiceWrapper.java b/cmactions/src/com/cyanogenmod/settings/device/ServiceWrapper.java
new file mode 100644
index 0000000..0a4b21c
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/ServiceWrapper.java
@@ -0,0 +1,65 @@
+/*
+ * 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.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.Log;
+
+public class ServiceWrapper extends android.app.Service {
+ static final String TAG = "CMActions-ServiceWrapper";
+
+ private final IBinder mBinder = new LocalBinder();
+ private CMActionsService mCmActionsService;
+
+ public interface ServiceCallback {
+ void sendResults(int resultCode, Bundle b);
+ }
+
+ public class LocalBinder extends Binder {
+ ServiceWrapper getService() {
+ // Return this instance of the service so clients can call public
+ // methods
+ return ServiceWrapper.this;
+ }
+ }
+
+ @Override
+ public void onCreate() {
+ Log.i(TAG, "onCreate");
+ super.onCreate();
+ mCmActionsService = new CMActionsService(this);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ Log.i(TAG, "onBind");
+ return null;
+ }
+
+ public void setCallback(ServiceCallback callback) {
+ }
+
+ public void start() {
+ Log.i(TAG, "start");
+ }
+
+ public void stop() {
+ }
+}
diff --git a/cmactions/src/com/cyanogenmod/settings/device/StowSensor.java b/cmactions/src/com/cyanogenmod/settings/device/StowSensor.java
new file mode 100644
index 0000000..84f0f08
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/StowSensor.java
@@ -0,0 +1,86 @@
+/*
+ * 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.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.util.Log;
+
+import java.lang.System;
+
+public class StowSensor implements ScreenStateNotifier, SensorEventListener {
+ private static final String TAG = "CMActions-StowSensor";
+ private static final int IN_POCKET_MIN_TIME = 5000;
+
+ private final CMActionsSettings mCMActionsSettings;
+ private final SensorHelper mSensorHelper;
+ private final SensorAction mSensorAction;
+ private final Sensor mSensor;
+
+ private boolean mEnabled;
+ private boolean mLastStowed;
+ private long isStowedTime;
+
+ public StowSensor(CMActionsSettings cmActionsSettings, SensorHelper sensorHelper,
+ SensorAction action) {
+ mCMActionsSettings = cmActionsSettings;
+ mSensorHelper = sensorHelper;
+ mSensorAction = action;
+
+ mSensor = sensorHelper.getStowSensor();
+ }
+
+ @Override
+ public void screenTurnedOn() {
+ if (mEnabled) {
+ Log.d(TAG, "Disabling");
+ mSensorHelper.unregisterListener(this);
+ mEnabled = false;
+ }
+ }
+
+ @Override
+ public void screenTurnedOff() {
+ if (!mCMActionsSettings.isIrWakeupEnabled() &&
+ mCMActionsSettings.isPickUpEnabled() && !mEnabled) {
+ Log.d(TAG, "Enabling");
+ mSensorHelper.registerListener(mSensor, this);
+ mEnabled = true;
+ }
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ boolean thisStowed = (event.values[0] != 0);
+ if(thisStowed){
+ isStowedTime = System.currentTimeMillis();
+ } else if (mLastStowed && !thisStowed) {
+ long inPocketTime = System.currentTimeMillis() - isStowedTime;
+ if(inPocketTime >= IN_POCKET_MIN_TIME){
+ Log.d(TAG, "Triggered after " + inPocketTime / 1000 + " seconds");
+ mSensorAction.action();
+ }
+ }
+ mLastStowed = thisStowed;
+ Log.d(TAG, "event: " + thisStowed);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+}
diff --git a/cmactions/src/com/cyanogenmod/settings/device/TorchAction.java b/cmactions/src/com/cyanogenmod/settings/device/TorchAction.java
new file mode 100644
index 0000000..f66ae59
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/TorchAction.java
@@ -0,0 +1,81 @@
+/*
+ * 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.content.Context;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraAccessException;
+import android.os.Vibrator;
+import android.util.Log;
+
+public class TorchAction implements SensorAction {
+ private static final String TAG = "CMActions";
+
+ private static final int TURN_SCREEN_ON_WAKE_LOCK_MS = 500;
+
+ private CameraManager mCameraManager;
+ private final Vibrator mVibrator;
+ private String mRearCameraId;
+ private static boolean mTorchEnabled;
+
+ public TorchAction(Context mContext) {
+ mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
+ mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
+ 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) {
+ }
+ }
+
+ @Override
+ public void action() {
+ mVibrator.vibrate(250);
+ if (mRearCameraId != null) {
+ try {
+ mCameraManager.setTorchMode(mRearCameraId, !mTorchEnabled);
+ mTorchEnabled = !mTorchEnabled;
+ } catch (CameraAccessException e) {
+ }
+ }
+ }
+
+ 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;
+ }
+ }
+}
diff --git a/cmactions/src/com/cyanogenmod/settings/device/TouchscreenGesturePreferenceActivity.java b/cmactions/src/com/cyanogenmod/settings/device/TouchscreenGesturePreferenceActivity.java
new file mode 100644
index 0000000..72b9804
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/TouchscreenGesturePreferenceActivity.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2016 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.os.Bundle;
+
+import android.preference.PreferenceActivity;
+
+public class TouchscreenGesturePreferenceActivity extends PreferenceActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getFragmentManager().beginTransaction()
+ .replace(android.R.id.content, new TouchscreenGesturePreferenceFragment()).commit();
+ }
+}
diff --git a/cmactions/src/com/cyanogenmod/settings/device/TouchscreenGesturePreferenceFragment.java b/cmactions/src/com/cyanogenmod/settings/device/TouchscreenGesturePreferenceFragment.java
new file mode 100644
index 0000000..cf008a6
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/TouchscreenGesturePreferenceFragment.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015-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.cyanogenmod.settings.device;
+
+import android.app.ActionBar;
+import android.app.AlertDialog;
+import android.app.NotificationManager;
+import android.os.Bundle;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.support.v7.preference.PreferenceCategory;
+import android.support.v14.preference.PreferenceFragment;
+import android.support.v7.preference.Preference;
+import android.support.v14.preference.SwitchPreference;
+import android.view.Menu;
+import android.view.MenuItem;
+
+public class TouchscreenGesturePreferenceFragment extends PreferenceFragment {
+ private SwitchPreference mFlipPref;
+ private NotificationManager mNotificationManager;
+ private boolean mFlipClick = false;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.gesture_panel);
+ final ActionBar actionBar = getActivity().getActionBar();
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ mNotificationManager = (NotificationManager) getActivity().getSystemService(Context.NOTIFICATION_SERVICE);
+ mFlipPref = (SwitchPreference) findPreference("gesture_flip_to_mute");
+ mFlipPref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ public boolean onPreferenceClick(Preference preference) {
+ if (!mNotificationManager.isNotificationPolicyAccessGranted()) {
+ mFlipPref.setChecked(false);
+ new AlertDialog.Builder(getContext())
+ .setTitle(getString(R.string.flip_to_mute_title))
+ .setMessage(getString(R.string.dnd_access))
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ mFlipClick = true;
+ startActivity(new Intent(
+ android.provider.Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS));
+ }
+ }).show();
+ }
+ return true;
+ }
+ });
+
+ //Users may deny DND access after giving it
+ if (!mNotificationManager.isNotificationPolicyAccessGranted()) {
+ mFlipPref.setChecked(false);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ if (mNotificationManager.isNotificationPolicyAccessGranted() && mFlipClick) {
+ mFlipPref.setChecked(true);
+ }
+ }
+
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ getActivity().onBackPressed();
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/cmactions/src/com/cyanogenmod/settings/device/UpdatedStateNotifier.java b/cmactions/src/com/cyanogenmod/settings/device/UpdatedStateNotifier.java
new file mode 100644
index 0000000..268b4bd
--- /dev/null
+++ b/cmactions/src/com/cyanogenmod/settings/device/UpdatedStateNotifier.java
@@ -0,0 +1,21 @@
+/*
+ * 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;
+
+interface UpdatedStateNotifier {
+ public void updateState();
+}
diff --git a/cmactions/src/org/cyanogenmod/internal/util/FileUtils.java b/cmactions/src/org/cyanogenmod/internal/util/FileUtils.java
new file mode 100644
index 0000000..517c1de
--- /dev/null
+++ b/cmactions/src/org/cyanogenmod/internal/util/FileUtils.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2016 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 org.cyanogenmod.internal.util;
+
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+
+public final class FileUtils {
+ private static final String TAG = "FileUtils";
+
+ private FileUtils() {
+ // This class is not supposed to be instantiated
+ }
+
+ /**
+ * Reads the first line of text from the given file.
+ * Reference {@link BufferedReader#readLine()} for clarification on what a line is
+ *
+ * @return the read line contents, or null on failure
+ */
+ public static String readOneLine(String fileName) {
+ String line = null;
+ BufferedReader reader = null;
+
+ try {
+ reader = new BufferedReader(new FileReader(fileName), 512);
+ line = reader.readLine();
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "No such file " + fileName + " for reading", e);
+ } catch (IOException e) {
+ Log.e(TAG, "Could not read from file " + fileName, e);
+ } finally {
+ try {
+ if (reader != null) {
+ reader.close();
+ }
+ } catch (IOException e) {
+ // Ignored, not much we can do anyway
+ }
+ }
+
+ return line;
+ }
+
+ /**
+ * Writes the given value into the given file
+ *
+ * @return true on success, false on failure
+ */
+ public static boolean writeLine(String fileName, String value) {
+ BufferedWriter writer = null;
+
+ try {
+ writer = new BufferedWriter(new FileWriter(fileName));
+ writer.write(value);
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "No such file " + fileName + " for writing", e);
+ return false;
+ } catch (IOException e) {
+ Log.e(TAG, "Could not write to file " + fileName, e);
+ return false;
+ } finally {
+ try {
+ if (writer != null) {
+ writer.close();
+ }
+ } catch (IOException e) {
+ // Ignored, not much we can do anyway
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Checks whether the given file exists
+ *
+ * @return true if exists, false if not
+ */
+ public static boolean fileExists(String fileName) {
+ final File file = new File(fileName);
+ return file.exists();
+ }
+
+ /**
+ * Checks whether the given file is readable
+ *
+ * @return true if readable, false if not
+ */
+ public static boolean isFileReadable(String fileName) {
+ final File file = new File(fileName);
+ return file.exists() && file.canRead();
+ }
+
+ /**
+ * Checks whether the given file is writable
+ *
+ * @return true if writable, false if not
+ */
+ public static boolean isFileWritable(String fileName) {
+ final File file = new File(fileName);
+ return file.exists() && file.canWrite();
+ }
+}
\ No newline at end of file