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