diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 8b9f3680867..3f9978c3af8 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2925,6 +2925,24 @@
android:value="com.android.settings.deletionhelper.DeletionHelperFragment" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/gesture_preference.xml b/res/layout/gesture_preference.xml
new file mode 100644
index 00000000000..9a388c592b9
--- /dev/null
+++ b/res/layout/gesture_preference.xml
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/raw/gesture_ambient_move_lift.mp4 b/res/raw/gesture_ambient_move_lift.mp4
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/res/raw/gesture_ambient_tap.mp4 b/res/raw/gesture_ambient_tap.mp4
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/res/raw/gesture_double_tap.mp4 b/res/raw/gesture_double_tap.mp4
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/res/raw/gesture_fingerprint_swipe.mp4 b/res/raw/gesture_fingerprint_swipe.mp4
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/res/raw/gesture_twist.mp4 b/res/raw/gesture_twist.mp4
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index ca3d247354d..ffc233b3e60 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -137,4 +137,10 @@
+
+
+
+
+
+
diff --git a/res/values/colors.xml b/res/values/colors.xml
index a462edc3e98..b2d043f6a88 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -131,4 +131,7 @@
@color/accent_material_light
#ff7fcac3
+
+ #f5f5f5
+
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index c50e2267784..c4e471ff44f 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -296,4 +296,11 @@
16dp
+
+ 56dp
+ 206dp
+ 206dp
+ 206dp
+ 206dp
+ 20dp
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4a5650f935e..971ec82f94e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -7660,4 +7660,35 @@
Automatic Storage Management Service
+
+ Gestures
+
+
+ Jump to Camera
+
+
+ To quickly open camera, just double-tap the power button. Works from any screen.
+
+
+ Flip camera
+
+
+ To switch between front and back cameras, double-twist.
+
+
+ Quick screen check
+
+
+ To check your phone without waking it up fully, nudge or pick it up.
+
+
+ Swipe for notifications
+
+
+ To check your notifications from any screen, swipe down on the fingerprint sensor.
+
+
+ On
+ Off
+
diff --git a/res/xml/gesture_settings.xml b/res/xml/gesture_settings.xml
new file mode 100644
index 00000000000..e68d8f55a5f
--- /dev/null
+++ b/res/xml/gesture_settings.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 1bf1e0e41b2..f4a407af322 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -84,6 +84,7 @@ public class Settings extends SettingsActivity {
public static class AccountSyncSettingsActivity extends SettingsActivity { /* empty */ }
public static class AccountSettingsActivity extends SettingsActivity { /* empty */ }
public static class AccountSyncSettingsInAddAccountActivity extends SettingsActivity { /* empty */ }
+ public static class GestureSettingsActivity extends SettingsActivity { /* empty */ }
public static class CryptKeeperSettingsActivity extends SettingsActivity { /* empty */ }
public static class DeviceAdminSettingsActivity extends SettingsActivity { /* empty */ }
public static class DataUsageSummaryActivity extends SettingsActivity { /* empty */ }
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 330c9ea2170..223d5367c98 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -85,6 +85,7 @@ import com.android.settings.deviceinfo.StorageSettings;
import com.android.settings.fuelgauge.BatterySaverSettings;
import com.android.settings.fuelgauge.PowerUsageDetail;
import com.android.settings.fuelgauge.PowerUsageSummary;
+import com.android.settings.gestures.GestureSettings;
import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
@@ -242,6 +243,7 @@ public class SettingsActivity extends SettingsDrawerActivity
Settings.StorageSettingsActivity.class.getName(),
Settings.ManageApplicationsActivity.class.getName(),
Settings.PowerUsageSummaryActivity.class.getName(),
+ Settings.GestureSettingsActivity.class.getName(),
//personal_section
Settings.LocationSettingsActivity.class.getName(),
Settings.SecuritySettingsActivity.class.getName(),
@@ -301,6 +303,7 @@ public class SettingsActivity extends SettingsDrawerActivity
PowerUsageSummary.class.getName(),
AccountSyncSettings.class.getName(),
AccountSettings.class.getName(),
+ GestureSettings.class.getName(),
CryptKeeperSettings.class.getName(),
DataUsageSummary.class.getName(),
DreamSettings.class.getName(),
diff --git a/src/com/android/settings/gestures/GesturePreference.java b/src/com/android/settings/gestures/GesturePreference.java
new file mode 100644
index 00000000000..1541aece094
--- /dev/null
+++ b/src/com/android/settings/gestures/GesturePreference.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2016 The Android Open Source 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.android.settings.gestures;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.SurfaceTexture;
+import android.media.MediaMetadataRetriever;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.Surface;
+import android.view.TextureView;
+import android.widget.ImageView;
+import android.util.AttributeSet;
+import android.util.Log;
+
+import com.android.settings.R;
+
+/**
+ * Preference item for a gesture with a switch to signify if it should be enabled.
+ * This shows the title and description of the gesture along with an animation showing how to do
+ * the gesture
+ */
+public class GesturePreference extends SwitchPreference {
+ private static final String TAG = "GesturePreference";
+ private Uri mVideoPath;
+ private Context mContext;
+ private MediaPlayer mMediaPlayer;
+ private MediaMetadataRetriever mMediaMetadata;
+
+ public GesturePreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+ setLayoutResource(R.layout.gesture_preference);
+ TypedArray attributes = context.getTheme().obtainStyledAttributes(
+ attrs,
+ R.styleable.GesturePreference,
+ 0, 0);
+ try {
+ int animation = attributes.getResourceId(R.styleable.GesturePreference_animation, 0);
+ mVideoPath = Uri.parse(
+ "android.resource://" + context.getPackageName() + "/" + animation);
+ mMediaMetadata = new MediaMetadataRetriever();
+ mMediaMetadata.setDataSource(mContext, mVideoPath);
+ } catch (Exception e) {
+ // resource not available, show blank view
+ } finally {
+ attributes.recycle();
+ }
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+ final TextureView video = (TextureView) holder.findViewById(R.id.gesture_video);
+ final ImageView imageView = (ImageView) holder.findViewById(R.id.gesture_image);
+
+ video.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ if (mMediaPlayer != null) {
+ if (mMediaPlayer.isPlaying()) {
+ mMediaPlayer.pause();
+ } else {
+ mMediaPlayer.start();
+ imageView.setVisibility(View.GONE);
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+ });
+
+ video.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
+ @Override
+ public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
+ mMediaPlayer = MediaPlayer.create(mContext, mVideoPath);
+ if (mMediaPlayer != null) {
+ mMediaPlayer.setSurface(new Surface(surfaceTexture));
+ mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
+ @Override
+ public void onPrepared(MediaPlayer mediaPlayer) {
+ mediaPlayer.setLooping(true);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) {
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
+ imageView.setVisibility(View.VISIBLE);
+ if (mMediaPlayer != null) {
+ mMediaPlayer.stop();
+ mMediaPlayer.reset();
+ mMediaPlayer.release();
+ }
+ return false;
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
+ }
+ });
+
+ if (mMediaPlayer == null) {
+ Bitmap bitmap = mMediaMetadata.getFrameAtTime(0);
+ if (bitmap != null) {
+ imageView.setImageDrawable(new BitmapDrawable(bitmap));
+ }
+ imageView.setVisibility(View.VISIBLE);
+ }
+
+ }
+
+}
diff --git a/src/com/android/settings/gestures/GestureSettings.java b/src/com/android/settings/gestures/GestureSettings.java
new file mode 100644
index 00000000000..56f4184efa3
--- /dev/null
+++ b/src/com/android/settings/gestures/GestureSettings.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 The Android Open Source 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.android.settings.gestures;
+
+import android.os.Bundle;
+import android.support.v7.preference.PreferenceScreen;
+import android.provider.Settings;
+import android.support.v7.preference.Preference;
+import android.util.Log;
+
+import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+
+/**
+ * Top level fragment for gesture settings.
+ * This will create individual switch preference for each gesture and handle updates when each
+ * preference is updated
+ */
+public class GestureSettings extends SettingsPreferenceFragment implements
+ Preference.OnPreferenceChangeListener {
+
+ private static final String TAG = "GestureSettings";
+ private static final String PREF_KEY_DOUBLE_TAP_POWER = "gesture_double_tap_power";
+ private static final String PREF_KEY_DOUBLE_TWIST = "gesture_double_twist";
+ private static final String PREF_KEY_PICK_UP_AND_NUDGE = "gesture_pick_up_and_nudge";
+ private static final String PREF_KEY_SWIPE_DOWN_FINGERPRINT = "gesture_swipe_down_fingerprint";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.gesture_settings);
+
+ // Double tap power for camera
+ int cameraDisabled = Settings.Secure.getInt(getActivity().getContentResolver(),
+ Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, 1);
+ GesturePreference preference =
+ (GesturePreference) findPreference(PREF_KEY_DOUBLE_TAP_POWER);
+ preference.setChecked(cameraDisabled == 0);
+ preference.setOnPreferenceChangeListener(this);
+
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ boolean enabled = (boolean) newValue;
+ if (PREF_KEY_DOUBLE_TAP_POWER.equals(preference.getKey())) {
+ Settings.Secure.putInt(getActivity().getContentResolver(),
+ Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, enabled ? 0 : 1);
+ }
+ return true;
+ }
+
+ @Override
+ protected int getMetricsCategory() {
+ return MetricsEvent.SETTINGS_GESTURES;
+ }
+
+}
\ No newline at end of file