cmparts: Add button settings

 * Porting from CM13

Change-Id: I0ba91fc3c0a16a49646f269f9cbfd134a2212103
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9b49025..01e9548 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -35,6 +35,8 @@
 
     <uses-permission android:name="cyanogenmod.permission.BIND_CORE_SERVICE" />
 
+    <protected-broadcast android:name="cyanogenmod.platform.app.profiles.PROFILES_STATE_CHANGED" />
+
     <application android:label="@string/cmparts_title"
             android:theme="@style/Theme.Settings"
             android:hardwareAccelerated="true"
@@ -42,7 +44,7 @@
             android:defaultToDeviceProtectedStorage="true"
             android:directBootAware="true">
 
-        <activity android:name=".PartsActivity">
+        <activity android:name="PartsActivity">
             <intent-filter>
                 <action android:name="org.cyanogenmod.cmparts.PART" />
                 <category android:name="android.intent.category.DEFAULT" />
@@ -59,12 +61,14 @@
         </service>
 
 
-        <!-- Privacy settings header -->
+        <!-- Privacy settings (dashboard) -->
         <activity
-            android:name=".PrivacySettings"
+            android:name="PrivacySettings"
             android:label="@string/privacy_settings_title">
             <intent-filter android:priority="1">
                 <action android:name="com.android.settings.action.EXTRA_SETTINGS" />
+                <action android:name="org.cyanogenmod.cmparts.PRIVACY_SETTINGS" />
+                <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
             <meta-data
                 android:name="com.android.settings.category"
@@ -74,5 +78,23 @@
                 android:resource="@drawable/ic_settings_privacy" />
         </activity>
 
+        <!-- Button settings (dashboard) -->
+        <activity-alias
+            android:name=".input.ButtonSettings"
+            android:label="@string/button_pref_title"
+            android:targetActivity="PartsActivity">
+            <intent-filter android:priority="1">
+                <action android:name="com.android.settings.action.EXTRA_SETTINGS" />
+                <action android:name="org.cyanogenmod.cmparts.BUTTON_SETTINGS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <meta-data
+                android:name="com.android.settings.category"
+                android:value="com.android.settings.category.device" />
+            <meta-data
+                android:name="com.android.settings.icon"
+                android:resource="@drawable/ic_settings_buttons" />
+        </activity-alias>
+
     </application>
 </manifest>
diff --git a/proguard.flags b/proguard.flags
index 4d0b1d0..33b0dc4 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -5,6 +5,7 @@
 -keep class org.cyanogenmod.cmparts.notificationlight.*
 -keep class org.cyanogenmod.cmparts.livedisplay.*
 -keep class org.cyanogenmod.cmparts.privacyguard.*
+-keep class org.cyanogenmod.cmparts.input.*
 
 # Keep click responders
 -keepclassmembers class com.android.settings.inputmethod.UserDictionaryAddWordActivity {
diff --git a/res/drawable/ic_navbar_edit.xml b/res/drawable/ic_navbar_edit.xml
new file mode 100644
index 0000000..8570412
--- /dev/null
+++ b/res/drawable/ic_navbar_edit.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="@color/navbar_edit_icon_color"
+        android:pathData="M3,17.25 L3,21 L6.75,21 L17.81,9.94 L14.06,6.19 L3,17.25 Z M20.71,7.04
+C21.1,6.65,21.1,6.02,20.71,5.63 L18.37,3.29 C17.98,2.9,17.35,2.9,16.96,3.29
+L15.13,5.12 L18.88,8.87 L20.71,7.04 Z" />
+    <path
+        android:pathData="M0,0 L24,0 L24,24 L0,24 Z" />
+</vector>
diff --git a/res/drawable/ic_navbar_restore.xml b/res/drawable/ic_navbar_restore.xml
new file mode 100644
index 0000000..683c1f6
--- /dev/null
+++ b/res/drawable/ic_navbar_restore.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:pathData="M0,0 L24,0 L24,24 L0,24 Z" />
+    <path
+        android:fillColor="@color/navbar_edit_icon_color"
+        android:pathData="M14,12 C14,10.9,13.1,10,12,10 S10,10.9,10,12 S10.9,14,12,14 S14,13.1,14,12 Z
+M12,3 C7.03,3,3,7.03,3,12 L0,12 L4,16 L8,12 L5,12 C5,8.13,8.13,5,12,5
+S19,8.13,19,12 S15.87,19,12,19 C10.49,19,9.09,18.51,7.94,17.7 L6.52,19.14
+C8.04,20.3,9.94,21,12,21 C16.97,21,21,16.97,21,12 S16.97,3,12,3 Z" />
+</vector>
diff --git a/res/drawable/ic_navbar_save.xml b/res/drawable/ic_navbar_save.xml
new file mode 100644
index 0000000..035e4f8
--- /dev/null
+++ b/res/drawable/ic_navbar_save.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="@color/navbar_edit_icon_color"
+        android:pathData="M17,3 L5,3 C3.89,3,3,3.9,3,5 L3,19 C3,20.1,3.89,21,5,21 L19,21
+C20.1,21,21,20.1,21,19 L21,7 L17,3 Z M12,19 C10.34,19,9,17.66,9,16
+S10.34,13,12,13 S15,14.34,15,16 S13.66,19,12,19 Z M15,9 L5,9 L5,5 L15,5 L15,9 Z" />
+</vector>
diff --git a/res/drawable/ic_settings_buttons.xml b/res/drawable/ic_settings_buttons.xml
new file mode 100644
index 0000000..a063e02
--- /dev/null
+++ b/res/drawable/ic_settings_buttons.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="?android:attr/colorAccent"
+        android:pathData="M19,3H5C3.895,3,3,3.895,3,5v14c0,1.105,0.895,2,2,2h14c1.105,0,2-0.895,2-2V5C21,3.895,20.105,3,19,3Z M11.678,9.989H8.45v1.958h2.729v1.099H8.45V16H7.024V8.891h4.653V9.989z M17.059,16h-1.431v-3.232c0-0.377-0.065-0.646-0.198-0.803 c-0.131-0.158-0.329-0.237-0.593-0.237c-0.212,0-0.397,0.04-0.56,0.12c-0.16,0.079-0.293,0.189-0.397,0.329V16h-1.426v-5.283h1.328 l0.063,0.757c0.176-0.271,0.393-0.479,0.649-0.63s0.545-0.225,0.864-0.225c0.534,0,0.95,0.172,1.25,0.516 c0.3,0.343,0.449,0.886,0.449,1.628V16z" />
+</vector>
diff --git a/res/drawable/ripple_drawable.xml b/res/drawable/ripple_drawable.xml
new file mode 100644
index 0000000..5296121
--- /dev/null
+++ b/res/drawable/ripple_drawable.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:attr/colorControlHighlight" />
diff --git a/res/layout/button_backlight.xml b/res/layout/button_backlight.xml
new file mode 100644
index 0000000..8196755
--- /dev/null
+++ b/res/layout/button_backlight.xml
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2013 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+    <LinearLayout
+        android:id="@+id/button_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <LinearLayout
+            android:id="@+id/checkbox_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:padding="10dp">
+
+            <CheckBox
+                android:id="@+id/backlight_switch"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/button_backlight_enabled" />
+
+        </LinearLayout>
+
+        <RelativeLayout
+            android:id="@+id/seekbar_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingTop="10dp"
+            android:paddingBottom="10dp">
+
+            <TextView
+                android:id="@+id/text"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:paddingStart="10dip"
+                android:paddingEnd="10dip"
+                android:text="@string/button_backlight_seekbar_title" />
+            <TextView android:id="@+id/value"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentEnd="true"
+                android:paddingStart="10dip"
+                android:paddingEnd="10dip" />
+            <SeekBar android:id="@+id/seekbar"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@id/text"
+                android:paddingTop="2dip"
+                android:paddingStart="20dip"
+                android:paddingEnd="20dip" />
+
+        </RelativeLayout>
+
+    </LinearLayout>
+
+    <RelativeLayout
+        android:id="@+id/timeout_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center_horizontal"
+        android:paddingBottom="10dp">
+
+        <TextView android:id="@+id/timeout_text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/backlight_timeout_title"
+            android:paddingStart="10dip"
+            android:paddingEnd="10dip" />
+        <TextView android:id="@+id/timeout_value"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentEnd="true"
+            android:paddingStart="10dip"
+            android:paddingEnd="10dip" />
+        <org.cyanogenmod.cmparts.input.BacklightTimeoutSeekBar
+            android:id="@+id/timeout_seekbar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/timeout_text"
+            android:paddingTop="2dip"
+            android:paddingStart="20dip"
+            android:paddingEnd="20dip" />
+
+    </RelativeLayout>
+
+    <View
+        android:id="@+id/button_keyboard_divider"
+        android:layout_width="match_parent"
+        android:layout_height="2dip"
+        android:layout_marginStart="10dip"
+        android:layout_marginEnd="10dip"
+        android:layout_marginTop="5dip"
+        android:layout_marginBottom="5dip"
+        android:background="@android:drawable/divider_horizontal_dark" />
+
+    <LinearLayout
+        android:id="@+id/keyboard_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <LinearLayout
+            android:id="@+id/checkbox_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:padding="10dp">
+
+            <CheckBox
+                android:id="@+id/backlight_switch"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/keyboard_backlight_enabled" />
+
+        </LinearLayout>
+
+        <RelativeLayout
+            android:id="@+id/seekbar_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingTop="10dp"
+            android:paddingBottom="10dp">
+
+            <TextView
+                android:id="@+id/text"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:paddingStart="10dip"
+                android:paddingEnd="10dip"
+                android:text="@string/keyboard_backlight_seekbar_title" />
+            <TextView android:id="@+id/value"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentEnd="true"
+                android:paddingStart="10dip"
+                android:paddingEnd="10dip" />
+            <SeekBar android:id="@+id/seekbar"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@id/text"
+                android:paddingTop="2dip"
+                android:paddingStart="20dip"
+                android:paddingEnd="20dip" />
+
+        </RelativeLayout>
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 47dc7ab..2d33833 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -49,4 +49,53 @@
         <item>2500</item>
         <item>5000</item>
     </string-array>
+
+    <!-- Hardware key rebinding -->
+    <string-array name="hardware_keys_action_entries" translatable="false">
+        <item>@string/hardware_keys_action_nothing</item>
+        <item>@string/hardware_keys_action_menu</item>
+        <item>@string/hardware_keys_action_app_switch</item>
+        <item>@string/hardware_keys_action_search</item>
+        <item>@string/hardware_keys_action_voice_search</item>
+        <item>@string/hardware_keys_action_in_app_search</item>
+        <item>@string/hardware_keys_action_launch_camera</item>
+        <item>@string/hardware_keys_action_sleep</item>
+        <item>@string/hardware_keys_action_last_app</item>
+    </string-array>
+
+    <string-array name="hardware_keys_action_values" translatable="false">
+        <item>0</item>
+        <item>1</item>
+        <item>2</item>
+        <item>3</item>
+        <item>4</item>
+        <item>5</item>
+        <item>6</item>
+        <item>7</item>
+        <item>8</item>
+    </string-array>
+
+    <string-array name="power_menu_actions_array" translatable="false">
+        <item>power</item>
+        <item>reboot</item>
+        <item>screenshot</item>
+        <item>airplane</item>
+        <item>users</item>
+        <item>bugreport</item>
+        <item>silent</item>
+    </string-array>
+
+    <!-- Volume key cursor control -->
+    <string-array name="volbtn_cursor_control_entries" translatable="false">
+        <item>@string/volbtn_cursor_control_off</item>
+        <item>@string/volbtn_cursor_control_on</item>
+        <item>@string/volbtn_cursor_control_on_reverse</item>
+    </string-array>
+
+    <string-array name="volbtn_cursor_control_values" translatable="false">
+        <item>0</item>
+        <item>1</item>
+        <item>2</item>
+    </string-array>
+
 </resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 655ff74..455c35c 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -16,5 +16,6 @@
 -->
 <resources>
     <color name="theme_accent">#ff009688</color>
+    <color name="navbar_edit_icon_color">#607d8b</color>
 </resources>
 
diff --git a/res/values/plurals.xml b/res/values/plurals.xml
new file mode 100644
index 0000000..d9b3a31
--- /dev/null
+++ b/res/values/plurals.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2013-2014 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <plurals name="backlight_timeout_time">
+        <item quantity="one"><xliff:g id="seconds">%d</xliff:g> second</item>
+        <item quantity="other"><xliff:g id="seconds">%d</xliff:g> seconds</item>
+    </plurals>
+
+    <plurals name="app_ops_count">
+        <item quantity="one">once</item>
+        <item quantity="other">%d times</item>
+    </plurals>
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0158414..738a3d3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -132,7 +132,6 @@
     <string name="color_red_title">Red</string>
     <string name="color_green_title">Green</string>
     <string name="color_blue_title">Blue</string>
-    
 
     <!-- LiveDisplay : Picture Adjustment -->
     <string name="picture_adjustment_title">Picture adjustment</string>
@@ -142,6 +141,93 @@
     <string name="adj_intensity_title">Intensity</string>
     <string name="adj_contrast_title">Contrast</string>
 
+    <!-- Button settings -->
+    <string name="button_pref_title">Buttons</string>
+    <string name="hardware_keys_power_key_title">Power button</string>
+    <string name="hardware_keys_home_key_title">Home button</string>
+    <string name="hardware_keys_back_key_title">Back button</string>
+    <string name="hardware_keys_menu_key_title">Menu button</string>
+    <string name="hardware_keys_assist_key_title">Search button</string>
+    <string name="hardware_keys_appswitch_key_title">Recents button</string>
+    <string name="hardware_keys_camera_key_title">Camera button</string>
+    <string name="hardware_keys_volume_keys_title">Volume buttons</string>
+    <string name="hardware_keys_short_press_title">Short press action</string>
+    <string name="hardware_keys_long_press_title">Long press action</string>
+    <string name="hardware_keys_double_tap_title">Double tap action</string>
+    <string name="hardware_keys_action_nothing">No action</string>
+    <string name="hardware_keys_action_menu">Open/close menu</string>
+    <string name="hardware_keys_action_app_switch">Recent apps switcher</string>
+    <string name="hardware_keys_action_search">Search assistant</string>
+    <string name="hardware_keys_action_voice_search">Voice search</string>
+    <string name="hardware_keys_action_in_app_search">In-app search</string>
+    <string name="hardware_keys_action_launch_camera">Launch camera</string>
+    <string name="hardware_keys_action_sleep">Turn screen off</string>
+    <string name="hardware_keys_action_last_app">Last app</string>
+    <string name="camera_sleep_on_release_title">Screen peek</string>
+    <string name="camera_sleep_on_release_summary">A half press will keep the screen on only while the button is held down</string>
+    <string name="camera_launch_title">Launch camera</string>
+    <string name="camera_launch_summary">A longpress and release will launch camera</string>
+    <string name="volbtn_music_controls_title">Playback control</string>
+    <string name="volbtn_music_controls_summary">When the screen is off, long pressing the volume keys will seek music tracks</string>
+    <string name="volbtn_cursor_control_title">Keyboard cursor control</string>
+    <string name="volbtn_cursor_control_off">Disabled</string>
+    <string name="volbtn_cursor_control_on">Volume up/down moves cursor left/right</string>
+    <string name="volbtn_cursor_control_on_reverse">Volume up/down moves cursor right/left</string>
+    <string name="power_end_call_title">End call</string>
+    <string name="power_end_call_summary">Pressing the power button will end the current call</string>
+    <string name="swap_volume_buttons_title">Reorient</string>
+    <string name="swap_volume_buttons_summary">Swap volume buttons when the screen is rotated</string>
+    <string name="button_wake_title">Wake up device</string>
+    <string name="home_answer_call_title">Answer call</string>
+    <string name="home_answer_call_summary">Pressing the home button will answer the current incoming call</string>
+
+    <!-- Key backlight -->
+    <string name="button_backlight_title">Backlight</string>
+    <string name="button_backlight_enabled">Illuminate buttons</string>
+    <string name="keyboard_backlight_enabled">Illuminate keyboard</string>
+    <string name="button_backlight_seekbar_title">Button brightness</string>
+    <string name="keyboard_backlight_seekbar_title">Keyboard brightness</string>
+    <string name="backlight_timeout_title">Illumination timeout</string>
+    <string name="backlight_timeout_unlimited">Don\'t turn off</string>
+    <string name="backlight_summary_disabled">Disabled</string>
+    <string name="backlight_summary_enabled_with_timeout">Enabled for <xliff:g id="timeout">%s</xliff:g></string>
+    <string name="backlight_summary_enabled">Enabled</string>
+
+    <!-- Buttons - Enable navbar -->
+    <string name="disable_navkeys_title">Enable on-screen nav bar</string>
+    <string name="disable_navkeys_summary">Enable on-screen navigation bar and disable hardware buttons</string>
+
+    <!-- Navigation Bar -->
+    <string name="navigation_bar_category">Navigation bar</string>
+    <string name="navigation_bar_title">Buttons and layout</string>
+    <string name="navigation_bar_left_title">Left-handed mode</string>
+    <string name="navigation_bar_left_summary">Place the navigation bar on the left side of the screen in landscape mode</string>
+    <string name="navigation_bar_arrow_keys_title">Show arrow keys while typing</string>
+    <string name="navigation_bar_arrow_keys_summary">Display left and right cursor buttons while typing. Overrides IME switcher.</string>
+    <string name="navigation_bar_recents_title">Recents long press action</string>
+
+    <!-- Power menu -->
+    <string name="power_menu_title">Power menu</string>
+    <string name="power_menu_reboot_title">Reboot menu</string>
+    <string name="power_menu_screenshot_title">Screenshot</string>
+    <string name="power_menu_airplane_title">Airplane mode</string>
+    <string name="power_menu_users_title">User switcher</string>
+    <string name="power_menu_settings_title">Settings shortcut</string>
+    <string name="power_menu_lockdown_title">Device lockdown</string>
+    <string name="power_menu_bug_report_title">Bug report</string>
+    <string name="power_menu_sound_title">Sound panel</string>
+    <string name="power_menu_bug_report_disabled">Bug reporting is disabled in development settings</string>
+
+    <string name="volume_keys_control_ring_stream_title">Control ringtone volume</string>
+    <string name="volume_keys_control_ring_stream_summary_on">Volume keys control ringtone volume</string>
+    <string name="volume_keys_control_ring_stream_summary_off">Volume keys control media volume</string>
+
+
+    <string name="camera_double_tap_power_gesture_title">Press power button twice for camera</string>
+
+    <!-- Description of setting that controls gesture to open camera by double tapping the power button [CHAR LIMIT=NONE] -->
+    <string name="camera_double_tap_power_gesture_desc">Quickly open camera without unlocking your screen</string>
+
 </resources>
 
 
diff --git a/res/xml/button_settings.xml b/res/xml/button_settings.xml
new file mode 100644
index 0000000..c58650e
--- /dev/null
+++ b/res/xml/button_settings.xml
@@ -0,0 +1,268 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <SwitchPreference
+        android:key="disable_nav_keys"
+        android:title="@string/disable_navkeys_title"
+        android:summary="@string/disable_navkeys_summary"
+        android:defaultValue="false" />
+
+    <org.cyanogenmod.cmparts.input.ButtonBacklightBrightness
+        android:key="button_backlight"
+        android:title="@string/button_backlight_title"
+        android:dialogTitle="@string/button_backlight_title"
+        android:persistent="false" />
+
+    <PreferenceCategory
+        android:key="navigation_bar_category"
+        android:title="@string/navigation_bar_category" >
+
+        <cyanogenmod.preference.CMSystemSettingSwitchPreference
+            android:key="navigation_bar_left"
+            android:title="@string/navigation_bar_left_title"
+            android:summary="@string/navigation_bar_left_summary"
+            android:defaultValue="false" />
+
+        <cyanogenmod.preference.CMSystemSettingSwitchPreference
+                android:key="navigation_bar_menu_arrow_keys"
+                android:title="@string/navigation_bar_arrow_keys_title"
+                android:summary="@string/navigation_bar_arrow_keys_summary"
+                android:defaultValue="false" />
+
+        <PreferenceScreen
+            android:key="navigation_bar"
+            android:title="@string/navigation_bar_title">
+            <intent android:action="com.android.settings.action.NAV_BAR_TUNER" />
+        </PreferenceScreen>
+
+        <ListPreference
+            android:key="navigation_recents_long_press"
+            android:dialogTitle="@string/navigation_bar_recents_title"
+            android:title="@string/navigation_bar_recents_title"
+            android:persistent="false" />
+
+    </PreferenceCategory>
+
+    <PreferenceCategory
+        android:key="power_key"
+        android:title="@string/hardware_keys_power_key_title" >
+
+        <PreferenceScreen
+            android:key="power_menu"
+            android:title="@string/power_menu_title"
+            android:fragment="org.cyanogenmod.cmparts.input.PowerMenuActions" />
+
+        <SwitchPreference
+            android:key="power_end_call"
+            android:title="@string/power_end_call_title"
+            android:summary="@string/power_end_call_summary"
+            android:persistent="false"/>
+
+        <SwitchPreference
+            android:key="camera_double_tap_power_gesture"
+            android:title="@string/camera_double_tap_power_gesture_title"
+            android:summary="@string/camera_double_tap_power_gesture_desc"
+            android:persistent="false" />
+
+    </PreferenceCategory>
+
+    <PreferenceCategory
+        android:key="home_key"
+        android:title="@string/hardware_keys_home_key_title" >
+
+        <cyanogenmod.preference.CMSystemSettingSwitchPreference
+            android:key="home_wake_screen"
+            android:title="@string/button_wake_title"
+            android:defaultValue="true" />
+
+        <SwitchPreference
+            android:key="home_answer_call"
+            android:title="@string/home_answer_call_title"
+            android:summary="@string/home_answer_call_summary"
+            android:persistent="false"/>
+
+        <ListPreference
+            android:key="hardware_keys_home_long_press"
+            android:dialogTitle="@string/hardware_keys_long_press_title"
+            android:title="@string/hardware_keys_long_press_title"
+            android:entries="@array/hardware_keys_action_entries"
+            android:entryValues="@array/hardware_keys_action_values"
+            android:persistent="false" />
+
+        <ListPreference
+            android:key="hardware_keys_home_double_tap"
+            android:dialogTitle="@string/hardware_keys_double_tap_title"
+            android:title="@string/hardware_keys_double_tap_title"
+            android:entries="@array/hardware_keys_action_entries"
+            android:entryValues="@array/hardware_keys_action_values"
+            android:persistent="false" />
+
+    </PreferenceCategory>
+
+    <PreferenceCategory
+        android:key="back_key"
+        android:title="@string/hardware_keys_back_key_title" >
+
+        <cyanogenmod.preference.CMSystemSettingSwitchPreference
+            android:key="back_wake_screen"
+            android:title="@string/button_wake_title"
+            android:defaultValue="false" />
+
+    </PreferenceCategory>
+
+    <PreferenceCategory
+        android:key="menu_key"
+        android:title="@string/hardware_keys_menu_key_title" >
+
+        <cyanogenmod.preference.CMSystemSettingSwitchPreference
+            android:key="menu_wake_screen"
+            android:title="@string/button_wake_title"
+            android:defaultValue="false" />
+
+        <ListPreference
+            android:key="hardware_keys_menu_press"
+            android:dialogTitle="@string/hardware_keys_short_press_title"
+            android:title="@string/hardware_keys_short_press_title"
+            android:entries="@array/hardware_keys_action_entries"
+            android:entryValues="@array/hardware_keys_action_values"
+            android:persistent="false" />
+
+        <ListPreference
+            android:key="hardware_keys_menu_long_press"
+            android:dialogTitle="@string/hardware_keys_long_press_title"
+            android:title="@string/hardware_keys_long_press_title"
+            android:entries="@array/hardware_keys_action_entries"
+            android:entryValues="@array/hardware_keys_action_values"
+            android:persistent="false" />
+
+    </PreferenceCategory>
+
+    <PreferenceCategory
+        android:key="assist_key"
+        android:title="@string/hardware_keys_assist_key_title" >
+
+        <cyanogenmod.preference.CMSystemSettingSwitchPreference
+            android:key="assist_wake_screen"
+            android:title="@string/button_wake_title"
+            android:defaultValue="false" />
+
+        <ListPreference
+            android:key="hardware_keys_assist_press"
+            android:dialogTitle="@string/hardware_keys_short_press_title"
+            android:title="@string/hardware_keys_short_press_title"
+            android:entries="@array/hardware_keys_action_entries"
+            android:entryValues="@array/hardware_keys_action_values"
+            android:persistent="false" />
+
+        <ListPreference
+            android:key="hardware_keys_assist_long_press"
+            android:dialogTitle="@string/hardware_keys_long_press_title"
+            android:title="@string/hardware_keys_long_press_title"
+            android:entries="@array/hardware_keys_action_entries"
+            android:entryValues="@array/hardware_keys_action_values"
+            android:persistent="false" />
+
+    </PreferenceCategory>
+
+    <PreferenceCategory
+        android:key="app_switch_key"
+        android:title="@string/hardware_keys_appswitch_key_title" >
+
+        <cyanogenmod.preference.CMSystemSettingSwitchPreference
+            android:key="app_switch_wake_screen"
+            android:title="@string/button_wake_title"
+            android:defaultValue="false" />
+
+        <ListPreference
+            android:key="hardware_keys_app_switch_press"
+            android:dialogTitle="@string/hardware_keys_short_press_title"
+            android:title="@string/hardware_keys_short_press_title"
+            android:entries="@array/hardware_keys_action_entries"
+            android:entryValues="@array/hardware_keys_action_values"
+            android:persistent="false" />
+
+        <ListPreference
+            android:key="hardware_keys_app_switch_long_press"
+            android:dialogTitle="@string/hardware_keys_long_press_title"
+            android:title="@string/hardware_keys_long_press_title"
+            android:entries="@array/hardware_keys_action_entries"
+            android:entryValues="@array/hardware_keys_action_values"
+            android:persistent="false" />
+
+    </PreferenceCategory>
+
+    <PreferenceCategory
+        android:key="camera_key"
+        android:title="@string/hardware_keys_camera_key_title">
+
+        <cyanogenmod.preference.CMSystemSettingSwitchPreference
+            android:key="camera_wake_screen"
+            android:title="@string/button_wake_title"
+            android:defaultValue="false" />
+
+        <cyanogenmod.preference.CMSystemSettingSwitchPreference
+            android:key="camera_sleep_on_release"
+            android:title="@string/camera_sleep_on_release_title"
+            android:summary="@string/camera_sleep_on_release_summary"
+            android:defaultValue="false" />
+
+        <cyanogenmod.preference.CMSystemSettingSwitchPreference
+            android:key="camera_launch"
+            android:title="@string/camera_launch_title"
+            android:summary="@string/camera_launch_summary"
+            android:defaultValue="false" />
+
+    </PreferenceCategory>
+
+    <PreferenceCategory
+        android:key="volume_keys"
+        android:title="@string/hardware_keys_volume_keys_title" >
+
+        <cyanogenmod.preference.CMSystemSettingSwitchPreference
+            android:key="volume_wake_screen"
+            android:title="@string/button_wake_title"
+            android:defaultValue="false" />
+
+        <cyanogenmod.preference.CMSystemSettingSwitchPreference
+            android:key="volbtn_music_controls"
+            android:title="@string/volbtn_music_controls_title"
+            android:summary="@string/volbtn_music_controls_summary"
+            android:defaultValue="true" />
+
+        <cyanogenmod.preference.CMSystemSettingSwitchPreference
+            android:key="volume_keys_control_ring_stream"
+            android:title="@string/volume_keys_control_ring_stream_title"
+            android:summaryOn="@string/volume_keys_control_ring_stream_summary_on"
+            android:summaryOff="@string/volume_keys_control_ring_stream_summary_off"
+            android:defaultValue="true" />
+
+        <ListPreference
+            android:key="volume_key_cursor_control"
+            android:dialogTitle="@string/volbtn_cursor_control_title"
+            android:title="@string/volbtn_cursor_control_title"
+            android:entries="@array/volbtn_cursor_control_entries"
+            android:entryValues="@array/volbtn_cursor_control_values"
+            android:persistent="false" />
+
+        <SwitchPreference
+            android:key="swap_volume_buttons"
+            android:title="@string/swap_volume_buttons_title"
+            android:summary="@string/swap_volume_buttons_summary" />
+    </PreferenceCategory>
+
+</PreferenceScreen>
diff --git a/res/xml/parts_catalog.xml b/res/xml/parts_catalog.xml
index 4eee180..51f9ce4 100644
--- a/res/xml/parts_catalog.xml
+++ b/res/xml/parts_catalog.xml
@@ -30,4 +30,8 @@
           android:summary="@string/live_display_summary"
           android:fragment="org.cyanogenmod.cmparts.livedisplay.LiveDisplay" />
 
+    <part cm:key="button_settings"
+          cm:title="@string/button_pref_title"
+          cm:fragment="org.cyanogenmod.cmparts.input.ButtonSettings" />
+
 </parts-catalog>
diff --git a/res/xml/power_menu_settings.xml b/res/xml/power_menu_settings.xml
new file mode 100644
index 0000000..36e1d65
--- /dev/null
+++ b/res/xml/power_menu_settings.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014-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.
+-->
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:title="@string/power_menu_title">
+
+    <CheckBoxPreference
+        android:key="reboot"
+        android:title="@string/power_menu_reboot_title"
+        android:defaultValue="true" />
+
+    <CheckBoxPreference
+        android:key="screenshot"
+        android:title="@string/power_menu_screenshot_title"
+        android:defaultValue="false" />
+
+    <CheckBoxPreference
+        android:key="airplane"
+        android:title="@string/power_menu_airplane_title"
+        android:defaultValue="true" />
+
+    <CheckBoxPreference
+        android:key="users"
+        android:title="@string/power_menu_users_title"
+        android:defaultValue="false" />
+
+    <CheckBoxPreference
+        android:key="settings"
+        android:title="@string/power_menu_settings_title"
+        android:defaultValue="false" />
+
+    <CheckBoxPreference
+        android:key="lockdown"
+        android:title="@string/power_menu_lockdown_title"
+        android:defaultValue="false" />
+
+    <CheckBoxPreference
+        android:key="bugreport"
+        android:title="@string/power_menu_bug_report_title"
+        android:defaultValue="false" />
+
+    <CheckBoxPreference
+        android:key="silent"
+        android:title="@string/power_menu_sound_title"
+        android:defaultValue="true" />
+
+    <CheckBoxPreference
+        android:key="voiceassist"
+        android:title="@string/power_menu_sound_title"
+        android:defaultValue="true" />
+
+    <CheckBoxPreference
+        android:key="assist"
+        android:title="@string/power_menu_sound_title"
+        android:defaultValue="true" />
+
+</PreferenceScreen>
diff --git a/src/org/cyanogenmod/cmparts/PartsActivity.java b/src/org/cyanogenmod/cmparts/PartsActivity.java
index a51cb41..d3b5167 100644
--- a/src/org/cyanogenmod/cmparts/PartsActivity.java
+++ b/src/org/cyanogenmod/cmparts/PartsActivity.java
@@ -48,7 +48,7 @@
         connectCatalog();
 
         Log.d(TAG, "Launched with: " + getIntent().toString() + " action: " +
-                getIntent().getAction() + " component: " + getIntent().getComponent().flattenToString() +
+                getIntent().getAction() + " component: " + getIntent().getComponent().getClassName() +
                 " extras: " + getIntent().getExtras().toString());
 
         PartInfo info = null;
diff --git a/src/org/cyanogenmod/cmparts/Utils.java b/src/org/cyanogenmod/cmparts/Utils.java
new file mode 100644
index 0000000..12f2b61
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/Utils.java
@@ -0,0 +1,95 @@
+/*
+ * 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.cmparts;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.telephony.TelephonyManager;
+import android.view.Surface;
+
+import org.cyanogenmod.cmparts.input.ButtonSettings;
+
+public class Utils {
+
+    /**
+     * Returns whether the device is voice-capable (meaning, it is also a phone).
+     */
+    public static boolean isVoiceCapable(Context context) {
+        TelephonyManager telephony =
+                (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+        return telephony != null && telephony.isVoiceCapable();
+    }
+
+    /* returns whether the device has volume rocker or not. */
+    public static boolean hasVolumeRocker(Context context) {
+        final int deviceKeys = context.getResources().getInteger(
+                com.android.internal.R.integer.config_deviceHardwareKeys);
+        return (deviceKeys & ButtonSettings.KEY_MASK_VOLUME) != 0;
+    }
+
+    public static boolean isPackageInstalled(Context context, String pkg, boolean ignoreState) {
+        if (pkg != null) {
+            try {
+                PackageInfo pi = context.getPackageManager().getPackageInfo(pkg, 0);
+                if (!pi.applicationInfo.enabled && !ignoreState) {
+                    return false;
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Locks the activity orientation to the current device orientation
+     * @param activity
+     */
+    public static void lockCurrentOrientation(Activity activity) {
+        int currentRotation = activity.getWindowManager().getDefaultDisplay().getRotation();
+        int orientation = activity.getResources().getConfiguration().orientation;
+        int frozenRotation = 0;
+        switch (currentRotation) {
+            case Surface.ROTATION_0:
+                frozenRotation = orientation == Configuration.ORIENTATION_LANDSCAPE
+                        ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+                        : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+                break;
+            case Surface.ROTATION_90:
+                frozenRotation = orientation == Configuration.ORIENTATION_PORTRAIT
+                        ? ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT
+                        : ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+                break;
+            case Surface.ROTATION_180:
+                frozenRotation = orientation == Configuration.ORIENTATION_LANDSCAPE
+                        ? ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
+                        : ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
+                break;
+            case Surface.ROTATION_270:
+                frozenRotation = orientation == Configuration.ORIENTATION_PORTRAIT
+                        ? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
+                        : ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+                break;
+        }
+        activity.setRequestedOrientation(frozenRotation);
+    }
+
+}
diff --git a/src/org/cyanogenmod/cmparts/input/BacklightTimeoutSeekBar.java b/src/org/cyanogenmod/cmparts/input/BacklightTimeoutSeekBar.java
new file mode 100644
index 0000000..4ffe17d
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/input/BacklightTimeoutSeekBar.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2013 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.cmparts.input;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.SeekBar;
+
+public class BacklightTimeoutSeekBar extends SeekBar {
+    private int mMax;
+    private int mGap;
+    private boolean mUpdatingThumb;
+
+    public BacklightTimeoutSeekBar(Context context) {
+        super(context);
+    }
+
+    public BacklightTimeoutSeekBar(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public BacklightTimeoutSeekBar(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        mUpdatingThumb = true;
+        super.onSizeChanged(w, h, oldw, oldh);
+        mUpdatingThumb = false;
+    }
+
+    @Override
+    public void setThumb(Drawable thumb) {
+        mUpdatingThumb = true;
+        super.setThumb(thumb);
+        mUpdatingThumb = false;
+    }
+
+    @Override
+    public void setMax(int max) {
+        mMax = max;
+        mGap = max / 10;
+        super.setMax(max + 2 * mGap - 1);
+    }
+
+    @Override
+    protected int updateTouchProgress(int lastProgress, int newProgress) {
+        if (newProgress < mMax) {
+            return newProgress;
+        }
+        if (newProgress < mMax + mGap) {
+            return mMax - 1;
+        }
+        return getMax();
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/input/ButtonBacklightBrightness.java b/src/org/cyanogenmod/cmparts/input/ButtonBacklightBrightness.java
new file mode 100644
index 0000000..ddc82ac
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/input/ButtonBacklightBrightness.java
@@ -0,0 +1,455 @@
+/*
+ * Copyright (C) 2013 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.cmparts.input;
+
+import android.app.AlertDialog;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.v7.preference.PreferenceManager;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager.LayoutParams;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import org.cyanogenmod.cmparts.CustomDialogPreference;
+import org.cyanogenmod.cmparts.R;
+
+import cyanogenmod.providers.CMSettings;
+
+public class ButtonBacklightBrightness extends CustomDialogPreference<AlertDialog> implements
+        SeekBar.OnSeekBarChangeListener {
+    private static final int DEFAULT_BUTTON_TIMEOUT = 5;
+
+    public static final String KEY_BUTTON_BACKLIGHT = "pre_navbar_button_backlight";
+
+    private Window mWindow;
+
+    private BrightnessControl mButtonBrightness;
+    private BrightnessControl mKeyboardBrightness;
+    private BrightnessControl mActiveControl;
+
+    private ViewGroup mTimeoutContainer;
+    private SeekBar mTimeoutBar;
+    private TextView mTimeoutValue;
+
+    private ContentResolver mResolver;
+
+    public ButtonBacklightBrightness(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        mResolver = context.getContentResolver();
+
+        setDialogLayoutResource(R.layout.button_backlight);
+
+        if (isKeyboardSupported()) {
+            mKeyboardBrightness = new BrightnessControl(
+                    CMSettings.Secure.KEYBOARD_BRIGHTNESS, false);
+            mActiveControl = mKeyboardBrightness;
+        }
+        if (isButtonSupported()) {
+            boolean isSingleValue = !context.getResources().getBoolean(
+                    com.android.internal.R.bool.config_deviceHasVariableButtonBrightness);
+
+            int defaultBrightness = context.getResources().getInteger(
+                    com.android.internal.R.integer.config_buttonBrightnessSettingDefault);
+
+            mButtonBrightness = new BrightnessControl(
+                    CMSettings.Secure.BUTTON_BRIGHTNESS, isSingleValue, defaultBrightness);
+            mActiveControl = mButtonBrightness;
+        }
+
+        updateSummary();
+    }
+
+    @Override
+    protected void onClick(AlertDialog d, int which) {
+        super.onClick(d, which);
+
+        if (getDialog() != null) {
+            mWindow = getDialog().getWindow();
+        }
+        updateBrightnessPreview();
+    }
+
+    @Override
+    protected void onPrepareDialogBuilder(AlertDialog.Builder builder, DialogInterface.OnClickListener listener) {
+        super.onPrepareDialogBuilder(builder, listener);
+        builder.setNeutralButton(R.string.reset,
+                new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+            }
+        });
+    }
+
+    @Override
+    protected boolean onDismissDialog(AlertDialog dialog, int which) {
+        if (which == DialogInterface.BUTTON_NEUTRAL) {
+            mTimeoutBar.setProgress(DEFAULT_BUTTON_TIMEOUT);
+            if (mButtonBrightness != null) {
+                mButtonBrightness.reset();
+            }
+            if (mKeyboardBrightness != null) {
+                mKeyboardBrightness.reset();
+            }
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    protected void onBindDialogView(View view) {
+        super.onBindDialogView(view);
+
+        mTimeoutContainer = (ViewGroup) view.findViewById(R.id.timeout_container);
+        mTimeoutBar = (SeekBar) view.findViewById(R.id.timeout_seekbar);
+        mTimeoutValue = (TextView) view.findViewById(R.id.timeout_value);
+        mTimeoutBar.setMax(30);
+        mTimeoutBar.setOnSeekBarChangeListener(this);
+        mTimeoutBar.setProgress(getTimeout());
+        handleTimeoutUpdate(mTimeoutBar.getProgress());
+
+        ViewGroup buttonContainer = (ViewGroup) view.findViewById(R.id.button_container);
+        if (mButtonBrightness != null) {
+            mButtonBrightness.init(buttonContainer);
+        } else {
+            buttonContainer.setVisibility(View.GONE);
+            mTimeoutContainer.setVisibility(View.GONE);
+        }
+
+        ViewGroup keyboardContainer = (ViewGroup) view.findViewById(R.id.keyboard_container);
+        if (mKeyboardBrightness != null) {
+            mKeyboardBrightness.init(keyboardContainer);
+        } else {
+            keyboardContainer.setVisibility(View.GONE);
+        }
+
+        if (mButtonBrightness == null || mKeyboardBrightness == null) {
+            view.findViewById(R.id.button_keyboard_divider).setVisibility(View.GONE);
+        }
+    }
+
+    @Override
+    protected void onDialogClosed(boolean positiveResult) {
+        super.onDialogClosed(positiveResult);
+
+        if (!positiveResult) {
+            return;
+        }
+
+        if (mButtonBrightness != null) {
+            PreferenceManager.getDefaultSharedPreferences(getContext())
+                    .edit()
+                    .putInt(KEY_BUTTON_BACKLIGHT, mButtonBrightness.getBrightness(false))
+                    .apply();
+        }
+
+        applyTimeout(mTimeoutBar.getProgress());
+        if (mButtonBrightness != null) {
+            mButtonBrightness.applyBrightness();
+        }
+        if (mKeyboardBrightness != null) {
+            mKeyboardBrightness.applyBrightness();
+        }
+
+        updateSummary();
+    }
+
+    @Override
+    protected Parcelable onSaveInstanceState() {
+        final Parcelable superState = super.onSaveInstanceState();
+        if (getDialog() == null || !getDialog().isShowing()) {
+            return superState;
+        }
+
+        // Save the dialog state
+        final SavedState myState = new SavedState(superState);
+        myState.timeout = mTimeoutBar.getProgress();
+        if (mButtonBrightness != null) {
+            myState.button = mButtonBrightness.getBrightness(false);
+        }
+        if (mKeyboardBrightness != null) {
+            myState.keyboard = mKeyboardBrightness.getBrightness(false);
+        }
+
+        return myState;
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Parcelable state) {
+        if (state == null || !state.getClass().equals(SavedState.class)) {
+            // Didn't save state for us in onSaveInstanceState
+            super.onRestoreInstanceState(state);
+            return;
+        }
+
+        SavedState myState = (SavedState) state;
+        super.onRestoreInstanceState(myState.getSuperState());
+
+        mTimeoutBar.setProgress(myState.timeout);
+        if (mButtonBrightness != null) {
+            mButtonBrightness.setBrightness(myState.button);
+        }
+        if (mKeyboardBrightness != null) {
+            mKeyboardBrightness.setBrightness(myState.keyboard);
+        }
+    }
+
+    public boolean isButtonSupported() {
+        final Resources res = getContext().getResources();
+        final int deviceKeys = res.getInteger(
+                com.android.internal.R.integer.config_deviceHardwareKeys);
+        // All hardware keys besides volume and camera can possibly have a backlight
+        boolean hasBacklightKey = (deviceKeys & ButtonSettings.KEY_MASK_HOME) != 0
+                || (deviceKeys & ButtonSettings.KEY_MASK_BACK) != 0
+                || (deviceKeys & ButtonSettings.KEY_MASK_MENU) != 0
+                || (deviceKeys & ButtonSettings.KEY_MASK_ASSIST) != 0
+                || (deviceKeys & ButtonSettings.KEY_MASK_APP_SWITCH) != 0;
+        boolean hasBacklight = res.getInteger(
+                com.android.internal.R.integer.config_buttonBrightnessSettingDefault) > 0;
+
+        return hasBacklightKey && hasBacklight;
+    }
+
+    public boolean isKeyboardSupported() {
+        return getContext().getResources().getInteger(
+                com.android.internal.R.integer.config_keyboardBrightnessSettingDefault) > 0;
+    }
+
+    public void updateSummary() {
+        if (mButtonBrightness != null) {
+            int buttonBrightness = mButtonBrightness.getBrightness(true);
+            int timeout = getTimeout();
+
+            if (buttonBrightness == 0) {
+                setSummary(R.string.backlight_summary_disabled);
+            } else if (timeout == 0) {
+                setSummary(R.string.backlight_timeout_unlimited);
+            } else {
+                setSummary(getContext().getString(R.string.backlight_summary_enabled_with_timeout,
+                        getTimeoutString(timeout)));
+            }
+        } else if (mKeyboardBrightness != null && mKeyboardBrightness.getBrightness(true) != 0) {
+            setSummary(R.string.backlight_summary_enabled);
+        } else {
+            setSummary(R.string.backlight_summary_disabled);
+        }
+    }
+
+    private String getTimeoutString(int timeout) {
+        return getContext().getResources().getQuantityString(
+                R.plurals.backlight_timeout_time, timeout, timeout);
+    }
+
+    private int getTimeout() {
+        return CMSettings.Secure.getInt(mResolver,
+                CMSettings.Secure.BUTTON_BACKLIGHT_TIMEOUT, DEFAULT_BUTTON_TIMEOUT * 1000) / 1000;
+    }
+
+    private void applyTimeout(int timeout) {
+        CMSettings.Secure.putInt(mResolver,
+                CMSettings.Secure.BUTTON_BACKLIGHT_TIMEOUT, timeout * 1000);
+    }
+
+    private void updateBrightnessPreview() {
+        if (mWindow != null) {
+            LayoutParams params = mWindow.getAttributes();
+            if (mActiveControl != null) {
+                params.buttonBrightness = (float) mActiveControl.getBrightness(false) / 255.0f;
+            } else {
+                params.buttonBrightness = -1;
+            }
+            mWindow.setAttributes(params);
+        }
+    }
+
+    private void updateTimeoutEnabledState() {
+        int buttonBrightness = mButtonBrightness != null
+                ? mButtonBrightness.getBrightness(false) : 0;
+        int count = mTimeoutContainer.getChildCount();
+        for (int i = 0; i < count; i++) {
+            mTimeoutContainer.getChildAt(i).setEnabled(buttonBrightness != 0);
+        }
+    }
+
+    private void handleTimeoutUpdate(int timeout) {
+        if (timeout == 0) {
+            mTimeoutValue.setText(R.string.backlight_timeout_unlimited);
+        } else {
+            mTimeoutValue.setText(getTimeoutString(timeout));
+        }
+    }
+
+    @Override
+    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+        handleTimeoutUpdate(progress);
+    }
+
+    @Override
+    public void onStartTrackingTouch(SeekBar seekBar) {
+        // Do nothing here
+    }
+
+    @Override
+    public void onStopTrackingTouch(SeekBar seekBar) {
+        // Do nothing here
+    }
+
+    private static class SavedState extends BaseSavedState {
+        int timeout;
+        int button;
+        int keyboard;
+
+        public SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        public SavedState(Parcel source) {
+            super(source);
+            timeout = source.readInt();
+            button = source.readInt();
+            keyboard = source.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeInt(timeout);
+            dest.writeInt(button);
+            dest.writeInt(keyboard);
+        }
+
+        public static final Parcelable.Creator<SavedState> CREATOR =
+                new Parcelable.Creator<SavedState>() {
+
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in);
+            }
+
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
+    }
+
+    private class BrightnessControl implements
+            SeekBar.OnSeekBarChangeListener, CheckBox.OnCheckedChangeListener {
+        private String mSetting;
+        private boolean mIsSingleValue;
+        private int mDefaultBrightness;
+        private CheckBox mCheckBox;
+        private SeekBar mSeekBar;
+        private TextView mValue;
+
+        public BrightnessControl(String setting, boolean singleValue, int defaultBrightness) {
+            mSetting = setting;
+            mIsSingleValue = singleValue;
+            mDefaultBrightness = defaultBrightness;
+        }
+
+        public BrightnessControl(String setting, boolean singleValue) {
+            this(setting, singleValue, 255);
+        }
+
+        public void init(ViewGroup container) {
+            int brightness = getBrightness(true);
+
+            if (mIsSingleValue) {
+                container.findViewById(R.id.seekbar_container).setVisibility(View.GONE);
+                mCheckBox = (CheckBox) container.findViewById(R.id.backlight_switch);
+                mCheckBox.setChecked(brightness != 0);
+                mCheckBox.setOnCheckedChangeListener(this);
+            } else {
+                container.findViewById(R.id.checkbox_container).setVisibility(View.GONE);
+                mSeekBar = (SeekBar) container.findViewById(R.id.seekbar);
+                mValue = (TextView) container.findViewById(R.id.value);
+
+                mSeekBar.setMax(255);
+                mSeekBar.setProgress(brightness);
+                mSeekBar.setOnSeekBarChangeListener(this);
+            }
+
+            handleBrightnessUpdate(brightness);
+        }
+
+        public int getBrightness(boolean persisted) {
+            if (mCheckBox != null && !persisted) {
+                return mCheckBox.isChecked() ? mDefaultBrightness : 0;
+            } else if (mSeekBar != null && !persisted) {
+                return mSeekBar.getProgress();
+            }
+            return CMSettings.Secure.getInt(mResolver, mSetting, mDefaultBrightness);
+        }
+
+        public void applyBrightness() {
+            CMSettings.Secure.putInt(mResolver, mSetting, getBrightness(false));
+        }
+
+        /* Behaviors when it's a seekbar */
+        @Override
+        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+            handleBrightnessUpdate(progress);
+        }
+
+        @Override
+        public void onStartTrackingTouch(SeekBar seekBar) {
+            mActiveControl = this;
+        }
+
+        @Override
+        public void onStopTrackingTouch(SeekBar seekBar) {
+            // Do nothing here
+        }
+
+        /* Behaviors when it's a plain checkbox */
+        @Override
+        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+            mActiveControl = this;
+            updateBrightnessPreview();
+            updateTimeoutEnabledState();
+        }
+
+        public void setBrightness(int value) {
+            if (mIsSingleValue) {
+                mCheckBox.setChecked(value != 0);
+            } else {
+                mSeekBar.setProgress(value);
+            }
+        }
+
+        public void reset() {
+            setBrightness(mDefaultBrightness);
+        }
+
+        private void handleBrightnessUpdate(int brightness) {
+            updateBrightnessPreview();
+            if (mValue != null) {
+                mValue.setText(String.format("%d%%", (int)((brightness * 100) / 255)));
+            }
+            updateTimeoutEnabledState();
+        }
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/input/ButtonSettings.java b/src/org/cyanogenmod/cmparts/input/ButtonSettings.java
new file mode 100644
index 0000000..976449e
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/input/ButtonSettings.java
@@ -0,0 +1,731 @@
+/*
+ * 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.cmparts.input;
+
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceCategory;
+import android.support.v7.preference.PreferenceScreen;
+import android.util.Log;
+import android.view.IWindowManager;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.WindowManagerGlobal;
+
+import org.cyanogenmod.cmparts.R;
+import org.cyanogenmod.cmparts.SettingsPreferenceFragment;
+import org.cyanogenmod.cmparts.Utils;
+import org.cyanogenmod.internal.util.ScreenType;
+
+import java.util.List;
+
+import cyanogenmod.hardware.CMHardwareManager;
+import cyanogenmod.providers.CMSettings;
+
+import static android.provider.Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED;
+
+public class ButtonSettings extends SettingsPreferenceFragment implements
+        Preference.OnPreferenceChangeListener {
+    private static final String TAG = "SystemSettings";
+
+    private static final String KEY_BUTTON_BACKLIGHT = "button_backlight";
+    private static final String KEY_HOME_LONG_PRESS = "hardware_keys_home_long_press";
+    private static final String KEY_HOME_DOUBLE_TAP = "hardware_keys_home_double_tap";
+    private static final String KEY_MENU_PRESS = "hardware_keys_menu_press";
+    private static final String KEY_MENU_LONG_PRESS = "hardware_keys_menu_long_press";
+    private static final String KEY_ASSIST_PRESS = "hardware_keys_assist_press";
+    private static final String KEY_ASSIST_LONG_PRESS = "hardware_keys_assist_long_press";
+    private static final String KEY_APP_SWITCH_PRESS = "hardware_keys_app_switch_press";
+    private static final String KEY_APP_SWITCH_LONG_PRESS = "hardware_keys_app_switch_long_press";
+    private static final String KEY_VOLUME_KEY_CURSOR_CONTROL = "volume_key_cursor_control";
+    private static final String KEY_SWAP_VOLUME_BUTTONS = "swap_volume_buttons";
+    private static final String DISABLE_NAV_KEYS = "disable_nav_keys";
+    private static final String KEY_NAVIGATION_BAR_LEFT = "navigation_bar_left";
+    private static final String KEY_NAVIGATION_RECENTS_LONG_PRESS = "navigation_recents_long_press";
+    private static final String KEY_POWER_END_CALL = "power_end_call";
+    private static final String KEY_HOME_ANSWER_CALL = "home_answer_call";
+    private static final String KEY_VOLUME_MUSIC_CONTROLS = "volbtn_music_controls";
+    private static final String KEY_VOLUME_CONTROL_RING_STREAM = "volume_keys_control_ring_stream";
+    private static final String KEY_CAMERA_DOUBLE_TAP_POWER_GESTURE
+            = "camera_double_tap_power_gesture";
+
+    private static final String CATEGORY_POWER = "power_key";
+    private static final String CATEGORY_HOME = "home_key";
+    private static final String CATEGORY_BACK = "back_key";
+    private static final String CATEGORY_MENU = "menu_key";
+    private static final String CATEGORY_ASSIST = "assist_key";
+    private static final String CATEGORY_APPSWITCH = "app_switch_key";
+    private static final String CATEGORY_CAMERA = "camera_key";
+    private static final String CATEGORY_VOLUME = "volume_keys";
+    private static final String CATEGORY_BACKLIGHT = "key_backlight";
+    private static final String CATEGORY_NAVBAR = "navigation_bar_category";
+
+    // Available custom actions to perform on a key press.
+    // Must match values for KEY_HOME_LONG_PRESS_ACTION in:
+    // frameworks/base/core/java/android/provider/Settings.java
+    private static final int ACTION_NOTHING = 0;
+    private static final int ACTION_MENU = 1;
+    private static final int ACTION_APP_SWITCH = 2;
+    private static final int ACTION_SEARCH = 3;
+    private static final int ACTION_VOICE_SEARCH = 4;
+    private static final int ACTION_IN_APP_SEARCH = 5;
+    private static final int ACTION_LAUNCH_CAMERA = 6;
+    private static final int ACTION_SLEEP = 7;
+    private static final int ACTION_LAST_APP = 8;
+
+    // Masks for checking presence of hardware keys.
+    // Must match values in frameworks/base/core/res/res/values/config.xml
+    public static final int KEY_MASK_HOME = 0x01;
+    public static final int KEY_MASK_BACK = 0x02;
+    public static final int KEY_MASK_MENU = 0x04;
+    public static final int KEY_MASK_ASSIST = 0x08;
+    public static final int KEY_MASK_APP_SWITCH = 0x10;
+    public static final int KEY_MASK_CAMERA = 0x20;
+    public static final int KEY_MASK_VOLUME = 0x40;
+
+    private ListPreference mHomeLongPressAction;
+    private ListPreference mHomeDoubleTapAction;
+    private ListPreference mMenuPressAction;
+    private ListPreference mMenuLongPressAction;
+    private ListPreference mAssistPressAction;
+    private ListPreference mAssistLongPressAction;
+    private ListPreference mAppSwitchPressAction;
+    private ListPreference mAppSwitchLongPressAction;
+    private SwitchPreference mCameraWakeScreen;
+    private SwitchPreference mCameraSleepOnRelease;
+    private SwitchPreference mCameraLaunch;
+    private ListPreference mVolumeKeyCursorControl;
+    private SwitchPreference mVolumeWakeScreen;
+    private SwitchPreference mVolumeMusicControls;
+    private SwitchPreference mSwapVolumeButtons;
+    private SwitchPreference mDisableNavigationKeys;
+    private SwitchPreference mNavigationBarLeftPref;
+    private ListPreference mNavigationRecentsLongPressAction;
+    private SwitchPreference mPowerEndCall;
+    private SwitchPreference mHomeAnswerCall;
+    private SwitchPreference mCameraDoubleTapPowerGesture;
+
+    private PreferenceCategory mNavigationPreferencesCat;
+
+    private Handler mHandler;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        addPreferencesFromResource(R.xml.button_settings);
+
+        final Resources res = getResources();
+        final ContentResolver resolver = getActivity().getContentResolver();
+        final PreferenceScreen prefScreen = getPreferenceScreen();
+
+        final int deviceKeys = getResources().getInteger(
+                com.android.internal.R.integer.config_deviceHardwareKeys);
+        final int deviceWakeKeys = getResources().getInteger(
+                com.android.internal.R.integer.config_deviceHardwareWakeKeys);
+
+        final boolean hasPowerKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_POWER);
+        final boolean hasHomeKey = (deviceKeys & KEY_MASK_HOME) != 0;
+        final boolean hasBackKey = (deviceKeys & KEY_MASK_BACK) != 0;
+        final boolean hasMenuKey = (deviceKeys & KEY_MASK_MENU) != 0;
+        final boolean hasAssistKey = (deviceKeys & KEY_MASK_ASSIST) != 0;
+        final boolean hasAppSwitchKey = (deviceKeys & KEY_MASK_APP_SWITCH) != 0;
+        final boolean hasCameraKey = (deviceKeys & KEY_MASK_CAMERA) != 0;
+        final boolean hasVolumeKeys = (deviceKeys & KEY_MASK_VOLUME) != 0;
+
+        final boolean showHomeWake = (deviceWakeKeys & KEY_MASK_HOME) != 0;
+        final boolean showBackWake = (deviceWakeKeys & KEY_MASK_BACK) != 0;
+        final boolean showMenuWake = (deviceWakeKeys & KEY_MASK_MENU) != 0;
+        final boolean showAssistWake = (deviceWakeKeys & KEY_MASK_ASSIST) != 0;
+        final boolean showAppSwitchWake = (deviceWakeKeys & KEY_MASK_APP_SWITCH) != 0;
+        final boolean showCameraWake = (deviceWakeKeys & KEY_MASK_CAMERA) != 0;
+        final boolean showVolumeWake = (deviceWakeKeys & KEY_MASK_VOLUME) != 0;
+
+        boolean hasAnyBindableKey = false;
+        final PreferenceCategory powerCategory =
+                (PreferenceCategory) prefScreen.findPreference(CATEGORY_POWER);
+        final PreferenceCategory homeCategory =
+                (PreferenceCategory) prefScreen.findPreference(CATEGORY_HOME);
+        final PreferenceCategory backCategory =
+                (PreferenceCategory) prefScreen.findPreference(CATEGORY_BACK);
+        final PreferenceCategory menuCategory =
+                (PreferenceCategory) prefScreen.findPreference(CATEGORY_MENU);
+        final PreferenceCategory assistCategory =
+                (PreferenceCategory) prefScreen.findPreference(CATEGORY_ASSIST);
+        final PreferenceCategory appSwitchCategory =
+                (PreferenceCategory) prefScreen.findPreference(CATEGORY_APPSWITCH);
+        final PreferenceCategory volumeCategory =
+                (PreferenceCategory) prefScreen.findPreference(CATEGORY_VOLUME);
+        final PreferenceCategory cameraCategory =
+                (PreferenceCategory) prefScreen.findPreference(CATEGORY_CAMERA);
+
+        // Power button ends calls.
+        mPowerEndCall = (SwitchPreference) findPreference(KEY_POWER_END_CALL);
+
+        // Double press power to launch camera.
+        mCameraDoubleTapPowerGesture
+                    = (SwitchPreference) findPreference(KEY_CAMERA_DOUBLE_TAP_POWER_GESTURE);
+
+        // Home button answers calls.
+        mHomeAnswerCall = (SwitchPreference) findPreference(KEY_HOME_ANSWER_CALL);
+
+        mHandler = new Handler();
+
+        // Force Navigation bar related options
+        mDisableNavigationKeys = (SwitchPreference) findPreference(DISABLE_NAV_KEYS);
+
+        mNavigationPreferencesCat = (PreferenceCategory) findPreference(CATEGORY_NAVBAR);
+
+        // Navigation bar left
+        mNavigationBarLeftPref = (SwitchPreference) findPreference(KEY_NAVIGATION_BAR_LEFT);
+
+        // Navigation bar recents long press activity needs custom setup
+        mNavigationRecentsLongPressAction =
+                initRecentsLongPressAction(KEY_NAVIGATION_RECENTS_LONG_PRESS);
+
+        final CMHardwareManager hardware = CMHardwareManager.getInstance(getActivity());
+
+        // Only visible on devices that does not have a navigation bar already,
+        // and don't even try unless the existing keys can be disabled
+        boolean needsNavigationBar = false;
+        if (hardware.isSupported(CMHardwareManager.FEATURE_KEY_DISABLE)) {
+            try {
+                IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+                needsNavigationBar = wm.needsNavigationBar();
+            } catch (RemoteException e) {
+            }
+
+            if (needsNavigationBar) {
+                prefScreen.removePreference(mDisableNavigationKeys);
+            } else {
+                // Remove keys that can be provided by the navbar
+                updateDisableNavkeysOption();
+                mNavigationPreferencesCat.setEnabled(mDisableNavigationKeys.isChecked());
+                updateDisableNavkeysCategories(mDisableNavigationKeys.isChecked());
+            }
+        } else {
+            prefScreen.removePreference(mDisableNavigationKeys);
+        }
+
+        if (hasPowerKey) {
+            if (!Utils.isVoiceCapable(getActivity())) {
+                powerCategory.removePreference(mPowerEndCall);
+                mPowerEndCall = null;
+            }
+            if (mCameraDoubleTapPowerGesture != null &&
+                    isCameraDoubleTapPowerGestureAvailable(getResources())) {
+                // Update double tap power to launch camera if available.
+                mCameraDoubleTapPowerGesture.setOnPreferenceChangeListener(this);
+                int cameraDoubleTapPowerDisabled = Settings.Secure.getInt(
+                        getContentResolver(), CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, 0);
+                mCameraDoubleTapPowerGesture.setChecked(cameraDoubleTapPowerDisabled == 0);
+            } else {
+                powerCategory.removePreference(mCameraDoubleTapPowerGesture);
+                mCameraDoubleTapPowerGesture = null;
+            }
+        } else {
+            prefScreen.removePreference(powerCategory);
+        }
+
+        if (hasHomeKey) {
+            if (!showHomeWake) {
+                homeCategory.removePreference(findPreference(CMSettings.System.HOME_WAKE_SCREEN));
+            }
+
+            if (!Utils.isVoiceCapable(getActivity())) {
+                homeCategory.removePreference(mHomeAnswerCall);
+                mHomeAnswerCall = null;
+            }
+
+            int defaultLongPressAction = res.getInteger(
+                    com.android.internal.R.integer.config_longPressOnHomeBehavior);
+            if (defaultLongPressAction < ACTION_NOTHING ||
+                    defaultLongPressAction > ACTION_LAST_APP) {
+                defaultLongPressAction = ACTION_NOTHING;
+            }
+
+            int defaultDoubleTapAction = res.getInteger(
+                    com.android.internal.R.integer.config_doubleTapOnHomeBehavior);
+            if (defaultDoubleTapAction < ACTION_NOTHING ||
+                    defaultDoubleTapAction > ACTION_LAST_APP) {
+                defaultDoubleTapAction = ACTION_NOTHING;
+            }
+
+            int longPressAction = CMSettings.System.getInt(resolver,
+                    CMSettings.System.KEY_HOME_LONG_PRESS_ACTION,
+                    defaultLongPressAction);
+            mHomeLongPressAction = initActionList(KEY_HOME_LONG_PRESS, longPressAction);
+
+            int doubleTapAction = CMSettings.System.getInt(resolver,
+                    CMSettings.System.KEY_HOME_DOUBLE_TAP_ACTION,
+                    defaultDoubleTapAction);
+            mHomeDoubleTapAction = initActionList(KEY_HOME_DOUBLE_TAP, doubleTapAction);
+
+            hasAnyBindableKey = true;
+        } else {
+            prefScreen.removePreference(homeCategory);
+        }
+
+        if (hasBackKey) {
+            if (!showBackWake) {
+                backCategory.removePreference(findPreference(CMSettings.System.BACK_WAKE_SCREEN));
+                prefScreen.removePreference(backCategory);
+            }
+        } else {
+            prefScreen.removePreference(backCategory);
+        }
+
+        if (hasMenuKey) {
+            if (!showMenuWake) {
+                menuCategory.removePreference(findPreference(CMSettings.System.MENU_WAKE_SCREEN));
+            }
+
+            int pressAction = CMSettings.System.getInt(resolver,
+                    CMSettings.System.KEY_MENU_ACTION, ACTION_MENU);
+            mMenuPressAction = initActionList(KEY_MENU_PRESS, pressAction);
+
+            int longPressAction = CMSettings.System.getInt(resolver,
+                        CMSettings.System.KEY_MENU_LONG_PRESS_ACTION,
+                        hasAssistKey ? ACTION_NOTHING : ACTION_SEARCH);
+            mMenuLongPressAction = initActionList(KEY_MENU_LONG_PRESS, longPressAction);
+
+            hasAnyBindableKey = true;
+        } else {
+            prefScreen.removePreference(menuCategory);
+        }
+
+        if (hasAssistKey) {
+            if (!showAssistWake) {
+                assistCategory.removePreference(findPreference(CMSettings.System.ASSIST_WAKE_SCREEN));
+            }
+
+            int pressAction = CMSettings.System.getInt(resolver,
+                    CMSettings.System.KEY_ASSIST_ACTION, ACTION_SEARCH);
+            mAssistPressAction = initActionList(KEY_ASSIST_PRESS, pressAction);
+
+            int longPressAction = CMSettings.System.getInt(resolver,
+                    CMSettings.System.KEY_ASSIST_LONG_PRESS_ACTION, ACTION_VOICE_SEARCH);
+            mAssistLongPressAction = initActionList(KEY_ASSIST_LONG_PRESS, longPressAction);
+
+            hasAnyBindableKey = true;
+        } else {
+            prefScreen.removePreference(assistCategory);
+        }
+
+        if (hasAppSwitchKey) {
+            if (!showAppSwitchWake) {
+                appSwitchCategory.removePreference(findPreference(
+                        CMSettings.System.APP_SWITCH_WAKE_SCREEN));
+            }
+
+            int pressAction = CMSettings.System.getInt(resolver,
+                    CMSettings.System.KEY_APP_SWITCH_ACTION, ACTION_APP_SWITCH);
+            mAppSwitchPressAction = initActionList(KEY_APP_SWITCH_PRESS, pressAction);
+
+            int longPressAction = CMSettings.System.getInt(resolver,
+                    CMSettings.System.KEY_APP_SWITCH_LONG_PRESS_ACTION, ACTION_NOTHING);
+            mAppSwitchLongPressAction = initActionList(KEY_APP_SWITCH_LONG_PRESS, longPressAction);
+
+            hasAnyBindableKey = true;
+        } else {
+            prefScreen.removePreference(appSwitchCategory);
+        }
+
+        if (hasCameraKey) {
+            mCameraWakeScreen = (SwitchPreference) findPreference(CMSettings.System.CAMERA_WAKE_SCREEN);
+            mCameraSleepOnRelease =
+                    (SwitchPreference) findPreference(CMSettings.System.CAMERA_SLEEP_ON_RELEASE);
+            mCameraLaunch = (SwitchPreference) findPreference(CMSettings.System.CAMERA_LAUNCH);
+
+            if (!showCameraWake) {
+                prefScreen.removePreference(mCameraWakeScreen);
+            }
+            // Only show 'Camera sleep on release' if the device has a focus key
+            if (res.getBoolean(com.android.internal.R.bool.config_singleStageCameraKey)) {
+                prefScreen.removePreference(mCameraSleepOnRelease);
+            }
+        } else {
+            prefScreen.removePreference(cameraCategory);
+        }
+
+        if (Utils.hasVolumeRocker(getActivity())) {
+            if (!showVolumeWake) {
+                volumeCategory.removePreference(findPreference(CMSettings.System.VOLUME_WAKE_SCREEN));
+            }
+
+            int cursorControlAction = Settings.System.getInt(resolver,
+                    Settings.System.VOLUME_KEY_CURSOR_CONTROL, 0);
+            mVolumeKeyCursorControl = initActionList(KEY_VOLUME_KEY_CURSOR_CONTROL,
+                    cursorControlAction);
+
+            int swapVolumeKeys = CMSettings.System.getInt(getContentResolver(),
+                    CMSettings.System.SWAP_VOLUME_KEYS_ON_ROTATION, 0);
+            mSwapVolumeButtons = (SwitchPreference)
+                    prefScreen.findPreference(KEY_SWAP_VOLUME_BUTTONS);
+            if (mSwapVolumeButtons != null) {
+                mSwapVolumeButtons.setChecked(swapVolumeKeys > 0);
+            }
+        } else {
+            prefScreen.removePreference(volumeCategory);
+        }
+
+        try {
+            // Only show the navigation bar category on devices that have a navigation bar
+            // unless we are forcing it via development settings
+            boolean forceNavbar = CMSettings.Global.getInt(getContentResolver(),
+                    CMSettings.Global.DEV_FORCE_SHOW_NAVBAR, 0) == 1;
+            boolean hasNavBar = WindowManagerGlobal.getWindowManagerService().hasNavigationBar()
+                    || forceNavbar;
+
+            if (!ScreenType.isPhone(getActivity())) {
+                mNavigationPreferencesCat.removePreference(mNavigationBarLeftPref);
+            }
+
+            if (!hasNavBar && (needsNavigationBar ||
+                    !hardware.isSupported(CMHardwareManager.FEATURE_KEY_DISABLE))) {
+                    // Hide navigation bar category
+                    prefScreen.removePreference(mNavigationPreferencesCat);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error getting navigation bar status");
+        }
+
+        final ButtonBacklightBrightness backlight =
+                (ButtonBacklightBrightness) findPreference(KEY_BUTTON_BACKLIGHT);
+        if (!backlight.isButtonSupported() && !backlight.isKeyboardSupported()) {
+            prefScreen.removePreference(backlight);
+        }
+
+        if (mCameraWakeScreen != null) {
+            if (mCameraSleepOnRelease != null && !getResources().getBoolean(
+                    com.android.internal.R.bool.config_singleStageCameraKey)) {
+                mCameraSleepOnRelease.setDependency(CMSettings.System.CAMERA_WAKE_SCREEN);
+            }
+        }
+        mVolumeWakeScreen = (SwitchPreference) findPreference(CMSettings.System.VOLUME_WAKE_SCREEN);
+        mVolumeMusicControls = (SwitchPreference) findPreference(KEY_VOLUME_MUSIC_CONTROLS);
+
+        if (mVolumeWakeScreen != null) {
+            if (mVolumeMusicControls != null) {
+                mVolumeMusicControls.setDependency(CMSettings.System.VOLUME_WAKE_SCREEN);
+                mVolumeWakeScreen.setDisableDependentsState(true);
+            }
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        // Power button ends calls.
+        if (mPowerEndCall != null) {
+            final int incallPowerBehavior = Settings.Secure.getInt(getContentResolver(),
+                    Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR,
+                    Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_DEFAULT);
+            final boolean powerButtonEndsCall =
+                    (incallPowerBehavior == Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP);
+            mPowerEndCall.setChecked(powerButtonEndsCall);
+        }
+
+        // Home button answers calls.
+        if (mHomeAnswerCall != null) {
+            final int incallHomeBehavior = CMSettings.Secure.getInt(getContentResolver(),
+                    CMSettings.Secure.RING_HOME_BUTTON_BEHAVIOR,
+                    CMSettings.Secure.RING_HOME_BUTTON_BEHAVIOR_DEFAULT);
+            final boolean homeButtonAnswersCall =
+                (incallHomeBehavior == CMSettings.Secure.RING_HOME_BUTTON_BEHAVIOR_ANSWER);
+            mHomeAnswerCall.setChecked(homeButtonAnswersCall);
+        }
+    }
+
+    private ListPreference initActionList(String key, int value) {
+        ListPreference list = (ListPreference) getPreferenceScreen().findPreference(key);
+        if (list == null) return null;
+        list.setValue(Integer.toString(value));
+        list.setSummary(list.getEntry());
+        list.setOnPreferenceChangeListener(this);
+        return list;
+    }
+
+    private ListPreference initRecentsLongPressAction(String key) {
+        ListPreference list = (ListPreference) getPreferenceScreen().findPreference(key);
+        list.setOnPreferenceChangeListener(this);
+
+        // Read the componentName from Settings.Secure, this is the user's prefered setting
+        String componentString = CMSettings.Secure.getString(getContentResolver(),
+                CMSettings.Secure.RECENTS_LONG_PRESS_ACTIVITY);
+        ComponentName targetComponent = null;
+        if (componentString == null) {
+            list.setSummary(getString(R.string.hardware_keys_action_last_app));
+        } else {
+            targetComponent = ComponentName.unflattenFromString(componentString);
+        }
+
+        // Dyanamically generate the list array,
+        // query PackageManager for all Activites that are registered for ACTION_RECENTS_LONG_PRESS
+        PackageManager pm = getPackageManager();
+        Intent intent = new Intent(cyanogenmod.content.Intent.ACTION_RECENTS_LONG_PRESS);
+        List<ResolveInfo> recentsActivities = pm.queryIntentActivities(intent,
+                PackageManager.MATCH_DEFAULT_ONLY);
+        if (recentsActivities.size() == 0) {
+            // No entries available, disable
+            list.setSummary(getString(R.string.hardware_keys_action_last_app));
+            CMSettings.Secure.putString(getContentResolver(),
+                    CMSettings.Secure.RECENTS_LONG_PRESS_ACTIVITY, null);
+            list.setEnabled(false);
+            return list;
+        }
+
+        CharSequence[] entries = new CharSequence[recentsActivities.size() + 1];
+        CharSequence[] values = new CharSequence[recentsActivities.size() + 1];
+        // First entry is always default last app
+        entries[0] = getString(R.string.hardware_keys_action_last_app);
+        values[0] = "";
+        list.setValue(values[0].toString());
+        int i = 1;
+        for (ResolveInfo info : recentsActivities) {
+            try {
+                // Use pm.getApplicationInfo for the label,
+                // we cannot rely on ResolveInfo that comes back from queryIntentActivities.
+                entries[i] = pm.getApplicationInfo(info.activityInfo.packageName, 0).loadLabel(pm);
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.e(TAG, "Error package not found: " + info.activityInfo.packageName, e);
+                // Fallback to package name
+                entries[i] = info.activityInfo.packageName;
+            }
+
+            // Set the value to the ComponentName that will handle this intent
+            ComponentName entryComponent = new ComponentName(info.activityInfo.packageName,
+                    info.activityInfo.name);
+            values[i] = entryComponent.flattenToString();
+            if (targetComponent != null) {
+                if (entryComponent.equals(targetComponent)) {
+                    // Update the selected value and the preference summary
+                    list.setSummary(entries[i]);
+                    list.setValue(values[i].toString());
+                }
+            }
+            i++;
+        }
+        list.setEntries(entries);
+        list.setEntryValues(values);
+        return list;
+    }
+
+    private void handleActionListChange(ListPreference pref, Object newValue, String setting) {
+        String value = (String) newValue;
+        int index = pref.findIndexOfValue(value);
+        pref.setSummary(pref.getEntries()[index]);
+        CMSettings.System.putInt(getContentResolver(), setting, Integer.valueOf(value));
+    }
+
+    private void handleSystemActionListChange(ListPreference pref, Object newValue, String setting) {
+        String value = (String) newValue;
+        int index = pref.findIndexOfValue(value);
+        pref.setSummary(pref.getEntries()[index]);
+        Settings.System.putInt(getContentResolver(), setting, Integer.valueOf(value));
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        if (preference == mHomeLongPressAction) {
+            handleActionListChange(mHomeLongPressAction, newValue,
+                    CMSettings.System.KEY_HOME_LONG_PRESS_ACTION);
+            return true;
+        } else if (preference == mHomeDoubleTapAction) {
+            handleActionListChange(mHomeDoubleTapAction, newValue,
+                    CMSettings.System.KEY_HOME_DOUBLE_TAP_ACTION);
+            return true;
+        } else if (preference == mMenuPressAction) {
+            handleActionListChange(mMenuPressAction, newValue,
+                    CMSettings.System.KEY_MENU_ACTION);
+            return true;
+        } else if (preference == mMenuLongPressAction) {
+            handleActionListChange(mMenuLongPressAction, newValue,
+                    CMSettings.System.KEY_MENU_LONG_PRESS_ACTION);
+            return true;
+        } else if (preference == mAssistPressAction) {
+            handleActionListChange(mAssistPressAction, newValue,
+                    CMSettings.System.KEY_ASSIST_ACTION);
+            return true;
+        } else if (preference == mAssistLongPressAction) {
+            handleActionListChange(mAssistLongPressAction, newValue,
+                    CMSettings.System.KEY_ASSIST_LONG_PRESS_ACTION);
+            return true;
+        } else if (preference == mAppSwitchPressAction) {
+            handleActionListChange(mAppSwitchPressAction, newValue,
+                    CMSettings.System.KEY_APP_SWITCH_ACTION);
+            return true;
+        } else if (preference == mAppSwitchLongPressAction) {
+            handleActionListChange(mAppSwitchLongPressAction, newValue,
+                    CMSettings.System.KEY_APP_SWITCH_LONG_PRESS_ACTION);
+            return true;
+        } else if (preference == mVolumeKeyCursorControl) {
+            handleSystemActionListChange(mVolumeKeyCursorControl, newValue,
+                    Settings.System.VOLUME_KEY_CURSOR_CONTROL);
+            return true;
+        } else if (preference == mNavigationRecentsLongPressAction) {
+            // RecentsLongPressAction is handled differently because it intentionally uses
+            // Settings.System
+            String putString = (String) newValue;
+            int index = mNavigationRecentsLongPressAction.findIndexOfValue(putString);
+            CharSequence summary = mNavigationRecentsLongPressAction.getEntries()[index];
+            // Update the summary
+            mNavigationRecentsLongPressAction.setSummary(summary);
+            if (putString.length() == 0) {
+                putString = null;
+            }
+            CMSettings.Secure.putString(getContentResolver(),
+                    CMSettings.Secure.RECENTS_LONG_PRESS_ACTIVITY, putString);
+            return true;
+        } else if (preference == mCameraDoubleTapPowerGesture) {
+            boolean value = (Boolean) newValue;
+            Settings.Secure.putInt(getContentResolver(), CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED,
+                    value ? 0 : 1 /* Backwards because setting is for disabling */);
+            return true;
+        }
+        return false;
+    }
+
+    private static void writeDisableNavkeysOption(Context context, boolean enabled) {
+        CMSettings.Global.putInt(context.getContentResolver(),
+                CMSettings.Global.DEV_FORCE_SHOW_NAVBAR, enabled ? 1 : 0);
+    }
+
+    private void updateDisableNavkeysOption() {
+        boolean enabled = CMSettings.Global.getInt(getActivity().getContentResolver(),
+                CMSettings.Global.DEV_FORCE_SHOW_NAVBAR, 0) != 0;
+
+        mDisableNavigationKeys.setChecked(enabled);
+    }
+
+    private void updateDisableNavkeysCategories(boolean navbarEnabled) {
+        final PreferenceScreen prefScreen = getPreferenceScreen();
+
+        /* Disable hw-key options if they're disabled */
+        final PreferenceCategory homeCategory =
+                (PreferenceCategory) prefScreen.findPreference(CATEGORY_HOME);
+        final PreferenceCategory backCategory =
+                (PreferenceCategory) prefScreen.findPreference(CATEGORY_BACK);
+        final PreferenceCategory menuCategory =
+                (PreferenceCategory) prefScreen.findPreference(CATEGORY_MENU);
+        final PreferenceCategory assistCategory =
+                (PreferenceCategory) prefScreen.findPreference(CATEGORY_ASSIST);
+        final PreferenceCategory appSwitchCategory =
+                (PreferenceCategory) prefScreen.findPreference(CATEGORY_APPSWITCH);
+        final ButtonBacklightBrightness backlight =
+                (ButtonBacklightBrightness) prefScreen.findPreference(KEY_BUTTON_BACKLIGHT);
+
+        /* Toggle backlight control depending on navbar state, force it to
+           off if enabling */
+        if (backlight != null) {
+            backlight.setEnabled(!navbarEnabled);
+            backlight.updateSummary();
+        }
+
+        /* Toggle hardkey control availability depending on navbar state */
+        if (homeCategory != null) {
+            homeCategory.setEnabled(!navbarEnabled);
+        }
+        if (backCategory != null) {
+            backCategory.setEnabled(!navbarEnabled);
+        }
+        if (menuCategory != null) {
+            menuCategory.setEnabled(!navbarEnabled);
+        }
+        if (assistCategory != null) {
+            assistCategory.setEnabled(!navbarEnabled);
+        }
+        if (appSwitchCategory != null) {
+            appSwitchCategory.setEnabled(!navbarEnabled);
+        }
+    }
+
+    public static void restoreKeyDisabler(Context context) {
+        CMHardwareManager hardware = CMHardwareManager.getInstance(context);
+        if (!hardware.isSupported(CMHardwareManager.FEATURE_KEY_DISABLE)) {
+            return;
+        }
+
+        writeDisableNavkeysOption(context, CMSettings.Global.getInt(context.getContentResolver(),
+                CMSettings.Global.DEV_FORCE_SHOW_NAVBAR, 0) != 0);
+    }
+
+
+    @Override
+    public boolean onPreferenceTreeClick(Preference preference) {
+        if (preference == mSwapVolumeButtons) {
+            int value = mSwapVolumeButtons.isChecked()
+                    ? (ScreenType.isTablet(getActivity()) ? 2 : 1) : 0;
+            CMSettings.System.putInt(getActivity().getContentResolver(),
+                    CMSettings.System.SWAP_VOLUME_KEYS_ON_ROTATION, value);
+        } else if (preference == mDisableNavigationKeys) {
+            mDisableNavigationKeys.setEnabled(false);
+            mNavigationPreferencesCat.setEnabled(false);
+            writeDisableNavkeysOption(getActivity(), mDisableNavigationKeys.isChecked());
+            updateDisableNavkeysOption();
+            updateDisableNavkeysCategories(true);
+            mHandler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    mDisableNavigationKeys.setEnabled(true);
+                    mNavigationPreferencesCat.setEnabled(mDisableNavigationKeys.isChecked());
+                    updateDisableNavkeysCategories(mDisableNavigationKeys.isChecked());
+                }
+            }, 1000);
+        } else if (preference == mPowerEndCall) {
+            handleTogglePowerButtonEndsCallPreferenceClick();
+            return true;
+        } else if (preference == mHomeAnswerCall) {
+            handleToggleHomeButtonAnswersCallPreferenceClick();
+            return true;
+        }
+
+        return super.onPreferenceTreeClick(preference);
+    }
+
+    private void handleTogglePowerButtonEndsCallPreferenceClick() {
+        Settings.Secure.putInt(getContentResolver(),
+                Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR, (mPowerEndCall.isChecked()
+                        ? Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP
+                        : Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_SCREEN_OFF));
+    }
+
+    private void handleToggleHomeButtonAnswersCallPreferenceClick() {
+        CMSettings.Secure.putInt(getContentResolver(),
+                CMSettings.Secure.RING_HOME_BUTTON_BEHAVIOR, (mHomeAnswerCall.isChecked()
+                        ? CMSettings.Secure.RING_HOME_BUTTON_BEHAVIOR_ANSWER
+                        : CMSettings.Secure.RING_HOME_BUTTON_BEHAVIOR_DO_NOTHING));
+    }
+
+    private static boolean isCameraDoubleTapPowerGestureAvailable(Resources res) {
+        return res.getBoolean(
+                com.android.internal.R.bool.config_cameraDoubleTapPowerGestureEnabled);
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/input/PowerMenuActions.java b/src/org/cyanogenmod/cmparts/input/PowerMenuActions.java
new file mode 100644
index 0000000..6dc2993
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/input/PowerMenuActions.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2014-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 org.cyanogenmod.cmparts.input;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.support.v7.preference.CheckBoxPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.internal.util.cm.PowerMenuConstants;
+
+import org.cyanogenmod.cmparts.R;
+import org.cyanogenmod.cmparts.SettingsPreferenceFragment;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import cyanogenmod.providers.CMSettings;
+
+import static com.android.internal.util.cm.PowerMenuConstants.GLOBAL_ACTION_KEY_AIRPLANE;
+import static com.android.internal.util.cm.PowerMenuConstants.GLOBAL_ACTION_KEY_ASSIST;
+import static com.android.internal.util.cm.PowerMenuConstants.GLOBAL_ACTION_KEY_BUGREPORT;
+import static com.android.internal.util.cm.PowerMenuConstants.GLOBAL_ACTION_KEY_LOCKDOWN;
+import static com.android.internal.util.cm.PowerMenuConstants.GLOBAL_ACTION_KEY_REBOOT;
+import static com.android.internal.util.cm.PowerMenuConstants.GLOBAL_ACTION_KEY_SCREENSHOT;
+import static com.android.internal.util.cm.PowerMenuConstants.GLOBAL_ACTION_KEY_SETTINGS;
+import static com.android.internal.util.cm.PowerMenuConstants.GLOBAL_ACTION_KEY_SILENT;
+import static com.android.internal.util.cm.PowerMenuConstants.GLOBAL_ACTION_KEY_USERS;
+import static com.android.internal.util.cm.PowerMenuConstants.GLOBAL_ACTION_KEY_VOICEASSIST;
+
+public class PowerMenuActions extends SettingsPreferenceFragment {
+    final static String TAG = "PowerMenuActions";
+
+    private CheckBoxPreference mRebootPref;
+    private CheckBoxPreference mScreenshotPref;
+    private CheckBoxPreference mAirplanePref;
+    private CheckBoxPreference mUsersPref;
+    private CheckBoxPreference mSettingsPref;
+    private CheckBoxPreference mLockdownPref;
+    private CheckBoxPreference mBugReportPref;
+    private CheckBoxPreference mSilentPref;
+    private CheckBoxPreference mVoiceAssistPref;
+    private CheckBoxPreference mAssistPref;
+
+    Context mContext;
+    private ArrayList<String> mLocalUserConfig = new ArrayList<String>();
+    private String[] mAvailableActions;
+    private String[] mAllActions;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        addPreferencesFromResource(R.xml.power_menu_settings);
+        mContext = getActivity().getApplicationContext();
+
+        mAvailableActions = getActivity().getResources().getStringArray(
+                R.array.power_menu_actions_array);
+        mAllActions = PowerMenuConstants.getAllActions();
+
+        for (String action : mAllActions) {
+        // Remove preferences not present in the overlay
+            if (!isActionAllowed(action)) {
+                getPreferenceScreen().removePreference(findPreference(action));
+                continue;
+            }
+
+            if (action.equals(GLOBAL_ACTION_KEY_REBOOT)) {
+                mRebootPref = (CheckBoxPreference) findPreference(GLOBAL_ACTION_KEY_REBOOT);
+            } else if (action.equals(GLOBAL_ACTION_KEY_SCREENSHOT)) {
+                mScreenshotPref = (CheckBoxPreference) findPreference(GLOBAL_ACTION_KEY_SCREENSHOT);
+            } else if (action.equals(GLOBAL_ACTION_KEY_AIRPLANE)) {
+                mAirplanePref = (CheckBoxPreference) findPreference(GLOBAL_ACTION_KEY_AIRPLANE);
+            } else if (action.equals(GLOBAL_ACTION_KEY_USERS)) {
+                mUsersPref = (CheckBoxPreference) findPreference(GLOBAL_ACTION_KEY_USERS);
+            } else if (action.equals(GLOBAL_ACTION_KEY_SETTINGS)) {
+                mSettingsPref = (CheckBoxPreference) findPreference(GLOBAL_ACTION_KEY_SETTINGS);
+            } else if (action.equals(GLOBAL_ACTION_KEY_LOCKDOWN)) {
+                mLockdownPref = (CheckBoxPreference) findPreference(GLOBAL_ACTION_KEY_LOCKDOWN);
+            } else if (action.equals(GLOBAL_ACTION_KEY_BUGREPORT)) {
+                mBugReportPref = (CheckBoxPreference) findPreference(GLOBAL_ACTION_KEY_BUGREPORT);
+            } else if (action.equals(GLOBAL_ACTION_KEY_SILENT)) {
+                mSilentPref = (CheckBoxPreference) findPreference(GLOBAL_ACTION_KEY_SILENT);
+            } else if (action.equals(GLOBAL_ACTION_KEY_VOICEASSIST)) {
+                mSilentPref = (CheckBoxPreference) findPreference(GLOBAL_ACTION_KEY_VOICEASSIST);
+            } else if (action.equals(GLOBAL_ACTION_KEY_ASSIST)) {
+                mSilentPref = (CheckBoxPreference) findPreference(GLOBAL_ACTION_KEY_ASSIST);
+            }
+        }
+
+        getUserConfig();
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+
+        if (mRebootPref != null) {
+            mRebootPref.setChecked(settingsArrayContains(GLOBAL_ACTION_KEY_REBOOT));
+        }
+
+        if (mScreenshotPref != null) {
+            mScreenshotPref.setChecked(settingsArrayContains(GLOBAL_ACTION_KEY_SCREENSHOT));
+        }
+
+        if (mAirplanePref != null) {
+            mAirplanePref.setChecked(settingsArrayContains(GLOBAL_ACTION_KEY_AIRPLANE));
+        }
+
+        if (mUsersPref != null) {
+            if (!UserHandle.MU_ENABLED || !UserManager.supportsMultipleUsers()) {
+                getPreferenceScreen().removePreference(findPreference(GLOBAL_ACTION_KEY_USERS));
+                mUsersPref = null;
+            } else {
+                List<UserInfo> users = ((UserManager) mContext.getSystemService(
+                        Context.USER_SERVICE)).getUsers();
+                boolean enabled = (users.size() > 1);
+                mUsersPref.setChecked(settingsArrayContains(GLOBAL_ACTION_KEY_USERS) && enabled);
+                mUsersPref.setEnabled(enabled);
+            }
+        }
+
+        if (mSettingsPref != null) {
+            mSettingsPref.setChecked(settingsArrayContains(GLOBAL_ACTION_KEY_SETTINGS));
+        }
+
+        if (mLockdownPref != null) {
+            mLockdownPref.setChecked(settingsArrayContains(GLOBAL_ACTION_KEY_LOCKDOWN));
+        }
+
+        if (mBugReportPref != null) {
+            mBugReportPref.setChecked(settingsArrayContains(GLOBAL_ACTION_KEY_BUGREPORT));
+        }
+
+        if (mSilentPref != null) {
+            mSilentPref.setChecked(settingsArrayContains(GLOBAL_ACTION_KEY_SILENT));
+        }
+
+        if (mVoiceAssistPref != null) {
+            mVoiceAssistPref.setChecked(settingsArrayContains(GLOBAL_ACTION_KEY_VOICEASSIST));
+        }
+
+        if (mAssistPref != null) {
+            mAssistPref.setChecked(settingsArrayContains(GLOBAL_ACTION_KEY_ASSIST));
+        }
+
+        updatePreferences();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        updatePreferences();
+    }
+
+    @Override
+    public boolean onPreferenceTreeClick(Preference preference) {
+        boolean value;
+
+        if (preference == mRebootPref) {
+            value = mRebootPref.isChecked();
+            updateUserConfig(value, GLOBAL_ACTION_KEY_REBOOT);
+
+        } else if (preference == mScreenshotPref) {
+            value = mScreenshotPref.isChecked();
+            updateUserConfig(value, GLOBAL_ACTION_KEY_SCREENSHOT);
+
+        } else if (preference == mAirplanePref) {
+            value = mAirplanePref.isChecked();
+            updateUserConfig(value, GLOBAL_ACTION_KEY_AIRPLANE);
+
+        } else if (preference == mUsersPref) {
+            value = mUsersPref.isChecked();
+            updateUserConfig(value, GLOBAL_ACTION_KEY_USERS);
+
+        } else if (preference == mSettingsPref) {
+            value = mSettingsPref.isChecked();
+            updateUserConfig(value, GLOBAL_ACTION_KEY_SETTINGS);
+
+        } else if (preference == mLockdownPref) {
+            value = mLockdownPref.isChecked();
+            updateUserConfig(value, GLOBAL_ACTION_KEY_LOCKDOWN);
+
+        } else if (preference == mBugReportPref) {
+            value = mBugReportPref.isChecked();
+            updateUserConfig(value, GLOBAL_ACTION_KEY_BUGREPORT);
+
+        } else if (preference == mSilentPref) {
+            value = mSilentPref.isChecked();
+            updateUserConfig(value, GLOBAL_ACTION_KEY_SILENT);
+
+        } else if (preference == mVoiceAssistPref) {
+            value = mVoiceAssistPref.isChecked();
+            updateUserConfig(value, GLOBAL_ACTION_KEY_VOICEASSIST);
+
+        } else if (preference == mAssistPref) {
+            value = mAssistPref.isChecked();
+            updateUserConfig(value, GLOBAL_ACTION_KEY_ASSIST);
+
+        } else {
+            return super.onPreferenceTreeClick(preference);
+        }
+        return true;
+    }
+
+    private boolean settingsArrayContains(String preference) {
+        return mLocalUserConfig.contains(preference);
+    }
+
+    private boolean isActionAllowed(String action) {
+        if (Arrays.asList(mAvailableActions).contains(action)) {
+            return true;
+        }
+        return false;
+    }
+
+    private void updateUserConfig(boolean enabled, String action) {
+        if (enabled) {
+            if (!settingsArrayContains(action)) {
+                mLocalUserConfig.add(action);
+            }
+        } else {
+            if (settingsArrayContains(action)) {
+                mLocalUserConfig.remove(action);
+            }
+        }
+        saveUserConfig();
+    }
+
+    private void updatePreferences() {
+        boolean bugreport = Settings.Secure.getInt(getContentResolver(),
+                Settings.Secure.BUGREPORT_IN_POWER_MENU, 0) != 0;
+
+        if (mBugReportPref != null) {
+            mBugReportPref.setEnabled(bugreport);
+            if (bugreport) {
+                mBugReportPref.setSummary(null);
+            } else {
+                mBugReportPref.setSummary(R.string.power_menu_bug_report_disabled);
+            }
+        }
+    }
+
+    private void getUserConfig() {
+        mLocalUserConfig.clear();
+        String[] defaultActions;
+        String savedActions = CMSettings.Secure.getStringForUser(mContext.getContentResolver(),
+                CMSettings.Secure.POWER_MENU_ACTIONS, UserHandle.USER_CURRENT);
+
+        if (savedActions == null) {
+            defaultActions = mContext.getResources().getStringArray(
+                    com.android.internal.R.array.config_globalActionsList);
+            for (String action : defaultActions) {
+                mLocalUserConfig.add(action);
+            }
+        } else {
+            for (String action : savedActions.split("\\|")) {
+                mLocalUserConfig.add(action);
+            }
+        }
+    }
+
+    private void saveUserConfig() {
+        StringBuilder s = new StringBuilder();
+
+        // TODO: Use DragSortListView
+        ArrayList<String> setactions = new ArrayList<String>();
+        for (String action : mAllActions) {
+            if (settingsArrayContains(action) && isActionAllowed(action)) {
+                setactions.add(action);
+            } else {
+                continue;
+            }
+        }
+
+        for (int i = 0; i < setactions.size(); i++) {
+            s.append(setactions.get(i).toString());
+            if (i != setactions.size() - 1) {
+                s.append("|");
+            }
+        }
+
+        CMSettings.Secure.putStringForUser(getContentResolver(),
+                CMSettings.Secure.POWER_MENU_ACTIONS, s.toString(), UserHandle.USER_CURRENT);
+        updatePowerMenuDialog();
+    }
+
+    private void updatePowerMenuDialog() {
+        Intent u = new Intent();
+        u.setAction(Intent.UPDATE_POWER_MENU);
+        mContext.sendBroadcastAsUser(u, UserHandle.ALL);
+    }
+}