[3/3] OmniGears: Ambient Display configurations

make simple options

PS17
-use ListPreference value as summary

fix dependency
change string

PS24
-reworked shake test handling

<!------------------------------------------------------------>

Note :

   since each minimum and maximum value for device is different
   (minimum and maximum value in this patch is for hammerhead and shamu)
   you must replace :

   1. doze_pulse_duration_in_entries and doze_pulse_duration_in_values
   2. doze_pulse_duration_visible_entries and doze_pulse_duration_visible_values

   and put in OmniGears overlay inside your device repo

<!------------------------------------------------------------>

Example :
   my device is falcon a.k.a Moto G, cant handle with value that present in this patch
   so i need to check which minimum and maximum value that working

   after that, i get minimum 1 s and maximum 2s for time in and out
   also i get minimum 2 s and maximum 4 s for visible time

   then i put value into overlay

    <string-array name="doze_pulse_duration_in_entries" translatable="false">
        <item>@string/one_second_default</item>
        <item>@string/onehalf_second</item>
        <item>@string/two_second</item>
    </string-array>

    <string-array name="doze_pulse_duration_in_values" translatable="false">
        <item>1000</item>
        <item>1500</item>
        <item>2000</item>
    </string-array>

    <string-array name="doze_pulse_duration_visible_entries" translatable="false">
        <item>@string/two_second</item>
        <item>@string/twohalf_second</item>
        <item>@string/three_second_default</item>
        <item>@string/threehalf_second</item>
        <item>@string/four_second</item>
    </string-array>

    <string-array name="doze_pulse_duration_visible_values" translatable="false">
        <item>2000</item>
        <item>2500</item>
        <item>3000</item>
        <item>3500</item>
        <item>4000</item>
    </string-array>

    make sure the you keep default value in the array
<!------------------------------------------------------------>

last patch: make sure option not showing in search mode if not support

Change-Id: I48e5f576af7a619b5ad6b030bf606aaee771a095
diff --git a/res/values/custom_arrays.xml b/res/values/custom_arrays.xml
index f645974..dc58e10 100644
--- a/res/values/custom_arrays.xml
+++ b/res/values/custom_arrays.xml
@@ -47,4 +47,80 @@
         <item>10</item>
         <item>11</item>
     </string-array>
+
+    <string-array name="doze_pulse_duration_in_entries" translatable="false">
+        <item>@string/twohundred_ms</item>
+        <item>@string/fivehundred_ms</item>
+        <item>@string/one_second_default</item>
+        <item>@string/onehalf_second</item>
+        <item>@string/two_second</item>
+    </string-array>
+
+    <string-array name="doze_pulse_duration_in_values" translatable="false">
+        <item>200</item>
+        <item>500</item>
+        <item>1000</item>
+        <item>1500</item>
+        <item>2000</item>
+    </string-array>
+
+    <string-array name="doze_pulse_duration_visible_entries" translatable="false">
+        <item>@string/one_second</item>
+        <item>@string/onehalf_second</item>
+        <item>@string/two_second</item>
+        <item>@string/twohalf_second</item>
+        <item>@string/three_second_default</item>
+        <item>@string/threehalf_second</item>
+        <item>@string/four_second</item>
+        <item>@string/fourhalf_second</item>
+        <item>@string/five_second</item>
+    </string-array>
+
+    <string-array name="doze_pulse_duration_visible_values" translatable="false">
+        <item>1000</item>
+        <item>1500</item>
+        <item>2000</item>
+        <item>2500</item>
+        <item>3000</item>
+        <item>3500</item>
+        <item>4000</item>
+        <item>4500</item>
+        <item>5000</item>
+    </string-array>
+
+    <string-array name="doze_shake_acc_threshold_entries" translatable="false">
+        <item>@string/veryeasy_shake</item>
+        <item>@string/easy_shake</item>
+        <item>@string/medium_shake</item>
+        <item>@string/hard_shake</item>
+        <item>@string/veryhard_shake</item>
+        <item>@string/strong_shake</item>
+    </string-array>
+
+    <string-array name="doze_shake_acc_threshold_values" translatable="false">
+        <item>2</item>
+        <item>5</item>
+        <item>10</item>
+        <item>13</item>
+        <item>16</item>
+        <item>20</item>
+    </string-array>
+
+    <string-array name="doze_list_mode_entries" translatable="false">
+        <item>@string/fully_mode</item>
+        <item>@string/pocket_shake_mode</item>
+        <item>@string/pocket_pulse_mode</item>
+        <item>@string/pocket_only_mode</item>
+        <item>@string/shake_pulse_mode</item>
+        <item>@string/pulse_only_mode</item>
+    </string-array>
+
+    <string-array name="doze_list_mode_values" translatable="false">
+        <item>1</item>
+        <item>2</item>
+        <item>3</item>
+        <item>4</item>
+        <item>5</item>
+        <item>6</item>
+    </string-array>
 </resources>
diff --git a/res/values/custom_strings.xml b/res/values/custom_strings.xml
index 1223f69..421227e 100644
--- a/res/values/custom_strings.xml
+++ b/res/values/custom_strings.xml
@@ -18,6 +18,7 @@
  -->
 
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
     <!-- Hardware keys -->
     <string name="button_keys_title">Keys</string>
     <string name="keys_bindings_title">Key actions</string>
@@ -63,6 +64,68 @@
     <string name="button_home_answers_call_title">Button answers call</string>
     <string name="button_home_answers_call_summary">Pressing the home button will answer incoming call</string>
 
+    <!-- Doze pulse (ambient display) options -->
+    <string name="doze_overwrite_value_title">Overwrite value</string>
+    <string name="doze_overwrite_value_summary">Allow to overwrite default value with user config</string>
+
+    <string name="doze_list_mode_title">Doze mode</string>
+    <string name="doze_list_mode_summary">Option to configure doze mode</string>
+
+    <string name="doze_pulse_title">Time settings</string>
+    <string name="doze_pulse_duration_in_title">Fade in</string>
+    <string name="doze_pulse_duration_in_summary"></string>
+
+    <string name="doze_pulse_duration_visible_title">Visible duration</string>
+    <string name="doze_pulse_duration_visible_summary"></string>
+
+    <string name="doze_pulse_duration_out_title">Fade out</string>
+    <string name="doze_pulse_duration_out_summary"></string>
+
+    <string name="doze_pulse_on_notifications_title">Pulse on notifications</string>
+    <string name="doze_pulse_on_notifications_summary">Trigger activation only on pending notifications</string>
+
+    <string name="doze_shake_title">Shake settings</string>
+    <string name="doze_shake_mode_title">Shake mode</string>
+    <string name="doze_shake_mode_summary">Use shake sensor value to trigger activation</string>
+    <string name="doze_shake_mode_test_summary">Do you want to test shake sensor?</string>
+    <string name="doze_shake_mode_test_result">Apply current value?</string>
+    <string name="doze_shake_test">Shake your device now</string>
+    <string name="doze_shake_it">Shake detected!</string>
+
+    <string name="doze_shake_acc_threshold_title">Shake threshold</string>
+    <string name="doze_shake_acc_threshold_summary">Configure shake strength required to trigger activation</string>
+
+    <string name="doze_time_mode_title">Show clock and date</string>
+    <string name="doze_time_mode_summary">Force showing time when there is no notification</string>
+
+    <string name="twohundred_ms">200 ms</string>
+    <string name="fivehundred_ms">500 ms</string>
+    <string name="one_second">1 s</string>
+    <string name="one_second_default">1 s (default)</string>
+    <string name="onehalf_second">1.5 s</string>
+    <string name="two_second">2 s</string>
+    <string name="twohalf_second">2.5 s</string>
+    <string name="three_second">3 s</string>
+    <string name="three_second_default">3 s (default)</string>
+    <string name="threehalf_second">3.5 s</string>
+    <string name="four_second">4 s</string>
+    <string name="fourhalf_second">4.5 s</string>
+    <string name="five_second">5 s</string>
+
+    <string name="veryeasy_shake">Very easy</string>
+    <string name="easy_shake">Easy</string>
+    <string name="medium_shake">Default</string>
+    <string name="hard_shake">Hard</string>
+    <string name="veryhard_shake">Very hard</string>
+    <string name="strong_shake">Strong</string>
+
+    <string name="fully_mode">Full mode</string>
+    <string name="pocket_shake_mode">Pocket and shake</string>
+    <string name="pocket_pulse_mode">Pocket and pulse</string>
+    <string name="pocket_only_mode">Pocket</string>
+    <string name="shake_pulse_mode">Shake and pulse</string>
+    <string name="pulse_only_mode">Pulse (default)</string>
+
     <!-- Swap volume buttons when the screen is rotated -->
     <string name="swap_volume_buttons_title">Match rotation</string>
     <string name="swap_volume_buttons_summary">Adjust volume buttons action based on current rotation</string>
diff --git a/res/xml/ambient_settings.xml b/res/xml/ambient_settings.xml
new file mode 100644
index 0000000..b2b0aff
--- /dev/null
+++ b/res/xml/ambient_settings.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--  Copyright (C) 2015 The OmniROM Project
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+                  xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
+        android:title="@string/doze_title"
+        settings:keywords="@string/doze_title">
+
+            <SwitchPreference
+                    android:key="doze"
+                    android:title="@string/doze_title"
+                    android:summary="@string/doze_summary"
+                    android:persistent="false" />
+
+            <ListPreference
+                    android:entries="@array/doze_list_mode_entries"
+                    android:entryValues="@array/doze_list_mode_values"
+                    android:key="doze_list_mode"
+                    android:title="@string/doze_list_mode_title"
+                    android:summary="@string/doze_list_mode_summary"
+                    android:persistent="false"
+                    android:dependency="doze" />
+
+            <org.omnirom.omnigears.preference.SystemCheckBoxPreference
+                    android:key="doze_time_mode"
+                    android:title="@string/doze_time_mode_title"
+                    android:summary="@string/doze_time_mode_summary"
+                    android:dependency="doze" />
+
+            <org.omnirom.omnigears.preference.SystemCheckBoxPreference
+                    android:key="doze_overwrite_value"
+                    android:title="@string/doze_overwrite_value_title"
+                    android:summary="@string/doze_overwrite_value_summary"
+                    android:dependency="doze" />
+
+            <org.omnirom.omnigears.preference.SystemCheckBoxPreference
+                    android:key="doze_pulse_on_notifications"
+                    android:title="@string/doze_pulse_on_notifications_title"
+                    android:summary="@string/doze_pulse_on_notifications_summary"
+                    android:defaultValue="true"
+                    android:dependency="doze_overwrite_value" />
+
+            <PreferenceCategory
+                    android:key="doze_pulse_category"
+                    android:title="@string/doze_pulse_title" >
+                    <ListPreference
+                          android:entries="@array/doze_pulse_duration_in_entries"
+                          android:entryValues="@array/doze_pulse_duration_in_values"
+                          android:key="doze_pulse_in"
+                          android:title="@string/doze_pulse_duration_in_title"
+                          android:summary="@string/doze_pulse_duration_in_summary"
+                          android:persistent="false"
+                          android:dependency="doze_overwrite_value" />
+
+                    <ListPreference
+                          android:entries="@array/doze_pulse_duration_visible_entries"
+                          android:entryValues="@array/doze_pulse_duration_visible_values"
+                          android:key="doze_pulse_visible"
+                          android:title="@string/doze_pulse_duration_visible_title"
+                          android:summary="@string/doze_pulse_duration_visible_summary"
+                          android:persistent="false"
+                          android:dependency="doze_overwrite_value" />
+
+                    <ListPreference
+                          android:entries="@array/doze_pulse_duration_in_entries"
+                          android:entryValues="@array/doze_pulse_duration_in_values"
+                          android:key="doze_pulse_out"
+                          android:title="@string/doze_pulse_duration_out_title"
+                          android:summary="@string/doze_pulse_duration_out_summary"
+                          android:persistent="false"
+                          android:dependency="doze_overwrite_value" />
+
+            </PreferenceCategory>
+
+            <PreferenceCategory
+                    android:key="doze_shake_category"
+                    android:title="@string/doze_shake_title" >
+
+                <ListPreference
+                        android:entries="@array/doze_shake_acc_threshold_entries"
+                        android:entryValues="@array/doze_shake_acc_threshold_values"
+                        android:key="doze_shake_threshold"
+                        android:title="@string/doze_shake_acc_threshold_title"
+                        android:summary="@string/doze_shake_acc_threshold_summary"
+                        android:persistent="false"
+                        android:dependency="doze" />
+            </PreferenceCategory>
+</PreferenceScreen>
diff --git a/src/org/omnirom/omnigears/AmbientSettings.java b/src/org/omnirom/omnigears/AmbientSettings.java
new file mode 100644
index 0000000..07e80e8
--- /dev/null
+++ b/src/org/omnirom/omnigears/AmbientSettings.java
@@ -0,0 +1,485 @@
+/*
+ *  Copyright (C) 2015 The OmniROM Project
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+package org.omnirom.omnigears;
+
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.search.Indexable;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.database.ContentObserver;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.preference.PreferenceScreen;
+import android.preference.PreferenceCategory;
+import android.preference.SwitchPreference;
+import android.provider.SearchIndexableResource;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.widget.Button;
+
+import com.android.settings.SettingsPreferenceFragment;
+import org.omnirom.omnigears.preference.SystemCheckBoxPreference;
+import org.omnirom.omnigears.sensor.ShakeSensorManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AmbientSettings extends SettingsPreferenceFragment implements
+        Preference.OnPreferenceChangeListener, OnPreferenceClickListener,
+        Indexable, ShakeSensorManager.ShakeListener {
+    private static final String TAG = "AmbientSettings";
+
+    private static final String KEY_DOZE = "doze";
+    private static final String KEY_DOZE_OVERWRITE_VALUE = "doze_overwrite_value";
+    private static final String KEY_DOZE_PULSE_IN = "doze_pulse_in";
+    private static final String KEY_DOZE_PULSE_VISIBLE = "doze_pulse_visible";
+    private static final String KEY_DOZE_PULSE_OUT = "doze_pulse_out";
+    private static final String KEY_DOZE_LIST_MODE = "doze_list_mode";
+    private static final String KEY_DOZE_PULSE_MODE = "doze_pulse_on_notifications";
+    private static final String KEY_DOZE_SHAKE_CATEGORY = "doze_shake_category";
+    private static final String KEY_DOZE_SHAKE_THRESHOLD = "doze_shake_threshold";
+    private static final String KEY_DOZE_TIME_MODE = "doze_time_mode";
+
+    private int mAccValue;
+    private int mOldAccValue;
+    private SwitchPreference mDozePreference;
+    private ListPreference mDozeListMode;
+    private ListPreference mDozePulseIn;
+    private ListPreference mDozePulseVisible;
+    private ListPreference mDozePulseOut;
+    private ListPreference mDozeShakeThreshold;
+    private SystemCheckBoxPreference mDozeTimeMode;
+    private ShakeSensorManager mShakeSensorManager;
+    private AlertDialog mDialog;
+    private Button mShakeFoundButton;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        final Activity activity = getActivity();
+
+        addPreferencesFromResource(R.xml.ambient_settings);
+
+        mDozePreference = (SwitchPreference) findPreference(KEY_DOZE);
+        mDozePreference.setOnPreferenceChangeListener(this);
+
+        mDozePulseIn = (ListPreference) findPreference(KEY_DOZE_PULSE_IN);
+        mDozePulseIn.setOnPreferenceChangeListener(this);
+
+        mDozePulseVisible = (ListPreference) findPreference(KEY_DOZE_PULSE_VISIBLE);
+        mDozePulseVisible.setOnPreferenceChangeListener(this);
+
+        mDozePulseOut = (ListPreference) findPreference(KEY_DOZE_PULSE_OUT);
+        mDozePulseOut.setOnPreferenceChangeListener(this);
+
+        if (isAccelerometerAvailable(activity)) {
+            mDozeListMode = (ListPreference) findPreference(KEY_DOZE_LIST_MODE);
+            mDozeListMode.setOnPreferenceChangeListener(this);
+
+            mDozeTimeMode = (SystemCheckBoxPreference) findPreference(KEY_DOZE_TIME_MODE);
+
+            mDozeShakeThreshold = (ListPreference) findPreference(KEY_DOZE_SHAKE_THRESHOLD);
+            mDozeShakeThreshold.setOnPreferenceChangeListener(this);
+
+            removePreference(KEY_DOZE_PULSE_MODE);
+        } else {
+            removePreference(KEY_DOZE_LIST_MODE);
+            removePreference(KEY_DOZE_TIME_MODE);
+            removePreference(KEY_DOZE_SHAKE_THRESHOLD);
+            removePreference(KEY_DOZE_SHAKE_CATEGORY);
+        }
+        updateDozeListMode();
+        updateDozeOptions();
+        mShakeSensorManager = new ShakeSensorManager(activity, this);
+    }
+
+    private static boolean isAccelerometerAvailable(Context context) {
+        boolean useAccelerometer = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_dozeUseAccelerometer);
+        return useAccelerometer;
+    }
+
+    private static boolean isDozeAvailable(Context context) {
+        String name = Build.IS_DEBUGGABLE ? SystemProperties.get("debug.doze.component") : null;
+        if (TextUtils.isEmpty(name)) {
+            name = context.getResources().getString(
+                    com.android.internal.R.string.config_dozeComponent);
+        }
+        return !TextUtils.isEmpty(name);
+    }
+
+    private void updateDozeListMode() {
+        final boolean pocketMode = Settings.System.getInt(getContentResolver(),
+                   Settings.System.DOZE_POCKET_MODE, 0) != 0;
+        final boolean shakeMode = Settings.System.getInt(getContentResolver(),
+                   Settings.System.DOZE_SHAKE_MODE, 0) != 0;
+        final boolean pulseMode = Settings.System.getInt(getContentResolver(),
+                   Settings.System.DOZE_PULSE_ON_NOTIFICATIONS, 1) != 0;
+        int listMode = 6;
+        if (pocketMode && shakeMode && pulseMode) {
+            listMode = 1;
+            enableShakeThreshold(true);
+        } else if (pocketMode && shakeMode && !pulseMode) {
+            listMode = 2;
+            enableShakeThreshold(true);
+        } else if (pocketMode && !shakeMode && pulseMode) {
+            listMode = 3;
+            enableShakeThreshold(false);
+        } else if (pocketMode && !shakeMode && !pulseMode) {
+            listMode = 4;
+            enableShakeThreshold(false);
+        } else if (!pocketMode && shakeMode && pulseMode) {
+            listMode = 5;
+            enableShakeThreshold(true);
+        } else if (!pocketMode && !shakeMode && pulseMode) {
+            listMode = 6;
+            enableShakeThreshold(false);
+        }
+        enabledTimeMode(pocketMode);
+        if (mDozeListMode != null) {
+            mDozeListMode.setValue(String.valueOf(listMode));
+            int index = mDozeListMode.findIndexOfValue(String.valueOf(listMode));
+            if (index != -1) {
+                mDozeListMode.setSummary(mDozeListMode.getEntries()[index]);
+            }
+        }
+    }
+
+    private void enabledTimeMode(boolean enable) {
+        if (mDozeTimeMode != null) {
+            mDozeTimeMode.setEnabled(enable);
+        }
+    }
+
+    private void showAccDialog() {
+        AlertDialog.Builder alertDialog = new AlertDialog.Builder(getActivity());
+        alertDialog.setTitle(R.string.doze_shake_mode_title);
+        alertDialog.setMessage(R.string.doze_shake_mode_test_summary);
+        alertDialog.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
+            public void onClick(DialogInterface dialog, int id) {
+                startAcctest();
+            }
+        });
+        alertDialog.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
+            public void onClick(DialogInterface dialog, int id) {
+                stopAcctest();
+            }
+        });
+        mDialog = alertDialog.create();
+        mDialog.show();
+    }
+
+    @Override
+    public synchronized void onShake() {
+        String msg1 = getResources().getString(R.string.doze_shake_it);
+        String msg2 = getResources().getString(R.string.doze_shake_mode_test_result);
+
+        String msg = msg1 + "\n" + msg2;
+        mDialog.setMessage(msg);
+        Button shakeCancelButton = mDialog.getButton(DialogInterface.BUTTON_NEGATIVE);
+        if (shakeCancelButton != null) {
+            shakeCancelButton.setText(R.string.no);
+        }
+        if (mShakeFoundButton != null) {
+            mShakeFoundButton.setEnabled(true);
+        }
+        mShakeSensorManager.disable();
+    }
+
+    private void startAcctest() {
+        mShakeSensorManager.enable(mAccValue);
+        AlertDialog.Builder alertDialog = new AlertDialog.Builder(getActivity());
+        alertDialog.setTitle(R.string.doze_shake_mode_title);
+        alertDialog.setMessage(R.string.doze_shake_test);
+        alertDialog.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
+            public void onClick(DialogInterface dialog, int id) {
+                stopAcctest();
+            }
+        });
+        alertDialog.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+            public void onClick(DialogInterface dialog, int id) {
+                revertAcctest();
+            }
+        });
+        mDialog = alertDialog.create();
+        mDialog.show();
+
+        mShakeFoundButton = mDialog.getButton(DialogInterface.BUTTON_POSITIVE);
+        if (mShakeFoundButton != null) {
+            mShakeFoundButton.setEnabled(false);
+        }
+    }
+
+    private void revertAcctest() {
+        mAccValue = mOldAccValue;
+        mDozeShakeThreshold.setValue(String.valueOf(mOldAccValue));
+        mShakeSensorManager.disable();
+    }
+
+    private void stopAcctest() {
+        appliedAccTest();
+        mShakeSensorManager.disable();
+    }
+
+    private void appliedAccTest() {
+        Settings.System.putInt(getContentResolver(),
+                    Settings.System.DOZE_SHAKE_ACC_THRESHOLD, mAccValue);
+        mDozeShakeThreshold.setValue(String.valueOf(mAccValue));
+        int index = mDozeShakeThreshold.findIndexOfValue(String.valueOf(mAccValue));
+        if (index != -1) {
+            mDozeShakeThreshold.setSummary(mDozeShakeThreshold.getEntries()[index]);
+        }
+    }
+
+    private void enableShakeThreshold(boolean enabled) {
+        final boolean writeMode = Settings.System.getInt(getContentResolver(),
+                   Settings.System.DOZE_OVERWRITE_VALUE, 0) != 0;
+        if (mDozeShakeThreshold != null) {
+            mDozeShakeThreshold.setEnabled(enabled && writeMode);
+        }
+    }
+
+    private void updateDozeListModeValue(int listMode) {
+        if (listMode == 1) {
+            enableShakeThreshold(true);
+            enabledTimeMode(true);
+            Settings.System.putInt(getContentResolver(),
+                   Settings.System.DOZE_POCKET_MODE, 1);
+            Settings.System.putInt(getContentResolver(),
+                   Settings.System.DOZE_SHAKE_MODE, 1);
+            Settings.System.putInt(getContentResolver(),
+                   Settings.System.DOZE_PULSE_ON_NOTIFICATIONS, 1);
+        } else if (listMode == 2) {
+            enableShakeThreshold(true);
+            enabledTimeMode(true);
+            Settings.System.putInt(getContentResolver(),
+                   Settings.System.DOZE_POCKET_MODE, 1);
+            Settings.System.putInt(getContentResolver(),
+                   Settings.System.DOZE_SHAKE_MODE, 1);
+            Settings.System.putInt(getContentResolver(),
+                   Settings.System.DOZE_PULSE_ON_NOTIFICATIONS, 0);
+        } else if (listMode == 3) {
+            enableShakeThreshold(false);
+            enabledTimeMode(true);
+            Settings.System.putInt(getContentResolver(),
+                   Settings.System.DOZE_POCKET_MODE, 1);
+            Settings.System.putInt(getContentResolver(),
+                   Settings.System.DOZE_SHAKE_MODE, 0);
+            Settings.System.putInt(getContentResolver(),
+                   Settings.System.DOZE_PULSE_ON_NOTIFICATIONS, 1);
+        } else if (listMode == 4) {
+            enableShakeThreshold(false);
+            enabledTimeMode(true);
+            Settings.System.putInt(getContentResolver(),
+                   Settings.System.DOZE_POCKET_MODE, 1);
+            Settings.System.putInt(getContentResolver(),
+                   Settings.System.DOZE_SHAKE_MODE, 0);
+            Settings.System.putInt(getContentResolver(),
+                   Settings.System.DOZE_PULSE_ON_NOTIFICATIONS, 0);
+        } else if (listMode == 5) {
+            enableShakeThreshold(true);
+            enabledTimeMode(false);
+            Settings.System.putInt(getContentResolver(),
+                   Settings.System.DOZE_POCKET_MODE, 0);
+            Settings.System.putInt(getContentResolver(),
+                   Settings.System.DOZE_SHAKE_MODE, 1);
+            Settings.System.putInt(getContentResolver(),
+                   Settings.System.DOZE_PULSE_ON_NOTIFICATIONS, 1);
+        } else if (listMode == 6) {
+            enableShakeThreshold(false);
+            enabledTimeMode(false);
+            Settings.System.putInt(getContentResolver(),
+                   Settings.System.DOZE_POCKET_MODE, 0);
+            Settings.System.putInt(getContentResolver(),
+                   Settings.System.DOZE_SHAKE_MODE, 0);
+            Settings.System.putInt(getContentResolver(),
+                   Settings.System.DOZE_PULSE_ON_NOTIFICATIONS, 1);
+        }
+    }
+
+    private void updateDozeOptions() {
+        if (mDozePulseIn != null) {
+            final int statusDozePulseIn = Settings.System.getInt(getContentResolver(),
+                    Settings.System.DOZE_PULSE_DURATION_IN, 1000);
+            mDozePulseIn.setValue(String.valueOf(statusDozePulseIn));
+            int index = mDozePulseIn.findIndexOfValue(String.valueOf(statusDozePulseIn));
+            if (index != -1) {
+                mDozePulseIn.setSummary(mDozePulseIn.getEntries()[index]);
+            }
+        }
+        if (mDozePulseVisible != null) {
+            final int statusDozePulseVisible = Settings.System.getInt(getContentResolver(),
+                    Settings.System.DOZE_PULSE_DURATION_VISIBLE, 3000);
+            mDozePulseVisible.setValue(String.valueOf(statusDozePulseVisible));
+            int index = mDozePulseVisible.findIndexOfValue(String.valueOf(statusDozePulseVisible));
+            if (index != -1) {
+                mDozePulseVisible.setSummary(mDozePulseVisible.getEntries()[index]);
+            }
+        }
+        if (mDozePulseOut != null) {
+            final int statusDozePulseOut = Settings.System.getInt(getContentResolver(),
+                    Settings.System.DOZE_PULSE_DURATION_OUT, 1000);
+            mDozePulseOut.setValue(String.valueOf(statusDozePulseOut));
+            int index = mDozePulseOut.findIndexOfValue(String.valueOf(statusDozePulseOut));
+            if (index != -1) {
+               mDozePulseOut.setSummary(mDozePulseOut.getEntries()[index]);
+            }
+        }
+        if (mDozeShakeThreshold != null) {
+            mAccValue = Settings.System.getInt(getContentResolver(),
+                    Settings.System.DOZE_SHAKE_ACC_THRESHOLD, 10);
+            mDozeShakeThreshold.setValue(String.valueOf(mAccValue));
+            int index = mDozeShakeThreshold.findIndexOfValue(String.valueOf(mAccValue));
+            if (index != -1) {
+                mDozeShakeThreshold.setSummary(mDozeShakeThreshold.getEntries()[index]);
+            }
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        updateState();
+        updateDozeOptions();
+        updateDozeListMode();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mShakeSensorManager.disable();
+        if (mDialog != null) {
+            mDialog.dismiss();
+        }
+    }
+
+    private void updateState() {
+        // Update doze if it is available.
+        if (mDozePreference != null) {
+            int value = Settings.Secure.getInt(getContentResolver(), Settings.Secure.DOZE_ENABLED, 1);
+            mDozePreference.setChecked(value != 0);
+        }
+    }
+
+    @Override
+    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
+        if (preference == findPreference(KEY_DOZE_OVERWRITE_VALUE)) {
+            updateDozeListMode();
+        }
+        return super.onPreferenceTreeClick(preferenceScreen, preference);
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object objValue) {
+        final String key = preference.getKey();
+        if (preference == mDozePreference) {
+            boolean value = (Boolean) objValue;
+            Settings.Secure.putInt(getContentResolver(), Settings.Secure.DOZE_ENABLED, value ? 1 : 0);
+        }
+        if (preference == mDozePulseIn) {
+            int dozePulseIn = Integer.parseInt((String)objValue);
+            int index = mDozePulseIn.findIndexOfValue((String) objValue);
+            mDozePulseIn.setSummary(mDozePulseIn.getEntries()[index]);
+            Settings.System.putInt(getContentResolver(),
+                    Settings.System.DOZE_PULSE_DURATION_IN, dozePulseIn);
+        }
+        if (preference == mDozePulseVisible) {
+            int dozePulseVisible = Integer.parseInt((String)objValue);
+            int index = mDozePulseVisible.findIndexOfValue((String) objValue);
+            mDozePulseVisible.setSummary(mDozePulseVisible.getEntries()[index]);
+            Settings.System.putInt(getContentResolver(),
+                    Settings.System.DOZE_PULSE_DURATION_VISIBLE, dozePulseVisible);
+        }
+        if (preference == mDozePulseOut) {
+            int dozePulseOut = Integer.parseInt((String)objValue);
+            int index = mDozePulseOut.findIndexOfValue((String) objValue);
+            mDozePulseOut.setSummary(mDozePulseOut.getEntries()[index]);
+            Settings.System.putInt(getContentResolver(),
+                    Settings.System.DOZE_PULSE_DURATION_OUT, dozePulseOut);
+        }
+        if (preference == mDozeShakeThreshold) {
+            int accValue = Integer.parseInt((String)objValue);
+            if (accValue != mAccValue) {
+                mOldAccValue = mAccValue;
+                mAccValue = accValue;
+                showAccDialog();
+            }
+        }
+        if (preference == mDozeListMode) {
+            int dozeListMode = Integer.parseInt((String)objValue);
+            updateDozeListModeValue(dozeListMode);
+            int index = mDozeListMode.findIndexOfValue((String) objValue);
+            mDozeListMode.setSummary(mDozeListMode.getEntries()[index]);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean onPreferenceClick(Preference preference) {
+        return false;
+    }
+
+    public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+            new BaseSearchIndexProvider() {
+                @Override
+                public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
+                        boolean enabled) {
+                    ArrayList<SearchIndexableResource> result =
+                            new ArrayList<SearchIndexableResource>();
+
+                    SearchIndexableResource sir = new SearchIndexableResource(context);
+                    sir.xmlResId = R.xml.ambient_settings;
+                    result.add(sir);
+
+                    return result;
+                }
+
+                @Override
+                public List<String> getNonIndexableKeys(Context context) {
+                    ArrayList<String> result = new ArrayList<String>();
+                    if (!isDozeAvailable(context)) {
+                        result.add(KEY_DOZE);
+                        result.add(KEY_DOZE_LIST_MODE);
+                        result.add(KEY_DOZE_TIME_MODE);
+                        result.add(KEY_DOZE_OVERWRITE_VALUE);
+                        result.add(KEY_DOZE_PULSE_MODE);
+                        result.add(KEY_DOZE_PULSE_IN);
+                        result.add(KEY_DOZE_PULSE_VISIBLE);
+                        result.add(KEY_DOZE_PULSE_OUT);
+                        result.add(KEY_DOZE_SHAKE_THRESHOLD);
+                    } else {
+                       if (!isAccelerometerAvailable(context)) {
+                           result.add(KEY_DOZE_LIST_MODE);
+                           result.add(KEY_DOZE_SHAKE_THRESHOLD);
+                       }
+                       if (isAccelerometerAvailable(context)) {
+                           result.add(KEY_DOZE_PULSE_MODE);
+                       }
+                    }
+                    return result;
+                }
+            };
+}
diff --git a/src/org/omnirom/omnigears/sensor/ShakeSensorManager.java b/src/org/omnirom/omnigears/sensor/ShakeSensorManager.java
new file mode 100644
index 0000000..f35fbe0
--- /dev/null
+++ b/src/org/omnirom/omnigears/sensor/ShakeSensorManager.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2011 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 org.omnirom.omnigears.sensor;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+
+public class ShakeSensorManager {
+
+    public interface ShakeListener {
+        public void onShake();
+    }
+
+    private final ShakeSensorEventListener mShakeSensorListener;
+
+    private boolean mManagerEnabled;
+
+    private static float SENSITIVITY = 10;
+
+    private static class ShakeSensorEventListener implements SensorEventListener {
+        private static final int BUFFER = 5;
+        private float[] gravity = new float[3];
+        private float average = 0;
+        private int fill = 0;
+
+        private final SensorManager mSensorManager;
+        private final Sensor mShakeSensor;
+        private final ShakeListener mListener;
+
+        public ShakeSensorEventListener(SensorManager sensorManager, Sensor shakeSensor,
+                ShakeListener listener) {
+            mSensorManager = sensorManager;
+            mShakeSensor = shakeSensor;
+            mListener = listener;
+        }
+
+        @Override
+        public void onSensorChanged(SensorEvent event) {
+            final float alpha = 0.8F;
+            for (int i = 0; i < 3; i++) {
+                 gravity[i] = alpha * gravity[i] + (1 - alpha) * event.values[i];
+            }
+            float x = event.values[0] - gravity[0];
+            float y = event.values[1] - gravity[1];
+            float z = event.values[2] - gravity[2];
+
+            if (fill <= BUFFER) {
+                average += Math.abs(x) + Math.abs(y) + Math.abs(z);
+                fill++;
+            } else {
+                if (average / BUFFER >= SENSITIVITY) {
+                    mListener.onShake();
+                }
+                average = 0;
+                fill = 0;
+            }
+        }
+
+        @Override
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        }
+
+        public synchronized void unregisterWhenIdle() {
+            unregisterWithoutNotification();
+        }
+
+        public synchronized void register() {
+            mSensorManager.registerListener(this, mShakeSensor,
+                   SensorManager.SENSOR_DELAY_GAME,
+                   50 * 1000);
+        }
+
+        public void unregister() {
+            synchronized (this) {
+                unregisterWithoutNotification();
+            }
+        }
+
+        private void unregisterWithoutNotification() {
+            mSensorManager.unregisterListener(this);
+        }
+    }
+
+    public ShakeSensorManager(Context context, ShakeListener listener) {
+        SensorManager sensorManager =
+                (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+        Sensor shakeSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+        if (shakeSensor == null) {
+            mShakeSensorListener = null;
+        } else {
+            mShakeSensorListener =
+                    new ShakeSensorEventListener(sensorManager, shakeSensor, listener);
+        }
+    }
+
+    public void enable(float threshold) {
+        SENSITIVITY = threshold;
+        if (mShakeSensorListener != null && !mManagerEnabled) {
+            mShakeSensorListener.register();
+            mManagerEnabled = true;
+        }
+    }
+
+    public void disable() {
+        if (mShakeSensorListener != null && mManagerEnabled) {
+            mShakeSensorListener.unregisterWhenIdle();
+            mManagerEnabled = false;
+        }
+    }
+}