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 & 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"><new profile></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;
+ }
+}