cmparts: Profiles settings

 * So many people have worked on this, I've lost count.
 * This patch rolls it all up and moves it into CMParts.
 * Also brought in required Settings infrastructure to support
   fragment navigation.

Change-Id: I58da5f7bca2d571865afcf4fafbaff881311fe16
diff --git a/Android.mk b/Android.mk
index 07894b5..1ecfd8e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -36,4 +36,6 @@
     LOCAL_JACK_FLAGS := --multi-dex native
 endif
 
+include frameworks/base/packages/SettingsLib/common.mk
+
 include $(BUILD_PACKAGE)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 01e9548..ff39f43 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -32,6 +32,7 @@
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
     <uses-permission android:name="android.permission.DEVICE_POWER" />
     <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
+    <uses-permission android:name="android.permission.BIND_DEVICE_ADMIN" />
 
     <uses-permission android:name="cyanogenmod.permission.BIND_CORE_SERVICE" />
 
@@ -96,5 +97,23 @@
                 android:resource="@drawable/ic_settings_buttons" />
         </activity-alias>
 
+        <!-- Profiles settings (dashboard) -->
+        <activity-alias
+            android:name=".profiles.ProfilesSettings"
+            android:label="@string/profiles_settings_title"
+            android:targetActivity="PartsActivity">
+            <intent-filter>
+                <action android:name="com.android.settings.action.EXTRA_SETTINGS" />
+                <action android:name="org.cyanogenmod.cmparts.PROFILES_SETTINGS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <meta-data
+                android:name="com.android.settings.category"
+                android:value="com.android.settings.category.personal" />
+            <meta-data
+                android:name="com.android.settings.icon"
+                android:resource="@drawable/ic_settings_profiles" />
+        </activity-alias>
+
     </application>
 </manifest>
diff --git a/proguard.flags b/proguard.flags
index 33b0dc4..04f1111 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -6,6 +6,7 @@
 -keep class org.cyanogenmod.cmparts.livedisplay.*
 -keep class org.cyanogenmod.cmparts.privacyguard.*
 -keep class org.cyanogenmod.cmparts.input.*
+-keep class org.cyanogenmod.cmparts.profiles.*
 
 # Keep click responders
 -keepclassmembers class com.android.settings.inputmethod.UserDictionaryAddWordActivity {
diff --git a/res/anim/fab_elevation.xml b/res/anim/fab_elevation.xml
new file mode 100644
index 0000000..af75db0
--- /dev/null
+++ b/res/anim/fab_elevation.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="true" android:state_pressed="true">
+        <set>
+            <objectAnimator
+                android:duration="@android:integer/config_shortAnimTime"
+                android:propertyName="translationZ"
+                android:valueTo="@dimen/fab_press_translation_z"
+                android:valueType="floatType" />
+        </set>
+    </item>
+    <item>
+        <set>
+            <objectAnimator
+                android:duration="@android:integer/config_shortAnimTime"
+                android:propertyName="translationZ"
+                android:valueTo="0"
+                android:valueType="floatType" />
+        </set>
+    </item>
+</selector>
\ No newline at end of file
diff --git a/res/drawable-hdpi/ic_actionbar_delete.png b/res/drawable-hdpi/ic_actionbar_delete.png
new file mode 100644
index 0000000..716a1ee
--- /dev/null
+++ b/res/drawable-hdpi/ic_actionbar_delete.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_trash_holo_dark.png b/res/drawable-hdpi/ic_menu_trash_holo_dark.png
new file mode 100644
index 0000000..c7a0832
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_trash_holo_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_settings_bluetooth_alpha.png b/res/drawable-hdpi/ic_settings_bluetooth_alpha.png
new file mode 100644
index 0000000..9dbc509
--- /dev/null
+++ b/res/drawable-hdpi/ic_settings_bluetooth_alpha.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_sysbar_quicksettings.png b/res/drawable-hdpi/ic_sysbar_quicksettings.png
new file mode 100644
index 0000000..a204936
--- /dev/null
+++ b/res/drawable-hdpi/ic_sysbar_quicksettings.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_actionbar_delete.png b/res/drawable-mdpi/ic_actionbar_delete.png
new file mode 100644
index 0000000..9d6ab09
--- /dev/null
+++ b/res/drawable-mdpi/ic_actionbar_delete.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_trash_holo_dark.png b/res/drawable-mdpi/ic_menu_trash_holo_dark.png
new file mode 100644
index 0000000..b9575aa
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_trash_holo_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_settings_bluetooth_alpha.png b/res/drawable-mdpi/ic_settings_bluetooth_alpha.png
new file mode 100644
index 0000000..f51e5ca
--- /dev/null
+++ b/res/drawable-mdpi/ic_settings_bluetooth_alpha.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_sysbar_quicksettings.png b/res/drawable-mdpi/ic_sysbar_quicksettings.png
new file mode 100644
index 0000000..d266b39
--- /dev/null
+++ b/res/drawable-mdpi/ic_sysbar_quicksettings.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_actionbar_delete.png b/res/drawable-xhdpi/ic_actionbar_delete.png
new file mode 100644
index 0000000..bbcf820
--- /dev/null
+++ b/res/drawable-xhdpi/ic_actionbar_delete.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_trash_holo_dark.png b/res/drawable-xhdpi/ic_menu_trash_holo_dark.png
new file mode 100644
index 0000000..33add13
--- /dev/null
+++ b/res/drawable-xhdpi/ic_menu_trash_holo_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_settings_bluetooth_alpha.png b/res/drawable-xhdpi/ic_settings_bluetooth_alpha.png
new file mode 100644
index 0000000..a3e09db
--- /dev/null
+++ b/res/drawable-xhdpi/ic_settings_bluetooth_alpha.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_sysbar_quicksettings.png b/res/drawable-xhdpi/ic_sysbar_quicksettings.png
new file mode 100644
index 0000000..46077be
--- /dev/null
+++ b/res/drawable-xhdpi/ic_sysbar_quicksettings.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_actionbar_delete.png b/res/drawable-xxhdpi/ic_actionbar_delete.png
new file mode 100644
index 0000000..a04ecdc
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_actionbar_delete.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_settings_bluetooth_alpha.png b/res/drawable-xxhdpi/ic_settings_bluetooth_alpha.png
new file mode 100644
index 0000000..2cc0577
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_settings_bluetooth_alpha.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_sysbar_quicksettings.png b/res/drawable-xxhdpi/ic_sysbar_quicksettings.png
new file mode 100644
index 0000000..ad964b4
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_sysbar_quicksettings.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_settings_bluetooth_alpha.png b/res/drawable-xxxhdpi/ic_settings_bluetooth_alpha.png
new file mode 100644
index 0000000..284c94e
--- /dev/null
+++ b/res/drawable-xxxhdpi/ic_settings_bluetooth_alpha.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_sysbar_quicksettings.png b/res/drawable-xxxhdpi/ic_sysbar_quicksettings.png
new file mode 100644
index 0000000..e89d484
--- /dev/null
+++ b/res/drawable-xxxhdpi/ic_sysbar_quicksettings.png
Binary files differ
diff --git a/res/drawable/fab_background.xml b/res/drawable/fab_background.xml
new file mode 100644
index 0000000..a692a2a
--- /dev/null
+++ b/res/drawable/fab_background.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="@color/fab_ripple">
+    <item>
+        <shape>
+            <solid android:color="@color/fab_shape" />
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/res/drawable/ic_bluetooth_48px.xml b/res/drawable/ic_bluetooth_48px.xml
new file mode 100644
index 0000000..94a5cc7
--- /dev/null
+++ b/res/drawable/ic_bluetooth_48px.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="48dp"
+    android:height="48dp"
+    android:viewportWidth="48"
+    android:viewportHeight="48">
+
+    <path
+        android:fillColor="@color/theme_accent"
+        android:pathData="M35.41 15.41L24 4h-2v15.17L12.83 10 10 12.83 21.17 24 10 35.17 12.83 38 22
+28.83V44h2l11.41-11.41L26.83 24l8.58-8.59zM26 11.66l3.76 3.76L26
+19.17v-7.51zm3.76 20.93L26 36.34v-7.52l3.76 3.77z" />
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_local_offer_48px.xml b/res/drawable/ic_local_offer_48px.xml
new file mode 100644
index 0000000..be20381
--- /dev/null
+++ b/res/drawable/ic_local_offer_48px.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="48dp"
+    android:height="48dp"
+    android:viewportWidth="48"
+    android:viewportHeight="48">
+
+    <path
+        android:fillColor="@color/theme_accent"
+        android:pathData="M42.82 23.16L24.83 5.17C24.1 4.45 23.1 4 22 4H8C5.79 4 4 5.79 4 8v14c0 1.11 .45
+2.11 1.18 2.83l18 18C23.9 43.55 24.9 44 26 44c1.1 0 2.11-.45
+2.83-1.17l14-14C43.55 28.1 44 27.1 44 26c0-1.11-.45-2.11-1.18-2.84zM11 14c-1.66
+0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3z" />
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_menu_add.xml b/res/drawable/ic_menu_add.xml
new file mode 100644
index 0000000..ed58072
--- /dev/null
+++ b/res/drawable/ic_menu_add.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2015 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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@drawable/ic_menu_add_white"
+    android:tint="?android:attr/colorAccent" />
diff --git a/res/drawable/ic_menu_delete.xml b/res/drawable/ic_menu_delete.xml
new file mode 100644
index 0000000..74f63c3
--- /dev/null
+++ b/res/drawable/ic_menu_delete.xml
@@ -0,0 +1,25 @@
+<!--
+    Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0"
+        android:tint="?android:attr/colorAccent">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12.0,38.0c0.0,2.21 1.79,4.0 4.0,4.0l16.0,0.0c2.21,0.0 4.0,-1.79 4.0,-4.0L36.0,14.0L12.0,14.0l0.0,24.0zM38.0,8.0l-7.0,0.0l-2.0,-2.0L19.0,6.0l-2.0,2.0l-7.0,0.0l0.0,4.0l28.0,0.0L38.0,8.0z"/>
+</vector>
diff --git a/res/drawable/ic_settings_bluetooth.xml b/res/drawable/ic_settings_bluetooth.xml
new file mode 100644
index 0000000..a76cd2c
--- /dev/null
+++ b/res/drawable/ic_settings_bluetooth.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2015 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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:src="@drawable/ic_settings_bluetooth_alpha"
+    android:tint="?android:attr/colorAccent" />
+
diff --git a/res/drawable/ic_settings_profiles.xml b/res/drawable/ic_settings_profiles.xml
new file mode 100644
index 0000000..79f7725
--- /dev/null
+++ b/res/drawable/ic_settings_profiles.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+<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,5v14H5V5H19
+M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3L19,3z
+M17,8 c0-0.6-0.4-1-1-1c-0.6,0-1,0.4-1,1s0.4,1,1,1C16.6,9,17,8.6,17,8z
+M13,8c0-0.6-0.4-1-1-1c-0.6,0-1,0.4-1,1s0.4,1,1,1 C12.6,9,13,8.6,13,8z
+M9,8c0-0.6-0.4-1-1-1S7,7.4,7,8s0.4,1,1,1S9,8.6,9,8z
+M17,13h-6v-2H9v2H7v2h2v2h2v-2h6V13z" />
+</vector>
diff --git a/res/drawable/ic_signal_wifi_4_bar_48px.xml b/res/drawable/ic_signal_wifi_4_bar_48px.xml
new file mode 100644
index 0000000..8f978ee
--- /dev/null
+++ b/res/drawable/ic_signal_wifi_4_bar_48px.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="48dp"
+    android:height="48dp"
+    android:viewportWidth="48"
+    android:viewportHeight="48">
+
+    <path
+        android:fillColor="@color/theme_accent"
+        android:pathData="M24.02 42.98L47.28 14c-.9-.68-9.85-8-23.28-8S1.62 13.32 .72 14l23.26 28.98 .02
+.02 .02 -.02z" />
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_wifi_signal_4.xml b/res/drawable/ic_wifi_signal_4.xml
new file mode 100644
index 0000000..bb7dbd0
--- /dev/null
+++ b/res/drawable/ic_wifi_signal_4.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="26dp"
+    android:height="24dp"
+    android:viewportWidth="26"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="?attr/wifi_signal_color"
+        android:pathData="M13.0,22.0L25.6,6.5C25.1,6.1 20.3,2.1 13.0,2.1S0.9,6.1 0.4,6.5L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0L13.0,22.0z"/>
+</vector>
diff --git a/res/drawable/switchbar_background.xml b/res/drawable/switchbar_background.xml
new file mode 100644
index 0000000..ac340be
--- /dev/null
+++ b/res/drawable/switchbar_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:attr/colorControlHighlight">
+    <item android:drawable="@color/switchbar_background_color" />
+</ripple>
+
diff --git a/res/layout-land-hdpi/nfc_writer.xml b/res/layout-land-hdpi/nfc_writer.xml
new file mode 100644
index 0000000..4428c1d
--- /dev/null
+++ b/res/layout-land-hdpi/nfc_writer.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent"
+    android:gravity="center">
+
+    <LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:layout_centerInParent="true">
+
+        <TextView
+            style="?android:attr/textAppearanceMedium"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="30dip"
+            android:layout_marginEnd="30dip"
+            android:layout_marginBottom="30dip"
+            android:layout_gravity="center"
+            android:text="@string/profile_nfc_text" />
+
+        <ImageView
+            android:id="@+id/nfc_writer_image"
+            android:layout_width="40dp"
+            android:layout_height="40dp"
+            android:layout_gravity="center"
+            android:tint="@color/theme_accent"
+            android:src="@drawable/ic_local_offer_48px" />
+
+        <TextView android:id="@+id/touch_tag"
+            style="?android:attr/textAppearanceLarge"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="12dip"
+            android:layout_gravity="center"
+            android:text="@string/profile_write_touch_tag" />
+
+    </LinearLayout>
+</RelativeLayout>
diff --git a/res/layout-land-hdpi/profile_bluetooth_empty_view.xml b/res/layout-land-hdpi/profile_bluetooth_empty_view.xml
new file mode 100644
index 0000000..4557f5f
--- /dev/null
+++ b/res/layout-land-hdpi/profile_bluetooth_empty_view.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@android:id/empty"
+
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="center">
+
+    <LinearLayout
+            android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:layout_centerInParent="true">
+
+        <TextView
+                style="?android:attr/textAppearanceMedium"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="30dip"
+                android:layout_marginEnd="30dip"
+                android:layout_marginBottom="30dip"
+                android:layout_gravity="center"
+                android:text="@string/no_bluetooth_triggers" />
+
+        <ImageView
+                android:layout_width="40dp"
+                android:layout_height="40dp"
+                android:layout_gravity="center"
+                android:src="@drawable/ic_bluetooth_48px" />
+
+    </LinearLayout>
+</RelativeLayout>
diff --git a/res/layout/abstract_trigger_row.xml b/res/layout/abstract_trigger_row.xml
new file mode 100644
index 0000000..02b734d
--- /dev/null
+++ b/res/layout/abstract_trigger_row.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="?android:attr/listPreferredItemHeight"
+    android:paddingStart="@*android:dimen/preference_item_padding_side"
+    android:paddingEnd="@*android:dimen/preference_item_padding_side">
+
+    <ImageView
+        android:id="@+id/icon"
+
+        android:layout_width="wrap_content"
+        android:layout_height="fill_parent"
+
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentTop="true"
+        android:layout_marginRight="6dip"
+
+        android:src="@android:drawable/ic_menu_add" />
+
+    <TextView
+        android:id="@+id/desc"
+
+        android:layout_width="fill_parent"
+        android:layout_height="26dip"
+        android:paddingLeft="16dp"
+
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentRight="true"
+        android:layout_toEndOf="@id/icon"
+
+        android:ellipsize="marquee"
+        android:singleLine="true" />
+
+    <TextView
+        android:id="@+id/title"
+
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:paddingLeft="16dp"
+        android:paddingTop="12dip"
+
+        android:layout_above="@id/desc"
+        android:layout_alignParentRight="true"
+        android:layout_alignParentTop="true"
+        android:layout_alignWithParentIfMissing="true"
+        android:layout_toEndOf="@id/icon"
+
+        android:gravity="center_vertical"
+        android:textAppearance="@android:style/TextAppearance.Material.Subhead" />
+
+</RelativeLayout>
diff --git a/res/layout/cmparts.xml b/res/layout/cmparts.xml
new file mode 100644
index 0000000..4dbe3da
--- /dev/null
+++ b/res/layout/cmparts.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2014, 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.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_height="match_parent"
+              android:layout_width="match_parent">
+
+    <LinearLayout
+            android:orientation="vertical"
+            android:layout_height="0px"
+            android:layout_width="match_parent"
+            android:layout_weight="1">
+
+        <org.cyanogenmod.cmparts.SwitchBar android:id="@+id/switch_bar"
+                  android:layout_height="?android:attr/actionBarSize"
+                  android:layout_width="match_parent"
+                  android:background="@drawable/switchbar_background"
+                  android:theme="?attr/switchBarTheme"
+                />
+
+        <FrameLayout
+                android:id="@+id/main_content"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                />
+
+    </LinearLayout>
+
+    <RelativeLayout android:id="@+id/button_bar"
+                    android:layout_height="wrap_content"
+                    android:layout_width="match_parent"
+                    android:layout_weight="0"
+                    android:visibility="gone">
+
+        <Button android:id="@+id/back_button"
+                android:layout_width="150dip"
+                android:layout_height="wrap_content"
+                android:layout_margin="5dip"
+                android:layout_alignParentStart="true"
+                android:text="@*android:string/back_button_label"
+                />
+
+        <LinearLayout
+                android:orientation="horizontal"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentEnd="true">
+
+            <Button android:id="@+id/skip_button"
+                    android:layout_width="150dip"
+                    android:layout_height="wrap_content"
+                    android:layout_margin="5dip"
+                    android:text="@*android:string/skip_button_label"
+                    android:visibility="gone"
+                    />
+
+            <Button android:id="@+id/next_button"
+                    android:layout_width="150dip"
+                    android:layout_height="wrap_content"
+                    android:layout_margin="5dip"
+                    android:text="@*android:string/next_button_label"
+                    />
+
+        </LinearLayout>
+
+    </RelativeLayout>
+
+</LinearLayout>
+
diff --git a/res/layout/dialog_profiles_brightness_override.xml b/res/layout/dialog_profiles_brightness_override.xml
new file mode 100644
index 0000000..238072e
--- /dev/null
+++ b/res/layout/dialog_profiles_brightness_override.xml
@@ -0,0 +1,37 @@
+<?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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical"
+              android:paddingStart="20dip"
+              android:paddingEnd="20dip"
+              android:gravity="center_horizontal">
+
+    <CheckBox
+            android:id="@+id/checkbox"
+            android:text="@string/profile_brightness_override_checkbox_label"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="20dip" />
+
+    <SeekBar android:id="@+id/seekbar"
+             android:layout_width="match_parent"
+             android:layout_height="wrap_content"
+             android:layout_marginTop="20dip"
+             android:layout_marginBottom="20dip" />
+
+</LinearLayout>
diff --git a/res/layout/dialog_profiles_volume_override.xml b/res/layout/dialog_profiles_volume_override.xml
new file mode 100644
index 0000000..82130b0
--- /dev/null
+++ b/res/layout/dialog_profiles_volume_override.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical"
+              android:paddingStart="20dip"
+              android:paddingEnd="20dip"
+              android:gravity="center_horizontal">
+
+    <CheckBox
+            android:id="@+id/checkbox"
+            android:text="@string/profile_volume_override_checkbox_label"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="20dip" />
+
+    <SeekBar android:id="@+id/seekbar"
+             android:layout_width="match_parent"
+             android:layout_height="wrap_content"
+             android:layout_marginTop="20dip"
+             android:layout_marginBottom="20dip" />
+
+</LinearLayout>
diff --git a/res/layout/empty_list_entry_footer.xml b/res/layout/empty_list_entry_footer.xml
new file mode 100644
index 0000000..47e8967
--- /dev/null
+++ b/res/layout/empty_list_entry_footer.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="?android:attr/listPreferredItemHeight"/>
diff --git a/res/layout/empty_textview.xml b/res/layout/empty_textview.xml
new file mode 100644
index 0000000..80a4461
--- /dev/null
+++ b/res/layout/empty_textview.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/empty"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:text="@string/profile_empty_list_profiles_off"
+        android:gravity="center"/>
\ No newline at end of file
diff --git a/res/layout/fragment_setup_actions.xml b/res/layout/fragment_setup_actions.xml
new file mode 100644
index 0000000..45595bc
--- /dev/null
+++ b/res/layout/fragment_setup_actions.xml
@@ -0,0 +1,28 @@
+<!-- Copyright (C) 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <ListView
+        android:id="@android:id/list"
+        android:layout_weight="1"
+        android:layout_width="match_parent"
+        android:layout_height="0dp" />
+
+    <View style="@style/settingSeparator" />
+
+</LinearLayout>
diff --git a/res/layout/fragment_setup_triggers.xml b/res/layout/fragment_setup_triggers.xml
new file mode 100644
index 0000000..dcf0ac2
--- /dev/null
+++ b/res/layout/fragment_setup_triggers.xml
@@ -0,0 +1,46 @@
+<!-- Copyright (C) 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/profile_setup_setup_triggers_description"
+        android:padding="8dp"
+        android:id="@+id/instructions" />
+
+    <android.support.v4.view.ViewPager
+        android:id="@+id/view_pager"
+        android:layout_weight="1"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:background="?android:attr/colorBackgroundFloating"
+        android:gravity="center">
+        <android.support.v4.view.PagerTabStrip
+                android:id="@+id/tabs"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="top"
+                android:textAppearance="@style/TextAppearance.PagerTabs"
+                android:paddingStart="@dimen/pager_tabs_padding"
+                android:paddingEnd="@dimen/pager_tabs_padding"/>
+    </android.support.v4.view.ViewPager>
+
+    <View style="@style/settingSeparator" />
+
+</LinearLayout>
diff --git a/res/layout/nfc_select.xml b/res/layout/nfc_select.xml
new file mode 100644
index 0000000..e72a979
--- /dev/null
+++ b/res/layout/nfc_select.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent"
+    android:gravity="center">
+
+    <LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:layout_centerInParent="true">
+
+        <TextView
+            style="?android:attr/textAppearanceMedium"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="30dip"
+            android:layout_marginEnd="30dip"
+            android:layout_marginBottom="30dip"
+            android:layout_gravity="center"
+            android:text="@string/profile_add_nfc_text" />
+
+        <Button android:id="@+id/add_tag"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="12dip"
+            android:layout_gravity="center"
+            android:text="@string/profile_select" />
+
+    </LinearLayout>
+</RelativeLayout>
diff --git a/res/layout/nfc_writer.xml b/res/layout/nfc_writer.xml
new file mode 100644
index 0000000..c3f35fe
--- /dev/null
+++ b/res/layout/nfc_writer.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent"
+    android:gravity="center">
+
+    <LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:layout_centerInParent="true">
+
+        <TextView
+            style="?android:attr/textAppearanceMedium"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="30dip"
+            android:layout_marginEnd="30dip"
+            android:layout_marginBottom="30dip"
+            android:layout_gravity="center"
+            android:text="@string/profile_nfc_text" />
+
+        <ImageView
+            android:id="@+id/nfc_writer_image"
+            android:layout_width="84dp"
+            android:layout_height="84dp"
+            android:layout_gravity="center"
+            android:tint="@color/theme_accent"
+            android:src="@drawable/ic_local_offer_48px" />
+
+        <TextView android:id="@+id/touch_tag"
+            style="?android:attr/textAppearanceLarge"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="12dip"
+            android:layout_gravity="center"
+            android:text="@string/profile_write_touch_tag" />
+
+    </LinearLayout>
+</RelativeLayout>
diff --git a/res/layout/preference_dialog_seekbar_material.xml b/res/layout/preference_dialog_seekbar_material.xml
new file mode 100644
index 0000000..09b963a
--- /dev/null
+++ b/res/layout/preference_dialog_seekbar_material.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:gravity="center_horizontal"
+              android:orientation="vertical">
+
+    <ImageView
+        android:id="@android:id/icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingTop="?android:attr/dialogPreferredPadding" />
+
+    <SeekBar
+        android:id="@+id/seekbar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="?android:attr/dialogPreferredPadding"
+        android:paddingStart="?android:attr/dialogPreferredPadding"
+        android:paddingEnd="?android:attr/dialogPreferredPadding" />
+
+</LinearLayout>
diff --git a/res/layout/preference_icon.xml b/res/layout/preference_icon.xml
index adbd5c9..fd1b96f 100644
--- a/res/layout/preference_icon.xml
+++ b/res/layout/preference_icon.xml
@@ -18,25 +18,21 @@
      Preference is able to place a specific widget for its particular
      type in the "widget_frame" layout. -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@android:id/widget_frame"
+    android:id="@+android:id/widget_frame"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:minHeight="56dp"
     android:gravity="center_vertical"
-    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:background="?android:attr/selectableItemBackground">
+    android:paddingEnd="?android:attr/scrollbarSize">
 
     <ImageView
         android:id="@+id/icon"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginEnd="12dip"
-        android:padding="2dp"
-        android:maxWidth="36dip"
-        android:maxHeight="36dip"
-        android:adjustViewBounds="true"
-        android:layout_gravity="center" />
+        android:layout_marginStart="6dip"
+        android:layout_marginEnd="6dip"
+        android:layout_gravity="center"
+        android:contentDescription="@null" />
 
     <RelativeLayout
         android:layout_width="wrap_content"
@@ -47,22 +43,20 @@
         android:layout_marginBottom="6dip"
         android:layout_weight="1">
 
-        <TextView android:id="@android:id/title"
+        <TextView android:id="@+android:id/title"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:singleLine="true"
-            android:textAppearance="@android:style/TextAppearance.Material.Subhead"
-            android:textColor="?android:attr/textColorPrimary"
+            android:textAppearance="?android:attr/textAppearanceLarge"
             android:ellipsize="marquee"
             android:fadingEdge="horizontal" />
 
-        <TextView android:id="@android:id/summary"
+        <TextView android:id="@+android:id/summary"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_below="@android:id/title"
             android:layout_alignStart="@android:id/title"
-            android:textAppearance="@android:style/TextAppearance.Material.Body1"
-            android:textColor="?android:attr/textColorSecondary"
+            android:textAppearance="?android:attr/textAppearanceSmall"
             android:maxLines="2" />
 
     </RelativeLayout>
diff --git a/res/layout/preference_list_fragment.xml b/res/layout/preference_list_fragment.xml
index 2e51e47..ecab428 100644
--- a/res/layout/preference_list_fragment.xml
+++ b/res/layout/preference_list_fragment.xml
@@ -52,6 +52,18 @@
 
         <include layout="@layout/loading_container" />
 
+        <org.cyanogenmod.cmparts.FloatingActionButton
+            android:id="@+id/fab"
+            android:visibility="gone"
+            android:clickable="true"
+            android:layout_width="@dimen/fab_size"
+            android:layout_height="@dimen/fab_size"
+            android:layout_gravity="bottom|end"
+            android:layout_marginEnd="@dimen/fab_margin"
+            android:layout_marginBottom="@dimen/fab_margin"
+            android:elevation="@dimen/fab_elevation"
+            android:background="@drawable/fab_background" />
+
     </FrameLayout>
 
     <TextView android:id="@android:id/empty"
diff --git a/res/layout/preference_name.xml b/res/layout/preference_name.xml
new file mode 100644
index 0000000..5b3ff86
--- /dev/null
+++ b/res/layout/preference_name.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeight"
+    android:gravity="center_vertical">
+
+    <LinearLayout
+        android:id="@+id/name_pref"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:gravity="center_vertical"
+        android:clickable="true"
+        android:focusable="true"
+        android:paddingStart="@*android:dimen/preference_item_padding_side"
+        android:paddingEnd="?android:attr/scrollbarSize"
+        android:background="?android:attr/selectableItemBackground">
+
+        <RelativeLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@*android:dimen/preference_icon_minWidth"
+            android:layout_marginEnd="6dip"
+            android:layout_marginTop="6dip"
+            android:layout_marginBottom="6dip"
+            android:layout_weight="1">
+
+            <TextView
+                android:id="@+id/title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:singleLine="true"
+                android:textAppearance="?android:attr/textAppearanceLarge"
+                android:ellipsize="marquee"
+                android:fadingEdge="horizontal"/>
+
+        </RelativeLayout>
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/res/layout/preference_profiles.xml b/res/layout/preference_profiles.xml
new file mode 100644
index 0000000..9afa730
--- /dev/null
+++ b/res/layout/preference_profiles.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:minHeight="?android:attr/listPreferredItemHeight">
+
+    <LinearLayout
+        android:id="@+id/profiles_pref"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:background="?android:attr/selectableItemBackground"
+        android:clickable="true"
+        android:focusable="true"
+        android:gravity="center_vertical"
+        android:paddingStart="@*android:dimen/preference_item_padding_side">
+
+        <LinearLayout
+            android:id="@android:id/widget_frame"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginEnd="4dip"
+            android:layout_marginStart="10dip"
+            android:gravity="center_vertical"
+            android:orientation="vertical" />
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:clickable="true"
+            android:focusable="true"
+            android:gravity="center_vertical"
+            android:orientation="vertical">
+
+            <TextView
+                android:id="@+android:id/title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:ellipsize="end"
+                android:singleLine="true"
+                android:textAppearance="?android:attr/textAppearanceMedium" />
+
+            <TextView
+                android:id="@+android:id/summary"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textAppearance="?android:attr/textAppearanceSmall" />
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <View
+        android:layout_width="2dip"
+        android:layout_height="match_parent"
+        android:layout_marginBottom="5dip"
+        android:layout_marginTop="5dip"
+        android:background="@android:drawable/divider_horizontal_dark" />
+
+    <ImageView
+        android:id="@+id/profiles_settings"
+        android:layout_width="wrap_content"
+        android:layout_height="fill_parent"
+        android:layout_gravity="center"
+        android:background="?android:attr/selectableItemBackground"
+        android:clickable="true"
+        android:contentDescription="@string/settings"
+        android:focusable="true"
+        android:paddingEnd="12dp"
+        android:paddingStart="12dip"
+        android:src="@drawable/ic_sysbar_quicksettings" />
+
+</LinearLayout>
diff --git a/res/layout/preference_profiles_widget.xml b/res/layout/preference_profiles_widget.xml
new file mode 100644
index 0000000..ab63a10
--- /dev/null
+++ b/res/layout/preference_profiles_widget.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<RadioButton
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+android:id/checkbox"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center"
+    android:focusable="false"
+    android:clickable="false" />
diff --git a/res/layout/profile_action_item.xml b/res/layout/profile_action_item.xml
new file mode 100644
index 0000000..43e04ff
--- /dev/null
+++ b/res/layout/profile_action_item.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+
+<!-- Based off frameworks/base/core/res/res/layout/preference_material.xml
+     except that this has the negative margin on the image removed. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:gravity="center_vertical"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:background="?android:attr/activatedBackgroundIndicator"
+    android:clipToPadding="false">
+
+    <RelativeLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp">
+
+        <TextView android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceListItem"
+            android:ellipsize="marquee" />
+
+        <TextView android:id="@android:id/summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@android:id/title"
+            android:layout_alignStart="@android:id/title"
+            android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+            android:textColor="?android:attr/textColorSecondary"
+            android:maxLines="10" />
+
+    </RelativeLayout>
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="end|center_vertical"
+        android:paddingStart="16dp"
+        android:orientation="vertical" />
+
+</LinearLayout>
diff --git a/res/layout/profile_bluetooth_empty_view.xml b/res/layout/profile_bluetooth_empty_view.xml
new file mode 100644
index 0000000..777ae3b
--- /dev/null
+++ b/res/layout/profile_bluetooth_empty_view.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@android:id/empty"
+
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="center">
+
+    <LinearLayout
+            android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:layout_centerInParent="true">
+
+        <TextView
+                style="?android:attr/textAppearanceMedium"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="30dip"
+                android:layout_marginEnd="30dip"
+                android:layout_marginBottom="30dip"
+                android:layout_gravity="center"
+                android:text="@string/no_bluetooth_triggers" />
+
+        <ImageView
+                android:layout_width="84dp"
+                android:layout_height="84dp"
+                android:layout_gravity="center"
+                android:src="@drawable/ic_bluetooth_48px" />
+
+    </LinearLayout>
+</RelativeLayout>
diff --git a/res/layout/profile_icon_small.xml b/res/layout/profile_icon_small.xml
new file mode 100644
index 0000000..3fd4bfc
--- /dev/null
+++ b/res/layout/profile_icon_small.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+<ImageView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/icon"
+        android:layout_width="30dp"
+        android:layout_height="30dp"
+        android:layout_marginEnd="4dip"
+        android:gravity="center"
+        android:layout_gravity="center_vertical"
+        android:contentDescription="@null" />
diff --git a/res/layout/profile_name_dialog.xml b/res/layout/profile_name_dialog.xml
new file mode 100644
index 0000000..11ea1d3
--- /dev/null
+++ b/res/layout/profile_name_dialog.xml
@@ -0,0 +1,41 @@
+<!--
+     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="wrap_content"
+    android:baselineAligned="false"
+    android:orientation="vertical"
+    android:padding="16dip">
+
+    <TextView
+        android:id="@+id/prompt"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingLeft="8dp"
+        android:paddingBottom="8dip"
+        android:text="@string/rename_dialog_message"
+        android:textAppearance="?android:attr/textAppearanceSmall" />
+
+    <EditText
+        android:id="@+id/name"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:hint="@string/rename_dialog_hint"
+        android:singleLine="true"
+        android:textAppearance="?android:attr/textAppearanceMedium" />
+
+</LinearLayout>
diff --git a/res/layout/profile_wifi_empty_view.xml b/res/layout/profile_wifi_empty_view.xml
new file mode 100644
index 0000000..9330f53
--- /dev/null
+++ b/res/layout/profile_wifi_empty_view.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@android:id/empty"
+
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="center">
+
+    <LinearLayout
+            android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:layout_centerInParent="true">
+
+        <TextView
+                style="?android:attr/textAppearanceMedium"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="30dip"
+                android:layout_marginEnd="30dip"
+                android:layout_marginBottom="30dip"
+                android:layout_gravity="center"
+                android:text="@string/no_wifi_triggers" />
+
+        <ImageView
+                android:layout_width="84dp"
+                android:layout_height="84dp"
+                android:layout_gravity="center"
+                android:src="@drawable/ic_signal_wifi_4_bar_48px" />
+
+    </LinearLayout>
+</RelativeLayout>
diff --git a/res/layout/switch_bar.xml b/res/layout/switch_bar.xml
new file mode 100644
index 0000000..14c84d9
--- /dev/null
+++ b/res/layout/switch_bar.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2014, 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.
+*/
+-->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <TextView android:id="@+id/switch_text"
+              android:layout_height="wrap_content"
+              android:layout_width="0dp"
+              android:layout_weight="1"
+              android:layout_gravity="center_vertical"
+              android:maxLines="2"
+              android:ellipsize="end"
+              android:textAppearance="@style/TextAppearance.Switch"
+              android:textColor="@color/switchbar_text_color"
+              android:textAlignment="viewStart" />
+
+    <org.cyanogenmod.cmparts.ToggleSwitch android:id="@+id/switch_widget"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:background="@null"
+            android:theme="@style/ThemeOverlay.SwitchBar" />
+
+</merge>
+
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 2d33833..40570c7 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -98,4 +98,77 @@
         <item>2</item>
     </string-array>
 
+    <string-array name="profile_action_generic_connection_entries" translatable="false">
+        <item>@string/profile_action_none</item>
+        <item>@string/profile_action_disable</item>
+        <item>@string/profile_action_enable</item>
+    </string-array>
+    
+    <string-array name="profile_doze_entries" translatable="false">
+        <item>@string/profile_action_none</item>
+        <item>@string/profile_action_enable</item>
+        <item>@string/profile_action_disable</item>
+    </string-array>
+    
+    <string-array name="profile_notification_light_entries" translatable="false">
+        <item>@string/profile_action_none</item>
+        <item>@string/profile_action_enable</item>
+        <item>@string/profile_action_disable</item>
+    </string-array>
+
+    <!-- Profile mode options. -->
+    <string-array name="profile_entries" translatable="false">
+        <item>@string/profile_entries_on</item>
+        <item>@string/profile_entries_off</item>
+        <item>@string/profile_entries_no_override</item>
+    </string-array>
+
+    <!-- Values for vibrate_entries matching constants in SoundSettings. Do not translate. -->
+    <string-array name="profile_values" translatable="false">
+        <item>OVERRIDE</item>
+        <item>SUPPRESS</item>
+        <item>DEFAULT</item>
+    </string-array>
+
+    <!-- Profile lock mode summaries. Do not translate. -->
+    <string-array name="profile_lockmode_entries" translatable="false">
+        <item>@string/profile_action_system</item>
+        <item>@string/profile_lockmode_insecure_summary</item>
+        <item>@string/profile_lockmode_disabled_summary</item>
+    </string-array>
+
+    <!-- Ring mode options. -->
+    <string-array name="ring_mode_entries" translatable="false">
+        <item>@string/ring_mode_normal</item>
+        <item>@string/ring_mode_vibrate</item>
+        <item>@string/ring_mode_mute</item>
+        <item>@string/profile_action_none</item>
+    </string-array>
+
+    <!-- Values for Ring mode. Do not translate. -->
+    <string-array name="ring_mode_values" translatable="false">
+        <item>normal</item>
+        <item>vibrate</item>
+        <item>mute</item>
+        <item></item>
+    </string-array>
+
+    <!--  Values for profile Wi-Fi triggers -->
+    <string-array name="profile_trigger_wifi_options" translatable="false">
+        <item>@string/profile_trigger_connect</item>
+        <item>@string/profile_trigger_disconnect</item>
+        <item>@string/profile_trigger_a2dp_connect</item>
+        <item>@string/profile_trigger_a2dp_disconnect</item>
+        <item>@string/profile_trigger_notrigger</item>
+    </string-array>
+
+    <!--  Values for profile Wi-Fi triggers.  Do not translate. -->
+    <string-array name="profile_trigger_wifi_options_values" translatable="false">
+        <item>0</item>
+        <item>1</item>
+        <item>3</item>
+        <item>4</item>
+        <item>2</item>
+    </string-array>
+
 </resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index aa7a340..08859a0 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -14,11 +14,38 @@
      limitations under the License.
 -->
 
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-           xmlns:cm="http://schemas.android.com/apk/res-auto">
+<resources>
+    <declare-styleable name="IconPreferenceScreen">
+        <attr name="icon" />
+    </declare-styleable>
+
+    <!-- For Search -->
+    <declare-styleable name="Preference">
+        <attr name="keywords" format="string" />
+    </declare-styleable>
+
+    <!-- For DotsPageIndicator -->
+    <declare-styleable name="DotsPageIndicator">
+        <attr name="dotDiameter" format="dimension" />
+        <attr name="dotGap" format="dimension" />
+        <attr name="animationDuration" format="integer" />
+        <attr name="pageIndicatorColor" format="color" />
+        <attr name="currentPageIndicatorColor" format="color" />
+    </declare-styleable>
+
+    <attr name="switchBarTheme" format="reference" />
+    <attr name="switchBarMarginStart" format="dimension" />
+    <attr name="switchBarMarginEnd" format="dimension" />
+    <attr name="switchBarBackgroundColor" format="color" />
 
     <attr name="preferenceBackgroundColor" format="color" />
 
+    <declare-styleable name="DividerPreference">
+        <attr name="allowDividerAbove" format="boolean" />
+        <attr name="allowDividerBelow" format="boolean" />
+    </declare-styleable>
+
+
     <declare-styleable name="IntervalSeekBar">
         <attr name="min" format="float" />
         <attr name="max" format="float" />
@@ -33,6 +60,7 @@
         <attr name="fragment" format="string" />
         <attr name="enabled" format="boolean" />
         <attr name="icon" />
-		<attr name="alias" format="string" />
-	</declare-styleable>
+        <attr name="alias" format="string" />
+    </declare-styleable>
+
 </resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 455c35c..135843c 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -17,5 +17,16 @@
 <resources>
     <color name="theme_accent">#ff009688</color>
     <color name="navbar_edit_icon_color">#607d8b</color>
+    <color name="divider">#ffe0e0e0</color>
+    <color name="setup_wizard_wifi_color_dark">#89ffffff</color>
+
+    <color name="divider_color">#20ffffff</color>
+
+    <color name="switchbar_background_color">#ff37474f</color>
+    <color name="switchbar_text_color">#ffffffff</color>
+    <color name="switch_accent_color">#ff7fcac3</color>
+
+    <color name="fab_ripple">#1fffffff</color><!-- 12% white -->
+    <color name="fab_shape">?android:attr/colorAccent</color>
 </resources>
 
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 1600171..9e31a1f 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -21,6 +21,7 @@
 
     <!-- ActionBar contentInsetStart -->
     <dimen name="actionbar_contentInsetStart">16dp</dimen>
+    <dimen name="actionbar_subsettings_contentInsetStart">72dp</dimen>
 
     <!-- Dashboard padding in its container -->
     <dimen name="dashboard_padding_start">0dp</dimen>
@@ -34,5 +35,25 @@
     <dimen name="settings_side_margin">0dip</dimen>
 
     <dimen name="oval_notification_size">26dp</dimen>
+
+    <dimen name="vert_divider_width">1dip</dimen>
+
+    <dimen name="pager_tabs_padding">0dp</dimen>
+
+    <!-- SwitchBar margin start / end -->
+    <dimen name="switchbar_margin_start">16dp</dimen>
+    <dimen name="switchbar_margin_end">16dp</dimen>
+
+    <!-- SwitchBar sub settings margin start / end -->
+    <dimen name="switchbar_subsettings_margin_start">72dp</dimen>
+    <dimen name="switchbar_subsettings_margin_end">16dp</dimen>
+
+    <!-- FAB Dimensions -->
+    <dimen name="fab_size">56dp</dimen>
+    <dimen name="fab_margin">16dp</dimen>
+    <dimen name="fab_elevation">12dp</dimen>
+    <dimen name="fab_press_translation_z">9dp</dimen>
+
+    <dimen name="profile_instruction_padding">8dp</dimen>
 </resources>
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 738a3d3..4c1a580 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -24,6 +24,15 @@
     <string name="choose_app">Choose app</string>
     <string name="reset">Reset</string>
     <string name="advanced">Advanced</string>
+    <string name="settings">Settings</string>
+    <string name="name">Name</string>
+    <string name="back">Back</string>
+    <string name="finish">Finish</string>
+    <string name="next">Next</string>
+    <string name="on">On</string>
+    <string name="off">Off</string>
+    <string name="yes">Yes</string>
+    <string name="no">No</string>
 
     <!-- Privacy Settings Header item -->
     <string name="privacy_settings_title">Privacy</string>
@@ -222,12 +231,185 @@
     <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>
 
+    <!-- Profiles -->
+    <string name="profile_menu_delete_title">Delete</string>
+    <string name="profile_action_none">Leave unchanged</string>
+    <string name="profile_action_system">System default</string>
+    <string name="profile_action_disable">Turn off</string>
+    <string name="profile_action_enable">Turn on</string>
+
+    <string name="profile_trigger_a2dp_connect">On A2DP connect</string>
+    <string name="profile_trigger_a2dp_disconnect">On A2DP disconnect</string>
+
+    <string name="profile_tabs_wifi">Wi\u2011Fi</string>
+    <string name="profile_tabs_bluetooth">Bluetooth</string>
+    <string name="profile_tabs_nfc">NFC</string>
+
+    <string name="profile_triggers_header">Triggers which will activate this profile</string>
+
+    <string name="profile_setup_setup_triggers_title">Step 1: Add triggers</string>
+    <string name="profile_setup_setup_triggers_title_config">Modify triggers: <xliff:g id="profile_name">%1$s</xliff:g></string>
+
+    <string name="profile_setup_actions_title">Step 2: Setup actions</string>
+    <string name="profile_setup_actions_title_config">Reconfigure actions</string>
+
+    <string name="profile_appgroups_manage">App groups</string>
+    <string name="profile_appgroup_manage">Manage app group</string>
+    <string name="wireless_networks_settings_title">Wireless &amp; networks</string>
+
+    <!-- Title for application group setting screen -->
+    <string name="profile_appgroups_title">App groups</string>
+    <string name="profile_new_appgroup">New app group</string>
+    <string name="profile_delete_appgroup">Delete this app group?</string>
+    <string name="profile_appgroup_name_prompt">Enter a name for the new app group</string>
+    <string name="profile_appgroup_name_title">Name</string>
+    <string name="duplicate_appgroup_name">Duplicate app group name!</string>
+    <string name="profile_app_delete_confirm">Remove this app?</string>
+    <string name="no_bluetooth_triggers">No Bluetooth devices paired.\nTap to pair Bluetooth device before configuring triggers.</string>
+    <string name="no_wifi_triggers">No Wi\u2011Fi access points configured.\nTap to connect Wi\u2011Fi before configuring triggers.</string>
+    <string name="no_triggers_configured">No triggers configured. Tap to add more.</string>
+    <string name="no_triggers_configured_nfc">Tap to setup a new NFC trigger.</string>
+
+    <string name="profile_setup_setup_triggers_description">Please select triggers which will activate this profile</string>
+    <string name="profile_setup_actions_description">Now configure what happens when the profile is activated</string>
+
+    <!-- Profiles settings  -->
+    <string name="profiles_settings_title">System profiles</string>
+    <string name="profiles_add">Add</string>
+    <string name="profile_menu_delete">Delete</string>
+    <string name="profile_settings_title">Profile</string>
+    <string name="profile_empty_list_profiles_off">To configure and use system profiles, turn profiles on.</string>
+    <string name="profile_trigger_configure">Configure trigger</string>
+    <string name="profile_write_nfc_tag">Write to NFC tag</string>
+    <string name="profile_write_touch_tag">Touch tag to write</string>
+    <string name="profile_write_success">Tag successfully written</string>
+    <string name="profile_write_failed">Tag writing failed!</string>
+    <string name="profile_selected">Profile selected: %1$s</string>
+    <string name="profile_nfc_text">Writing a profile to a NFC tag allows for tapping the tag to select the profile. Tapping a second time will select the previously selected profile.</string>
+    <string name="profile_unknown_nfc_tag">Unknown profile</string>
+    <string name="profile_add_nfc_text">This NFC tag refers to an unknown profile. Attaching this NFC tag to an existing profile will allow for selecting the profile in the future.</string>
+    <string name="profile_select">Select profile</string>
+    <string name="profile_remove_dialog_message">Remove profile %1$s?</string>
+    <string name="profile_populate_profile_from_state">Configure profile using current device settings?</string>
+    <string name="profile_menu_fill_from_state">Import current device settings</string>
+    <string name="profile_remove_current_profile">Cannot delete current profile!</string>
+    <string name="profile_app_group_category_title">Notification overrides</string>
+    <string name="profile_app_group_item_instructions">Add or remove groups</string>
+    <string name="profile_app_group_item_instructions_summary">Add or remove notification override app groups to this profile</string>
+
+    <!-- Profile mode options. -->
+    <string name="profile_entries_on">On</string>
+    <string name="profile_entries_off">Off</string>
+    <string name="profile_entries_no_override">No override</string>
+
+    <!-- Add Profile -->
+    <string name="profile_name_title">Name</string>
+    <string name="new_profile_name">&lt;new profile&gt;</string>
+
+    <!-- Rename Dialog  -->
+    <string name="rename_dialog_title">Rename</string>
+    <string name="rename_dialog_message">Enter a new name</string>
+    <string name="duplicate_appgroup_name">Duplicate app group name!</string>
+    <string name="rename_dialog_hint">Enter profile name</string>
+
+    <!-- Reset Profiles -->
+    <string name="profile_reset_title">Reset</string>
+    <string name="profile_reset_message">Delete all user created profiles and app groups and restore them to default?</string>
+
+    <!-- Delete confimation messages -->
+    <string name="profile_app_delete_confirm">Remove this app?</string>
+
+    <!-- Profile network mode -->
+    <string name="profile_networkmode_2g">2G</string>
+    <string name="profile_networkmode_3g">3G</string>
+    <string name="profile_networkmode_4g">LTE</string>
+    <string name="profile_networkmode_2g3g">2G/3G</string>
+    <string name="profile_networkmode_2g3g4g">2G/3G/LTE</string>
+
+    <!-- Profile Config screen PreferenceGroup titles -->
+    <string name="profile_volumeoverrides_title">Volume overrides</string>
+    <string name="connection_state_enabled">Enable</string>
+    <string name="volume_override_summary">Set to %1$s/%2$s</string>
+    <string name="profile_volume_override_checkbox_label">Override volume</string>
+
+    <!-- Menu item for managing profiles -->
+    <string name="profile_profiles_manage">Profiles</string>
+    <string name="profile_profile_manage">Manage profile</string>
+    <string name="profile_appgroups_manage">App groups</string>
+    <string name="profile_appgroup_manage">Manage app group</string>
+
+    <!-- Profile settings screen, section header for settings related to notification profiles -->
+    <string name="profile_settings">Profile settings</string>
+    <string name="profile_trigger_connect">On connect</string>
+    <string name="profile_trigger_disconnect">On disconnect</string>
+    <string name="profile_trigger_notrigger">No trigger</string>
+
+    <!-- Profile Settings sound modes labels -->
+    <string name="sound_mode">Notification mode</string>
+    <string name="ringer_mode">Ring mode</string>
+    <string name="lights_mode">Lights mode</string>
+    <string name="vibrate_mode">Vibrate mode</string>
+    <string name="choose_soundtone">Choose notification tone</string>
+    <string name="choose_ringtone">Choose ringtone</string>
+    <string name="ringtone_title">Phone ringtone</string>
+    <string name="ringtone_summary">""</string>
+
+    <!-- Sound settings screen, setting option name to pick ringtone (a list dialog comes up)-->
+    <string name="soundtone_title">Notification tone</string>
+    <string name="soundtone_summary" translatable="false">""</string>
+
+    <!-- Title for application group setting screen -->
+    <string name="profile_appgroups_title">App groups</string>
+    <string name="profile_applist_title">Apps</string>
+    <string name="profile_new_appgroup">New app group</string>
+    <string name="profile_delete_appgroup">Delete this app group?</string>
+    <string name="profile_appgroup_name_prompt">Enter a name for the new app group</string>
+    <string name="profile_appgroup_name_title">Name</string>
+
+    <!-- Add application dialog box title -->
+    <string name="profile_choose_app">Choose app</string>
+
+   <!-- Profiles - system settings -->
+    <string name="profile_system_settings_title">System settings</string>
+    <string name="profile_lockmode_title">Lock screen mode</string>
+    <string name="profile_lockmode_policy_disabled_summary">This profile option is disabled by a device administrator policy</string>
+    <string name="profile_lockmode_insecure_summary">Don\'t ask for PIN or password</string>
+    <string name="profile_lockmode_disabled_summary">Disable lock screen</string>
+    <string name="profile_airplanemode_title">Airplane mode</string>
+
+    <string name="profile_brightness_title">Screen brightness</string>
+
+    <string name="profile_brightness_override_summary">Set to %1$d%%</string>
+    <string name="profile_brightness_override_checkbox_label">Override brightness</string>
+
+    <!-- Connection override toggles (not all are used at this time ) -->
+    <string name="toggleWifi">Wi\u2011Fi</string>
+    <string name="toggleWifiAp">Portable Wi\u2011Fi hotspot</string>
+    <string name="toggleBluetooth">Bluetooth</string>
+    <string name="toggleGPS">GPS</string>
+    <string name="toggleData">Data connection</string>
+    <string name="toggleSync">Auto-sync data</string>
+    <string name="toggle2g3g4g">Preferred network type</string>
+    <string name="toggle2g3g4g_msim">Preferred network type (%1$s)</string>
+    <string name="toggleNfc">NFC</string>
+
+    <!-- Sound settings screen -->
+    <string name="ring_mode_title">Ring mode</string>
+    <string name="ring_mode_normal">Normal</string>
+    <string name="ring_mode_vibrate">Vibrate</string>
+    <string name="ring_mode_mute">Mute</string>
+    <string name="ring_volume_title">Ringer volume</string>
+    <string name="incoming_call_volume_title">Ringtone</string>
+    <string name="notification_volume_title">Notification</string>
+    <string name="media_volume_title">Media</string>
+    <string name="alarm_volume_title">Alarm</string>
+    <string name="doze_title">Ambient display</string>
+
 </resources>
 
 
diff --git a/res/values/styles.xml b/res/values/styles.xml
index f810add..480a8ea 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -4,7 +4,7 @@
      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
@@ -12,9 +12,69 @@
      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>
+
+    <style name="info_label">
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:textAppearance">@style/TextAppearance.info_label</item>
+        <item name="android:paddingEnd">4dip</item>
+    </style>
+
+    <style name="info_value">
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:textAppearance">@style/TextAppearance.info_value</item>
+    </style>
+
+    <style name="info_small">
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:textAppearance">@style/TextAppearance.info_small</item>
+    </style>
+
+    <style name="info_layout">
+        <item name="android:orientation">vertical</item>
+        <item name="android:paddingStart">10dip</item>
+        <item name="android:paddingTop">10dip</item>
+        <item name="android:paddingEnd">10dip</item>
+        <item name="android:paddingBottom">10dip</item>
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">match_parent</item>
+    </style>
+
+    <style name="entry_layout">
+        <item name="android:orientation">horizontal</item>
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+    </style>
+
+    <style name="form_value">
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_width">match_parent</item>
+    </style>
+
+
+    <style name="TextAppearance" parent="android:TextAppearance.Material">
+    </style>
+
+    <style name="TextAppearance.info_label">
+        <item name="android:textSize">14sp</item>
+        <item name="android:textStyle">bold</item>
+    </style>
+
+    <style name="TextAppearance.info_small">
+        <item name="android:textSize">12sp</item>
+        <item name="android:textStyle">normal</item>
+    </style>
+
+    <style name="TextAppearance.info_value">
+        <item name="android:textSize">14sp</item>
+        <item name="android:textStyle">normal</item>
+    </style>
+
     <style name="Preference">
         <item name="android:layout">@layout/preference_material_settings</item>
     </style>
@@ -23,17 +83,13 @@
         <item name="android:dialogLayout">@layout/preference_dialog_edittext</item>
     </style>
 
-    <style name="PreferenceFragmentStyle" parent="@*android:style/PreferenceFragment.Material">
-        <item name="android:layout">@layout/preference_list_fragment</item>
-    </style>
-
     <style name="PreferenceHeaderPanelSinglePane">
         <item name="android:layout_marginStart">0dp</item>
         <item name="android:layout_marginEnd">0dp</item>
         <item name="android:background">@null</item>
     </style>
 
-   <style name="PreferencePanelSinglePane" parent="@*android:style/PreferencePanel">
+    <style name="PreferencePanelSinglePane" parent="@*android:style/PreferencePanel">
         <item name="android:layout_marginStart">0dp</item>
         <item name="android:layout_marginEnd">0dp</item>
         <item name="android:paddingStart">0dp</item>
@@ -68,6 +124,30 @@
     <style name="SettingsPreferenceHeaderList" parent="@*android:style/PreferenceHeaderList">
     </style>
 
+    <style name="PreferenceFragmentStyle" parent="@*android:style/PreferenceFragment.Material">
+        <item name="android:layout">@layout/preference_list_fragment</item>
+    </style>
+
+    <style name="VertDivider">
+        <item name="android:layout_width">@dimen/vert_divider_width</item>
+        <item name="android:layout_height">fill_parent</item>
+        <item name="android:background">@color/divider_color</item>
+        <item name="android:focusable">false</item>
+        <item name="android:clickable">false</item>
+    </style>
+
+    <style name="Transparent">
+        <item name="android:alertDialogTheme">@style/Theme.AlertDialog</item>
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:windowIsFloating">true</item>
+    </style>
+
+    <style name="TextAppearance.PagerTabs" parent="@android:style/TextAppearance.Material.Widget.TabWidget" />
+
+    <!-- Scrollbar style OUTSIDE_OVERLAY -->
+    <integer name="preference_scrollbar_style">33554432</integer>
+
     <style name="TextAppearance.Medium" parent="@android:style/TextAppearance.Material.Medium" />
     <style name="TextAppearance.Small" parent="@android:style/TextAppearance.Material.Small" />
     <style name="TextAppearance.Switch" parent="@android:style/TextAppearance.Material.Title" />
@@ -76,7 +156,37 @@
         <item name="android:textColor">?android:attr/textColorSecondary</item>
     </style>
 
-    <style name="TextAppearance.TileTitle" parent="@android:style/TextAppearance.Material.Subhead" />
-    <style name="TextAppearance.TileSubTitle" parent="@android:style/TextAppearance.Material.Body1" />
+    <style name="TextAppearance.Small.SwitchBar">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textStyle">normal</item>
+    </style>
+
+    <style name="TextAppearance.RemoveDialogContent" parent="@android:style/TextAppearance.Material">
+        <item name="android:textSize">16sp</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="PreviewPagerPageIndicator">
+        <item name="dotGap">8dp</item>
+        <item name="pageIndicatorColor">?android:attr/colorControlNormal</item>
+        <item name="currentPageIndicatorColor">?android:attr/colorControlActivated</item>
+    </style>
+
+    <style name="ProfilesPreferenceStyle">
+        <item name="android:layout">@layout/preference_profiles</item>
+        <item name="android:widgetLayout">@layout/preference_profiles_widget</item>
+    </style>
+
+    <style name="settingSeparator">
+        <item name="android:paddingTop">2dp</item>
+        <item name="android:paddingBottom">2dp</item>
+        <item name="android:background">@android:drawable/divider_horizontal_dark</item>
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">1dp</item>
+    </style>
+
+    <style name="SolidSettingSeparator" parent="@style/settingSeparator">
+        <item name="android:background">@color/divider</item>
+    </style>
 
 </resources>
diff --git a/res/values/themes.xml b/res/values/themes.xml
index e575ba4..1920e55 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -15,10 +15,87 @@
 -->
 
 <resources>
+    <attr name="side_margin" format="reference|dimension" />
+    <attr name="wifi_signal_color" format="reference" />
+
+    <style name="PreferenceTheme" parent="@android:style/Theme.DeviceDefault.Settings">
+        <item name="@android:preferenceStyle">@style/Preference</item>
+        <item name="@android:editTextPreferenceStyle">@style/EditTextPreference</item>
+        <item name="@dropdownPreferenceStyle">@style/Preference.DropDown.Material</item>
+        <item name="@android:preferenceFragmentStyle">@style/PreferenceFragmentStyle</item>
+    </style>
+
+    <!-- Theme with no local references, used by AccountPreferenceBase where we have to inflate
+         layouts against a remote context using our local theme colors. Due to the implementation
+         details of Theme, we can't reference any local resources and MUST instead use the values
+         directly. So use #ff263238 instead of @color/theme_primary and so on. -->
+    <style name="Theme.SettingsBase" parent="@android:style/Theme.Material.Settings" />
+
+    <style name="Theme.Settings" parent="Theme.SettingsBase">
+        <item name="preferenceTheme">@style/PreferenceTheme</item>
+        <item name="@*android:preferenceHeaderPanelStyle">@style/PreferenceHeaderPanelSinglePane</item>
+        <item name="@*android:preferencePanelStyle">@style/PreferencePanelSinglePane</item>
+        <item name="@*android:preferenceListStyle">@style/PreferenceHeaderListSinglePane</item>
+        <item name="@*android:preferenceFragmentListStyle">@style/PreferenceFragmentListSinglePane</item>
+        <item name="@*android:preferenceFragmentPaddingSide">@dimen/settings_side_margin</item>
+
+        <item name="wifi_signal_color">?android:attr/colorAccent</item>
+        <item name="side_margin">@dimen/settings_side_margin</item>
+
+        <!-- Redefine the ActionBar style for contentInsetStart -->
+        <item name="android:actionBarStyle">@style/Theme.ActionBar</item>
+        <item name="@*android:actionBarSize">@dimen/actionbar_size</item>
+
+        <item name="switchBarTheme">@style/ThemeOverlay.SwitchBar.Settings</item>
+
+        <item name="preferenceBackgroundColor">@drawable/preference_background</item>
+
+        <!-- For all Alert Dialogs -->
+        <item name="android:alertDialogTheme">@style/Theme.AlertDialog</item>
+    </style>
+
+    <style name="Theme.SubSettings" parent="Theme.Settings">
+        <!-- Redefine the ActionBar style for contentInsetStart -->
+        <item name="android:actionBarStyle">@style/Theme.ActionBar.SubSettings</item>
+
+        <item name="switchBarTheme">@style/ThemeOverlay.SwitchBar.SubSettings</item>
+    </style>
+
     <style name="Theme.ActionBar" parent="@android:style/Widget.Material.ActionBar.Solid">
         <item name="android:contentInsetStart">@dimen/actionbar_contentInsetStart</item>
     </style>
 
+    <style name="Theme.ActionBar.SubSettings" parent="Theme.ActionBar">
+        <item name="android:contentInsetStart">@dimen/actionbar_subsettings_contentInsetStart</item>
+    </style>
+
+    <style name="ThemeOverlay.SwitchBar.Settings" parent="@android:style/ThemeOverlay.Material.Dark.ActionBar">
+        <item name="switchBarMarginStart">@dimen/switchbar_subsettings_margin_start</item>
+        <item name="switchBarMarginEnd">@dimen/switchbar_subsettings_margin_end</item>
+        <item name="switchBarBackgroundColor">@color/switchbar_background_color</item>
+    </style>
+
+    <style name="ThemeOverlay.SwitchBar.SubSettings" parent="@android:style/ThemeOverlay.Material.Dark.ActionBar">
+        <item name="switchBarMarginStart">@dimen/switchbar_subsettings_margin_start</item>
+        <item name="switchBarMarginEnd">@dimen/switchbar_subsettings_margin_end</item>
+        <item name="switchBarBackgroundColor">@color/switchbar_background_color</item>
+    </style>
+
+    <style name="Theme.DialogWhenLarge" parent="@*android:style/Theme.Material.Settings.DialogWhenLarge">
+        <!-- Redefine the ActionBar style for contentInsetStart -->
+        <item name="android:actionBarStyle">@style/Theme.ActionBar</item>
+
+        <item name="preferenceBackgroundColor">@drawable/preference_background</item>
+    </style>
+
+    <style name="Theme.SubSettingsDialogWhenLarge" parent="Theme.DialogWhenLarge">
+        <item name="preferenceTheme">@style/PreferenceTheme</item>
+        <item name="android:actionBarWidgetTheme">@null</item>
+        <item name="android:actionBarTheme">@android:style/ThemeOverlay.Material.Dark.ActionBar</item>
+
+        <item name="preferenceBackgroundColor">@drawable/preference_background</item>
+    </style>
+
     <style name="ThemeOverlay.AlertDialog" parent="@android:style/ThemeOverlay.Material.Dialog.Alert">
         <item name="android:windowSoftInputMode">adjustResize</item>
     </style>
@@ -30,40 +107,11 @@
         <item name="android:actionBarStyle">@style/Theme.ActionBar</item>
     </style>
 
-    <style name="PreferenceTheme" parent="@android:style/Theme.DeviceDefault.Settings">
-        <item name="@android:preferenceStyle">@style/Preference</item>
-        <item name="@android:editTextPreferenceStyle">@style/EditTextPreference</item>
-        <item name="@dropdownPreferenceStyle">@style/Preference.DropDown.Material</item>
-        <item name="@android:preferenceFragmentStyle">@style/PreferenceFragmentStyle</item>
-    </style>
-
-    <style name="Theme.SettingsBase" parent="@android:style/Theme.Material.Settings" />
-
-
-    <style name="Theme.Settings" parent="Theme.SettingsBase">
-        <item name="preferenceTheme">@style/PreferenceTheme</item>
-        <item name="@*android:preferenceHeaderPanelStyle">@style/PreferenceHeaderPanelSinglePane</item>
-        <item name="@*android:preferencePanelStyle">@style/PreferencePanelSinglePane</item>
-        <item name="@*android:preferenceListStyle">@style/PreferenceHeaderListSinglePane</item>
-        <item name="@*android:preferenceFragmentListStyle">@style/PreferenceFragmentListSinglePane</item>
-        <item name="@*android:preferenceFragmentPaddingSide">@dimen/settings_side_margin</item>
-
-        <!-- Redefine the ActionBar style for contentInsetStart -->
-        <item name="android:actionBarStyle">@style/Theme.ActionBar</item>
-        <item name="@*android:actionBarSize">@dimen/actionbar_size</item>
-
-        <item name="preferenceBackgroundColor">@drawable/preference_background</item>
-
-        <!-- For all Alert Dialogs -->
-        <item name="android:alertDialogTheme">@style/Theme.AlertDialog</item>
-    </style>
-
-    <style name="Theme.DialogWhenLarge" parent="@*android:style/Theme.Material.Settings.DialogWhenLarge">
-        <!-- Redefine the ActionBar style for contentInsetStart -->
-        <item name="android:actionBarStyle">@style/Theme.ActionBar</item>
-
-        <item name="preferenceBackgroundColor">@drawable/preference_background</item>
+    <!-- Used to color the switch bar controls -->
+    <style name="ThemeOverlay.SwitchBar" parent="@android:style/ThemeOverlay">
+        <!-- Used by controls, e.g. CheckBox, ProgressBar, etc. -->
+        <item name="android:colorAccent">@color/switch_accent_color</item>
+        <item name="switchBarBackgroundColor">@color/switchbar_background_color</item>
     </style>
 
 </resources>
-
diff --git a/res/xml/appgroup_list.xml b/res/xml/appgroup_list.xml
new file mode 100644
index 0000000..848ce21
--- /dev/null
+++ b/res/xml/appgroup_list.xml
@@ -0,0 +1,22 @@
+<?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"
+        android:key="profile_appgroups_list"
+        xmlns:settings="http://schemas.android.com/apk/res/com.android.settings">
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/application_list.xml b/res/xml/application_list.xml
new file mode 100644
index 0000000..43c506f
--- /dev/null
+++ b/res/xml/application_list.xml
@@ -0,0 +1,30 @@
+<?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"
+                  xmlns:settings="http://schemas.android.com/apk/res/com.android.settings">
+
+    <PreferenceCategory
+            android:key="general_section"
+            android:title="@string/profile_appgroup_name_title">
+    </PreferenceCategory>
+
+    <PreferenceCategory
+            android:key="applications_list"
+            android:title="@string/profile_applist_title" >
+    </PreferenceCategory>
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/parts_catalog.xml b/res/xml/parts_catalog.xml
index 51f9ce4..9be45dc 100644
--- a/res/xml/parts_catalog.xml
+++ b/res/xml/parts_catalog.xml
@@ -34,4 +34,8 @@
           cm:title="@string/button_pref_title"
           cm:fragment="org.cyanogenmod.cmparts.input.ButtonSettings" />
 
+    <part cm:key="profiles_settings"
+          cm:title="@string/profiles_settings_title"
+          cm:fragment="org.cyanogenmod.cmparts.profiles.ProfilesSettings" />
+
 </parts-catalog>
diff --git a/res/xml/profile_settings.xml b/res/xml/profile_settings.xml
new file mode 100644
index 0000000..8bce716
--- /dev/null
+++ b/res/xml/profile_settings.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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/profile_settings"
+        android:key="profile_settings"
+        xmlns:settings="http://schemas.android.com/apk/res/com.android.settings">
+
+    <ListPreference
+            android:key="sound_mode"
+            android:title="@string/sound_mode"
+            android:summary="@string/sound_mode"
+            android:entries="@array/profile_entries"
+            android:persistent="false"
+            android:entryValues="@array/profile_values" />
+
+    <com.android.settings.profiles.ProfileRingtonePreference
+            android:key="soundtone"
+            android:title="@string/soundtone_title"
+            android:summary="@string/soundtone_summary"
+            android:dialogTitle="@string/soundtone_title"
+            android:persistent="false"
+            android:ringtoneType="notification" />
+
+    <ListPreference
+            android:key="ringer_mode"
+            android:title="@string/ringer_mode"
+            android:summary="@string/ringer_mode"
+            android:entries="@array/profile_entries"
+            android:persistent="false"
+            android:entryValues="@array/profile_values" />
+
+    <com.android.settings.profiles.ProfileRingtonePreference
+            android:key="ringtone"
+            android:title="@string/ringtone_title"
+            android:summary="@string/ringtone_summary"
+            android:dialogTitle="@string/ringtone_title"
+            android:persistent="false"
+            android:ringtoneType="ringtone" />
+
+    <ListPreference
+            android:key="vibrate_mode"
+            android:title="@string/vibrate_mode"
+            android:summary="@string/sound_mode"
+            android:entries="@array/profile_entries"
+            android:persistent="false"
+            android:entryValues="@array/profile_values" />
+
+    <ListPreference
+            android:key="lights_mode"
+            android:title="@string/lights_mode"
+            android:summary="@string/sound_mode"
+            android:entries="@array/profile_entries"
+            android:persistent="false"
+            android:entryValues="@array/profile_values" />
+
+
+</PreferenceScreen>
diff --git a/res/xml/profiles_settings.xml b/res/xml/profiles_settings.xml
new file mode 100644
index 0000000..d3c477c
--- /dev/null
+++ b/res/xml/profiles_settings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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"
+    xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
+    android:key="profiles_list" />
diff --git a/src/org/cyanogenmod/cmparts/CMBaseSystemSettingSwitchBar.java b/src/org/cyanogenmod/cmparts/CMBaseSystemSettingSwitchBar.java
new file mode 100644
index 0000000..c10ce5d
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/CMBaseSystemSettingSwitchBar.java
@@ -0,0 +1,155 @@
+/*
+* Copyright (C) 2015 The CyanogenMod Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package org.cyanogenmod.cmparts;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.widget.Switch;
+
+import cyanogenmod.providers.CMSettings;
+
+public class CMBaseSystemSettingSwitchBar implements SwitchBar.OnSwitchChangeListener  {
+    private Context mContext;
+    private SwitchBar mSwitchBar;
+    private SettingsObserver mSettingsObserver;
+    private boolean mListeningToOnSwitchChange = false;
+
+    private boolean mStateMachineEvent;
+
+    private final String mSettingKey;
+    private final int mDefaultState;
+
+    private final SwitchBarChangeCallback mCallback;
+    public interface SwitchBarChangeCallback {
+        public void onEnablerChanged(boolean isEnabled);
+    }
+
+    public CMBaseSystemSettingSwitchBar(Context context, SwitchBar switchBar, String key,
+                                      boolean defaultState, SwitchBarChangeCallback callback) {
+        mContext = context;
+        mSwitchBar = switchBar;
+        mSettingKey = key;
+        mDefaultState = defaultState ? 1 : 0;
+        mCallback = callback;
+        mSettingsObserver = new SettingsObserver(new Handler());
+        setupSwitchBar();
+    }
+
+    public void setupSwitchBar() {
+        setSwitchState();
+        if (!mListeningToOnSwitchChange) {
+            mSwitchBar.addOnSwitchChangeListener(this);
+            mListeningToOnSwitchChange = true;
+        }
+        mSwitchBar.show();
+    }
+
+    public void teardownSwitchBar() {
+        if (mListeningToOnSwitchChange) {
+            mSwitchBar.removeOnSwitchChangeListener(this);
+            mListeningToOnSwitchChange = false;
+        }
+        mSwitchBar.hide();
+    }
+
+    public void resume(Context context) {
+        mContext = context;
+        if (!mListeningToOnSwitchChange) {
+            mSwitchBar.addOnSwitchChangeListener(this);
+            mSettingsObserver.observe();
+
+            mListeningToOnSwitchChange = true;
+        }
+    }
+
+    public void pause() {
+        if (mListeningToOnSwitchChange) {
+            mSwitchBar.removeOnSwitchChangeListener(this);
+            mSettingsObserver.unobserve();
+
+            mListeningToOnSwitchChange = false;
+        }
+    }
+
+    private void setSwitchBarChecked(boolean checked) {
+        mStateMachineEvent = true;
+        mSwitchBar.setChecked(checked);
+        mStateMachineEvent = false;
+        if (mCallback != null) {
+            mCallback.onEnablerChanged(checked);
+        }
+    }
+
+    private void setSwitchState() {
+        boolean enabled = CMSettings.System.getInt(mContext.getContentResolver(),
+                mSettingKey, mDefaultState) == 1;
+        mStateMachineEvent = true;
+        setSwitchBarChecked(enabled);
+        mStateMachineEvent = false;
+    }
+
+    @Override
+    public void onSwitchChanged(Switch switchView, boolean isChecked) {
+        //Do nothing if called as a result of a state machine event
+        if (mStateMachineEvent) {
+            return;
+        }
+
+        // Handle a switch change
+        CMSettings.System.putInt(mContext.getContentResolver(),
+                mSettingKey, isChecked ? 1 : 0);
+
+        if (mCallback != null) {
+            mCallback.onEnablerChanged(isChecked);
+        }
+    }
+
+    class SettingsObserver extends ContentObserver {
+        SettingsObserver(Handler handler) {
+            super(handler);
+        }
+
+        void observe() {
+            ContentResolver resolver = mContext.getContentResolver();
+            resolver.registerContentObserver(CMSettings.System.getUriFor(
+                    mSettingKey), false, this);
+            update();
+        }
+
+        void unobserve() {
+            ContentResolver resolver = mContext.getContentResolver();
+            resolver.unregisterContentObserver(this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            update();
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            update();
+        }
+
+        public void update() {
+            setSwitchState();
+        }
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/FloatingActionButton.java b/src/org/cyanogenmod/cmparts/FloatingActionButton.java
new file mode 100644
index 0000000..976dd54
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/FloatingActionButton.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.cyanogenmod.cmparts;
+
+import android.animation.AnimatorInflater;
+import android.content.Context;
+import android.graphics.Outline;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+import android.widget.ImageView;
+
+public class FloatingActionButton extends ImageView {
+
+    public FloatingActionButton(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setScaleType(ScaleType.CENTER);
+        setStateListAnimator(AnimatorInflater.loadStateListAnimator(context, R.anim.fab_elevation));
+        setOutlineProvider(new ViewOutlineProvider() {
+            @Override
+            public void getOutline(View view, Outline outline) {
+                outline.setOval(0, 0, getWidth(), getHeight());
+            }
+        });
+        setClipToOutline(true);
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        invalidateOutline();
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/PartsActivity.java b/src/org/cyanogenmod/cmparts/PartsActivity.java
index d3b5167..6f1eb0d 100644
--- a/src/org/cyanogenmod/cmparts/PartsActivity.java
+++ b/src/org/cyanogenmod/cmparts/PartsActivity.java
@@ -24,10 +24,15 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.nfc.NfcAdapter;
+import android.nfc.Tag;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.util.Log;
+import android.view.View;
+import android.widget.Button;
 
+import org.cyanogenmod.cmparts.profiles.NFCProfileTagCallback;
 import org.cyanogenmod.internal.cmparts.IPartsCatalog;
 import org.cyanogenmod.internal.cmparts.PartInfo;
 
@@ -35,51 +40,71 @@
 
     private static final String TAG = "PartsActivity";
 
-    public static final String EXTRA_PART = "part";
-    public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
+    // Parts mode
     public static final String ACTION_PART = "org.cyanogenmod.cmparts.PART";
+    public static final String EXTRA_PART = "part";
+
+    // Settings compatibility
+    public static final String EXTRA_SHOW_FRAGMENT = ":settings:show_fragment";
+    public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
+    public static final String EXTRA_SHOW_FRAGMENT_TITLE = ":settings:show_fragment_title";
+    public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
+    public static final String EXTRA_SHOW_FRAGMENT_TITLE_RESID =
+            ":settings:show_fragment_title_resid";
 
     private IPartsCatalog mCatalog;
 
+    private NFCProfileTagCallback mNfcProfileCallback;
+
+    private ActionBar mActionBar;
+    private SwitchBar mSwitchBar;
+    private CharSequence mInitialTitle;
+    private int mInitialTitleResId;
+
     @Override
     public void onCreate(Bundle bundle) {
         super.onCreate(bundle);
-
         connectCatalog();
 
-        Log.d(TAG, "Launched with: " + getIntent().toString() + " action: " +
-                getIntent().getAction() + " component: " + getIntent().getComponent().getClassName() +
-                " extras: " + getIntent().getExtras().toString());
+        setContentView(R.layout.cmparts);
 
         PartInfo info = null;
-        String extra = getIntent().getStringExtra(EXTRA_PART);
-        if (ACTION_PART.equals(getIntent().getAction()) && extra != null) {
-            info = PartsCatalog.getPartInfo(getResources(), extra);
-        } else {
-            info = PartsCatalog.getPartInfoForClass(getResources(),
-                    getIntent().getComponent().getClassName());
+        String action = getIntent().getAction();
+        String partExtra = getIntent().getStringExtra(EXTRA_PART);
+        String fragmentClass = getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT);
+        String component = getIntent().getComponent().getClassName();
+        Bundle initialArgs = getIntent().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
+
+        Log.d(TAG, "Launched with: " + getIntent().toString() + " action: " +
+                getIntent().getAction() + " component: " + component +
+                " part: " + partExtra + " fragment: " + fragmentClass);
+
+        if (!ACTION_PART.equals(action) && component == null) {
+            throw new UnsupportedOperationException("Unknown action: " + getIntent().getAction());
         }
 
-        if (info == null) {
-            throw new UnsupportedOperationException("Unable to get part: " + getIntent().toString());
+        if (fragmentClass == null) {
+            if (partExtra != null) {
+                // Parts mode
+                info = PartsCatalog.getPartInfo(getResources(), partExtra);
+            } else {
+                // Alias mode
+                info = PartsCatalog.getPartInfoForClass(getResources(),
+                        getIntent().getComponent().getClassName());
+            }
+            if (info == null) {
+                throw new UnsupportedOperationException("Unable to get part info: " + getIntent().toString());
+            }
+            fragmentClass = info.getFragmentClass();
         }
 
-        Log.d(TAG, "Launching fragment: " + info.getFragmentClass());
-
-        Fragment fragment = Fragment.instantiate(this, info.getFragmentClass());
-
-        ActionBar actionBar = getActionBar();
-        if (actionBar != null) {
-            actionBar.setDisplayHomeAsUpEnabled(true);
-            actionBar.setHomeButtonEnabled(true);
+        if (fragmentClass == null) {
+            throw new UnsupportedOperationException("Unable to get fragment class: " + getIntent().toString());
         }
 
-        actionBar.setTitle(info.getTitle());
+        setTitleFromIntent(getIntent(), info);
 
-        getFragmentManager().beginTransaction().replace(android.R.id.content, fragment)
-                .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
-                .commitAllowingStateLoss();
-        getFragmentManager().executePendingTransactions();
+        switchToFragment(fragmentClass, initialArgs, mInitialTitleResId, mInitialTitle);
     }
 
     @Override
@@ -108,5 +133,128 @@
             mCatalog = null;
         }
     };
+
+    public void setNfcProfileCallback(NFCProfileTagCallback callback) {
+        mNfcProfileCallback = callback;
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
+            Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
+            if (mNfcProfileCallback != null) {
+                mNfcProfileCallback.onTagRead(detectedTag);
+            }
+            return;
+        }
+        super.onNewIntent(intent);
+    }
+
+    public SwitchBar getSwitchBar() {
+        return mSwitchBar;
+    }
+
+    public void startPreferencePanel(String fragmentClass, Bundle args, int titleRes,
+                                     CharSequence titleText, Fragment resultTo, int resultRequestCode) {
+        String title = null;
+        if (titleRes < 0) {
+            if (titleText != null) {
+                title = titleText.toString();
+            } else {
+                // There not much we can do in that case
+                title = "";
+            }
+        }
+
+        Intent intent = new Intent(ACTION_PART);
+        intent.putExtra(EXTRA_SHOW_FRAGMENT, fragmentClass);
+        intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
+        intent.putExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, titleRes);
+        intent.putExtra(EXTRA_SHOW_FRAGMENT_TITLE, titleText);
+
+        if (resultTo == null) {
+            startActivity(intent);
+        } else {
+            startActivityForResult(intent, resultRequestCode);
+        }
+    }
+
+    public void finishPreferencePanel(Fragment caller, int resultCode, Intent resultData) {
+        setResult(resultCode, resultData);
+        finish();
+    }
+
+    public void switchToFragment(String fragmentClass, Bundle args, int titleRes, CharSequence titleText) {
+        Log.d(TAG, "Launching fragment: " + fragmentClass);
+
+        Fragment fragment = Fragment.instantiate(this, fragmentClass);
+        if (fragment == null) {
+            Log.e(TAG, "Invalid fragment! " + fragmentClass);
+            return;
+        }
+        fragment.setArguments(args);
+
+        FragmentTransaction transaction = getFragmentManager().beginTransaction();
+        transaction.replace(R.id.main_content, fragment);
+        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
+        if (titleRes > 0) {
+            transaction.setBreadCrumbTitle(titleRes);
+        } else if (titleText != null) {
+            transaction.setBreadCrumbTitle(titleText);
+        }
+
+        transaction.commitAllowingStateLoss();
+        getFragmentManager().executePendingTransactions();
+
+        refreshBars();
+    }
+
+
+    public Button getBackButton() {
+        return (Button) findViewById(R.id.back_button);
+    }
+
+    public Button getNextButton() {
+        return (Button) findViewById(R.id.next_button);
+    }
+
+    public void showButtonBar(boolean show) {
+        findViewById(R.id.button_bar).setVisibility(show ? View.VISIBLE : View.GONE);
+    }
+
+    private void refreshBars() {
+        mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar);
+        mActionBar = getActionBar();
+        if (mActionBar != null) {
+            mActionBar.setDisplayHomeAsUpEnabled(true);
+            mActionBar.setHomeButtonEnabled(true);
+        }
+    }
+
+    private void setTitleFromPart(PartInfo part) {
+        mInitialTitleResId = 0;
+        mInitialTitle = part.getTitle();
+        setTitle(mInitialTitle);
+    }
+
+    private void setTitleFromIntent(Intent intent, PartInfo part) {
+        if (part != null) {
+            mInitialTitleResId = -1;
+            mInitialTitle = part.getTitle();
+            setTitle(mInitialTitle);
+        } else {
+            final int initialTitleResId = intent.getIntExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, -1);
+            if (initialTitleResId > 0) {
+                mInitialTitle = null;
+                mInitialTitleResId = initialTitleResId;
+                setTitle(mInitialTitleResId);
+            } else {
+                mInitialTitleResId = -1;
+                final String initialTitle = intent.getStringExtra(EXTRA_SHOW_FRAGMENT_TITLE);
+                mInitialTitle = (initialTitle != null) ? initialTitle : getTitle();
+                setTitle(mInitialTitle);
+            }
+        }
+    }
 }
 
diff --git a/src/org/cyanogenmod/cmparts/RingtonePreference.java b/src/org/cyanogenmod/cmparts/RingtonePreference.java
new file mode 100644
index 0000000..d262fad
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/RingtonePreference.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.cyanogenmod.cmparts;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.TypedArray;
+import android.media.AudioAttributes;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.provider.Settings.System;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceManager;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+
+/**
+ * A {@link Preference} that allows the user to choose a ringtone from those on the device.
+ * The chosen ringtone's URI will be persisted as a string.
+ * <p>
+ * If the user chooses the "Default" item, the saved string will be one of
+ * {@link System#DEFAULT_RINGTONE_URI},
+ * {@link System#DEFAULT_NOTIFICATION_URI}, or
+ * {@link System#DEFAULT_ALARM_ALERT_URI}. If the user chooses the "Silent"
+ * item, the saved string will be an empty string.
+ *
+ * @attr ref android.R.styleable#RingtonePreference_ringtoneType
+ * @attr ref android.R.styleable#RingtonePreference_showDefault
+ * @attr ref android.R.styleable#RingtonePreference_showSilent
+ *
+ * Based of frameworks/base/core/java/android/preference/RingtonePreference.java
+ * but extends android.support.v7.preference.Preference instead.
+ */
+public class RingtonePreference extends Preference {
+
+    private static final String TAG = "RingtonePreference";
+
+    private int mRingtoneType;
+    private boolean mShowDefault;
+    private boolean mShowSilent;
+
+    private int mRequestCode;
+
+    public RingtonePreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        final TypedArray a = context.obtainStyledAttributes(attrs,
+                com.android.internal.R.styleable.RingtonePreference, 0, 0);
+        mRingtoneType = a.getInt(com.android.internal.R.styleable.RingtonePreference_ringtoneType,
+                RingtoneManager.TYPE_RINGTONE);
+        mShowDefault = a.getBoolean(com.android.internal.R.styleable.RingtonePreference_showDefault,
+                true);
+        mShowSilent = a.getBoolean(com.android.internal.R.styleable.RingtonePreference_showSilent,
+                true);
+        setIntent(new Intent(RingtoneManager.ACTION_RINGTONE_PICKER));
+        a.recycle();
+    }
+
+    /**
+     * Returns the sound type(s) that are shown in the picker.
+     *
+     * @return The sound type(s) that are shown in the picker.
+     * @see #setRingtoneType(int)
+     */
+    public int getRingtoneType() {
+        return mRingtoneType;
+    }
+
+    /**
+     * Sets the sound type(s) that are shown in the picker.
+     *
+     * @param type The sound type(s) that are shown in the picker.
+     * @see RingtoneManager#EXTRA_RINGTONE_TYPE
+     */
+    public void setRingtoneType(int type) {
+        mRingtoneType = type;
+    }
+
+    /**
+     * Returns whether to a show an item for the default sound/ringtone.
+     *
+     * @return Whether to show an item for the default sound/ringtone.
+     */
+    public boolean getShowDefault() {
+        return mShowDefault;
+    }
+
+    /**
+     * Sets whether to show an item for the default sound/ringtone. The default
+     * to use will be deduced from the sound type(s) being shown.
+     *
+     * @param showDefault Whether to show the default or not.
+     * @see RingtoneManager#EXTRA_RINGTONE_SHOW_DEFAULT
+     */
+    public void setShowDefault(boolean showDefault) {
+        mShowDefault = showDefault;
+    }
+
+    /**
+     * Returns whether to a show an item for 'Silent'.
+     *
+     * @return Whether to show an item for 'Silent'.
+     */
+    public boolean getShowSilent() {
+        return mShowSilent;
+    }
+
+    /**
+     * Sets whether to show an item for 'Silent'.
+     *
+     * @param showSilent Whether to show 'Silent'.
+     * @see RingtoneManager#EXTRA_RINGTONE_SHOW_SILENT
+     */
+    public void setShowSilent(boolean showSilent) {
+        mShowSilent = showSilent;
+    }
+
+    public int getRequestCode() {
+        return mRequestCode;
+    }
+
+    /**
+     * Prepares the intent to launch the ringtone picker. This can be modified
+     * to adjust the parameters of the ringtone picker.
+     *
+     * @param ringtonePickerIntent The ringtone picker intent that can be
+     *            modified by putting extras.
+     */
+    public void onPrepareRingtonePickerIntent(Intent ringtonePickerIntent) {
+
+        ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI,
+                onRestoreRingtone());
+
+        ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, mShowDefault);
+        if (mShowDefault) {
+            ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI,
+                    RingtoneManager.getDefaultUri(getRingtoneType()));
+        }
+
+        ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, mShowSilent);
+        ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, mRingtoneType);
+        ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, getTitle());
+        ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_AUDIO_ATTRIBUTES_FLAGS,
+                AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY);
+    }
+
+    /**
+     * Called when a ringtone is chosen.
+     * <p>
+     * By default, this saves the ringtone URI to the persistent storage as a
+     * string.
+     *
+     * @param ringtoneUri The chosen ringtone's {@link Uri}. Can be null.
+     */
+    protected void onSaveRingtone(Uri ringtoneUri) {
+        persistString(ringtoneUri != null ? ringtoneUri.toString() : "");
+    }
+
+    /**
+     * Called when the chooser is about to be shown and the current ringtone
+     * should be marked. Can return null to not mark any ringtone.
+     * <p>
+     * By default, this restores the previous ringtone URI from the persistent
+     * storage.
+     *
+     * @return The ringtone to be marked as the current ringtone.
+     */
+    protected Uri onRestoreRingtone() {
+        final String uriString = getPersistedString(null);
+        return !TextUtils.isEmpty(uriString) ? Uri.parse(uriString) : null;
+    }
+
+    @Override
+    protected Object onGetDefaultValue(TypedArray a, int index) {
+        return a.getString(index);
+    }
+
+    @Override
+    protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValueObj) {
+        String defaultValue = (String) defaultValueObj;
+
+        /*
+         * This method is normally to make sure the internal state and UI
+         * matches either the persisted value or the default value. Since we
+         * don't show the current value in the UI (until the dialog is opened)
+         * and we don't keep local state, if we are restoring the persisted
+         * value we don't need to do anything.
+         */
+        if (restorePersistedValue) {
+            return;
+        }
+
+        // If we are setting to the default value, we should persist it.
+        if (!TextUtils.isEmpty(defaultValue)) {
+            onSaveRingtone(Uri.parse(defaultValue));
+        }
+    }
+    protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
+        super.onAttachedToHierarchy(preferenceManager);
+    }
+
+    public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (data != null) {
+            Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
+
+            if (callChangeListener(uri != null ? uri.toString() : "")) {
+                onSaveRingtone(uri);
+            }
+        }
+
+        return true;
+    }
+
+}
diff --git a/src/org/cyanogenmod/cmparts/SettingsPreferenceFragment.java b/src/org/cyanogenmod/cmparts/SettingsPreferenceFragment.java
index f691916..03f445a 100644
--- a/src/org/cyanogenmod/cmparts/SettingsPreferenceFragment.java
+++ b/src/org/cyanogenmod/cmparts/SettingsPreferenceFragment.java
@@ -90,6 +90,7 @@
     };
 
     private ViewGroup mPinnedHeaderFrameLayout;
+    private FloatingActionButton mFloatingActionButton;
     private ViewGroup mButtonBar;
 
     private LayoutPreference mHeader;
@@ -115,6 +116,7 @@
             Bundle savedInstanceState) {
         final View root = super.onCreateView(inflater, container, savedInstanceState);
         mPinnedHeaderFrameLayout = (ViewGroup) root.findViewById(R.id.pinned_header);
+        mFloatingActionButton = (FloatingActionButton) root.findViewById(R.id.fab);
         mButtonBar = (ViewGroup) root.findViewById(R.id.button_bar);
         return root;
     }
@@ -129,6 +131,10 @@
         checkAvailablePrefs(getPreferenceScreen());
     }
 
+    public FloatingActionButton getFloatingActionButton() {
+        return mFloatingActionButton;
+    }
+
     private void checkAvailablePrefs(PreferenceGroup preferenceGroup) {
         if (preferenceGroup == null) return;
         for (int i = 0; i < preferenceGroup.getPreferenceCount(); i++) {
@@ -462,6 +468,10 @@
         getActivity().onBackPressed();
     }
 
+    public final void finishPreferencePanel(Fragment caller, int resultCode, Intent data) {
+        ((PartsActivity)getActivity()).finishPreferencePanel(caller, resultCode, data);
+    }
+
     // Some helpers for functions used by the settings fragments when they were activities
 
     /**
@@ -683,6 +693,18 @@
         }
     }
 
+    protected Button getBackButton() {
+        return (Button) ((PartsActivity)getActivity()).getBackButton();
+    }
+
+    protected Button getNextButton() {
+        return (Button) ((PartsActivity)getActivity()).getNextButton();
+    }
+
+    protected void showButtonBar(boolean show) {
+        ((PartsActivity)getActivity()).showButtonBar(show);
+    }
+
     public void finish() {
         Activity activity = getActivity();
         if (activity == null) return;
@@ -730,6 +752,20 @@
         return getPreferenceManager().getContext();
     }
 
+    public boolean startFragment(Fragment caller, String fragmentClass, int titleRes,
+                                 int requestCode, Bundle extras) {
+        final Activity activity = getActivity();
+        if (activity instanceof PartsActivity) {
+            PartsActivity sa = (PartsActivity) activity;
+            sa.startPreferencePanel(fragmentClass, extras, titleRes, null, caller, requestCode);
+            return true;
+        } else {
+            Log.w(TAG,
+                    "Parent isn't PartsActivity! (name: " + fragmentClass + ")");
+            return false;
+        }
+    }
+
     public static class HighlightablePreferenceGroupAdapter extends PreferenceGroupAdapter {
 
         private int mHighlightPosition = -1;
diff --git a/src/org/cyanogenmod/cmparts/SwitchBar.java b/src/org/cyanogenmod/cmparts/SwitchBar.java
new file mode 100644
index 0000000..7739b55
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/SwitchBar.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.cyanogenmod.cmparts;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+import android.text.style.TextAppearanceSpan;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CompoundButton;
+import android.widget.LinearLayout;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+public class SwitchBar extends LinearLayout implements CompoundButton.OnCheckedChangeListener,
+        View.OnClickListener {
+
+    public static interface OnSwitchChangeListener {
+        /**
+         * Called when the checked state of the Switch has changed.
+         *
+         * @param switchView The Switch view whose state has changed.
+         * @param isChecked  The new checked state of switchView.
+         */
+        void onSwitchChanged(Switch switchView, boolean isChecked);
+    }
+
+    private final TextAppearanceSpan mSummarySpan;
+
+    private ToggleSwitch mSwitch;
+    private TextView mTextView;
+    private String mLabel;
+    private String mSummary;
+
+    private int mStateOnLabel = R.string.on;
+    private int mStateOffLabel = R.string.off;
+
+    private ArrayList<OnSwitchChangeListener> mSwitchChangeListeners =
+            new ArrayList<OnSwitchChangeListener>();
+
+    private static int[] MARGIN_ATTRIBUTES = {
+            R.attr.switchBarMarginStart, R.attr.switchBarMarginEnd};
+
+    public SwitchBar(Context context) {
+        this(context, null);
+    }
+
+    public SwitchBar(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public SwitchBar(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public SwitchBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        LayoutInflater.from(context).inflate(R.layout.switch_bar, this);
+
+        final TypedArray a = context.obtainStyledAttributes(attrs, MARGIN_ATTRIBUTES);
+        int switchBarMarginStart = (int) a.getDimension(0, 0);
+        int switchBarMarginEnd = (int) a.getDimension(1, 0);
+        a.recycle();
+
+        mTextView = (TextView) findViewById(R.id.switch_text);
+        mLabel = getResources().getString(R.string.off);
+        mSummarySpan = new TextAppearanceSpan(mContext, R.style.TextAppearance_Small_SwitchBar);
+        updateText();
+        ViewGroup.MarginLayoutParams lp = (MarginLayoutParams) mTextView.getLayoutParams();
+        lp.setMarginStart(switchBarMarginStart);
+
+        mSwitch = (ToggleSwitch) findViewById(R.id.switch_widget);
+        // Prevent onSaveInstanceState() to be called as we are managing the state of the Switch
+        // on our own
+        mSwitch.setSaveEnabled(false);
+        lp = (MarginLayoutParams) mSwitch.getLayoutParams();
+        lp.setMarginEnd(switchBarMarginEnd);
+
+        addOnSwitchChangeListener(new OnSwitchChangeListener() {
+            @Override
+            public void onSwitchChanged(Switch switchView, boolean isChecked) {
+                setTextViewLabel(isChecked);
+            }
+        });
+
+        setOnClickListener(this);
+
+        // Default is hide
+        setVisibility(View.GONE);
+    }
+
+    public void setOnStateOnLabel(int stringRes) {
+        mStateOnLabel = stringRes;
+    }
+
+    public void setOnStateOffLabel(int stringRes) {
+        mStateOffLabel = stringRes;
+    }
+
+    public void setTextViewLabel(boolean isChecked) {
+        mLabel = getResources()
+                .getString(isChecked ? R.string.on : R.string.off);
+        updateText();
+    }
+
+    public void setSummary(String summary) {
+        mSummary = summary;
+        updateText();
+    }
+
+    private void updateText() {
+        if (TextUtils.isEmpty(mSummary)) {
+            mTextView.setText(mLabel);
+            return;
+        }
+        final SpannableStringBuilder ssb = new SpannableStringBuilder(mLabel).append('\n');
+        final int start = ssb.length();
+        ssb.append(mSummary);
+        ssb.setSpan(mSummarySpan, start, ssb.length(), 0);
+        mTextView.setText(ssb);
+    }
+
+    public void setChecked(boolean checked) {
+        setTextViewLabel(checked);
+        mSwitch.setChecked(checked);
+    }
+
+    public void setCheckedInternal(boolean checked) {
+        setTextViewLabel(checked);
+        mSwitch.setCheckedInternal(checked);
+    }
+
+    public boolean isChecked() {
+        return mSwitch.isChecked();
+    }
+
+    public void setEnabled(boolean enabled) {
+        super.setEnabled(enabled);
+        mTextView.setEnabled(enabled);
+        mSwitch.setEnabled(enabled);
+    }
+
+    public final ToggleSwitch getSwitch() {
+        return mSwitch;
+    }
+
+    public void show() {
+        if (!isShowing()) {
+            setVisibility(View.VISIBLE);
+            mSwitch.setOnCheckedChangeListener(this);
+        }
+    }
+
+    public void hide() {
+        if (isShowing()) {
+            setVisibility(View.GONE);
+            mSwitch.setOnCheckedChangeListener(null);
+        }
+    }
+
+    public boolean isShowing() {
+        return (getVisibility() == View.VISIBLE);
+    }
+
+    @Override
+    public void onClick(View v) {
+        final boolean isChecked = !mSwitch.isChecked();
+        setChecked(isChecked);
+    }
+
+    public void propagateChecked(boolean isChecked) {
+        final int count = mSwitchChangeListeners.size();
+        for (int n = 0; n < count; n++) {
+            mSwitchChangeListeners.get(n).onSwitchChanged(mSwitch, isChecked);
+        }
+    }
+
+    @Override
+    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+        propagateChecked(isChecked);
+    }
+
+    public void addOnSwitchChangeListener(OnSwitchChangeListener listener) {
+        if (mSwitchChangeListeners.contains(listener)) {
+            throw new IllegalStateException("Cannot add twice the same OnSwitchChangeListener");
+        }
+        mSwitchChangeListeners.add(listener);
+    }
+
+    public void removeOnSwitchChangeListener(OnSwitchChangeListener listener) {
+        if (!mSwitchChangeListeners.contains(listener)) {
+            throw new IllegalStateException("Cannot remove OnSwitchChangeListener");
+        }
+        mSwitchChangeListeners.remove(listener);
+    }
+
+    static class SavedState extends BaseSavedState {
+        boolean checked;
+        boolean visible;
+        int resOnLabel;
+        int resOffLabel;
+
+        SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        /**
+         * Constructor called from {@link #CREATOR}
+         */
+        private SavedState(Parcel in) {
+            super(in);
+            checked = (Boolean)in.readValue(null);
+            visible = (Boolean)in.readValue(null);
+            resOnLabel = in.readInt();
+            resOffLabel = in.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            super.writeToParcel(out, flags);
+            out.writeValue(checked);
+            out.writeValue(visible);
+            out.writeInt(resOnLabel);
+            out.writeInt(resOffLabel);
+        }
+
+        @Override
+        public String toString() {
+            return "SwitchBar.SavedState{"
+                    + Integer.toHexString(System.identityHashCode(this))
+                    + " checked=" + checked
+                    + " visible=" + visible
+                    + " resOnLabel = " + resOnLabel
+                    + " resOffLabel = " + resOffLabel
+                    + "}";
+        }
+
+        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];
+            }
+        };
+    }
+
+    @Override
+    public Parcelable onSaveInstanceState() {
+        Parcelable superState = super.onSaveInstanceState();
+
+        SavedState ss = new SavedState(superState);
+        ss.checked = mSwitch.isChecked();
+        ss.visible = isShowing();
+        ss.resOnLabel = mStateOnLabel;
+        ss.resOffLabel = mStateOffLabel;
+        return ss;
+    }
+
+    @Override
+    public void onRestoreInstanceState(Parcelable state) {
+        SavedState ss = (SavedState) state;
+
+        super.onRestoreInstanceState(ss.getSuperState());
+
+        mSwitch.setCheckedInternal(ss.checked);
+        setOnStateOnLabel(ss.resOnLabel);
+        setOnStateOffLabel(ss.resOffLabel);
+        setTextViewLabel(ss.checked);
+        setVisibility(ss.visible ? View.VISIBLE : View.GONE);
+        mSwitch.setOnCheckedChangeListener(ss.visible ? this : null);
+
+        requestLayout();
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/ToggleSwitch.java b/src/org/cyanogenmod/cmparts/ToggleSwitch.java
new file mode 100644
index 0000000..8858a30
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/ToggleSwitch.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.cyanogenmod.cmparts;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.Switch;
+
+public class ToggleSwitch extends Switch {
+
+    private ToggleSwitch.OnBeforeCheckedChangeListener mOnBeforeListener;
+
+    public static interface OnBeforeCheckedChangeListener {
+        public boolean onBeforeCheckedChanged(ToggleSwitch toggleSwitch, boolean checked);
+    }
+
+    public ToggleSwitch(Context context) {
+        super(context);
+    }
+
+    public ToggleSwitch(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public ToggleSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public ToggleSwitch(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    public void setOnBeforeCheckedChangeListener(OnBeforeCheckedChangeListener listener) {
+        mOnBeforeListener = listener;
+    }
+
+    @Override
+    public void setChecked(boolean checked) {
+        if (mOnBeforeListener != null
+                && mOnBeforeListener.onBeforeCheckedChanged(this, checked)) {
+            return;
+        }
+        super.setChecked(checked);
+    }
+
+    public void setCheckedInternal(boolean checked) {
+        super.setChecked(checked);
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/Utils.java b/src/org/cyanogenmod/cmparts/Utils.java
index 12f2b61..84d0891 100644
--- a/src/org/cyanogenmod/cmparts/Utils.java
+++ b/src/org/cyanogenmod/cmparts/Utils.java
@@ -21,7 +21,10 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
+import android.os.Build;
+import android.os.SystemProperties;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 import android.view.Surface;
 
 import org.cyanogenmod.cmparts.input.ButtonSettings;
@@ -92,4 +95,13 @@
         activity.setRequestedOrientation(frozenRotation);
     }
 
+    public static boolean isDozeAvailable(Context context) {
+        String name = Build.IS_DEBUGGABLE ? SystemProperties.get("debug.doze.component") : null;
+        if (TextUtils.isEmpty(name)) {
+            name = context.getResources().getString(
+                    com.android.internal.R.string.config_dozeComponent);
+        }
+        return !TextUtils.isEmpty(name);
+    }
+
 }
diff --git a/src/org/cyanogenmod/cmparts/profiles/AppGroupConfig.java b/src/org/cyanogenmod/cmparts/profiles/AppGroupConfig.java
new file mode 100644
index 0000000..2b1a21f
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/AppGroupConfig.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2012 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.profiles;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.NotificationGroup;
+import android.content.DialogInterface;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceGroup;
+import android.support.v7.preference.PreferenceScreen;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ListView;
+import android.widget.Toast;
+
+import org.cyanogenmod.cmparts.PackageListAdapter;
+import org.cyanogenmod.cmparts.PackageListAdapter.PackageItem;
+import org.cyanogenmod.cmparts.R;
+import org.cyanogenmod.cmparts.SettingsPreferenceFragment;
+
+import cyanogenmod.app.ProfileManager;
+
+public class AppGroupConfig extends SettingsPreferenceFragment
+        implements Preference.OnPreferenceChangeListener {
+
+    private static String TAG = "AppGroupConfig";
+
+    private static final int DIALOG_APPS = 0;
+
+    private static final int DELETE_CONFIRM = 1;
+
+    private static final int DELETE_GROUP_CONFIRM = 2;
+
+    public static final String PROFILE_SERVICE = "profile";
+
+    private ListView mListView;
+
+    private PackageManager mPackageManager;
+
+    private NotificationGroup mNotificationGroup;
+
+    private ProfileManager mProfileManager;
+
+    private NamePreference mNamePreference;
+
+    private static final int MENU_DELETE = Menu.FIRST;
+
+    private static final int MENU_ADD = Menu.FIRST + 1;
+
+    private PackageListAdapter mAppAdapter;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (savedInstanceState != null) {
+            mPackageToDelete = savedInstanceState.getString("package_delete");
+        }
+
+        mProfileManager = ProfileManager.getInstance(getActivity());
+        addPreferencesFromResource(R.xml.application_list);
+
+        final Bundle args = getArguments();
+        if (args != null) {
+            mNotificationGroup = (NotificationGroup) args.getParcelable("NotificationGroup");
+            mPackageManager = getPackageManager();
+            mAppAdapter = new PackageListAdapter(getActivity());
+
+            updatePackages();
+
+            setHasOptionsMenu(true);
+        }
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        MenuItem delete = menu.add(0, MENU_DELETE, 0, R.string.profile_menu_delete_title)
+                .setIcon(R.drawable.ic_menu_trash_holo_dark);
+        delete.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM |
+                MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+
+        MenuItem addApplication = menu.add(0, MENU_ADD, 0, R.string.profiles_add)
+                .setIcon(R.drawable.ic_menu_add)
+                .setAlphabeticShortcut('a');
+        addApplication.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM |
+                MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        return super.onCreateView(inflater, container, savedInstanceState);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case MENU_DELETE:
+                deleteNotificationGroup();
+                return true;
+            case MENU_ADD:
+                addNewApp();
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    Preference mAddPreference;
+
+    Preference mDeletePreference;
+
+    private void updatePackages() {
+        PreferenceScreen prefSet = getPreferenceScreen();
+
+        // Add the General section
+        PreferenceGroup generalPrefs = (PreferenceGroup) prefSet.findPreference("general_section");
+        if (generalPrefs != null) {
+            generalPrefs.removeAll();
+
+            // Name preference
+            mNamePreference = new NamePreference(getActivity(), mNotificationGroup.getName());
+            mNamePreference.setOnPreferenceChangeListener(this);
+            generalPrefs.addPreference(mNamePreference);
+        }
+
+        PreferenceGroup applicationsList = (PreferenceGroup) prefSet.findPreference("applications_list");
+        if (applicationsList != null) {
+            applicationsList.removeAll();
+            for (String pkg : mNotificationGroup.getPackages()) {
+                Preference pref = new Preference(getActivity());
+                try {
+                    PackageInfo group = mPackageManager.getPackageInfo(pkg, 0);
+                    pref.setKey(group.packageName);
+                    pref.setTitle(group.applicationInfo.loadLabel(mPackageManager));
+                    Drawable icon = group.applicationInfo.loadIcon(mPackageManager);
+                    pref.setIcon(icon);
+                    pref.setSelectable(true);
+                    pref.setPersistent(false);
+                    applicationsList.addPreference(pref);
+                } catch (NameNotFoundException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+        menu.add(0, R.string.profile_menu_delete_title, 0, R.string.profile_menu_delete_title);
+    }
+
+    @Override
+    public boolean onContextItemSelected(MenuItem item) {
+        AdapterContextMenuInfo aMenuInfo = (AdapterContextMenuInfo) item.getMenuInfo();
+        PackageItem selectedGroup = (PackageItem) mListView.getItemAtPosition(aMenuInfo.position);
+        switch (item.getItemId()) {
+            case R.string.profile_menu_delete_title:
+                deleteAppFromGroup(selectedGroup);
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    private void deleteAppFromGroup(PackageItem selectedGroup) {
+        if (selectedGroup != null) {
+            mNotificationGroup.removePackage(selectedGroup.packageName);
+            updatePackages();
+        }
+    }
+
+    @Override
+    public void onPause() {
+        if (mNotificationGroup != null) {
+            mProfileManager.addNotificationGroup(mNotificationGroup);
+        }
+        super.onPause();
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        if (preference == mNamePreference) {
+            String name = mNamePreference.getName().toString();
+            if (!name.equals(mNotificationGroup.getName())) {
+                if (!mProfileManager.notificationGroupExists(name)) {
+                    mNotificationGroup.setName(name);
+                } else {
+                    mNamePreference.setName(mNotificationGroup.getName());
+                    Toast.makeText(getActivity(), R.string.duplicate_appgroup_name, Toast.LENGTH_LONG).show();
+                }
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean onPreferenceTreeClick(Preference preference) {
+        if (preference instanceof Preference) {
+            String deleteItem = preference.getKey();
+            removeApp(deleteItem);
+            return true;
+        }
+        return super.onPreferenceTreeClick(preference);
+    }
+
+    private void addNewApp() {
+        showDialog(DIALOG_APPS);
+        // TODO: switch to using the built in app list rather than dialog box?
+    }
+
+    private void removeApp(String key) {
+        mPackageToDelete = key.toString();
+        showDialog(DELETE_CONFIRM);
+    }
+
+    private void deleteNotificationGroup() {
+        showDialog(DELETE_GROUP_CONFIRM);
+    }
+
+    @Override
+    public Dialog onCreateDialog(int id) {
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        final Dialog dialog;
+        switch (id) {
+            case DIALOG_APPS:
+                final ListView list = new ListView(getActivity());
+                list.setAdapter(mAppAdapter);
+                builder.setTitle(R.string.profile_choose_app);
+                builder.setView(list);
+                dialog = builder.create();
+                list.setOnItemClickListener(new OnItemClickListener() {
+                    @Override
+                    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                        PackageItem info = (PackageItem) parent.getItemAtPosition(position);
+                        mNotificationGroup.addPackage(info.packageName);
+                        updatePackages();
+                        dialog.cancel();
+                    }
+                });
+                break;
+            case DELETE_CONFIRM:
+                builder.setMessage(R.string.profile_app_delete_confirm);
+                builder.setTitle(R.string.profile_menu_delete_title);
+                builder.setIconAttribute(android.R.attr.alertDialogIcon);
+                builder.setPositiveButton(android.R.string.yes,
+                        new DialogInterface.OnClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which) {
+                                doDelete();
+                            }
+                        });
+                builder.setNegativeButton(android.R.string.no,
+                        new DialogInterface.OnClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which) {
+                            }
+                        });
+                dialog = builder.create();
+                break;
+            case DELETE_GROUP_CONFIRM:
+                builder.setMessage(R.string.profile_delete_appgroup);
+                builder.setTitle(R.string.profile_menu_delete_title);
+                builder.setIconAttribute(android.R.attr.alertDialogIcon);
+                builder.setPositiveButton(android.R.string.yes,
+                        new DialogInterface.OnClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which) {
+                                mProfileManager.removeNotificationGroup(mNotificationGroup);
+                                mNotificationGroup = null;
+                                finish();
+                            }
+                        });
+                builder.setNegativeButton(android.R.string.no,
+                        new DialogInterface.OnClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which) {
+                            }
+                        });
+                dialog = builder.create();
+                break;
+            default:
+                dialog = null;
+        }
+        return dialog;
+    }
+
+    String mPackageToDelete;
+
+    private void doDelete() {
+        mNotificationGroup.removePackage(mPackageToDelete);
+        updatePackages();
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle in) {
+        super.onSaveInstanceState(in);
+        in.putString("package_delete", mPackageToDelete);
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/AppGroupList.java b/src/org/cyanogenmod/cmparts/profiles/AppGroupList.java
new file mode 100644
index 0000000..ab267fe
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/AppGroupList.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2012 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.profiles;
+
+import android.annotation.Nullable;
+import android.app.AlertDialog;
+import android.app.NotificationGroup;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import org.cyanogenmod.cmparts.FloatingActionButton;
+import org.cyanogenmod.cmparts.R;
+import org.cyanogenmod.cmparts.SettingsPreferenceFragment;
+import org.cyanogenmod.internal.util.ScreenType;
+
+import java.util.UUID;
+
+import cyanogenmod.app.ProfileManager;
+
+public class AppGroupList extends SettingsPreferenceFragment {
+
+    private static final String TAG = "AppGroupSettings";
+
+    private ProfileManager mProfileManager;
+
+    // constant value that can be used to check return code from sub activity.
+    private static final int APP_GROUP_CONFIG = 1;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        addPreferencesFromResource(R.xml.appgroup_list);
+        mProfileManager = ProfileManager.getInstance(getActivity());
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        refreshList();
+
+        // On tablet devices remove the padding
+        if (ScreenType.isTablet(getActivity())) {
+            getListView().setPadding(0, 0, 0, 0);
+        }
+    }
+
+    @Override
+    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        final FloatingActionButton fab = getFloatingActionButton();
+        fab.setImageResource(R.drawable.ic_menu_add_white);
+        fab.setContentDescription(getString(R.string.profiles_add));
+        fab.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                addAppGroup();
+            }
+        });
+        fab.setVisibility(View.VISIBLE);
+    }
+
+    public void refreshList() {
+        PreferenceScreen appgroupList = getPreferenceScreen();
+        appgroupList.removeAll();
+
+        // Add the existing app groups
+        for (NotificationGroup group : mProfileManager.getNotificationGroups()) {
+            PreferenceScreen pref = new PreferenceScreen(getActivity(), null);
+            pref.setKey(group.getUuid().toString());
+            pref.setTitle(group.getName());
+            pref.setPersistent(false);
+            appgroupList.addPreference(pref);
+        }
+    }
+
+    @Override
+    public boolean onPreferenceTreeClick(Preference preference) {
+        if (preference instanceof PreferenceScreen) {
+            NotificationGroup group = mProfileManager.getNotificationGroup(
+                    UUID.fromString(preference.getKey()));
+            editGroup(group);
+        }
+        return super.onPreferenceTreeClick(preference);
+    }
+
+    private void addAppGroup() {
+        LayoutInflater inflater = getActivity().getLayoutInflater();
+        View content = inflater.inflate(R.layout.profile_name_dialog, null);
+        final TextView prompt = (TextView) content.findViewById(R.id.prompt);
+        final EditText entry = (EditText) content.findViewById(R.id.name);
+
+        prompt.setText(R.string.profile_appgroup_name_prompt);
+
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        builder.setTitle(R.string.profile_new_appgroup);
+        builder.setView(content);
+
+        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                String name = entry.getText().toString();
+                if (!mProfileManager.notificationGroupExists(name)) {
+                    NotificationGroup newGroup = new NotificationGroup(name);
+                    mProfileManager.addNotificationGroup(newGroup);
+
+                    refreshList();
+                } else {
+                    Toast.makeText(getActivity(),
+                            R.string.duplicate_appgroup_name, Toast.LENGTH_LONG).show();
+                }
+            }
+        });
+        builder.setNegativeButton(android.R.string.cancel, null);
+
+        AlertDialog dialog = builder.create();
+        dialog.show();
+    }
+
+    private void editGroup(NotificationGroup group) {
+        Bundle args = new Bundle();
+        args.putParcelable("NotificationGroup", group);
+
+        startFragment(this, AppGroupConfig.class.getName(), R.string.profile_appgroup_manage,
+                APP_GROUP_CONFIG, args);
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/NFCProfile.java b/src/org/cyanogenmod/cmparts/profiles/NFCProfile.java
new file mode 100644
index 0000000..135bbed
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/NFCProfile.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2012 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.profiles;
+
+import java.util.UUID;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.nfc.NfcAdapter;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.widget.Toast;
+
+import cyanogenmod.app.Profile;
+import cyanogenmod.app.ProfileManager;
+import cyanogenmod.providers.CMSettings;
+
+import org.cyanogenmod.cmparts.R;
+
+/**
+ * This activity handles NDEF_DISCOVERED intents with the cm/profile mime type.
+ * Tags should be encoded with the 16-byte UUID of the profile to be activated.
+ * Tapping a tag while that profile is already active will select the previously
+ * active profile.
+ */
+public class NFCProfile extends Activity {
+
+    private static final String PREFS_NAME = "NFCProfile";
+
+    private static final String PREFS_PREVIOUS_PROFILE = "previous-profile";
+
+    static final String PROFILE_MIME_TYPE = "cm/profile";
+
+    private ProfileManager mProfileManager;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mProfileManager = ProfileManager.getInstance(this);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        Intent intent = getIntent();
+        String action = intent.getAction();
+        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
+            Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
+            if (rawMsgs != null) {
+                NdefMessage[] msgs = new NdefMessage[rawMsgs.length];
+                for (int i = 0; i < rawMsgs.length; i++) {
+                    msgs[i] = (NdefMessage) rawMsgs[i];
+                    for (NdefRecord record : msgs[i].getRecords()) {
+                        String type = new String(record.getType());
+                        byte[] payload = record.getPayload();
+                        if (PROFILE_MIME_TYPE.equals(type) && payload != null
+                                && payload.length == 16) {
+                            handleProfileMimeType(payload);
+                        }
+                    }
+                }
+            }
+        }
+        finish();
+    }
+
+    private void handleProfileMimeType(byte[] payload) {
+        UUID profileUuid = NFCProfileUtils.toUUID(payload);
+
+        boolean enabled = CMSettings.System.getInt(getContentResolver(),
+                CMSettings.System.SYSTEM_PROFILES_ENABLED, 1) == 1;
+
+        if (enabled) {
+            // Only do NFC profile changing if System Profile support is enabled
+            Profile currentProfile = mProfileManager.getActiveProfile();
+            Profile targetProfile = mProfileManager.getProfile(profileUuid);
+
+            if (targetProfile == null) {
+                // show profile selection for unknown tag
+                Intent i = new Intent(this, NFCProfileSelect.class);
+                i.putExtra(NFCProfileSelect.EXTRA_PROFILE_UUID, profileUuid.toString());
+                i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+                this.startActivity(i);
+            } else {
+                // switch to profile
+                if (currentProfile == null || !currentProfile.getUuid().equals(profileUuid)) {
+                    saveCurrentProfile();
+                    switchTo(profileUuid);
+                } else {
+                    Profile lastProfile = getPreviouslySelectedProfile();
+                    if (lastProfile != null) {
+                        switchTo(lastProfile.getUuid());
+                        clearPreviouslySelectedProfile();
+                    }
+                }
+            }
+        }
+    }
+
+    private void switchTo(UUID uuid) {
+        Profile p = mProfileManager.getProfile(uuid);
+        if (p != null) {
+            mProfileManager.setActiveProfile(uuid);
+
+            Toast.makeText(
+                    this,
+                    getString(R.string.profile_selected, p.getName()),
+                    Toast.LENGTH_LONG).show();
+            NFCProfileUtils.vibrate(this);
+        }
+    }
+
+    private Profile getPreviouslySelectedProfile() {
+        Profile previous = null;
+        SharedPreferences prefs = getSharedPreferences(PREFS_NAME, 0);
+        String uuid = prefs.getString(PREFS_PREVIOUS_PROFILE, null);
+        if (uuid != null) {
+            previous = mProfileManager.getProfile(UUID.fromString(uuid));
+        }
+        return previous;
+    }
+
+    private void clearPreviouslySelectedProfile() {
+        SharedPreferences.Editor editor = getSharedPreferences(PREFS_NAME, 0).edit();
+        editor.remove(PREFS_PREVIOUS_PROFILE);
+        editor.commit();
+    }
+
+    private void saveCurrentProfile() {
+        Profile currentProfile = mProfileManager.getActiveProfile();
+        if (currentProfile != null) {
+            SharedPreferences.Editor editor = getSharedPreferences(PREFS_NAME, 0).edit();
+            editor.putString(PREFS_PREVIOUS_PROFILE, currentProfile.getUuid().toString());
+            editor.commit();
+        }
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/NFCProfileSelect.java b/src/org/cyanogenmod/cmparts/profiles/NFCProfileSelect.java
new file mode 100644
index 0000000..e4d29ca
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/NFCProfileSelect.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2012 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.profiles;
+
+import java.util.UUID;
+
+import android.app.Activity;
+import android.app.AlertDialog.Builder;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Toast;
+
+import cyanogenmod.app.Profile;
+import cyanogenmod.app.ProfileManager;
+
+import org.cyanogenmod.cmparts.R;
+
+/**
+ * Activity to support attaching a unknown NFC tag to an existing profile.
+ */
+public class NFCProfileSelect extends Activity {
+
+    private static final String TAG = "NFCProfileSelect";
+
+    static final String EXTRA_PROFILE_UUID = "PROFILE_UUID";
+
+    private ProfileManager mProfileManager;
+
+    private UUID mProfileUuid;
+
+    final static int defaultChoice = -1;
+
+    private int currentChoice = defaultChoice;
+
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mProfileManager = ProfileManager.getInstance(this);
+
+        setContentView(R.layout.nfc_select);
+        setTitle(R.string.profile_unknown_nfc_tag);
+
+        findViewById(R.id.add_tag).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                showProfileSelectionDialog();
+            }
+        });
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        String profileUuid = getIntent().getStringExtra(EXTRA_PROFILE_UUID);
+        if (profileUuid != null) {
+            mProfileUuid = UUID.fromString(profileUuid);
+        } else {
+            finish();
+        }
+    }
+
+    void showProfileSelectionDialog() {
+        final Profile[] profiles = mProfileManager.getProfiles();
+        final String[] profileNames = new String[profiles.length];
+        for (int i = 0; i < profiles.length; i++) {
+            profileNames[i] = profiles[i].getName();
+        }
+
+        Builder builder = new Builder(this);
+        builder.setTitle(R.string.profile_settings_title);
+        builder.setSingleChoiceItems(profileNames, currentChoice, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                currentChoice = which;
+            }
+        });
+        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                if (currentChoice != defaultChoice) {
+                    Profile profile = profiles[currentChoice];
+                    profile.addSecondaryUuid(mProfileUuid);
+                    mProfileManager.updateProfile(profile);
+                    Toast.makeText(NFCProfileSelect.this, R.string.profile_write_success, Toast.LENGTH_LONG).show();
+                }
+                finish();
+            }
+        });
+        builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                finish();
+            }
+        });
+        builder.show();
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/NFCProfileTagCallback.java b/src/org/cyanogenmod/cmparts/profiles/NFCProfileTagCallback.java
new file mode 100644
index 0000000..3f2d9a8
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/NFCProfileTagCallback.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 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.
+ */
+package org.cyanogenmod.cmparts.profiles;
+
+import android.nfc.Tag;
+
+public interface NFCProfileTagCallback {
+    public void onTagRead(Tag tag);
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/NFCProfileUtils.java b/src/org/cyanogenmod/cmparts/profiles/NFCProfileUtils.java
new file mode 100644
index 0000000..e2f1c6a
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/NFCProfileUtils.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2012 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.profiles;
+
+import android.content.Context;
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.nfc.Tag;
+import android.nfc.tech.Ndef;
+import android.nfc.tech.NdefFormatable;
+import android.os.Vibrator;
+import android.util.Log;
+
+import cyanogenmod.app.Profile;
+
+import java.io.IOException;
+import java.util.UUID;
+
+public class NFCProfileUtils {
+
+    private static final String TAG = "NFCUtils";
+
+    private static final long[] VIBRATION_PATTERN = {
+            0, 100, 10000
+    };
+
+    public static void vibrate(Context context) {
+        Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
+        vibrator.vibrate(VIBRATION_PATTERN, -1);
+    }
+
+    /*
+     * Writes an NdefMessage to a NFC tag
+     */
+    public static boolean writeTag(NdefMessage message, Tag tag) {
+        int size = message.toByteArray().length;
+        try {
+            Ndef ndef = Ndef.get(tag);
+            if (ndef != null) {
+                ndef.connect();
+                if (!ndef.isWritable()) {
+                    Log.e(TAG, "Tag is not writable!");
+                    return false;
+                }
+                if (ndef.getMaxSize() < size) {
+                    Log.e(TAG,
+                            "Tag exceeds max ndef message size! [" + size + " > "
+                                    + ndef.getMaxSize() + "]");
+                    return false;
+                }
+                ndef.writeNdefMessage(message);
+                return true;
+            } else {
+                NdefFormatable format = NdefFormatable.get(tag);
+                if (format != null) {
+                    try {
+                        format.connect();
+                        format.format(message);
+                        return true;
+                    } catch (IOException e) {
+                        Log.e(TAG, "Write error!", e);
+                        return false;
+                    }
+                } else {
+                    return false;
+                }
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Write error!", e);
+            return false;
+        }
+    }
+
+    /* Convert a 16-byte array to a UUID */
+    static UUID toUUID(byte[] byteArray) {
+
+        long msb = 0;
+        long lsb = 0;
+        for (int i = 0; i < 8; i++) {
+            msb = (msb << 8) | (byteArray[i] & 0xff);
+        }
+        for (int i = 8; i < 16; i++) {
+            lsb = (lsb << 8) | (byteArray[i] & 0xff);
+        }
+        UUID result = new UUID(msb, lsb);
+
+        return result;
+    }
+
+    /* Convert a UUID to a 16-byte array */
+    static byte[] asByteArray(UUID uuid) {
+        long msb = uuid.getMostSignificantBits();
+        long lsb = uuid.getLeastSignificantBits();
+        byte[] buffer = new byte[16];
+
+        for (int i = 0; i < 8; i++) {
+            buffer[i] = (byte) (msb >>> 8 * (7 - i));
+        }
+        for (int i = 8; i < 16; i++) {
+            buffer[i] = (byte) (lsb >>> 8 * (7 - i));
+        }
+
+        return buffer;
+    }
+
+    /*
+     * Convert a profiles into an NdefMessage. The profile UUID is 16 bytes and
+     * stored with the cm/profile mimetype
+     */
+    public static NdefMessage getProfileAsNdef(Profile profile) {
+        byte[] profileBytes = NFCProfileUtils.asByteArray(profile.getUuid());
+
+        NdefRecord record = NdefRecord.createMime(NFCProfile.PROFILE_MIME_TYPE, profileBytes);
+        return new NdefMessage(new NdefRecord[] { record });
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/NFCProfileWriter.java b/src/org/cyanogenmod/cmparts/profiles/NFCProfileWriter.java
new file mode 100644
index 0000000..6036744
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/NFCProfileWriter.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2012 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.profiles;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.nfc.NfcAdapter;
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Toast;
+
+import cyanogenmod.app.Profile;
+import cyanogenmod.app.ProfileManager;
+
+import org.cyanogenmod.cmparts.R;
+
+import java.util.UUID;
+
+/**
+ * Activity to support writing a profile to an NFC tag.
+ * The mime type is "cm/profile" and the payload is the raw bytes of the profile's
+ * UUID. The payload was intentionally kept small to support writing on 46-byte tags.
+ */
+public class NFCProfileWriter extends Activity {
+
+    private static final String TAG = "NFCProfileWriter";
+
+    static final String EXTRA_PROFILE_UUID = "PROFILE_UUID";
+
+    private NfcAdapter mNfcAdapter;
+
+    private IntentFilter[] mWriteTagFilters;
+
+    private Profile mProfile;
+
+    private ProfileManager mProfileManager;
+
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
+        mProfileManager = ProfileManager.getInstance(this);
+
+        setContentView(R.layout.nfc_writer);
+        setTitle(R.string.profile_write_nfc_tag);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        String profileUuid = getIntent().getStringExtra(EXTRA_PROFILE_UUID);
+        if (profileUuid != null) {
+            mProfile = mProfileManager.getProfile(UUID.fromString(profileUuid));
+            Log.d(TAG, "Profile to write: " + mProfile.getName());
+            enableTagWriteMode();
+        }
+    }
+
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        disableTagWriteMode();
+    }
+
+    private PendingIntent getPendingIntent() {
+        return PendingIntent.getActivity(this, 0,
+                new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
+    }
+
+    private void disableTagWriteMode() {
+        mNfcAdapter.disableForegroundDispatch(this);
+    }
+
+    private void enableTagWriteMode() {
+        IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
+        mWriteTagFilters = new IntentFilter[] {
+            tagDetected
+        };
+        mNfcAdapter.enableForegroundDispatch(this, getPendingIntent(), mWriteTagFilters, null);
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        // Tag writing mode
+        if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
+            Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
+            if (NFCProfileUtils.writeTag(NFCProfileUtils.getProfileAsNdef(mProfile), detectedTag)) {
+                Toast.makeText(this, R.string.profile_write_success, Toast.LENGTH_LONG).show();
+                NFCProfileUtils.vibrate(this);
+            } else {
+                Toast.makeText(this, R.string.profile_write_failed, Toast.LENGTH_LONG).show();
+            }
+            finish();
+        }
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/NamePreference.java b/src/org/cyanogenmod/cmparts/profiles/NamePreference.java
new file mode 100644
index 0000000..c48f737
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/NamePreference.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2012 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.profiles;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.cyanogenmod.cmparts.R;
+
+public class NamePreference extends Preference implements
+        View.OnClickListener, Preference.OnPreferenceChangeListener {
+    private static final String TAG = NamePreference.class.getSimpleName();
+
+    private TextView mNameView;
+
+    private String mName;
+
+    /**
+     * @param context
+     * @param title
+     */
+    public NamePreference(Context context, String name) {
+        super(context);
+        mName = name.toString();
+        init();
+    }
+
+    /**
+     * @param context
+     */
+    public NamePreference(Context context) {
+        super(context);
+        init();
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+
+        View namePref = holder.findViewById(R.id.name_pref);
+        if ((namePref != null) && namePref instanceof LinearLayout) {
+            namePref.setOnClickListener(this);
+        }
+
+        mNameView = (TextView) holder.findViewById(R.id.title);
+
+        updatePreferenceViews();
+    }
+
+    private void init() {
+        setLayoutResource(R.layout.preference_name);
+    }
+
+    public void setName(String name) {
+        mName = (name.toString());
+        updatePreferenceViews();
+    }
+
+    public String getName() {
+        return(mName.toString());
+    }
+
+    private void updatePreferenceViews() {
+        if (mNameView != null) {
+            mNameView.setText(mName.toString());
+        }
+    }
+
+    @Override
+    public void onClick(android.view.View v) {
+        if (v != null) {
+            Context context = getContext();
+            if (context != null) {
+                final EditText entry = new EditText(context);
+                entry.setSingleLine();
+                entry.setText(mName.toString());
+
+                AlertDialog.Builder builder = new AlertDialog.Builder(context);
+                builder.setTitle(R.string.rename_dialog_title);
+                builder.setMessage(R.string.rename_dialog_message);
+                builder.setView(entry, 34, 16, 34, 16);
+                builder.setPositiveButton(android.R.string.ok,
+                        new DialogInterface.OnClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which) {
+                                String value = entry.getText().toString();
+                                mName = value.toString();
+                                mNameView.setText(value.toString());
+                                callChangeListener(this);
+                            }
+                        });
+                builder.setNegativeButton(android.R.string.cancel, null);
+                AlertDialog dialog = builder.create();
+                dialog.show();
+                ((TextView)dialog.findViewById(android.R.id.message)).setTextAppearance(context,
+                        android.R.style.TextAppearance_DeviceDefault_Small);
+            }
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        callChangeListener(preference);
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/src/org/cyanogenmod/cmparts/profiles/ProfileGroupConfig.java b/src/org/cyanogenmod/cmparts/profiles/ProfileGroupConfig.java
new file mode 100644
index 0000000..9f6aaa6
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/ProfileGroupConfig.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2012 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.profiles;
+
+import java.util.UUID;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.Preference.OnPreferenceChangeListener;
+
+import cyanogenmod.app.Profile;
+import cyanogenmod.app.ProfileGroup;
+import cyanogenmod.app.ProfileGroup.Mode;
+import cyanogenmod.app.ProfileManager;
+
+import org.cyanogenmod.cmparts.R;
+import org.cyanogenmod.cmparts.SettingsPreferenceFragment;
+
+public class ProfileGroupConfig extends SettingsPreferenceFragment implements
+        OnPreferenceChangeListener {
+
+    private static final CharSequence KEY_SOUNDMODE = "sound_mode";
+    private static final CharSequence KEY_VIBRATEMODE = "vibrate_mode";
+    private static final CharSequence KEY_LIGHTSMODE = "lights_mode";
+    private static final CharSequence KEY_RINGERMODE = "ringer_mode";
+    private static final CharSequence KEY_SOUNDTONE = "soundtone";
+    private static final CharSequence KEY_RINGTONE = "ringtone";
+
+    Profile mProfile;
+    ProfileGroup mProfileGroup;
+
+    private ListPreference mSoundMode;
+    private ListPreference mRingerMode;
+    private ListPreference mVibrateMode;
+    private ListPreference mLightsMode;
+    private ProfileRingtonePreference mRingTone;
+    private ProfileRingtonePreference mSoundTone;
+    private ProfileManager mProfileManager;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        addPreferencesFromResource(R.xml.profile_settings);
+
+        final Bundle args = getArguments();
+        if (args != null) {
+            mProfile = (Profile) args.getParcelable("Profile");
+            UUID uuid = UUID.fromString(args.getString("ProfileGroup"));
+
+            mProfileManager = ProfileManager.getInstance(getActivity());
+            mProfileGroup = mProfile.getProfileGroup(uuid);
+
+            mRingerMode = (ListPreference) findPreference(KEY_RINGERMODE);
+            mSoundMode = (ListPreference) findPreference(KEY_SOUNDMODE);
+            mVibrateMode = (ListPreference) findPreference(KEY_VIBRATEMODE);
+            mLightsMode = (ListPreference) findPreference(KEY_LIGHTSMODE);
+            mRingTone = (ProfileRingtonePreference) findPreference(KEY_RINGTONE);
+            mSoundTone = (ProfileRingtonePreference) findPreference(KEY_SOUNDTONE);
+
+            mRingTone.setShowSilent(false);
+            mSoundTone.setShowSilent(false);
+
+            mSoundMode.setOnPreferenceChangeListener(this);
+            mRingerMode.setOnPreferenceChangeListener(this);
+            mVibrateMode.setOnPreferenceChangeListener(this);
+            mLightsMode.setOnPreferenceChangeListener(this);
+            mSoundTone.setOnPreferenceChangeListener(this);
+            mRingTone.setOnPreferenceChangeListener(this);
+
+            updateState();
+        }
+    }
+
+    private void updateState() {
+        mVibrateMode.setValue(mProfileGroup.getVibrateMode().name());
+        mSoundMode.setValue(mProfileGroup.getSoundMode().name());
+        mRingerMode.setValue(mProfileGroup.getRingerMode().name());
+        mLightsMode.setValue(mProfileGroup.getLightsMode().name());
+
+        mVibrateMode.setSummary(mVibrateMode.getEntry());
+        mSoundMode.setSummary(mSoundMode.getEntry());
+        mRingerMode.setSummary(mRingerMode.getEntry());
+        mLightsMode.setSummary(mLightsMode.getEntry());
+
+        if (mProfileGroup.getSoundOverride() != null) {
+            mSoundTone.setRingtone(mProfileGroup.getSoundOverride());
+        }
+
+        if (mProfileGroup.getRingerOverride() != null) {
+            mRingTone.setRingtone(mProfileGroup.getRingerOverride());
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        if (preference == mVibrateMode) {
+            mProfileGroup.setVibrateMode(Mode.valueOf((String) newValue));
+        } else if (preference == mSoundMode) {
+            mProfileGroup.setSoundMode(Mode.valueOf((String) newValue));
+        } else if (preference == mRingerMode) {
+            mProfileGroup.setRingerMode(Mode.valueOf((String) newValue));
+        } else if (preference == mLightsMode) {
+            mProfileGroup.setLightsMode(Mode.valueOf((String) newValue));
+        } else if (preference == mRingTone) {
+            Uri uri = Uri.parse((String) newValue);
+            mProfileGroup.setRingerOverride(uri);
+        } else if (preference == mSoundTone) {
+            Uri uri = Uri.parse((String) newValue);
+            mProfileGroup.setSoundOverride(uri);
+        }
+
+        mProfileManager.updateProfile(mProfile);
+
+        updateState();
+        return true;
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/ProfileRingtonePreference.java b/src/org/cyanogenmod/cmparts/profiles/ProfileRingtonePreference.java
new file mode 100644
index 0000000..6eda71c
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/ProfileRingtonePreference.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2012 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.profiles;
+
+import android.content.Context;
+import android.content.Intent;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.util.AttributeSet;
+
+import org.cyanogenmod.cmparts.RingtonePreference;
+
+public class ProfileRingtonePreference extends RingtonePreference {
+    private static final String TAG = "ProfileRingtonePreference";
+
+    public ProfileRingtonePreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void onPrepareRingtonePickerIntent(Intent ringtonePickerIntent) {
+        super.onPrepareRingtonePickerIntent(ringtonePickerIntent);
+
+        /*
+         * Since this preference is for choosing the default ringtone, it
+         * doesn't make sense to show a 'Default' item.
+         */
+        ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, false);
+    }
+
+    private Uri mRingtone;
+
+    void setRingtone(Uri uri) {
+        mRingtone = uri;
+    }
+
+    @Override
+    protected Uri onRestoreRingtone() {
+        if (mRingtone == null) {
+            return super.onRestoreRingtone();
+        } else {
+            return mRingtone;
+        }
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/ProfilesPreference.java b/src/org/cyanogenmod/cmparts/profiles/ProfilesPreference.java
new file mode 100644
index 0000000..02a3e19
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/ProfilesPreference.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2012 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.profiles;
+
+import android.content.ActivityNotFoundException;
+import android.os.Bundle;
+import android.support.v7.preference.CheckBoxPreference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.cyanogenmod.cmparts.PartsActivity;
+import org.cyanogenmod.cmparts.R;
+import org.cyanogenmod.cmparts.SettingsPreferenceFragment;
+
+public class ProfilesPreference extends CheckBoxPreference implements View.OnClickListener {
+    private static final String TAG = ProfilesPreference.class.getSimpleName();
+    private static final float DISABLED_ALPHA = 0.4f;
+    private final SettingsPreferenceFragment mFragment;
+    private final Bundle mSettingsBundle;
+
+    // constant value that can be used to check return code from sub activity.
+    private static final int PROFILE_DETAILS = 1;
+
+    private ImageView mProfilesSettingsButton;
+    private TextView mTitleText;
+    private TextView mSummaryText;
+    private View mProfilesPref;
+
+    public ProfilesPreference(SettingsPreferenceFragment fragment, Bundle settingsBundle) {
+        super(fragment.getActivity(), null, R.style.ProfilesPreferenceStyle);
+        setLayoutResource(R.layout.preference_profiles);
+        setWidgetLayoutResource(R.layout.preference_profiles_widget);
+        mFragment = fragment;
+        mSettingsBundle = settingsBundle;
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+
+        mProfilesPref = holder.findViewById(R.id.profiles_pref);
+        mProfilesPref.setOnClickListener(this);
+        mProfilesSettingsButton = (ImageView)holder.findViewById(R.id.profiles_settings);
+        mTitleText = (TextView)holder.findViewById(android.R.id.title);
+        mSummaryText = (TextView)holder.findViewById(android.R.id.summary);
+
+        if (mSettingsBundle != null) {
+            mProfilesSettingsButton.setOnClickListener(this);
+            updatePreferenceViews();
+        } else {
+            mProfilesSettingsButton.setVisibility(View.GONE);
+        }
+    }
+
+    @Override
+    public void onClick(View view) {
+        if (view == mProfilesSettingsButton) {
+            try {
+                startProfileConfigActivity();
+            } catch (ActivityNotFoundException e) {
+                // If the settings activity does not exist, we can just do nothing...
+            }
+        } else if (view == mProfilesPref) {
+            if (isEnabled() && !isChecked()) {
+                setChecked(true);
+                callChangeListener(getKey());
+            }
+        }
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        super.setEnabled(enabled);
+        if (enabled) {
+            updatePreferenceViews();
+        } else {
+            disablePreferenceViews();
+        }
+    }
+
+    private void disablePreferenceViews() {
+        if (mProfilesSettingsButton != null) {
+            mProfilesSettingsButton.setEnabled(false);
+            mProfilesSettingsButton.setAlpha(DISABLED_ALPHA);
+        }
+        if (mProfilesPref != null) {
+            mProfilesPref.setEnabled(false);
+            mProfilesPref.setBackgroundColor(0);
+        }
+    }
+
+    private void updatePreferenceViews() {
+        final boolean checked = isChecked();
+        if (mProfilesSettingsButton != null) {
+            mProfilesSettingsButton.setEnabled(true);
+            mProfilesSettingsButton.setClickable(true);
+            mProfilesSettingsButton.setFocusable(true);
+        }
+        if (mTitleText != null) {
+            mTitleText.setEnabled(true);
+        }
+        if (mSummaryText != null) {
+            mSummaryText.setEnabled(checked);
+        }
+        if (mProfilesPref != null) {
+            mProfilesPref.setEnabled(true);
+            mProfilesPref.setLongClickable(checked);
+            final boolean enabled = isEnabled();
+            mProfilesPref.setOnClickListener(enabled ? this : null);
+            if (!enabled) {
+                mProfilesPref.setBackgroundColor(0);
+            }
+        }
+    }
+
+    // utility method used to start sub activity
+    private void startProfileConfigActivity() {
+        PartsActivity pa = (PartsActivity) mFragment.getActivity();
+        pa.startPreferencePanel(SetupActionsFragment.class.getCanonicalName(), mSettingsBundle,
+                R.string.profile_profile_manage, null, null, PROFILE_DETAILS);
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/ProfilesSettings.java b/src/org/cyanogenmod/cmparts/profiles/ProfilesSettings.java
new file mode 100644
index 0000000..29ea35b
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/ProfilesSettings.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2012 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.profiles;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import org.cyanogenmod.cmparts.CMBaseSystemSettingSwitchBar;
+import org.cyanogenmod.cmparts.PartsActivity;
+import org.cyanogenmod.cmparts.R;
+import org.cyanogenmod.cmparts.SettingsPreferenceFragment;
+
+import java.util.UUID;
+
+import cyanogenmod.app.Profile;
+import cyanogenmod.app.ProfileManager;
+import cyanogenmod.providers.CMSettings;
+
+public class ProfilesSettings extends SettingsPreferenceFragment
+        implements CMBaseSystemSettingSwitchBar.SwitchBarChangeCallback,
+        Preference.OnPreferenceChangeListener {
+    private static final String TAG = "ProfilesSettings";
+
+    public static final String EXTRA_PROFILE = "Profile";
+    public static final String EXTRA_NEW_PROFILE = "new_profile_mode";
+
+    private static final int MENU_RESET = Menu.FIRST;
+    private static final int MENU_APP_GROUPS = Menu.FIRST + 1;
+
+    private final IntentFilter mFilter;
+    private final BroadcastReceiver mReceiver;
+
+    private ProfileManager mProfileManager;
+    private CMBaseSystemSettingSwitchBar mProfileEnabler;
+
+    private boolean mEnabled;
+
+    ViewGroup mContainer;
+
+    static Bundle mSavedState;
+
+    public ProfilesSettings() {
+        mFilter = new IntentFilter();
+        mFilter.addAction(ProfileManager.PROFILES_STATE_CHANGED_ACTION);
+
+        mReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                String action = intent.getAction();
+                if (ProfileManager.PROFILES_STATE_CHANGED_ACTION.equals(action)) {
+                    updateProfilesEnabledState();
+                }
+            }
+        };
+
+        setHasOptionsMenu(true);
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        addPreferencesFromResource(R.xml.profiles_settings);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        View view = super.onCreateView(inflater, container, savedInstanceState);
+        FrameLayout frameLayout = new FrameLayout(getActivity());
+        mContainer = frameLayout;
+        frameLayout.addView(view);
+        return frameLayout;
+    }
+
+    @Override
+    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        // Add a footer to avoid a situation where the FAB would cover the last
+        // item's options in a non-scrollable listview.
+        View footer = LayoutInflater.from(getActivity())
+                .inflate(R.layout.empty_list_entry_footer, null, false);
+        setFooterView(footer);
+        footer.setOnClickListener(null);
+
+        View v = LayoutInflater.from(getActivity())
+                .inflate(R.layout.empty_textview, (ViewGroup) view, true);
+
+        TextView emptyTextView = (TextView) v.findViewById(R.id.empty);
+        setEmptyView(emptyTextView);
+
+        getFloatingActionButton().setImageResource(R.drawable.ic_menu_add_white);
+        getFloatingActionButton().setContentDescription(getString(R.string.profiles_add));
+        getFloatingActionButton().setOnClickListener(
+                new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        addProfile();
+                    }
+                });
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        mProfileManager = ProfileManager.getInstance(getActivity());
+        // After confirming PreferenceScreen is available, we call super.
+        super.onActivityCreated(savedInstanceState);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (mProfileEnabler != null) {
+            mProfileEnabler.resume(getActivity());
+        }
+        getActivity().registerReceiver(mReceiver, mFilter);
+
+        // check if we are enabled
+        updateProfilesEnabledState();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        if (mProfileEnabler != null) {
+            mProfileEnabler.pause();
+        }
+        getActivity().unregisterReceiver(mReceiver);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        final PartsActivity activity = (PartsActivity) getActivity();
+        mProfileEnabler = new CMBaseSystemSettingSwitchBar(activity, activity.getSwitchBar(),
+                CMSettings.System.SYSTEM_PROFILES_ENABLED, true, this);
+    }
+
+    @Override
+    public void onDestroyView() {
+        if (mProfileEnabler != null) {
+            mProfileEnabler.teardownSwitchBar();
+        }
+        super.onDestroyView();
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        super.onCreateOptionsMenu(menu, inflater);
+        menu.add(0, MENU_RESET, 0, R.string.profile_reset_title)
+                .setAlphabeticShortcut('r')
+                .setEnabled(mEnabled)
+                .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+        menu.add(0, MENU_APP_GROUPS, 0, R.string.profile_appgroups_title)
+                .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case MENU_RESET:
+                resetAll();
+                return true;
+            case MENU_APP_GROUPS:
+                startFragment(this, AppGroupList.class.getName(),
+                        R.string.profile_appgroups_title, 0, null);
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    private void addProfile() {
+        Bundle args = new Bundle();
+        args.putBoolean(EXTRA_NEW_PROFILE, true);
+        args.putParcelable(EXTRA_PROFILE, new Profile(getString(R.string.new_profile_name)));
+
+        PartsActivity pa = (PartsActivity) getActivity();
+        pa.startPreferencePanel(SetupTriggersFragment.class.getCanonicalName(), args,
+                0, null, this, 0);
+    }
+
+    private void resetAll() {
+        new AlertDialog.Builder(getActivity())
+                .setTitle(R.string.profile_reset_title)
+                .setIconAttribute(android.R.attr.alertDialogIcon)
+                .setMessage(R.string.profile_reset_message)
+                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int id) {
+                        mProfileManager.resetAll();
+                        mProfileManager.setActiveProfile(
+                                mProfileManager.getActiveProfile().getUuid());
+                        dialog.dismiss();
+                        refreshList();
+
+                    }
+                })
+                .setNegativeButton(R.string.cancel, null)
+                .show();
+    }
+
+    private void updateProfilesEnabledState() {
+        Activity activity = getActivity();
+
+        mEnabled = CMSettings.System.getInt(activity.getContentResolver(),
+                CMSettings.System.SYSTEM_PROFILES_ENABLED, 1) == 1;
+        activity.invalidateOptionsMenu();
+
+        getFloatingActionButton().setVisibility(mEnabled ? View.VISIBLE : View.GONE);
+        if (!mEnabled) {
+            getPreferenceScreen().removeAll(); // empty it
+        } else {
+            refreshList();
+        }
+    }
+
+    @Override
+    public void onEnablerChanged(boolean isEnabled) {
+        Intent intent = new Intent(ProfileManager.PROFILES_STATE_CHANGED_ACTION);
+        intent.putExtra(ProfileManager.EXTRA_PROFILES_STATE,
+                isEnabled ?
+                        ProfileManager.PROFILES_STATE_ENABLED :
+                        ProfileManager.PROFILES_STATE_DISABLED);
+        getActivity().sendBroadcast(intent);
+    }
+
+    public void refreshList() {
+        PreferenceScreen plist = getPreferenceScreen();
+        plist.removeAll();
+
+        // Get active profile, if null
+        Profile prof = mProfileManager.getActiveProfile();
+        String selectedKey = prof != null ? prof.getUuid().toString() : null;
+
+        for (Profile profile : mProfileManager.getProfiles()) {
+            Bundle args = new Bundle();
+            args.putParcelable(ProfilesSettings.EXTRA_PROFILE, profile);
+            args.putBoolean(ProfilesSettings.EXTRA_NEW_PROFILE, false);
+
+            ProfilesPreference ppref = new ProfilesPreference(this, args);
+            ppref.setKey(profile.getUuid().toString());
+            ppref.setTitle(profile.getName());
+            ppref.setPersistent(false);
+            ppref.setOnPreferenceChangeListener(this);
+            ppref.setSelectable(true);
+            ppref.setEnabled(true);
+
+            if (TextUtils.equals(selectedKey, ppref.getKey())) {
+                ppref.setChecked(true);
+            }
+
+            plist.addPreference(ppref);
+        }
+    }
+
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        if (newValue instanceof String) {
+            setSelectedProfile((String) newValue);
+            refreshList();
+        }
+        return true;
+    }
+
+    private void setSelectedProfile(String key) {
+        try {
+            UUID selectedUuid = UUID.fromString(key);
+            mProfileManager.setActiveProfile(selectedUuid);
+        } catch (IllegalArgumentException ex) {
+            ex.printStackTrace();
+        }
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/SetupActionsFragment.java b/src/org/cyanogenmod/cmparts/profiles/SetupActionsFragment.java
new file mode 100644
index 0000000..448ed53
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/SetupActionsFragment.java
@@ -0,0 +1,1162 @@
+/*
+ * Copyright (C) 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.
+ */
+package org.cyanogenmod.cmparts.profiles;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.NotificationGroup;
+import android.app.admin.DevicePolicyManager;
+import android.bluetooth.BluetoothAdapter;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.location.LocationManager;
+import android.media.AudioManager;
+import android.media.RingtoneManager;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiManager;
+//import android.net.wimax.WimaxHelper;
+import android.nfc.NfcManager;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.preference.SeekBarVolumizer;
+import android.provider.Settings;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.ListView;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import cyanogenmod.app.Profile;
+import cyanogenmod.app.ProfileGroup;
+import cyanogenmod.app.ProfileManager;
+import cyanogenmod.profiles.AirplaneModeSettings;
+import cyanogenmod.profiles.BrightnessSettings;
+import cyanogenmod.profiles.ConnectionSettings;
+import cyanogenmod.profiles.LockSettings;
+import cyanogenmod.profiles.RingModeSettings;
+import cyanogenmod.profiles.StreamSettings;
+
+import org.cyanogenmod.cmparts.R;
+import org.cyanogenmod.cmparts.PartsActivity;
+import org.cyanogenmod.cmparts.SettingsPreferenceFragment;
+import org.cyanogenmod.cmparts.profiles.actions.ItemListAdapter;
+import org.cyanogenmod.cmparts.profiles.actions.item.AirplaneModeItem;
+import org.cyanogenmod.cmparts.profiles.actions.item.AppGroupItem;
+import org.cyanogenmod.cmparts.profiles.actions.item.BrightnessItem;
+import org.cyanogenmod.cmparts.profiles.actions.item.ConnectionOverrideItem;
+import org.cyanogenmod.cmparts.profiles.actions.item.DisabledItem;
+import org.cyanogenmod.cmparts.profiles.actions.item.DozeModeItem;
+import org.cyanogenmod.cmparts.profiles.actions.item.Header;
+import org.cyanogenmod.cmparts.profiles.actions.item.Item;
+import org.cyanogenmod.cmparts.profiles.actions.item.LockModeItem;
+import org.cyanogenmod.cmparts.profiles.actions.item.NotificationLightModeItem;
+import org.cyanogenmod.cmparts.profiles.actions.item.ProfileNameItem;
+import org.cyanogenmod.cmparts.profiles.actions.item.RingModeItem;
+import org.cyanogenmod.cmparts.profiles.actions.item.TriggerItem;
+import org.cyanogenmod.cmparts.profiles.actions.item.VolumeStreamItem;
+import org.cyanogenmod.cmparts.utils.DeviceUtils;
+import org.cyanogenmod.cmparts.utils.TelephonyUtils;
+import org.cyanogenmod.cmparts.Utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static cyanogenmod.profiles.ConnectionSettings.PROFILE_CONNECTION_2G3G4G;
+import static cyanogenmod.profiles.ConnectionSettings.PROFILE_CONNECTION_BLUETOOTH;
+import static cyanogenmod.profiles.ConnectionSettings.PROFILE_CONNECTION_GPS;
+import static cyanogenmod.profiles.ConnectionSettings.PROFILE_CONNECTION_MOBILEDATA;
+import static cyanogenmod.profiles.ConnectionSettings.PROFILE_CONNECTION_NFC;
+import static cyanogenmod.profiles.ConnectionSettings.PROFILE_CONNECTION_SYNC;
+import static cyanogenmod.profiles.ConnectionSettings.PROFILE_CONNECTION_WIFI;
+import static cyanogenmod.profiles.ConnectionSettings.PROFILE_CONNECTION_WIFIAP;
+
+public class SetupActionsFragment extends SettingsPreferenceFragment
+        implements AdapterView.OnItemClickListener {
+
+    private static final int RINGTONE_REQUEST_CODE = 1000;
+    private static final int NEW_TRIGGER_REQUEST_CODE = 1001;
+    private static final int SET_NETWORK_MODE_REQUEST_CODE = 1002;
+
+    public static final String EXTRA_NETWORK_MODE_PICKED = "network_mode_picker::chosen_value";
+
+    private static final int MENU_REMOVE = Menu.FIRST;
+    private static final int MENU_FILL_PROFILE = Menu.FIRST + 1;
+
+    private static final int DIALOG_FILL_FROM_SETTINGS = 1;
+    private static final int DIALOG_AIRPLANE_MODE = 2;
+    private static final int DIALOG_BRIGHTNESS = 3;
+    private static final int DIALOG_LOCK_MODE = 4;
+    private static final int DIALOG_DOZE_MODE = 5;
+    private static final int DIALOG_RING_MODE = 6;
+    private static final int DIALOG_CONNECTION_OVERRIDE = 7;
+    private static final int DIALOG_VOLUME_STREAM = 8;
+    private static final int DIALOG_PROFILE_NAME = 9;
+
+    private static final String LAST_SELECTED_POSITION = "last_selected_position";
+    private static final int DIALOG_REMOVE_PROFILE = 10;
+
+    private static final int DIALOG_NOTIFICATION_LIGHT_MODE = 11;
+
+    private int mLastSelectedPosition = -1;
+    private Item mSelectedItem;
+
+    Profile mProfile;
+    ItemListAdapter mAdapter;
+    ProfileManager mProfileManager;
+    ListView mListView;
+
+    boolean mNewProfileMode;
+
+    private static final int[] LOCKMODE_MAPPING = new int[] {
+            Profile.LockMode.DEFAULT, Profile.LockMode.INSECURE, Profile.LockMode.DISABLE
+    };
+    private static final int[] EXPANDED_DESKTOP_MAPPING = new int[] {
+            Profile.ExpandedDesktopMode.DEFAULT,
+            Profile.ExpandedDesktopMode.ENABLE,
+            Profile.ExpandedDesktopMode.DISABLE
+    };
+    private static final int[] DOZE_MAPPING = new int[] {
+            Profile.DozeMode.DEFAULT,
+            Profile.DozeMode.ENABLE,
+            Profile.DozeMode.DISABLE
+    };
+    private static final int[] NOTIFICATION_LIGHT_MAPPING = new int[] {
+            Profile.NotificationLightMode.DEFAULT,
+            Profile.NotificationLightMode.ENABLE,
+            Profile.NotificationLightMode.DISABLE
+    };
+    private List<Item> mItems = new ArrayList<Item>();
+
+    public static SetupActionsFragment newInstance(Profile profile, boolean newProfile) {
+        SetupActionsFragment fragment = new SetupActionsFragment();
+        Bundle args = new Bundle();
+        args.putParcelable(ProfilesSettings.EXTRA_PROFILE, profile);
+        args.putBoolean(ProfilesSettings.EXTRA_NEW_PROFILE, newProfile);
+
+        fragment.setArguments(args);
+        return fragment;
+    }
+
+    public SetupActionsFragment() {
+        // Required empty public constructor
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        if (getArguments() != null) {
+            mProfile = getArguments().getParcelable(ProfilesSettings.EXTRA_PROFILE);
+            mNewProfileMode = getArguments().getBoolean(ProfilesSettings.EXTRA_NEW_PROFILE, false);
+        }
+
+        mProfileManager = ProfileManager.getInstance(getActivity());
+        mAdapter = new ItemListAdapter(getActivity(), mItems);
+        rebuildItemList();
+
+        setHasOptionsMenu(true);
+        if (mNewProfileMode && savedInstanceState == null) {
+            // only pop this up on first creation
+            showDialog(DIALOG_FILL_FROM_SETTINGS);
+        } else if (savedInstanceState != null) {
+            mLastSelectedPosition = savedInstanceState.getInt("last_selected_position", -1);
+            if (mLastSelectedPosition != -1) {
+                mSelectedItem = mAdapter.getItem(mLastSelectedPosition);
+            }
+        }
+    }
+
+    private void rebuildItemList() {
+        mItems.clear();
+        // general prefs
+        mItems.add(new Header(getString(R.string.profile_name_title)));
+        mItems.add(new ProfileNameItem(mProfile));
+
+        if (!mNewProfileMode) {
+            // triggers
+            mItems.add(new Header(getString(R.string.profile_triggers_header)));
+            mItems.add(generateTriggerItem(TriggerItem.WIFI));
+            if (DeviceUtils.deviceSupportsBluetooth()) {
+                mItems.add(generateTriggerItem(TriggerItem.BLUETOOTH));
+            }
+            if (DeviceUtils.deviceSupportsNfc(getActivity())) {
+                mItems.add(generateTriggerItem(TriggerItem.NFC));
+            }
+        }
+
+        // connection overrides
+        mItems.add(new Header(getString(R.string.wireless_networks_settings_title)));
+        if (DeviceUtils.deviceSupportsBluetooth()) {
+            mItems.add(new ConnectionOverrideItem(PROFILE_CONNECTION_BLUETOOTH,
+                    mProfile.getSettingsForConnection(PROFILE_CONNECTION_BLUETOOTH)));
+        }
+        mItems.add(generateConnectionOverrideItem(PROFILE_CONNECTION_GPS));
+        mItems.add(generateConnectionOverrideItem(PROFILE_CONNECTION_WIFI));
+        mItems.add(generateConnectionOverrideItem(PROFILE_CONNECTION_SYNC));
+        if (DeviceUtils.deviceSupportsMobileData(getActivity())) {
+            mItems.add(generateConnectionOverrideItem(PROFILE_CONNECTION_MOBILEDATA));
+            mItems.add(generateConnectionOverrideItem(PROFILE_CONNECTION_WIFIAP));
+
+            final List<SubscriptionInfo> subs = SubscriptionManager.from(getContext())
+                    .getActiveSubscriptionInfoList();
+            if (subs != null) {
+                for (SubscriptionInfo sub : subs) {
+                    mItems.add(generatePreferredNetworkOverrideItem(sub.getSubscriptionId()));
+                }
+            } else {
+                if (TelephonyManager.from(getContext()).getPhoneCount() == 1) {
+                    mItems.add(generatePreferredNetworkOverrideItem(
+                            SubscriptionManager.INVALID_SUBSCRIPTION_ID));
+                }
+            }
+        }
+        //if (WimaxHelper.isWimaxSupported(getActivity())) {
+        //    mItems.add(generateConnectionOverrideItem(PROFILE_CONNECTION_WIMAX));
+        //}
+        if (DeviceUtils.deviceSupportsNfc(getActivity())) {
+            mItems.add(generateConnectionOverrideItem(PROFILE_CONNECTION_NFC));
+        }
+
+        // add volume streams
+        mItems.add(new Header(getString(R.string.profile_volumeoverrides_title)));
+        mItems.add(generateVolumeStreamItem(AudioManager.STREAM_ALARM));
+        mItems.add(generateVolumeStreamItem(AudioManager.STREAM_MUSIC));
+        mItems.add(generateVolumeStreamItem(AudioManager.STREAM_RING));
+        mItems.add(generateVolumeStreamItem(AudioManager.STREAM_NOTIFICATION));
+
+        // system settings
+        mItems.add(new Header(getString(R.string.profile_system_settings_title)));
+        mItems.add(new RingModeItem(mProfile.getRingMode()));
+        mItems.add(new AirplaneModeItem(mProfile.getAirplaneMode()));
+        DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
+        if (!dpm.requireSecureKeyguard()) {
+            mItems.add(new LockModeItem(mProfile));
+        } else {
+            mItems.add(new DisabledItem(R.string.profile_lockmode_title,
+                    R.string.profile_lockmode_policy_disabled_summary));
+        }
+        mItems.add(new BrightnessItem(mProfile.getBrightness()));
+
+        final Activity activity = getActivity();
+        if (Utils.isDozeAvailable(activity)) {
+            mItems.add(new DozeModeItem(mProfile));
+        }
+
+        if (getResources().getBoolean(
+                com.android.internal.R.bool.config_intrusiveNotificationLed)) {
+            mItems.add(new NotificationLightModeItem(mProfile));
+        }
+
+        // app groups
+        mItems.add(new Header(getString(R.string.profile_app_group_category_title)));
+
+        int groupsAdded = 0;
+        ProfileGroup[] profileGroups = mProfile.getProfileGroups();
+        if (profileGroups != null && profileGroups.length > 1) { // it will always have "other"
+            for (ProfileGroup profileGroup : profileGroups) {
+                // only display profile group if there's a matching notification group
+                // and don't' show the wildcard group
+                if (mProfileManager.getNotificationGroup(profileGroup.getUuid()) != null
+                        && !mProfile.getDefaultGroup().getUuid().equals(
+                        profileGroup.getUuid())) {
+                    mItems.add(new AppGroupItem(mProfile, profileGroup,
+                            mProfileManager.getNotificationGroup(
+                                    profileGroup.getUuid())));
+                    groupsAdded++;
+                }
+            }
+            if (groupsAdded > 0) {
+                // add "Other" at the end
+                mItems.add(new AppGroupItem(mProfile, mProfile.getDefaultGroup(),
+                        mProfileManager.getNotificationGroup(
+                                mProfile.getDefaultGroup().getUuid())));
+            }
+        }
+        if (mProfileManager.getNotificationGroups().length > 0) {
+            // if there are notification groups available, allow them to be configured
+            mItems.add(new AppGroupItem());
+        } else if (groupsAdded == 0) {
+            // no notification groups available at all, nothing to add/remove
+            mItems.remove(mItems.get(mItems.size() - 1));
+        }
+
+        mAdapter.notifyDataSetChanged();
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        super.onCreateOptionsMenu(menu, inflater);
+        if (!mNewProfileMode) {
+            menu.add(0, MENU_REMOVE, 0, R.string.profile_menu_delete_title)
+                    .setIcon(R.drawable.ic_actionbar_delete)
+                    .setAlphabeticShortcut('d')
+                    .setEnabled(true)
+                    .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM |
+                            MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+
+            menu.add(0, MENU_FILL_PROFILE, 0, R.string.profile_menu_fill_from_state)
+                    .setAlphabeticShortcut('f')
+                    .setEnabled(true)
+                    .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case MENU_REMOVE:
+                mLastSelectedPosition = -1; // reset
+                showDialog(DIALOG_REMOVE_PROFILE);
+                return true;
+            case MENU_FILL_PROFILE:
+                showDialog(DIALOG_FILL_FROM_SETTINGS);
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    private ConnectionOverrideItem generatePreferredNetworkOverrideItem(int subId) {
+        ConnectionSettings settings = mProfile.getConnectionSettingWithSubId(subId);
+        if (settings == null) {
+            settings = new ConnectionSettings(ConnectionSettings.PROFILE_CONNECTION_2G3G4G);
+            settings.setSubId(subId);
+            mProfile.setConnectionSettings(settings);
+        }
+        return new ConnectionOverrideItem(settings.getConnectionId(), settings);
+    }
+
+    private ConnectionOverrideItem generateConnectionOverrideItem(int connectionId) {
+        ConnectionSettings settings = mProfile.getSettingsForConnection(connectionId);
+        if (settings == null) {
+            settings = new ConnectionSettings(connectionId);
+            mProfile.setConnectionSettings(settings);
+        }
+        return new ConnectionOverrideItem(connectionId, settings);
+    }
+
+    private VolumeStreamItem generateVolumeStreamItem(int stream) {
+        StreamSettings settings = mProfile.getSettingsForStream(stream);
+        if (settings == null) {
+            settings = new StreamSettings(stream);
+            mProfile.setStreamSettings(settings);
+        }
+        return new VolumeStreamItem(stream, settings);
+    }
+
+    private TriggerItem generateTriggerItem(int whichTrigger) {
+        return new TriggerItem(mProfile, whichTrigger);
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+
+        if (mNewProfileMode) {
+            TextView desc = new TextView(getActivity());
+            int descPadding = getResources().getDimensionPixelSize(
+                    R.dimen.profile_instruction_padding);
+            desc.setPadding(descPadding, descPadding, descPadding, descPadding);
+            desc.setText(R.string.profile_setup_actions_description);
+            setHeaderView(desc);
+        }
+    }
+
+    private void updateProfile() {
+        mProfileManager.updateProfile(mProfile);
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        mListView.setAdapter(mAdapter);
+        final ActionBar actionBar = getActivity().getActionBar();
+        if (actionBar != null) {
+            if (mNewProfileMode) {
+                getActivity().getActionBar().setTitle(R.string.profile_setup_actions_title);
+            } else {
+                getActivity().getActionBar().setTitle(mProfile.getName());
+            }
+        }
+    }
+
+    private AlertDialog requestFillProfileFromSettingsDialog() {
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        builder.setMessage(R.string.profile_populate_profile_from_state);
+        builder.setNegativeButton(R.string.no, null);
+        builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                fillProfileFromCurrentSettings();
+                dialog.dismiss();
+            }
+        });
+        return builder.create();
+    }
+
+    private void fillProfileFromCurrentSettings() {
+        new AsyncTask<Void, Void, Void>() {
+            @Override
+            protected Void doInBackground(Void... params) {
+                fillProfileWithCurrentSettings(getActivity(), mProfile);
+                updateProfile();
+                return null;
+            }
+
+            @Override
+            protected void onPostExecute(Void aVoid) {
+                super.onPostExecute(aVoid);
+                rebuildItemList();
+            }
+        }.execute((Void) null);
+    }
+
+    public static void fillProfileWithCurrentSettings(Context context, Profile profile) {
+        // bt
+        if (DeviceUtils.deviceSupportsBluetooth()) {
+            profile.setConnectionSettings(
+                    new ConnectionSettings(ConnectionSettings.PROFILE_CONNECTION_BLUETOOTH,
+                            BluetoothAdapter.getDefaultAdapter().isEnabled() ? 1 : 0,
+                            true));
+        }
+
+        // gps
+        LocationManager locationManager = (LocationManager)
+                context.getSystemService(Context.LOCATION_SERVICE);
+        boolean gpsEnabled = locationManager.
+                isProviderEnabled(LocationManager.GPS_PROVIDER);
+        profile.setConnectionSettings(
+                new ConnectionSettings(ConnectionSettings.PROFILE_CONNECTION_GPS,
+                        gpsEnabled ? 1 : 0, true));
+
+        // wifi
+        WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+        profile.setConnectionSettings(
+                new ConnectionSettings(ConnectionSettings.PROFILE_CONNECTION_WIFI,
+                        wifiManager.isWifiEnabled() ? 1 : 0, true));
+
+        // auto sync data
+        profile.setConnectionSettings(
+                new ConnectionSettings(ConnectionSettings.PROFILE_CONNECTION_SYNC,
+                        ContentResolver.getMasterSyncAutomatically() ? 1 : 0, true));
+
+        // mobile data
+        if (DeviceUtils.deviceSupportsMobileData(context)) {
+            ConnectivityManager cm = (ConnectivityManager)
+                    context.getSystemService(Context.CONNECTIVITY_SERVICE);
+            profile.setConnectionSettings(
+                    new ConnectionSettings(ConnectionSettings.PROFILE_CONNECTION_MOBILEDATA,
+                            cm.getMobileDataEnabled() ? 1 : 0, true));
+        }
+
+        // wifi hotspot
+        profile.setConnectionSettings(
+                new ConnectionSettings(ConnectionSettings.PROFILE_CONNECTION_WIFIAP,
+                        wifiManager.isWifiApEnabled() ? 1 : 0, true));
+
+        // 2g/3g/4g
+        // skipping this one
+
+        // nfc
+        if (DeviceUtils.deviceSupportsNfc(context)) {
+            NfcManager nfcManager = (NfcManager) context.getSystemService(Context.NFC_SERVICE);
+            profile.setConnectionSettings(
+                    new ConnectionSettings(ConnectionSettings.PROFILE_CONNECTION_NFC,
+                            nfcManager.getDefaultAdapter().isEnabled() ? 1 : 0, true));
+        }
+
+        // alarm volume
+        final AudioManager am = (AudioManager) context
+                .getSystemService(Context.AUDIO_SERVICE);
+        profile.setStreamSettings(new StreamSettings(AudioManager.STREAM_ALARM,
+                am.getStreamVolume(AudioManager.STREAM_ALARM), true));
+
+        // media volume
+        profile.setStreamSettings(new StreamSettings(AudioManager.STREAM_MUSIC,
+                am.getStreamVolume(AudioManager.STREAM_MUSIC), true));
+
+        // ringtone volume
+        profile.setStreamSettings(new StreamSettings(AudioManager.STREAM_RING,
+                am.getStreamVolume(AudioManager.STREAM_RING), true));
+
+        // notification volume
+        profile.setStreamSettings(new StreamSettings(AudioManager.STREAM_NOTIFICATION,
+                am.getStreamVolume(AudioManager.STREAM_NOTIFICATION), true));
+
+        // ring mode
+        String ringValue;
+        switch (am.getRingerMode()) {
+            default:
+            case AudioManager.RINGER_MODE_NORMAL:
+                ringValue = "normal";
+                break;
+            case AudioManager.RINGER_MODE_SILENT:
+                ringValue = "mute";
+                break;
+            case AudioManager.RINGER_MODE_VIBRATE:
+                ringValue = "vibrate";
+                break;
+        }
+        profile.setRingMode(new RingModeSettings(ringValue, true));
+
+        // airplane mode
+        boolean airplaneMode = Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
+        profile.setAirplaneMode(new AirplaneModeSettings(airplaneMode ? 1 : 0, true));
+
+        // lock screen mode
+        // populated only from profiles, so we can read the current profile,
+        // but let's skip this one
+    }
+
+    @Override
+    public Dialog onCreateDialog(int dialogId) {
+        switch (dialogId) {
+            case DIALOG_FILL_FROM_SETTINGS:
+                return requestFillProfileFromSettingsDialog();
+
+            case DIALOG_AIRPLANE_MODE:
+                return requestAirplaneModeDialog(((AirplaneModeItem) mSelectedItem).getSettings());
+
+            case DIALOG_BRIGHTNESS:
+                return requestBrightnessDialog(((BrightnessItem) mSelectedItem).getSettings());
+
+            case DIALOG_LOCK_MODE:
+                return requestLockscreenModeDialog();
+
+            case DIALOG_DOZE_MODE:
+                return requestDozeModeDialog();
+
+            case DIALOG_NOTIFICATION_LIGHT_MODE:
+                return requestNotificationLightModeDialog();
+
+            case DIALOG_RING_MODE:
+                return requestRingModeDialog(((RingModeItem) mSelectedItem).getSettings());
+
+            case DIALOG_CONNECTION_OVERRIDE:
+                ConnectionOverrideItem connItem = (ConnectionOverrideItem) mSelectedItem;
+                return requestConnectionOverrideDialog(connItem.getSettings());
+
+            case DIALOG_VOLUME_STREAM:
+                VolumeStreamItem volumeItem = (VolumeStreamItem) mSelectedItem;
+                return requestVolumeDialog(volumeItem.getStreamType(), volumeItem.getSettings());
+
+            case DIALOG_PROFILE_NAME:
+                return requestProfileName();
+
+            case DIALOG_REMOVE_PROFILE:
+                return requestRemoveProfileDialog();
+
+        }
+        return super.onCreateDialog(dialogId);
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        if (mLastSelectedPosition != -1) {
+            outState.putInt(LAST_SELECTED_POSITION, mLastSelectedPosition);
+        }
+    }
+
+    private AlertDialog requestRemoveProfileDialog() {
+        Profile current = mProfileManager.getActiveProfile();
+        if (mProfile.getUuid().equals(current.getUuid())) {
+            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+            builder.setMessage(getString(R.string.profile_remove_current_profile));
+            builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                    dialog.dismiss();
+                }
+            });
+            return builder.create();
+        }
+
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        builder.setMessage(getString(R.string.profile_remove_dialog_message, mProfile.getName()));
+        builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                dialog.dismiss();
+                mProfileManager.removeProfile(mProfile);
+                finishFragment();
+            }
+        });
+        builder.setNegativeButton(R.string.no, null);
+        return builder.create();
+    }
+
+    private AlertDialog requestLockscreenModeDialog() {
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        final String[] lockEntries =
+                getResources().getStringArray(R.array.profile_lockmode_entries);
+
+        int defaultIndex = 0; // no action
+        for (int i = 0; i < LOCKMODE_MAPPING.length; i++) {
+            if (LOCKMODE_MAPPING[i] == mProfile.getScreenLockMode().getValue()) {
+                defaultIndex = i;
+                break;
+            }
+        }
+
+        builder.setTitle(R.string.profile_lockmode_title);
+        builder.setSingleChoiceItems(lockEntries, defaultIndex,
+                new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int item) {
+                        mProfile.setScreenLockMode(new LockSettings(LOCKMODE_MAPPING[item]));
+                        updateProfile();
+                        mAdapter.notifyDataSetChanged();
+                        dialog.dismiss();
+                    }
+                });
+
+        builder.setNegativeButton(android.R.string.cancel, null);
+        return builder.create();
+    }
+
+    private AlertDialog requestDozeModeDialog() {
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        final String[] dozeEntries =
+                getResources().getStringArray(R.array.profile_doze_entries);
+
+        int defaultIndex = 0; // no action
+        for (int i = 0; i < DOZE_MAPPING.length; i++) {
+            if (DOZE_MAPPING[i] == mProfile.getDozeMode()) {
+                defaultIndex = i;
+                break;
+            }
+        }
+
+        builder.setTitle(R.string.doze_title);
+        builder.setSingleChoiceItems(dozeEntries, defaultIndex,
+                new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int item) {
+                        mProfile.setDozeMode(DOZE_MAPPING[item]);
+                        updateProfile();
+                        mAdapter.notifyDataSetChanged();
+                        dialog.dismiss();
+                    }
+                });
+
+        builder.setNegativeButton(android.R.string.cancel, null);
+        return builder.create();
+    }
+
+    private AlertDialog requestNotificationLightModeDialog() {
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        final String[] notificationLightEntries =
+                getResources().getStringArray(R.array.profile_notification_light_entries);
+
+        int defaultIndex = 0; // no action
+        for (int i = 0; i < NOTIFICATION_LIGHT_MAPPING.length; i++) {
+            if (NOTIFICATION_LIGHT_MAPPING[i] == mProfile.getNotificationLightMode()) {
+                defaultIndex = i;
+                break;
+            }
+        }
+
+        builder.setTitle(org.cyanogenmod.platform.internal.R.string.notification_light_title);
+        builder.setSingleChoiceItems(notificationLightEntries, defaultIndex,
+                new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int item) {
+                        mProfile.setNotificationLightMode(NOTIFICATION_LIGHT_MAPPING[item]);
+                        updateProfile();
+                        mAdapter.notifyDataSetChanged();
+                        dialog.dismiss();
+                    }
+                });
+
+        builder.setNegativeButton(android.R.string.cancel, null);
+        return builder.create();
+    }
+
+    private AlertDialog requestAirplaneModeDialog(final AirplaneModeSettings setting) {
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        final String[] connectionNames =
+                getResources().getStringArray(R.array.profile_action_generic_connection_entries);
+
+        int defaultIndex = 0; // no action
+        if (setting.isOverride()) {
+            if (setting.getValue() == 1) {
+                defaultIndex = 2; // enabled
+            } else {
+                defaultIndex = 1; // disabled
+            }
+        }
+
+        builder.setTitle(R.string.profile_airplanemode_title);
+        builder.setSingleChoiceItems(connectionNames, defaultIndex,
+                new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int item) {
+                        switch (item) {
+                            case 0: // disable override
+                                setting.setOverride(false);
+                                break;
+                            case 1: // enable override, disable
+                                setting.setOverride(true);
+                                setting.setValue(0);
+                                break;
+                            case 2: // enable override, enable
+                                setting.setOverride(true);
+                                setting.setValue(1);
+                                break;
+                        }
+                        mProfile.setAirplaneMode(setting);
+                        mAdapter.notifyDataSetChanged();
+                        updateProfile();
+                        dialog.dismiss();
+                    }
+                });
+
+        builder.setNegativeButton(android.R.string.cancel, null);
+        return builder.create();
+    }
+
+    private void requestProfileRingMode() {
+        // Launch the ringtone picker
+        Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
+        intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, false);
+        intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
+        intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_RINGTONE);
+        startActivityForResult(intent, RINGTONE_REQUEST_CODE);
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        if (requestCode == NEW_TRIGGER_REQUEST_CODE) {
+            mProfile = mProfileManager.getProfile(mProfile.getUuid());
+            rebuildItemList();
+
+        } else if (requestCode == SET_NETWORK_MODE_REQUEST_CODE
+                && resultCode == Activity.RESULT_OK) {
+
+            int selectedMode = Integer.parseInt(data.getStringExtra(
+                    TelephonyUtils.EXTRA_NETWORK_PICKER_PICKED_VALUE));
+            int subId = data.getIntExtra(TelephonyUtils.EXTRA_SUBID,
+                    SubscriptionManager.getDefaultDataSubId());
+            ConnectionOverrideItem connItem = (ConnectionOverrideItem) mSelectedItem;
+            final ConnectionSettings setting = connItem.getSettings();
+//            final ConnectionSettings setting = mProfile.getConnectionSettingWithSubId(subId);
+
+            switch (selectedMode) {
+                case ConnectionOverrideItem.CM_MODE_SYSTEM_DEFAULT:
+                    setting.setOverride(false);
+                    break;
+                default:
+                    setting.setOverride(true);
+                    setting.setValue(selectedMode);
+            }
+            mProfile.setConnectionSettings(setting);
+            mAdapter.notifyDataSetChanged();
+            updateProfile();
+        }
+    }
+
+    private AlertDialog requestRingModeDialog(final RingModeSettings setting) {
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        final String[] values = getResources().getStringArray(R.array.ring_mode_values);
+        final String[] names = getResources().getStringArray(R.array.ring_mode_entries);
+
+        int defaultIndex = 0; // normal by default
+        if (setting.isOverride()) {
+            if (setting.getValue().equals(values[0] /* normal */)) {
+                defaultIndex = 0;
+            } else if (setting.getValue().equals(values[1] /* vibrate */)) {
+                defaultIndex = 1; // enabled
+            } else if (setting.getValue().equals(values[2] /* mute */)) {
+                defaultIndex = 2; // mute
+            }
+        } else {
+            defaultIndex = 3;
+        }
+
+        builder.setTitle(R.string.ring_mode_title);
+        builder.setSingleChoiceItems(names, defaultIndex,
+                new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int item) {
+                        switch (item) {
+                            case 0: // enable override, normal
+                                setting.setOverride(true);
+                                setting.setValue(values[0]);
+                                break;
+                            case 1: // enable override, vibrate
+                                setting.setOverride(true);
+                                setting.setValue(values[1]);
+                                break;
+                            case 2: // enable override, mute
+                                setting.setOverride(true);
+                                setting.setValue(values[2]);
+                                break;
+                            case 3:
+                                setting.setOverride(false);
+                                break;
+                        }
+                        mProfile.setRingMode(setting);
+                        mAdapter.notifyDataSetChanged();
+                        updateProfile();
+                        dialog.dismiss();
+                    }
+                });
+
+        builder.setNegativeButton(android.R.string.cancel, null);
+        return builder.create();
+    }
+
+    private AlertDialog requestConnectionOverrideDialog(final ConnectionSettings setting) {
+        if (setting == null) {
+            throw new UnsupportedOperationException("connection setting cannot be null");
+        }
+        if (setting.getConnectionId() == PROFILE_CONNECTION_2G3G4G) {
+            throw new UnsupportedOperationException("dialog must be requested from Telephony");
+        }
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        final String[] connectionNames =
+                getResources().getStringArray(R.array.profile_action_generic_connection_entries);
+
+        int defaultIndex = 0; // no action
+        if (setting.isOverride()) {
+            if (setting.getValue() == 1) {
+                defaultIndex = 2; // enabled
+            } else {
+                defaultIndex = 1; // disabled
+            }
+        }
+
+        builder.setTitle(ConnectionOverrideItem.getConnectionTitle(getContext(), setting));
+        builder.setSingleChoiceItems(connectionNames, defaultIndex,
+                new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int item) {
+                        switch (item) {
+                            case 0: // disable override
+                                setting.setOverride(false);
+                                break;
+                            case 1: // enable override, disable
+                                setting.setOverride(true);
+                                setting.setValue(0);
+                                break;
+                            case 2: // enable override, enable
+                                setting.setOverride(true);
+                                setting.setValue(1);
+                                break;
+                        }
+                        mProfile.setConnectionSettings(setting);
+                        mAdapter.notifyDataSetChanged();
+                        updateProfile();
+                        dialog.dismiss();
+                    }
+                });
+
+        builder.setNegativeButton(android.R.string.cancel, null);
+        return builder.create();
+    }
+
+    public AlertDialog requestVolumeDialog(int streamId,
+                                    final StreamSettings streamSettings) {
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        builder.setTitle(VolumeStreamItem.getNameForStream(streamId));
+
+        final AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+        final LayoutInflater inflater = LayoutInflater.from(getActivity());
+        final View view = inflater.inflate(R.layout.dialog_profiles_volume_override, null);
+        final SeekBar seekBar = (SeekBar) view.findViewById(R.id.seekbar);
+        final CheckBox override = (CheckBox) view.findViewById(R.id.checkbox);
+        override.setChecked(streamSettings.isOverride());
+        override.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                seekBar.setEnabled(isChecked);
+            }
+        });
+        final SeekBarVolumizer volumizer = new SeekBarVolumizer(getActivity(), streamId, null,
+                null);
+        volumizer.start();
+        volumizer.setSeekBar(seekBar);
+        seekBar.setEnabled(streamSettings.isOverride());
+
+        builder.setView(view);
+        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                int value = seekBar.getProgress();
+                streamSettings.setOverride(override.isChecked());
+                streamSettings.setValue(value);
+                mProfile.setStreamSettings(streamSettings);
+                mAdapter.notifyDataSetChanged();
+                updateProfile();
+            }
+        });
+        builder.setNegativeButton(android.R.string.cancel, null);
+        setOnDismissListener(new DialogInterface.OnDismissListener() {
+            @Override
+            public void onDismiss(DialogInterface dialogInterface) {
+                if (volumizer != null) {
+                    volumizer.stop();
+                }
+                setOnDismissListener(null); // re-set this for next dialog
+            }
+        });
+        return builder.create();
+    }
+
+    public AlertDialog requestBrightnessDialog(final BrightnessSettings brightnessSettings) {
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        builder.setTitle(R.string.profile_brightness_title);
+
+        final LayoutInflater inflater = LayoutInflater.from(getActivity());
+        final View view = inflater.inflate(R.layout.dialog_profiles_brightness_override, null);
+        final SeekBar seekBar = (SeekBar) view.findViewById(R.id.seekbar);
+        final CheckBox override = (CheckBox) view.findViewById(R.id.checkbox);
+        override.setChecked(brightnessSettings.isOverride());
+        override.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                seekBar.setEnabled(isChecked);
+            }
+        });
+        seekBar.setEnabled(brightnessSettings.isOverride());
+        seekBar.setMax(255);
+        seekBar.setProgress(brightnessSettings.getValue());
+        builder.setView(view);
+        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                int value = seekBar.getProgress();
+                brightnessSettings.setValue(value);
+                brightnessSettings.setOverride(override.isChecked());
+                mProfile.setBrightness(brightnessSettings);
+                mAdapter.notifyDataSetChanged();
+                updateProfile();
+                dialog.dismiss();
+            }
+        });
+        builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                dialog.dismiss();
+            }
+        });
+        return builder.create();
+    }
+
+    private AlertDialog requestProfileName() {
+        LayoutInflater inflater = LayoutInflater.from(getActivity());
+        View dialogView = inflater.inflate(R.layout.profile_name_dialog, null);
+
+        final EditText entry = (EditText) dialogView.findViewById(R.id.name);
+        entry.setText(mProfile.getName());
+        entry.setSelectAllOnFocus(true);
+
+        final AlertDialog alertDialog = new AlertDialog.Builder(getActivity())
+                .setTitle(R.string.rename_dialog_title)
+                .setView(dialogView)
+                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        String value = entry.getText().toString();
+                        mProfile.setName(value);
+                        mAdapter.notifyDataSetChanged();
+                        updateProfile();
+                    }
+                })
+                .setNegativeButton(android.R.string.cancel, null)
+                .create();
+
+        entry.addTextChangedListener(new TextWatcher() {
+            @Override
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+            }
+
+            @Override
+            public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+            }
+
+            @Override
+            public void afterTextChanged(Editable s) {
+                final String str = s.toString();
+                final boolean empty = TextUtils.isEmpty(str)
+                        || TextUtils.getTrimmedLength(str) == 0;
+                alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(!empty);
+            }
+        });
+        alertDialog.setOnShowListener(new DialogInterface.OnShowListener() {
+            @Override
+            public void onShow(DialogInterface dialog) {
+                InputMethodManager imm = (InputMethodManager)
+                        getSystemService(Context.INPUT_METHOD_SERVICE);
+                imm.showSoftInput(entry, InputMethodManager.SHOW_IMPLICIT);
+            }
+        });
+        return alertDialog;
+    }
+
+    private void requestActiveAppGroupsDialog() {
+        final NotificationGroup[] notificationGroups = mProfileManager.getNotificationGroups();
+
+        CharSequence[] items = new CharSequence[notificationGroups.length];
+        boolean[] checked = new boolean[notificationGroups.length];
+
+        for (int i = 0; i < notificationGroups.length; i++) {
+            items[i] = notificationGroups[i].getName();
+            checked[i] = mProfile.getProfileGroup(notificationGroups[i].getUuid()) != null;
+        }
+        DialogInterface.OnMultiChoiceClickListener listener =
+                new DialogInterface.OnMultiChoiceClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which, boolean isChecked) {
+                        if (isChecked) {
+                            mProfile.addProfileGroup(new ProfileGroup(notificationGroups[which].getUuid(), false));
+                        } else {
+                            mProfile.removeProfileGroup(notificationGroups[which].getUuid());
+                        }
+                        updateProfile();
+                        rebuildItemList();
+                    }
+                };
+
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+                .setMultiChoiceItems(items, checked, listener)
+                .setTitle(R.string.profile_appgroups_title)
+                .setPositiveButton(android.R.string.ok, null);
+        builder.show();
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        View view = inflater.inflate(R.layout.fragment_setup_actions, container, false);
+
+        mListView = (ListView) view.findViewById(android.R.id.list);
+        mListView.setOnItemClickListener(this);
+        if (mNewProfileMode) {
+            showButtonBar(true);
+
+            getBackButton().setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View view) {
+                    finishPreferencePanel(SetupActionsFragment.this, Activity.RESULT_OK, null);
+                }
+            });
+
+            getNextButton().setText(R.string.finish);
+            getNextButton().setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View view) {
+                    mProfileManager.addProfile(mProfile);
+                    finishPreferencePanel(SetupActionsFragment.this, Activity.RESULT_OK, null);
+                }
+            });
+        }
+        return view;
+    }
+
+    @Override
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        final Item itemAtPosition = (Item) parent.getItemAtPosition(position);
+        mSelectedItem = itemAtPosition;
+        mLastSelectedPosition = mAdapter.getPosition(itemAtPosition);
+
+        if (itemAtPosition instanceof AirplaneModeItem) {
+            showDialog(DIALOG_AIRPLANE_MODE);
+        } else if (itemAtPosition instanceof BrightnessItem) {
+            showDialog(DIALOG_BRIGHTNESS);
+        } else if (itemAtPosition instanceof LockModeItem) {
+            showDialog(DIALOG_LOCK_MODE);
+        } else if (itemAtPosition instanceof DozeModeItem) {
+            showDialog(DIALOG_DOZE_MODE);
+        } else if (itemAtPosition instanceof NotificationLightModeItem) {
+            showDialog(DIALOG_NOTIFICATION_LIGHT_MODE);
+        } else if (itemAtPosition instanceof RingModeItem) {
+            showDialog(DIALOG_RING_MODE);
+        } else if (itemAtPosition instanceof ConnectionOverrideItem) {
+
+            ConnectionOverrideItem connItem = (ConnectionOverrideItem) mSelectedItem;
+            if (connItem.getConnectionType() == ConnectionSettings.PROFILE_CONNECTION_2G3G4G) {
+                final Intent intent = new Intent(TelephonyUtils.ACTION_PICK_NETWORK_MODE);
+                intent.putExtra(TelephonyUtils.EXTRA_NONE_TEXT,
+                        getString(R.string.profile_action_none));
+                intent.putExtra(TelephonyUtils.EXTRA_SHOW_NONE, true);
+                intent.putExtra(TelephonyUtils.EXTRA_SUBID, connItem.getSettings().getSubId());
+                intent.putExtra(TelephonyUtils.EXTRA_INITIAL_NETWORK_VALUE,
+                        connItem.getSettings().isOverride()
+                                ? connItem.getSettings().getValue() : -1);
+                startActivityForResult(intent, SET_NETWORK_MODE_REQUEST_CODE);
+            } else {
+                showDialog(DIALOG_CONNECTION_OVERRIDE);
+            }
+        } else if (itemAtPosition instanceof VolumeStreamItem) {
+            showDialog(DIALOG_VOLUME_STREAM);
+        } else if (itemAtPosition instanceof ProfileNameItem) {
+            showDialog(DIALOG_PROFILE_NAME);
+        } else if (itemAtPosition instanceof TriggerItem) {
+            TriggerItem item = (TriggerItem) itemAtPosition;
+            openTriggersFragment(item.getTriggerType());
+        } else if (itemAtPosition instanceof AppGroupItem) {
+            AppGroupItem item = (AppGroupItem) itemAtPosition;
+            if (item.getGroupUuid() == null) {
+                requestActiveAppGroupsDialog();
+            } else {
+                startProfileGroupActivity(item);
+            }
+        }
+    }
+
+    private void startProfileGroupActivity(AppGroupItem item) {
+        Bundle args = new Bundle();
+        args.putString("ProfileGroup", item.getGroupUuid().toString());
+        args.putParcelable("Profile", mProfile);
+
+        startFragment(this, ProfileGroupConfig.class.getName(), 0, 0, args);
+    }
+
+    private void openTriggersFragment(int openTo) {
+        Bundle args = new Bundle();
+        args.putParcelable(ProfilesSettings.EXTRA_PROFILE, mProfile);
+        args.putBoolean(ProfilesSettings.EXTRA_NEW_PROFILE, false);
+        args.putInt(SetupTriggersFragment.EXTRA_INITIAL_PAGE, openTo);
+
+        PartsActivity pa = (PartsActivity) getActivity();
+        pa.startPreferencePanel(SetupTriggersFragment.class.getCanonicalName(), args,
+                R.string.profile_profile_manage, null, this, NEW_TRIGGER_REQUEST_CODE);
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/SetupDefaultProfileReceiver.java b/src/org/cyanogenmod/cmparts/profiles/SetupDefaultProfileReceiver.java
new file mode 100644
index 0000000..f26516b
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/SetupDefaultProfileReceiver.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.cyanogenmod.cmparts.profiles;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import cyanogenmod.app.Profile;
+import cyanogenmod.app.ProfileManager;
+import cyanogenmod.providers.CMSettings;
+
+import java.util.UUID;
+
+public class SetupDefaultProfileReceiver extends BroadcastReceiver {
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (CMSettings.System.getInt(context.getContentResolver(),
+                CMSettings.System.SYSTEM_PROFILES_ENABLED, 1) == 1) {
+            ProfileManager profileManager = ProfileManager.getInstance(context);
+            Profile defaultProfile = profileManager.getProfile(
+                    UUID.fromString("0230226d-0d05-494a-a9bd-d222a1117655"));
+            if (defaultProfile != null) {
+                SetupActionsFragment.fillProfileWithCurrentSettings(context, defaultProfile);
+                profileManager.updateProfile(defaultProfile);
+            }
+        }
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/SetupTriggersFragment.java b/src/org/cyanogenmod/cmparts/profiles/SetupTriggersFragment.java
new file mode 100644
index 0000000..55fa3d0
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/SetupTriggersFragment.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 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.
+ */
+package org.cyanogenmod.cmparts.profiles;
+
+import android.annotation.Nullable;
+import android.app.ActionBar;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.support.v4.view.PagerTabStrip;
+import android.support.v4.view.ViewPager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+
+import cyanogenmod.app.Profile;
+import cyanogenmod.app.ProfileManager;
+
+import org.cyanogenmod.cmparts.R;
+import org.cyanogenmod.cmparts.SettingsPreferenceFragment;
+import org.cyanogenmod.cmparts.PartsActivity;
+import org.cyanogenmod.cmparts.profiles.triggers.NfcTriggerFragment;
+
+public class SetupTriggersFragment extends SettingsPreferenceFragment {
+
+    ViewPager mPager;
+    Profile mProfile;
+    ProfileManager mProfileManager;
+    TriggerPagerAdapter mAdapter;
+    boolean mNewProfileMode;
+    int mPreselectedItem;
+
+    public static final String EXTRA_INITIAL_PAGE = "current_item";
+
+    private static final int REQUEST_SETUP_ACTIONS = 5;
+
+    public static SetupTriggersFragment newInstance(Profile profile, boolean newProfile) {
+        SetupTriggersFragment fragment = new SetupTriggersFragment();
+        Bundle args = new Bundle();
+        args.putParcelable(ProfilesSettings.EXTRA_PROFILE, profile);
+        args.putBoolean(ProfilesSettings.EXTRA_NEW_PROFILE, newProfile);
+        fragment.setArguments(args);
+        return fragment;
+    }
+
+    public SetupTriggersFragment() {
+        // Required empty public constructor
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        if (getArguments() != null) {
+            mProfile = getArguments().getParcelable(ProfilesSettings.EXTRA_PROFILE);
+            mNewProfileMode = getArguments().getBoolean(ProfilesSettings.EXTRA_NEW_PROFILE, false);
+            mPreselectedItem = getArguments().getInt(EXTRA_INITIAL_PAGE, 0);
+        }
+        mProfileManager = ProfileManager.getInstance(getActivity());
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        final ActionBar actionBar = getActivity().getActionBar();
+        if (actionBar != null) {
+            if (mNewProfileMode) {
+                actionBar.setTitle(R.string.profile_setup_setup_triggers_title);
+            } else {
+                String title = getString(R.string.profile_setup_setup_triggers_title_config,
+                        mProfile.getName());
+                actionBar.setTitle(title);
+            }
+        }
+    }
+
+    @Override
+    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        mPager.setCurrentItem(mPreselectedItem);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        // Inflate the layout for this fragment
+        View root = inflater.inflate(R.layout.fragment_setup_triggers, container, false);
+
+        mPager = (ViewPager) root.findViewById(R.id.view_pager);
+        mAdapter = new TriggerPagerAdapter(getActivity(), getChildFragmentManager());
+
+        Bundle profileArgs = new Bundle();
+        profileArgs.putParcelable(ProfilesSettings.EXTRA_PROFILE, mProfile);
+
+        final TriggerPagerAdapter.TriggerFragments[] fragments =
+                TriggerPagerAdapter.TriggerFragments.values();
+
+        for (final TriggerPagerAdapter.TriggerFragments fragment : fragments) {
+            if (fragment.getFragmentClass() == NfcTriggerFragment.class) {
+                if (!getActivity().getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_NFC)) {
+                    // device doesn't have NFC
+                    continue;
+                }
+            }
+            mAdapter.add(fragment.getFragmentClass(), profileArgs, fragment.getTitleRes());
+        }
+
+        mPager.setAdapter(mAdapter);
+
+        PagerTabStrip tabs = (PagerTabStrip) root.findViewById(R.id.tabs);
+        tabs.setTabIndicatorColorResource(R.color.theme_accent);
+
+        // HACK - https://code.google.com/p/android/issues/detail?id=213359
+        ((ViewPager.LayoutParams)tabs.getLayoutParams()).isDecor = true;
+
+        if (mNewProfileMode) {
+            showButtonBar(true);
+            getNextButton().setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View view) {
+                    Bundle args = new Bundle();
+                    args.putParcelable(ProfilesSettings.EXTRA_PROFILE,  mProfile);
+                    args.putBoolean(ProfilesSettings.EXTRA_NEW_PROFILE, mNewProfileMode);
+
+                    PartsActivity pa = (PartsActivity) getActivity();
+                    pa.startPreferencePanel(SetupActionsFragment.class.getCanonicalName(), args,
+                            R.string.profile_profile_manage, null,
+                            SetupTriggersFragment.this, REQUEST_SETUP_ACTIONS);
+                }
+            });
+
+            // back button
+            getBackButton().setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View view) {
+                    finishPreferencePanel(SetupTriggersFragment.this, Activity.RESULT_CANCELED, null);
+                }
+            });
+        }
+        return root;
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        if (requestCode == REQUEST_SETUP_ACTIONS) {
+            if (resultCode == Activity.RESULT_OK) {
+                // exit out of the wizard!
+                finishFragment();
+            }
+        }
+    }
+
+
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/TriggerPagerAdapter.java b/src/org/cyanogenmod/cmparts/profiles/TriggerPagerAdapter.java
new file mode 100644
index 0000000..1e2b2d1
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/TriggerPagerAdapter.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 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.
+ */
+package org.cyanogenmod.cmparts.profiles;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.os.Bundle;
+import android.support.v13.app.FragmentPagerAdapter;
+import android.util.SparseArray;
+import android.view.ViewGroup;
+
+import org.cyanogenmod.cmparts.R;
+import org.cyanogenmod.cmparts.profiles.triggers.BluetoothTriggerFragment;
+import org.cyanogenmod.cmparts.profiles.triggers.NfcTriggerFragment;
+import org.cyanogenmod.cmparts.profiles.triggers.WifiTriggerFragment;
+import com.google.android.collect.Lists;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+
+/**
+ * A {@link android.support.v4.app.FragmentPagerAdapter} class for swiping between playlists, recent,
+ * artists, albums, songs, and genre {@link android.support.v4.app.Fragment}s on phones.<br/>
+ */
+public class TriggerPagerAdapter extends FragmentPagerAdapter {
+
+    private final SparseArray<WeakReference<Fragment>> mFragmentArray =
+            new SparseArray<WeakReference<Fragment>>();
+
+    private final List<Holder> mHolderList = Lists.newArrayList();
+
+    private final Activity mFragmentActivity;
+
+    private int mCurrentPage;
+
+    /**
+     * Constructor of <code>PagerAdatper<code>
+     *
+     * @param activity The {@link android.support.v4.app.FragmentActivity} of the
+     *            {@link android.support.v4.app.Fragment}.
+     * @param fm the FragmentManager to use.
+     */
+    public TriggerPagerAdapter(Activity activity, FragmentManager fm) {
+        super(fm);
+        mFragmentActivity = activity;
+    }
+
+    /**
+     * Method that adds a new fragment class to the viewer (the fragment is
+     * internally instantiate)
+     *
+     * @param className The full qualified name of fragment class.
+     * @param params The instantiate params.
+     */
+    @SuppressWarnings("synthetic-access")
+    public void add(final Class<? extends Fragment> className, final Bundle params,
+                    final int titleResId) {
+        final Holder mHolder = new Holder();
+        mHolder.mClassName = className.getName();
+        mHolder.mParams = params;
+        mHolder.mTitleResId = titleResId;
+
+        final int mPosition = mHolderList.size();
+        mHolderList.add(mPosition, mHolder);
+        notifyDataSetChanged();
+    }
+
+    /**
+     * Method that returns the {@link android.support.v4.app.Fragment} in the argument
+     * position.
+     *
+     * @param position The position of the fragment to return.
+     * @return Fragment The {@link android.support.v4.app.Fragment} in the argument position.
+     */
+    public Fragment getFragment(final int position) {
+        final WeakReference<Fragment> mWeakFragment = mFragmentArray.get(position);
+        if (mWeakFragment != null && mWeakFragment.get() != null) {
+            return mWeakFragment.get();
+        }
+        return getItem(position);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Object instantiateItem(final ViewGroup container, final int position) {
+        final Fragment mFragment = (Fragment)super.instantiateItem(container, position);
+        final WeakReference<Fragment> mWeakFragment = mFragmentArray.get(position);
+        if (mWeakFragment != null) {
+            mWeakFragment.clear();
+        }
+        mFragmentArray.put(position, new WeakReference<Fragment>(mFragment));
+        return mFragment;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Fragment getItem(final int position) {
+        final Holder mCurrentHolder = mHolderList.get(position);
+        final Fragment mFragment = Fragment.instantiate(mFragmentActivity,
+                mCurrentHolder.mClassName, mCurrentHolder.mParams);
+        return mFragment;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void destroyItem(final ViewGroup container, final int position, final Object object) {
+        super.destroyItem(container, position, object);
+        final WeakReference<Fragment> mWeakFragment = mFragmentArray.get(position);
+        if (mWeakFragment != null) {
+            mWeakFragment.clear();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getCount() {
+        return mHolderList.size();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public CharSequence getPageTitle(final int position) {
+        return mFragmentActivity.getString(mHolderList.get(position).mTitleResId);
+    }
+
+    /**
+     * Method that returns the current page position.
+     *
+     * @return int The current page.
+     */
+    public int getCurrentPage() {
+        return mCurrentPage;
+    }
+
+    /**
+     * Method that sets the current page position.
+     *
+     * @param currentPage The current page.
+     */
+    protected void setCurrentPage(final int currentPage) {
+        mCurrentPage = currentPage;
+    }
+
+    /**
+     * An enumeration of all the main fragments supported.
+     */
+    public enum TriggerFragments {
+        /**
+         * The artist fragment
+         */
+        WIFI(WifiTriggerFragment.class, R.string.profile_tabs_wifi),
+        /**
+         * The album fragment
+         */
+        BLUETOOTH(BluetoothTriggerFragment.class, R.string.profile_tabs_bluetooth),
+        /**
+         * The song fragment
+         */
+        NFC(NfcTriggerFragment.class, R.string.profile_tabs_nfc);
+
+        private Class<? extends Fragment> mFragmentClass;
+        private int mNameRes;
+
+        /**
+         * Constructor of <code>MusicFragments</code>
+         *
+         * @param fragmentClass The fragment class
+         */
+        private TriggerFragments(final Class<? extends Fragment> fragmentClass, int nameRes) {
+            mFragmentClass = fragmentClass;
+            mNameRes = nameRes;
+        }
+
+        /**
+         * Method that returns the fragment class.
+         *
+         * @return Class<? extends Fragment> The fragment class.
+         */
+        public Class<? extends Fragment> getFragmentClass() {
+            return mFragmentClass;
+        }
+
+        public int getTitleRes() { return mNameRes; }
+    }
+
+    /**
+     * A private class with information about fragment initialization
+     */
+    private final static class Holder {
+        String mClassName;
+        int mTitleResId;
+        Bundle mParams;
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/actions/ItemListAdapter.java b/src/org/cyanogenmod/cmparts/profiles/actions/ItemListAdapter.java
new file mode 100644
index 0000000..3658a70
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/actions/ItemListAdapter.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 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.
+ */
+package org.cyanogenmod.cmparts.profiles.actions;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+
+import org.cyanogenmod.cmparts.profiles.actions.item.Item;
+
+import java.util.List;
+
+public class ItemListAdapter extends ArrayAdapter<Item> {
+    private LayoutInflater mInflater;
+
+    public enum RowType {
+        HEADER_ITEM,
+        DISABLED_ITEM,
+        CONNECTION_ITEM,
+        VOLUME_STREAM_ITEM,
+        NAME_ITEM,
+        RINGMODE_ITEM,
+        AIRPLANEMODE_ITEM,
+        LOCKSCREENMODE_ITEM,
+        TRIGGER_ITEM,
+        APP_GROUP_ITEM,
+        BRIGHTNESS_ITEM,
+        DOZEMODE_ITEM,
+        NOTIFICATIONLIGHTMODE_ITEM
+    }
+
+    public ItemListAdapter(Context context, List<Item> items) {
+        super(context, 0, items);
+        mInflater = LayoutInflater.from(context);
+    }
+
+    @Override
+    public int getViewTypeCount() {
+        return RowType.values().length;
+
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        return getItem(position).getRowType().ordinal();
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        return getItem(position).getView(mInflater, convertView, parent);
+    }
+
+    @Override
+    public boolean areAllItemsEnabled() {
+        return false;
+    }
+
+    @Override
+    public boolean isEnabled(int position) {
+        return getItem(position).isEnabled();
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/actions/item/AirplaneModeItem.java b/src/org/cyanogenmod/cmparts/profiles/actions/item/AirplaneModeItem.java
new file mode 100644
index 0000000..94f06ec
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/actions/item/AirplaneModeItem.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 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.
+ */
+package org.cyanogenmod.cmparts.profiles.actions.item;
+
+import org.cyanogenmod.cmparts.R;
+import org.cyanogenmod.cmparts.profiles.actions.ItemListAdapter;
+
+import cyanogenmod.profiles.AirplaneModeSettings;
+
+public class AirplaneModeItem extends BaseItem {
+    AirplaneModeSettings mSettings;
+
+    public AirplaneModeItem(AirplaneModeSettings airplaneModeSettings) {
+        if (airplaneModeSettings == null) {
+            airplaneModeSettings = new AirplaneModeSettings();
+        }
+        mSettings = airplaneModeSettings;
+    }
+
+    @Override
+    public ItemListAdapter.RowType getRowType() {
+        return ItemListAdapter.RowType.AIRPLANEMODE_ITEM;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return true;
+    }
+
+    @Override
+    public String getTitle() {
+        return getString(R.string.profile_airplanemode_title);
+    }
+
+    @Override
+    public String getSummary() {
+        return getString(getModeString(mSettings));
+    }
+
+    public AirplaneModeSettings getSettings() {
+        return mSettings;
+    }
+
+    public static int getModeString(AirplaneModeSettings settings) {
+        if (settings.isOverride()) {
+            if (settings.getValue() == 1) {
+                return R.string.profile_action_enable;
+            } else {
+                return R.string.profile_action_disable;
+            }
+        } else {
+            return R.string.profile_action_none;
+        }
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/actions/item/AppGroupItem.java b/src/org/cyanogenmod/cmparts/profiles/actions/item/AppGroupItem.java
new file mode 100644
index 0000000..ce5cd64
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/actions/item/AppGroupItem.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 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.
+ */
+package org.cyanogenmod.cmparts.profiles.actions.item;
+
+import android.app.NotificationGroup;
+
+import org.cyanogenmod.cmparts.R;
+import org.cyanogenmod.cmparts.profiles.actions.ItemListAdapter;
+
+import java.util.UUID;
+
+import cyanogenmod.app.Profile;
+import cyanogenmod.app.ProfileGroup;
+
+public class AppGroupItem extends BaseItem {
+    Profile mProfile;
+    ProfileGroup mGroup;
+    NotificationGroup mNotifGroup;
+
+    public AppGroupItem() {
+        // empty app group will act as a "Add/remove app groups" item
+    }
+
+    public AppGroupItem(Profile profile, ProfileGroup group, NotificationGroup nGroup) {
+        mProfile = profile;
+        if (group == null) {
+            throw new UnsupportedOperationException("profile group can't be null");
+        }
+        mGroup = group;
+        mNotifGroup = nGroup;
+    }
+
+    @Override
+    public ItemListAdapter.RowType getRowType() {
+        return ItemListAdapter.RowType.APP_GROUP_ITEM;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return true;
+    }
+
+    public UUID getGroupUuid() {
+        if (mGroup != null) {
+            return mGroup.getUuid();
+        }
+        return null;
+    }
+
+    @Override
+    public String getTitle() {
+        if (mGroup == null) {
+            return getString(R.string.profile_app_group_item_instructions);
+        }
+        if (mNotifGroup != null) {
+            return mNotifGroup.getName();
+        }
+        return "<unknown>";
+    }
+
+    @Override
+    public String getSummary() {
+        if (mGroup == null) {
+            return getString(R.string.profile_app_group_item_instructions_summary);
+        }
+        return null;
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/actions/item/BaseItem.java b/src/org/cyanogenmod/cmparts/profiles/actions/item/BaseItem.java
new file mode 100644
index 0000000..02988cf
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/actions/item/BaseItem.java
@@ -0,0 +1,61 @@
+package org.cyanogenmod.cmparts.profiles.actions.item;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import android.support.v7.preference.R;
+
+/**
+ * Created by shade on 9/12/16.
+ */
+
+public abstract class BaseItem implements Item {
+
+    protected abstract String getTitle();
+
+    protected abstract String getSummary();
+
+    private View mView;
+
+    @Override
+    public View getView(LayoutInflater inflater, View convertView, ViewGroup parent) {
+        View view;
+        if (convertView == null) {
+            view = inflater.inflate(R.layout.profile_action_item, parent, false);
+            // Do some initialization
+        } else {
+            view = convertView;
+        }
+
+        mView = view;
+
+        TextView text = (TextView) view.findViewById(android.R.id.title);
+        String title = getTitle();
+        if (title == null) {
+            text.setVisibility(View.GONE);
+        } else {
+            text.setText(title);
+        }
+
+        TextView desc = (TextView) view.findViewById(android.R.id.summary);
+        String summary = getSummary();
+        if (summary == null) {
+            desc.setVisibility(View.GONE);
+        } else {
+            desc.setText(summary);
+        }
+        
+        return view;
+    }
+
+    protected Context getContext() {
+        return mView.getContext();
+    }
+
+    protected String getString(int resId) {
+        return getContext().getResources().getString(resId);
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/actions/item/BrightnessItem.java b/src/org/cyanogenmod/cmparts/profiles/actions/item/BrightnessItem.java
new file mode 100644
index 0000000..b291058
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/actions/item/BrightnessItem.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.cyanogenmod.cmparts.profiles.actions.item;
+
+import org.cyanogenmod.cmparts.R;
+import org.cyanogenmod.cmparts.profiles.actions.ItemListAdapter;
+
+import cyanogenmod.profiles.BrightnessSettings;
+
+public class BrightnessItem extends BaseItem {
+    BrightnessSettings mSettings;
+
+    public BrightnessItem(BrightnessSettings brightnessSettings) {
+        if (brightnessSettings == null) {
+            brightnessSettings = new BrightnessSettings();
+        }
+        mSettings = brightnessSettings;
+    }
+
+    @Override
+    public ItemListAdapter.RowType getRowType() {
+        return ItemListAdapter.RowType.BRIGHTNESS_ITEM;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return true;
+    }
+
+    @Override
+    public String getTitle() {
+        return getString(R.string.profile_brightness_title);
+    }
+
+    @Override
+    public String getSummary() {
+        if (mSettings.isOverride()) {
+            return getContext().getResources().getString(
+                    R.string.profile_brightness_override_summary,
+                    (int)((mSettings.getValue() * 100f)/255));
+        }
+        return getString(R.string.profile_action_none);
+    }
+
+    public BrightnessSettings getSettings() {
+        return mSettings;
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/actions/item/ConnectionOverrideItem.java b/src/org/cyanogenmod/cmparts/profiles/actions/item/ConnectionOverrideItem.java
new file mode 100644
index 0000000..302dd02
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/actions/item/ConnectionOverrideItem.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 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.
+ */
+package org.cyanogenmod.cmparts.profiles.actions.item;
+
+import android.content.Context;
+import android.telephony.SubscriptionManager;
+
+import org.cyanogenmod.cmparts.R;
+import org.cyanogenmod.cmparts.profiles.actions.ItemListAdapter;
+import org.cyanogenmod.cmparts.utils.TelephonyUtils;
+
+import cyanogenmod.profiles.ConnectionSettings;
+
+public class ConnectionOverrideItem extends BaseItem {
+    int mConnectionId;
+    ConnectionSettings mConnectionSettings;
+
+    public static final int CM_MODE_SYSTEM_DEFAULT = -1;
+
+    public ConnectionOverrideItem(int connectionId, ConnectionSettings settings) {
+        mConnectionId = connectionId;
+        if (settings == null) {
+            settings = new ConnectionSettings(connectionId);
+        }
+        this.mConnectionSettings = settings;
+    }
+
+    @Override
+    public ItemListAdapter.RowType getRowType() {
+        return ItemListAdapter.RowType.CONNECTION_ITEM;
+    }
+
+    @Override
+    public String getTitle() {
+        return getConnectionTitle(getContext(), mConnectionSettings);
+    }
+
+    @Override
+    public String getSummary() {
+        return String.valueOf(getSummary(getContext()));
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return true;
+    }
+
+    public static String getConnectionTitle(Context context, ConnectionSettings settings) {
+        int r = 0;
+        switch (settings.getConnectionId()) {
+            case ConnectionSettings.PROFILE_CONNECTION_BLUETOOTH:
+                r = R.string.toggleBluetooth;
+                break;
+            case ConnectionSettings.PROFILE_CONNECTION_MOBILEDATA:
+                r =R.string.toggleData;
+                break;
+            case ConnectionSettings.PROFILE_CONNECTION_2G3G4G:
+                if (settings.getSubId() != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+                    final String displayName = SubscriptionManager.from(context)
+                            .getActiveSubscriptionInfo(settings.getSubId())
+                            .getDisplayName()
+                            .toString();
+                    return context.getString(R.string.toggle2g3g4g_msim, displayName);
+                }
+                r = R.string.toggle2g3g4g;
+                break;
+            case ConnectionSettings.PROFILE_CONNECTION_GPS:
+                r = R.string.toggleGPS;
+                break;
+            case ConnectionSettings.PROFILE_CONNECTION_NFC:
+                r = R.string.toggleNfc;
+                break;
+            case ConnectionSettings.PROFILE_CONNECTION_SYNC:
+                r = R.string.toggleSync;
+                break;
+            case ConnectionSettings.PROFILE_CONNECTION_WIFI:
+                r = R.string.toggleWifi;
+                break;
+            case ConnectionSettings.PROFILE_CONNECTION_WIFIAP:
+                r = R.string.toggleWifiAp;
+                break;
+        }
+        return context.getString(r);
+    }
+
+    public CharSequence getSummary(Context context) {
+        int resId = -1;
+        if (mConnectionSettings != null) {
+            if (mConnectionId == ConnectionSettings.PROFILE_CONNECTION_2G3G4G) { // different options
+                if (mConnectionSettings.isOverride()) {
+                    return TelephonyUtils.getNetworkModeString(context,
+                            mConnectionSettings.getValue(), SubscriptionManager.getDefaultDataSubId());
+                } else {
+                    resId = R.string.profile_action_none;
+                }
+            } else if (mConnectionSettings.isOverride()) { // enabled, disabled, or none
+                if (mConnectionSettings.getValue() == 1) {
+                    resId = R.string.profile_action_enable;
+                } else {
+                    resId = R.string.profile_action_disable;
+                }
+            } else {
+                resId = R.string.profile_action_none;
+            }
+        } else {
+            resId = R.string.profile_action_none;
+        }
+        return context.getString(resId);
+    }
+
+    public ConnectionSettings getSettings() {
+        return mConnectionSettings;
+    }
+
+    public int getConnectionType() {
+        return mConnectionId;
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/actions/item/DisabledItem.java b/src/org/cyanogenmod/cmparts/profiles/actions/item/DisabledItem.java
new file mode 100644
index 0000000..60ced30
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/actions/item/DisabledItem.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.cyanogenmod.cmparts.profiles.actions.item;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.cyanogenmod.cmparts.profiles.actions.ItemListAdapter;
+
+public class DisabledItem extends BaseItem {
+
+    private final int mResTitle;
+    private final int mResSummary;
+
+    public DisabledItem(int resTitle, int resSummary) {
+       mResTitle = resTitle;
+       mResSummary = resSummary;
+    }
+
+    @Override
+    public ItemListAdapter.RowType getRowType() {
+        return ItemListAdapter.RowType.DISABLED_ITEM;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return false;
+    }
+
+    @Override
+    public String getTitle() {
+        return getString(mResTitle);
+    }
+
+    @Override
+    public String getSummary() {
+        return getString(mResSummary);
+    }
+
+    @Override
+    public View getView(LayoutInflater inflater, View convertView, ViewGroup parent) {
+        View view = super.getView(inflater, convertView, parent);
+        view.findViewById(android.R.id.title).setEnabled(false);
+        view.findViewById(android.R.id.summary).setEnabled(false);
+        return view;
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/actions/item/DozeModeItem.java b/src/org/cyanogenmod/cmparts/profiles/actions/item/DozeModeItem.java
new file mode 100644
index 0000000..4cb8216
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/actions/item/DozeModeItem.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 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.
+ */
+package org.cyanogenmod.cmparts.profiles.actions.item;
+
+import org.cyanogenmod.cmparts.R;
+import org.cyanogenmod.cmparts.profiles.actions.ItemListAdapter;
+
+import cyanogenmod.app.Profile;
+
+public class DozeModeItem extends BaseItem {
+    Profile mProfile;
+
+    public DozeModeItem(Profile profile) {
+       mProfile = profile;
+    }
+
+    @Override
+    public ItemListAdapter.RowType getRowType() {
+        return ItemListAdapter.RowType.DOZEMODE_ITEM;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return true;
+    }
+
+    @Override
+    public String getTitle() {
+        return getString(R.string.doze_title);
+    }
+
+    @Override
+    public String getSummary() {
+        return getString(getSummaryString(mProfile));
+    }
+
+    public static int getSummaryString(Profile profile) {
+        switch (profile.getDozeMode()) {
+            case Profile.DozeMode.DEFAULT:
+                return R.string.profile_action_none; //"leave unchanged"
+            case Profile.DozeMode.ENABLE:
+                return R.string.profile_action_enable;
+            case Profile.DozeMode.DISABLE:
+                return R.string.profile_action_disable;
+            default: return 0;
+        }
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/actions/item/Header.java b/src/org/cyanogenmod/cmparts/profiles/actions/item/Header.java
new file mode 100644
index 0000000..c6fadb2
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/actions/item/Header.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 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.
+ */
+package org.cyanogenmod.cmparts.profiles.actions.item;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import android.support.v14.preference.R;
+import org.cyanogenmod.cmparts.profiles.actions.ItemListAdapter;
+
+public class Header implements Item {
+    private final String name;
+
+    public Header(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public ItemListAdapter.RowType getRowType() {
+        return ItemListAdapter.RowType.HEADER_ITEM;
+    }
+
+    @Override
+    public View getView(LayoutInflater inflater, View convertView, ViewGroup parent) {
+        View view;
+        if (convertView == null) {
+            view = inflater.inflate(R.layout.preference_category_material, parent, false);
+            // Do some initialization
+        } else {
+            view = convertView;
+        }
+
+        TextView text = (TextView) view.findViewById(android.R.id.title);
+        text.setText(name);
+
+        return view;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return false;
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/actions/item/Item.java b/src/org/cyanogenmod/cmparts/profiles/actions/item/Item.java
new file mode 100644
index 0000000..7016299
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/actions/item/Item.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 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.
+ */
+package org.cyanogenmod.cmparts.profiles.actions.item;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.cyanogenmod.cmparts.profiles.actions.ItemListAdapter;
+
+public interface Item {
+    public ItemListAdapter.RowType getRowType();
+    public View getView(LayoutInflater inflater, View convertView, ViewGroup parent);
+    public boolean isEnabled();
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/actions/item/LockModeItem.java b/src/org/cyanogenmod/cmparts/profiles/actions/item/LockModeItem.java
new file mode 100644
index 0000000..774e150
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/actions/item/LockModeItem.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 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.
+ */
+package org.cyanogenmod.cmparts.profiles.actions.item;
+
+import org.cyanogenmod.cmparts.R;
+import org.cyanogenmod.cmparts.profiles.actions.ItemListAdapter;
+
+import cyanogenmod.app.Profile;
+
+public class LockModeItem extends BaseItem {
+    Profile mProfile;
+
+    public LockModeItem(Profile profile) {
+       mProfile = profile;
+    }
+
+    @Override
+    public ItemListAdapter.RowType getRowType() {
+        return ItemListAdapter.RowType.LOCKSCREENMODE_ITEM;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return true;
+    }
+
+    @Override
+    public String getTitle() {
+        return getString(R.string.profile_lockmode_title);
+    }
+
+    @Override
+    public String getSummary() {
+        return getString(getSummaryString(mProfile));
+    }
+
+    public static int getSummaryString(Profile profile) {
+        switch (profile.getScreenLockMode().getValue()) {
+            case Profile.LockMode.DEFAULT:
+                return R.string.profile_action_system; //"leave unchanged"
+            case Profile.LockMode.DISABLE:
+                return R.string.profile_lockmode_disabled_summary;
+            case Profile.LockMode.INSECURE:
+                return R.string.profile_lockmode_insecure_summary;
+            default: return 0;
+        }
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/actions/item/NotificationLightModeItem.java b/src/org/cyanogenmod/cmparts/profiles/actions/item/NotificationLightModeItem.java
new file mode 100644
index 0000000..eea4978
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/actions/item/NotificationLightModeItem.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 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.
+ */
+package org.cyanogenmod.cmparts.profiles.actions.item;
+
+import org.cyanogenmod.cmparts.R;
+import org.cyanogenmod.cmparts.profiles.actions.ItemListAdapter;
+
+import cyanogenmod.app.Profile;
+
+public class NotificationLightModeItem extends BaseItem {
+    Profile mProfile;
+
+    public NotificationLightModeItem(Profile profile) {
+       mProfile = profile;
+    }
+
+    @Override
+    public ItemListAdapter.RowType getRowType() {
+        return ItemListAdapter.RowType.NOTIFICATIONLIGHTMODE_ITEM;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return true;
+    }
+
+    @Override
+    public String getTitle() {
+        return getString(org.cyanogenmod.platform.internal.R.string.notification_light_title);
+    }
+
+    @Override
+    public String getSummary() {
+        return getString(getSummaryString(mProfile));
+    }
+
+    public static int getSummaryString(Profile profile) {
+        switch (profile.getNotificationLightMode()) {
+            case Profile.NotificationLightMode.DEFAULT:
+                return R.string.profile_action_none; //"leave unchanged"
+            case Profile.NotificationLightMode.ENABLE:
+                return R.string.profile_action_enable;
+            case Profile.NotificationLightMode.DISABLE:
+                return R.string.profile_action_disable;
+            default: return 0;
+        }
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/actions/item/ProfileNameItem.java b/src/org/cyanogenmod/cmparts/profiles/actions/item/ProfileNameItem.java
new file mode 100644
index 0000000..4698666
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/actions/item/ProfileNameItem.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 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.
+ */
+package org.cyanogenmod.cmparts.profiles.actions.item;
+
+import org.cyanogenmod.cmparts.profiles.actions.ItemListAdapter;
+
+import cyanogenmod.app.Profile;
+
+public class ProfileNameItem extends BaseItem {
+    Profile mProfile;
+
+    public ProfileNameItem(Profile profile) {
+        this.mProfile = profile;
+    }
+
+    @Override
+    public ItemListAdapter.RowType getRowType() {
+        return ItemListAdapter.RowType.NAME_ITEM;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return true;
+    }
+
+    @Override
+    public String getTitle() {
+        return mProfile.getName();
+    }
+
+    @Override
+    public String getSummary() {
+        return null;
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/actions/item/RingModeItem.java b/src/org/cyanogenmod/cmparts/profiles/actions/item/RingModeItem.java
new file mode 100644
index 0000000..be83f24
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/actions/item/RingModeItem.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 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.
+ */
+package org.cyanogenmod.cmparts.profiles.actions.item;
+
+import org.cyanogenmod.cmparts.R;
+import org.cyanogenmod.cmparts.profiles.actions.ItemListAdapter;
+
+import cyanogenmod.profiles.RingModeSettings;
+
+public class RingModeItem extends BaseItem {
+    RingModeSettings mSettings;
+
+    public RingModeItem(RingModeSettings ringModeSettings) {
+        if (ringModeSettings == null) {
+            ringModeSettings = new RingModeSettings();
+        }
+        mSettings = ringModeSettings;
+    }
+
+    @Override
+    public ItemListAdapter.RowType getRowType() {
+        return ItemListAdapter.RowType.RINGMODE_ITEM;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return true;
+    }
+
+    @Override
+    public String getTitle() {
+        return getString(R.string.ring_mode_title);
+    }
+
+    @Override
+    public String getSummary() {
+        return getString(getModeString(mSettings));
+    }
+
+    public static int getModeString(RingModeSettings settings) {
+        if (settings == null) {
+            return R.string.ring_mode_normal;
+        }
+        if (settings.isOverride()) {
+            if (settings.getValue().equals("vibrate")) {
+                return R.string.ring_mode_vibrate;
+            } else if (settings.getValue().equals("normal")) {
+                return R.string.ring_mode_normal;
+            } else {
+                return R.string.ring_mode_mute;
+            }
+        } else {
+            return R.string.profile_action_none; //"leave unchanged"
+        }
+    }
+
+    public RingModeSettings getSettings() {
+        return mSettings;
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/actions/item/TriggerItem.java b/src/org/cyanogenmod/cmparts/profiles/actions/item/TriggerItem.java
new file mode 100644
index 0000000..35ef675
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/actions/item/TriggerItem.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 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.
+ */
+package org.cyanogenmod.cmparts.profiles.actions.item;
+
+import org.cyanogenmod.cmparts.R;
+import org.cyanogenmod.cmparts.profiles.actions.ItemListAdapter;
+
+import java.util.ArrayList;
+
+import cyanogenmod.app.Profile;
+
+public class TriggerItem extends BaseItem {
+
+    // from Profile.TriggerType
+    public static final int WIFI = 0;
+    public static final int BLUETOOTH = 1;
+    // not in Profile.TriggerType, but we need it.
+    public static final int NFC = 2;
+
+    Profile mProfile;
+    int mTriggerType;
+
+    public TriggerItem(Profile profile, int whichTrigger) {
+        mProfile = profile;
+        mTriggerType = whichTrigger;
+    }
+
+    @Override
+    public ItemListAdapter.RowType getRowType() {
+        return ItemListAdapter.RowType.TRIGGER_ITEM;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return true;
+    }
+
+    public int getTriggerType() {
+        return mTriggerType;
+    }
+
+    @Override
+    public String getTitle() {
+        return getString(getTitleString(mTriggerType));
+    }
+
+    @Override
+    public String getSummary() {
+        StringBuilder sb = new StringBuilder();
+        ArrayList<Profile.ProfileTrigger> triggers = mProfile.getTriggersFromType(mTriggerType);
+
+        for (int i = 0; i < triggers.size(); i++) {
+            sb.append(triggers.get(i).getName());
+            if (i < (triggers.size() - 1)) {
+                sb.append("\n");
+            }
+        }
+
+        if (sb.length() == 0) {
+            if (mTriggerType == NFC) {
+                return getString(R.string.no_triggers_configured_nfc);
+            } else {
+                return getString(R.string.no_triggers_configured);
+            }
+        }
+
+        return sb.toString();
+    }
+
+    public static int getTitleString(int triggerType) {
+        switch (triggerType) {
+            case WIFI:
+                return R.string.profile_tabs_wifi;
+            case BLUETOOTH:
+                return R.string.profile_tabs_bluetooth;
+            case NFC:
+                return R.string.profile_tabs_nfc;
+            default: return 0;
+        }
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/actions/item/VolumeStreamItem.java b/src/org/cyanogenmod/cmparts/profiles/actions/item/VolumeStreamItem.java
new file mode 100644
index 0000000..2852d66
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/actions/item/VolumeStreamItem.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 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.
+ */
+package org.cyanogenmod.cmparts.profiles.actions.item;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.provider.Settings;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.cyanogenmod.cmparts.R;
+import org.cyanogenmod.cmparts.profiles.actions.ItemListAdapter;
+
+import cyanogenmod.profiles.StreamSettings;
+
+public class VolumeStreamItem extends BaseItem {
+    private int mStreamId;
+    private StreamSettings mStreamSettings;
+    private boolean mEnabled = true;
+
+    public VolumeStreamItem(int streamId, StreamSettings streamSettings) {
+        mStreamId = streamId;
+        mStreamSettings = streamSettings;
+    }
+
+    @Override
+    public ItemListAdapter.RowType getRowType() {
+        return ItemListAdapter.RowType.VOLUME_STREAM_ITEM;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return mEnabled;
+    }
+
+    @Override
+    public String getTitle() {
+        return getString(getNameForStream(mStreamId));
+    }
+
+    @Override
+    public String getSummary() {
+        if (mStreamSettings.isOverride()) {
+            final AudioManager am =
+                    (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+
+            int denominator = mStreamSettings.getValue();
+            int numerator = am.getStreamMaxVolume(mStreamId);
+            return getContext().getResources().getString(
+                    R.string.volume_override_summary,
+                    denominator, numerator);
+        }
+        return getString(R.string.profile_action_none);
+    }
+
+    @Override
+    public View getView(LayoutInflater inflater, View convertView, ViewGroup parent) {
+        View view = super.getView(inflater, convertView, parent);
+        final boolean volumeLinkNotification = Settings.Secure.getInt(inflater.getContext()
+                .getContentResolver(), Settings.Secure.VOLUME_LINK_NOTIFICATION, 1) == 1;
+        if (mStreamId == AudioManager.STREAM_NOTIFICATION && volumeLinkNotification) {
+            view.findViewById(android.R.id.title).setEnabled(false);
+            view.findViewById(android.R.id.summary).setEnabled(false);
+            mEnabled = false;
+        }
+        return view;
+    }
+
+    public static int getNameForStream(int stream) {
+        switch (stream) {
+            case AudioManager.STREAM_ALARM:
+                return R.string.alarm_volume_title;
+            case AudioManager.STREAM_MUSIC:
+                return R.string.media_volume_title;
+            case AudioManager.STREAM_RING:
+                return R.string.incoming_call_volume_title;
+            case AudioManager.STREAM_NOTIFICATION:
+                return R.string.notification_volume_title;
+            default: return 0;
+        }
+    }
+
+    public int getStreamType() {
+        return mStreamId;
+    }
+
+    public StreamSettings getSettings() {
+        return mStreamSettings;
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/triggers/AbstractTriggerItem.java b/src/org/cyanogenmod/cmparts/profiles/triggers/AbstractTriggerItem.java
new file mode 100644
index 0000000..3b95831
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/triggers/AbstractTriggerItem.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 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.
+ */
+package org.cyanogenmod.cmparts.profiles.triggers;
+
+import cyanogenmod.app.Profile;
+
+public class AbstractTriggerItem {
+    private int mIcon;
+    private String mSummary;
+    private String mTitle;
+
+    private int mTriggerState = Profile.TriggerState.DISABLED;
+
+    public void setTriggerState(int trigger) {
+        mTriggerState = trigger;
+    }
+
+    public int getTriggerState() {
+        return mTriggerState;
+    }
+
+    public void setSummary(String summary) {
+        mSummary = summary;
+    }
+
+    public String getTitle() {
+        return mTitle;
+    }
+
+    public void setTitle(String title) {
+        mTitle = title;
+    }
+
+    public String getSummary() {
+        return mSummary;
+    }
+
+    public void setIcon(int icon) {
+        mIcon = icon;
+    }
+
+    public int getIcon() {
+        return mIcon;
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/triggers/BluetoothTriggerFragment.java b/src/org/cyanogenmod/cmparts/profiles/triggers/BluetoothTriggerFragment.java
new file mode 100644
index 0000000..9a9d1dc
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/triggers/BluetoothTriggerFragment.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 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.
+ */
+package org.cyanogenmod.cmparts.profiles.triggers;
+
+import android.app.AlertDialog;
+import android.app.ListFragment;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import cyanogenmod.app.Profile;
+import cyanogenmod.app.ProfileManager;
+
+import org.cyanogenmod.cmparts.R;
+import org.cyanogenmod.cmparts.Utils;
+import org.cyanogenmod.cmparts.profiles.ProfilesSettings;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class BluetoothTriggerFragment extends ListFragment {
+
+    private BluetoothAdapter mBluetoothAdapter;
+
+    Profile mProfile;
+    ProfileManager mProfileManager;
+
+    private View mEmptyView;
+
+    private List<BluetoothTrigger> mTriggers = new ArrayList<BluetoothTrigger>();
+    private BluetoothTriggerAdapter mListAdapter;
+
+    public static BluetoothTriggerFragment newInstance(Profile profile) {
+        BluetoothTriggerFragment fragment = new BluetoothTriggerFragment();
+
+        Bundle extras = new Bundle();
+        extras.putParcelable(ProfilesSettings.EXTRA_PROFILE, profile);
+
+        fragment.setArguments(extras);
+        return fragment;
+    }
+
+    public BluetoothTriggerFragment() {
+        // Required empty public constructor
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mProfileManager = ProfileManager.getInstance(getActivity());
+        if (getArguments() != null) {
+            mProfile = getArguments().getParcelable(ProfilesSettings.EXTRA_PROFILE);
+        }
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        reloadTriggerListItems();
+    }
+
+    private void initPreference(AbstractTriggerItem pref, int state, Resources res, int icon) {
+        String[] values = res.getStringArray(R.array.profile_trigger_wifi_options_values);
+        for (int i = 0; i < values.length; i++) {
+            if (Integer.parseInt(values[i]) == state) {
+                pref.setSummary(res.getStringArray(R.array.profile_trigger_wifi_options)[i]);
+                break;
+            }
+        }
+        pref.setTriggerState(state);
+        pref.setIcon(icon);
+    }
+
+    @Override
+    public void onListItemClick(ListView l, View v, int position, long id) {
+        super.onListItemClick(l, v, position, id);
+
+        final String triggerId;
+        final String triggerName;
+        final int triggerType;
+
+        String[] entries = getResources().getStringArray(R.array.profile_trigger_wifi_options);
+        String[] values =
+                getResources().getStringArray(R.array.profile_trigger_wifi_options_values);
+
+        List<Trigger> triggers = new ArrayList<Trigger>(entries.length);
+        for (int i = 0; i < entries.length; i++) {
+            Trigger toAdd = new Trigger();
+            toAdd.value = Integer.parseInt(values[i]);
+            toAdd.name = entries[i];
+            triggers.add(toAdd);
+        }
+
+        BluetoothTrigger btpref = mListAdapter.getItem(position);
+        triggerName = btpref.getTitle();
+        triggerId = btpref.getAddress();
+        triggerType = Profile.TriggerType.BLUETOOTH;
+        BluetoothDevice dev = mBluetoothAdapter.getRemoteDevice(triggerId);
+        if (!dev.getBluetoothClass().doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
+            removeTrigger(triggers, Profile.TriggerState.ON_A2DP_CONNECT);
+            removeTrigger(triggers, Profile.TriggerState.ON_A2DP_DISCONNECT);
+        }
+
+        entries = new String[triggers.size()];
+        final int[] valueInts = new int[triggers.size()];
+        int currentTriggerState = mProfile.getTriggerState(triggerType, triggerId);
+        int currentItem = -1;
+        for (int i = 0; i < triggers.size(); i++) {
+            Trigger t = triggers.get(i);
+            entries[i] = t.name;
+            valueInts[i] = t.value;
+            if (valueInts[i] == currentTriggerState) {
+                currentItem = i;
+            }
+        }
+
+        new AlertDialog.Builder(getActivity())
+                .setTitle(R.string.profile_trigger_configure)
+                .setSingleChoiceItems(entries, currentItem, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        mProfile.setTrigger(triggerType, triggerId, valueInts[which], triggerName);
+                        mProfileManager.updateProfile(mProfile);
+                        reloadTriggerListItems();
+                        dialog.dismiss();
+                    }
+                })
+                .show();
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        getListView().setEmptyView(mEmptyView);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        mEmptyView = inflater.inflate(R.layout.profile_bluetooth_empty_view, container, false);
+        mEmptyView.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Intent bluetoothSettings = new Intent();
+                bluetoothSettings.setAction(
+                        Settings.ACTION_BLUETOOTH_SETTINGS);
+                startActivity(bluetoothSettings);
+            }
+        });
+
+        ViewGroup view = (ViewGroup) super.onCreateView(inflater, container, savedInstanceState);
+        view.addView(mEmptyView);
+        return view;
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        reloadTriggerListItems();
+        mListAdapter = new BluetoothTriggerAdapter(getActivity());
+        setListAdapter(mListAdapter);
+    }
+
+    private void removeTrigger(List<Trigger> triggers, int value) {
+        for (Trigger t : triggers) {
+            if (t.value == value) {
+                triggers.remove(t);
+                return;
+            }
+        }
+    }
+
+    private void reloadTriggerListItems() {
+        mTriggers.clear();
+        final Resources res = getResources();
+
+        Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
+        if (!pairedDevices.isEmpty()) {
+            for (BluetoothDevice device : pairedDevices) {
+                BluetoothTrigger bt =
+                        new BluetoothTrigger(device);
+                int state = mProfile.getTriggerState(
+                        Profile.TriggerType.BLUETOOTH, bt.getAddress());
+                initPreference(bt, state, res, R.drawable.ic_settings_bluetooth);
+                mTriggers.add(bt);
+            }
+        } else {
+            final List<Profile.ProfileTrigger> triggers =
+                    mProfile.getTriggersFromType(Profile.TriggerType.BLUETOOTH);
+            for (Profile.ProfileTrigger trigger : triggers) {
+                BluetoothTrigger bt = new BluetoothTrigger(trigger.getName(), trigger.getId());
+                initPreference(bt, trigger.getState(), res, R.drawable.ic_settings_bluetooth);
+                mTriggers.add(bt);
+            }
+        }
+
+        if (mListAdapter != null) {
+            mListAdapter.notifyDataSetChanged();
+        }
+    }
+
+    private class Trigger {
+        int value;
+        String name;
+    }
+
+    private class BluetoothTriggerAdapter extends ArrayAdapter<BluetoothTrigger> {
+        public BluetoothTriggerAdapter(Context context) {
+            super(context, R.layout.abstract_trigger_row, R.id.title, mTriggers);
+        }
+
+        @Override
+        public View getView(int i, View view, ViewGroup viewGroup) {
+            LayoutInflater inflater = LayoutInflater.from(getContext());
+            View rowView = inflater.inflate(R.layout.abstract_trigger_row, viewGroup, false);
+            TextView title = (TextView) rowView.findViewById(R.id.title);
+            TextView desc = (TextView) rowView.findViewById(R.id.desc);
+            ImageView imageView = (ImageView) rowView.findViewById(R.id.icon);
+
+            BluetoothTrigger trigger = getItem(i);
+
+            title.setText(trigger.getTitle());
+            desc.setText(trigger.getSummary());
+            imageView.setImageResource(trigger.getIcon());
+
+            return rowView;
+        }
+    }
+
+    public static class BluetoothTrigger extends AbstractTriggerItem {
+        private String mAddress;
+
+        public BluetoothTrigger(BluetoothDevice device) {
+            mAddress = device.getAddress();
+            if (device.getAlias() != null) {
+                setTitle(device.getAlias());
+            } else {
+                setTitle(device.getName());
+            }
+        }
+
+        public BluetoothTrigger(String name, String address) {
+            mAddress = address;
+            setTitle(name);
+        }
+
+        public String getAddress() {
+            return mAddress;
+        }
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/triggers/NfcTriggerFragment.java b/src/org/cyanogenmod/cmparts/profiles/triggers/NfcTriggerFragment.java
new file mode 100644
index 0000000..e78a52c
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/triggers/NfcTriggerFragment.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 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.
+ */
+package org.cyanogenmod.cmparts.profiles.triggers;
+
+import android.app.Fragment;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.nfc.NfcAdapter;
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import cyanogenmod.app.Profile;
+
+import org.cyanogenmod.cmparts.R;
+import org.cyanogenmod.cmparts.PartsActivity;
+import org.cyanogenmod.cmparts.profiles.NFCProfileTagCallback;
+import org.cyanogenmod.cmparts.profiles.NFCProfileUtils;
+import org.cyanogenmod.cmparts.profiles.ProfilesSettings;
+
+public class NfcTriggerFragment extends Fragment implements NFCProfileTagCallback {
+    Profile mProfile;
+
+    private NfcAdapter mNfcAdapter;
+    private IntentFilter[] mWriteTagFilters;
+
+    public static NfcTriggerFragment newInstance(Profile profile) {
+        NfcTriggerFragment fragment = new NfcTriggerFragment();
+
+        Bundle extras = new Bundle();
+        extras.putParcelable(ProfilesSettings.EXTRA_PROFILE, profile);
+
+        fragment.setArguments(extras);
+        return fragment;
+    }
+
+    public NfcTriggerFragment() {
+        // Required empty public constructor
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mNfcAdapter = NfcAdapter.getDefaultAdapter(getActivity());
+        if (getArguments() != null) {
+            mProfile = getArguments().getParcelable(ProfilesSettings.EXTRA_PROFILE);
+        }
+        ((PartsActivity) getActivity()).setNfcProfileCallback(this);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        ((PartsActivity) getActivity()).setNfcProfileCallback(null);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (mProfile != null) {
+            enableTagWriteMode();
+        }
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        disableTagWriteMode();
+    }
+
+    private PendingIntent getPendingIntent() {
+        Intent intent = new Intent(getActivity(), getActivity().getClass())
+                .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+        return PendingIntent.getActivity(getActivity(), 0, intent, 0);
+    }
+
+    private void disableTagWriteMode() {
+        mNfcAdapter.disableForegroundDispatch(getActivity());
+    }
+
+    private void enableTagWriteMode() {
+        IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
+        mWriteTagFilters = new IntentFilter[] {
+            tagDetected
+        };
+        mNfcAdapter.enableForegroundDispatch(getActivity(), getPendingIntent(), mWriteTagFilters, null);
+    }
+
+    @Override
+    public void onTagRead(Tag tag) {
+        if (NFCProfileUtils.writeTag(NFCProfileUtils.getProfileAsNdef(mProfile), tag)) {
+            Toast.makeText(getActivity(), R.string.profile_write_success, Toast.LENGTH_LONG).show();
+            NFCProfileUtils.vibrate(getActivity());
+        } else {
+            Toast.makeText(getActivity(), R.string.profile_write_failed, Toast.LENGTH_LONG).show();
+        }
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.nfc_writer, container, false);
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/profiles/triggers/WifiTriggerFragment.java b/src/org/cyanogenmod/cmparts/profiles/triggers/WifiTriggerFragment.java
new file mode 100644
index 0000000..f9603d9
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/profiles/triggers/WifiTriggerFragment.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 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.
+ */
+package org.cyanogenmod.cmparts.profiles.triggers;
+
+import android.app.AlertDialog;
+import android.app.ListFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import cyanogenmod.app.Profile;
+import cyanogenmod.app.ProfileManager;
+
+import org.cyanogenmod.cmparts.R;
+import org.cyanogenmod.cmparts.profiles.ProfilesSettings;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class WifiTriggerFragment extends ListFragment {
+    WifiManager mWifiManager;
+    Profile mProfile;
+    private ProfileManager mProfileManager;
+
+    private View mEmptyView;
+
+    private List<WifiTrigger> mTriggers = new ArrayList<WifiTrigger>();
+    private WifiTriggerAdapter mListAdapter;
+
+    public static WifiTriggerFragment newInstance(Profile profile) {
+        WifiTriggerFragment fragment = new WifiTriggerFragment();
+
+        Bundle extras = new Bundle();
+        extras.putParcelable(ProfilesSettings.EXTRA_PROFILE, profile);
+
+        fragment.setArguments(extras);
+        return fragment;
+    }
+
+    public WifiTriggerFragment() {
+        // Required empty public constructor
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        if (getArguments() != null) {
+            mProfile = getArguments().getParcelable(ProfilesSettings.EXTRA_PROFILE);
+        } else {
+            throw new UnsupportedOperationException("no profile!");
+        }
+        mProfileManager = ProfileManager.getInstance(getActivity());
+        mWifiManager = (WifiManager) getActivity().getSystemService(Context.WIFI_SERVICE);
+    }
+
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        getListView().setEmptyView(mEmptyView);
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        mEmptyView = inflater.inflate(R.layout.profile_wifi_empty_view, container, false);
+        mEmptyView.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Intent wifiSettings = new Intent();
+                wifiSettings.setAction(
+                        Settings.ACTION_WIFI_SETTINGS);
+                startActivity(wifiSettings);
+            }
+        });
+
+        ViewGroup view = (ViewGroup) super.onCreateView(inflater, container, savedInstanceState);
+        view.addView(mEmptyView);
+        return view;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        reloadTriggerListItems();
+    }
+
+    private void initPreference(AbstractTriggerItem pref, int state, Resources res, int icon) {
+        String[] values = res.getStringArray(R.array.profile_trigger_wifi_options_values);
+        for (int i = 0; i < values.length; i++) {
+            if (Integer.parseInt(values[i]) == state) {
+                pref.setSummary(res.getStringArray(R.array.profile_trigger_wifi_options)[i]);
+                break;
+            }
+        }
+        pref.setTriggerState(state);
+        pref.setIcon(icon);
+    }
+
+    @Override
+    public void onListItemClick(ListView l, View v, int position, long id) {
+        super.onListItemClick(l, v, position, id);
+
+        final String triggerId;
+        final String triggerName;
+        final int triggerType;
+
+        String[] entries = getResources().getStringArray(R.array.profile_trigger_wifi_options);
+        String[] values =
+                getResources().getStringArray(R.array.profile_trigger_wifi_options_values);
+
+        List<Trigger> triggers = new ArrayList<Trigger>(entries.length);
+        for (int i = 0; i < entries.length; i++) {
+            Trigger toAdd = new Trigger();
+            toAdd.value = Integer.parseInt(values[i]);
+            toAdd.name = entries[i];
+            triggers.add(toAdd);
+        }
+
+        WifiTrigger pref = (WifiTrigger) l.getAdapter().getItem(position);
+        triggerName = pref.getTitle();
+        triggerId = pref.getSSID();
+        triggerType = Profile.TriggerType.WIFI;
+        removeTrigger(triggers, Profile.TriggerState.ON_A2DP_CONNECT);
+        removeTrigger(triggers, Profile.TriggerState.ON_A2DP_DISCONNECT);
+
+        entries = new String[triggers.size()];
+        final int[] valueInts = new int[triggers.size()];
+        int currentTriggerState = mProfile.getTriggerState(triggerType, triggerId);
+        int currentItem = -1;
+        for (int i = 0; i < triggers.size(); i++) {
+            Trigger t = triggers.get(i);
+            entries[i] = t.name;
+            valueInts[i] = t.value;
+            if (valueInts[i] == currentTriggerState) {
+                currentItem = i;
+            }
+        }
+
+        new AlertDialog.Builder(getActivity())
+                .setTitle(R.string.profile_trigger_configure)
+                .setSingleChoiceItems(entries, currentItem, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        mProfile.setTrigger(triggerType, triggerId, valueInts[which], triggerName);
+                        mProfileManager.updateProfile(mProfile);
+                        reloadTriggerListItems();
+                        dialog.dismiss();
+                    }
+                })
+                .show();
+    }
+
+    private void removeTrigger(List<Trigger> triggers, int value) {
+        for (Trigger t : triggers) {
+            if (t.value == value) {
+                triggers.remove(t);
+                return;
+            }
+        }
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        reloadTriggerListItems();
+        mListAdapter = new WifiTriggerAdapter(getActivity());
+        setListAdapter(mListAdapter);
+    }
+
+    private void reloadTriggerListItems() {
+        mTriggers.clear();
+        final Resources res = getResources();
+        final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+
+        if (configs != null) {
+            for (WifiConfiguration config : configs) {
+                WifiTrigger accessPoint = new WifiTrigger(config);
+                int state = mProfile.getTriggerState(
+                        Profile.TriggerType.WIFI, accessPoint.getSSID());
+                initPreference(accessPoint, state, res, R.drawable.ic_wifi_signal_4);
+                mTriggers.add(accessPoint);
+            }
+        } else {
+            final List<Profile.ProfileTrigger> triggers =
+                    mProfile.getTriggersFromType(Profile.TriggerType.WIFI);
+            for (Profile.ProfileTrigger trigger : triggers) {
+                WifiTrigger accessPoint = new WifiTrigger(trigger.getName());
+                initPreference(accessPoint, trigger.getState(), res,
+                        R.drawable.ic_wifi_signal_4);
+                mTriggers.add(accessPoint);
+            }
+        }
+        if (mListAdapter != null) {
+            mListAdapter.notifyDataSetChanged();
+        }
+    }
+
+    private class Trigger {
+        int value;
+        String name;
+    }
+
+    private class WifiTriggerAdapter extends ArrayAdapter<WifiTrigger> {
+        public WifiTriggerAdapter(Context context) {
+            super(context, R.layout.abstract_trigger_row, R.id.title, mTriggers);
+        }
+
+        @Override
+        public View getView(int i, View view, ViewGroup viewGroup) {
+            LayoutInflater inflater = LayoutInflater.from(getContext());
+            View rowView = inflater.inflate(R.layout.abstract_trigger_row, viewGroup, false);
+            TextView title = (TextView) rowView.findViewById(R.id.title);
+            TextView desc = (TextView) rowView.findViewById(R.id.desc);
+            ImageView imageView = (ImageView) rowView.findViewById(R.id.icon);
+
+            WifiTrigger trigger = getItem(i);
+
+            title.setText(trigger.getTitle());
+            desc.setText(trigger.getSummary());
+            imageView.setImageResource(trigger.getIcon());
+
+            return rowView;
+        }
+    }
+
+    public static class WifiTrigger extends AbstractTriggerItem {
+        public String mSSID;
+        public WifiConfiguration mConfig;
+
+        public WifiTrigger(WifiConfiguration config) {
+            mConfig = config;
+            loadConfig(config);
+        }
+
+        public WifiTrigger(String ssid) {
+            mSSID = ssid;
+        }
+
+        public String getSSID() {
+            return mSSID;
+        }
+
+        @Override
+        public String getTitle() {
+            return mSSID;
+        }
+
+        private void loadConfig(WifiConfiguration config) {
+            mSSID = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID));
+            mConfig = config;
+        }
+
+        public static String removeDoubleQuotes(String string) {
+            final int length = string.length();
+            if (length >= 2) {
+                if (string.startsWith("\"") && string.endsWith("\"")) {
+                    return string.substring(1, length - 1);
+                }
+            }
+            return string;
+        }
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/utils/DeviceUtils.java b/src/org/cyanogenmod/cmparts/utils/DeviceUtils.java
new file mode 100644
index 0000000..0d94b05
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/utils/DeviceUtils.java
@@ -0,0 +1,26 @@
+package org.cyanogenmod.cmparts.utils;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.nfc.NfcAdapter;
+import android.provider.Settings;
+
+/**
+ * Created by roman on 11/24/14.
+ */
+public class DeviceUtils {
+    public static boolean deviceSupportsMobileData(Context ctx) {
+        ConnectivityManager cm = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
+        return cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+    }
+
+    public static boolean deviceSupportsBluetooth() {
+        return (BluetoothAdapter.getDefaultAdapter() != null);
+    }
+
+    public static boolean deviceSupportsNfc(Context ctx) {
+        return NfcAdapter.getDefaultAdapter(ctx) != null;
+    }
+}
diff --git a/src/org/cyanogenmod/cmparts/utils/TelephonyUtils.java b/src/org/cyanogenmod/cmparts/utils/TelephonyUtils.java
new file mode 100644
index 0000000..8bd874f
--- /dev/null
+++ b/src/org/cyanogenmod/cmparts/utils/TelephonyUtils.java
@@ -0,0 +1,231 @@
+package org.cyanogenmod.cmparts.utils;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.RILConstants;
+
+/**
+ * Helper class which has the same logic as MobileNetworkSettings to display the same
+ * network modes and strings as it does.
+ */
+public class TelephonyUtils {
+
+    private static final String TAG = TelephonyUtils.class.getSimpleName();
+
+    // from MobileNetworkSettings
+    public static final String ACTION_PICK_NETWORK_MODE =
+            "cyanogenmod.platform.intent.action.NETWORK_MODE_PICKER";
+    public static final String EXTRA_NONE_TEXT = "network_mode_picker::neutral_text";
+    public static final String EXTRA_SHOW_NONE = "network_mode_picker::show_none";
+    public static final String EXTRA_INITIAL_NETWORK_VALUE = "network_mode_picker::selected_mode";
+    public static final String EXTRA_NETWORK_PICKER_PICKED_VALUE =
+            "network_mode_picker::chosen_value";
+    public static final String EXTRA_SUBID = "network_mode_picker::sub_id";
+
+    public static String getNetworkModeString(Context context, int networkMode, int subId) {
+        return getNetworkModeString(context,
+                networkMode,
+                TelephonyManager.from(context).getCurrentPhoneType(subId) /* phone type */,
+                show4GForLTE(context)/* show 4G for lte */,
+                isSupportTdscdma(context, subId)/* supports TDS CDMA*/,
+                isGlobalCDMA(context, subId, isLteOnCdma(context, subId))/* is Global cdma */,
+                isWorldMode(context)/* is worldwide */);
+    }
+
+    public static String getNetworkModeString(Context context, int networkMode,
+            int phoneType, boolean show4GForLTE, boolean isSupportTdsCdma, boolean isGlobalCdma,
+            boolean isWorldMode) {
+        String r = null;
+        switch (networkMode) {
+            case RILConstants.NETWORK_MODE_TDSCDMA_WCDMA:
+            case RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA:
+            case RILConstants.NETWORK_MODE_TDSCDMA_GSM:
+                r = "network_3G";
+                break;
+            case RILConstants.NETWORK_MODE_WCDMA_ONLY:
+                r = "network_wcdma_only";
+                break;
+            case RILConstants.NETWORK_MODE_GSM_UMTS:
+                r = "network_gsm_umts";
+                break;
+            case RILConstants.NETWORK_MODE_WCDMA_PREF:
+                r = "network_wcdma_pref";
+                break;
+            case RILConstants.NETWORK_MODE_GSM_ONLY:
+                r = "network_gsm_only";
+                break;
+            case RILConstants.NETWORK_MODE_LTE_GSM_WCDMA:
+                r = (show4GForLTE)
+                        ? "network_4G" : "network_lte_gsm_wcdma";
+                break;
+            case RILConstants.NETWORK_MODE_LTE_WCDMA:
+                r = (show4GForLTE)
+                        ? "network_4G" : "network_lte_cdma";
+                break;
+            case RILConstants.NETWORK_MODE_LTE_ONLY:
+                r = (show4GForLTE)
+                        ? "network_4G_only" : "network_lte_only";
+                break;
+            case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO:
+                r = (show4GForLTE)
+                        ? "network_4G" : "network_lte_cdma_and_evdo";
+                break;
+            case RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
+                r = "network_3G";
+                break;
+            case RILConstants.NETWORK_MODE_CDMA:
+                r = "network_cdma";
+                break;
+            case RILConstants.NETWORK_MODE_EVDO_NO_CDMA:
+                r = "network_evdo_no_cdma";
+                break;
+            case RILConstants.NETWORK_MODE_GLOBAL:
+                r = "network_3g_global";
+                break;
+            case RILConstants.NETWORK_MODE_CDMA_NO_EVDO:
+                r = "network_cdma_no_evdo";
+                break;
+            case RILConstants.NETWORK_MODE_TDSCDMA_ONLY:
+                r = "network_tdscdma";
+                break;
+            case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM:
+            case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA:
+            case RILConstants.NETWORK_MODE_LTE_TDSCDMA:
+            case RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA:
+            case RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
+            case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA:
+                if (isSupportTdsCdma) {
+                    r = "network_lte";
+                } else {
+                    if (phoneType == RILConstants.CDMA_PHONE || isGlobalCdma || isWorldMode) {
+                        r = "network_global";
+                    } else {
+                        r = (show4GForLTE)
+                                ? "network_4G" : "network_lte";
+                    }
+                }
+                break;
+            default:
+                Log.w(TAG, "unknown phone mode: " + networkMode);
+        }
+
+        if (r != null) {
+            // grab the phone resources
+            final Resources phoneResources = getPhoneResources(context);
+            if (phoneResources != null) {
+                int id = phoneResources.getIdentifier(r, "string", "com.android.phone");
+                if (id > 0) {
+                    return phoneResources.getString(id);
+                } else {
+                    Log.w(TAG, "couldn't find resource id with name: " + r);
+                }
+            }
+        }
+        return null;
+    }
+
+    private static boolean isSupportTdscdma(Context context, int subId) {
+        final Resources phoneResources = getPhoneResources(context);
+        if (phoneResources != null) {
+            int id = phoneResources.getIdentifier("config_support_tdscdma",
+                    "bool", "com.android.phone");
+            if (phoneResources.getBoolean(id)) {
+                return true;
+            }
+
+            final String operatorNumeric = TelephonyManager.from(context)
+                    .getSimOperatorNumeric(subId);
+
+            int tdcdmaArrId = phoneResources.getIdentifier("config_support_tdscdma_roaming_on_networks",
+                    "string-array", "com.android.phone");
+
+            if (tdcdmaArrId > 0) {
+                String[] numericArray = phoneResources.getStringArray(tdcdmaArrId);
+                if (numericArray.length == 0 || operatorNumeric == null) {
+                    return false;
+                }
+                for (String numeric : numericArray) {
+                    if (operatorNumeric.equals(numeric)) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    private static boolean show4GForLTE(Context context) {
+        try {
+            Context con = context.createPackageContext("com.android.systemui", 0);
+            int id = con.getResources().getIdentifier("config_show4GForLTE",
+                    "bool", "com.android.systemui");
+            return con.getResources().getBoolean(id);
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+
+    private static boolean isGlobalCDMA(Context context, int subId, boolean isLteOnCdma) {
+        final CarrierConfigManager carrierConfigMan = (CarrierConfigManager)
+                context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        final PersistableBundle carrierConfig = carrierConfigMan.getConfigForSubId(subId);
+        return isLteOnCdma
+                && carrierConfig.getBoolean(CarrierConfigManager.KEY_SHOW_CDMA_CHOICES_BOOL);
+    }
+
+    private static boolean isLteOnCdma(Context context, int subId) {
+        return TelephonyManager.from(context).getLteOnCdmaMode(subId)
+                == PhoneConstants.LTE_ON_CDMA_TRUE;
+    }
+
+    private static boolean isWorldMode(Context context) {
+        boolean worldModeOn = false;
+        final TelephonyManager tm = (TelephonyManager)
+                context.getSystemService(Context.TELEPHONY_SERVICE);
+
+        Resources phoneResources = getPhoneResources(context);
+        if (phoneResources != null) {
+            int id = phoneResources.getIdentifier("config_world_mode",
+                    "string", "com.android.phone");
+
+            if (id > 0) {
+                final String configString = phoneResources.getString(id);
+
+                if (!TextUtils.isEmpty(configString)) {
+                    String[] configArray = configString.split(";");
+                    // Check if we have World mode configuration set to True only or config is set to True
+                    // and SIM GID value is also set and matches to the current SIM GID.
+                    if (configArray != null &&
+                            ((configArray.length == 1 && configArray[0].equalsIgnoreCase("true")) ||
+                                    (configArray.length == 2 && !TextUtils.isEmpty(configArray[1]) &&
+                                            tm != null && configArray[1].equalsIgnoreCase(tm.getGroupIdLevel1())))) {
+                        worldModeOn = true;
+                    }
+                }
+            } else {
+                Log.w(TAG, "couldn't find resource of config_world_mode");
+            }
+        }
+
+        return worldModeOn;
+    }
+
+    private static Resources getPhoneResources(Context context) {
+        try {
+            final Context packageContext = context.createPackageContext("com.android.phone", 0);
+            return packageContext.getResources();
+        } catch (PackageManager.NameNotFoundException e) {
+            e.printStackTrace();
+        }
+        Log.w(TAG, "couldn't locate resources for com.android.phone!");
+        return null;
+    }
+}