OSDN Git Service

Profiles : Settings
authorDanny Baumann <dannybaumann@web.de>
Thu, 13 Nov 2014 01:14:27 +0000 (17:14 -0800)
committerAdnan Begovic <adnan@cyngn.com>
Mon, 26 Oct 2015 23:11:13 +0000 (16:11 -0700)
Change-Id: I072758a1c5ec04ef34077551220b6611068fe71d

59 files changed:
AndroidManifest.xml
res/drawable-hdpi/ic_location.png [new file with mode: 0644]
res/drawable-hdpi/ic_menu_add.png [new file with mode: 0644]
res/drawable-hdpi/ic_menu_nfc_writer_dark.png [new file with mode: 0644]
res/drawable-hdpi/ic_menu_trash_holo_dark.png [new file with mode: 0644]
res/drawable-hdpi/ic_settings_profiles.png [new file with mode: 0644]
res/drawable-hdpi/nfc_writer.png [new file with mode: 0644]
res/drawable-mdpi/ic_location.png [new file with mode: 0644]
res/drawable-mdpi/ic_menu_add.png [new file with mode: 0644]
res/drawable-mdpi/ic_menu_trash_holo_dark.png [new file with mode: 0644]
res/drawable-mdpi/ic_settings_profiles.png [new file with mode: 0644]
res/drawable-xhdpi/ic_location.png [new file with mode: 0644]
res/drawable-xhdpi/ic_menu_add.png [new file with mode: 0644]
res/drawable-xhdpi/ic_menu_nfc_writer_dark.png [new file with mode: 0644]
res/drawable-xhdpi/ic_menu_trash_holo_dark.png [new file with mode: 0644]
res/drawable-xhdpi/ic_settings_profiles.png [new file with mode: 0644]
res/drawable-xhdpi/nfc_writer.png [new file with mode: 0644]
res/layout/nfc_select.xml [new file with mode: 0644]
res/layout/nfc_writer.xml [new file with mode: 0644]
res/layout/preference_name.xml [new file with mode: 0644]
res/layout/preference_profiles.xml [new file with mode: 0644]
res/layout/preference_profiles_widget.xml [new file with mode: 0644]
res/layout/preference_streamvolume.xml [new file with mode: 0644]
res/layout/profile_name_dialog.xml [new file with mode: 0644]
res/layout/profile_tabs.xml [new file with mode: 0644]
res/values/cm_arrays.xml
res/values/cm_strings.xml
res/values/styles.xml
res/xml/appgroup_list.xml [new file with mode: 0644]
res/xml/application_list.xml [new file with mode: 0644]
res/xml/dashboard_categories.xml
res/xml/profile_config.xml [new file with mode: 0644]
res/xml/profile_settings.xml [new file with mode: 0644]
res/xml/profiles_settings.xml [new file with mode: 0644]
src/com/android/settings/Utils.java
src/com/android/settings/profiles/AbstractTriggerPreference.java [new file with mode: 0644]
src/com/android/settings/profiles/AppGroupConfig.java [new file with mode: 0644]
src/com/android/settings/profiles/AppGroupList.java [new file with mode: 0644]
src/com/android/settings/profiles/ApplicationItemPreference.java [new file with mode: 0644]
src/com/android/settings/profiles/BluetoothTriggerPreference.java [new file with mode: 0644]
src/com/android/settings/profiles/NFCProfile.java [new file with mode: 0644]
src/com/android/settings/profiles/NFCProfileSelect.java [new file with mode: 0644]
src/com/android/settings/profiles/NFCProfileUtils.java [new file with mode: 0644]
src/com/android/settings/profiles/NFCProfileWriter.java [new file with mode: 0644]
src/com/android/settings/profiles/NamePreference.java [new file with mode: 0644]
src/com/android/settings/profiles/ProfileAirplaneModePreference.java [new file with mode: 0644]
src/com/android/settings/profiles/ProfileConfig.java [new file with mode: 0644]
src/com/android/settings/profiles/ProfileConnectionPreference.java [new file with mode: 0644]
src/com/android/settings/profiles/ProfileEnabler.java [new file with mode: 0644]
src/com/android/settings/profiles/ProfileGroupConfig.java [new file with mode: 0644]
src/com/android/settings/profiles/ProfileRingModePreference.java [new file with mode: 0644]
src/com/android/settings/profiles/ProfileRingtonePreference.java [new file with mode: 0644]
src/com/android/settings/profiles/ProfilesList.java [new file with mode: 0644]
src/com/android/settings/profiles/ProfilesPreference.java [new file with mode: 0644]
src/com/android/settings/profiles/ProfilesSettings.java [new file with mode: 0644]
src/com/android/settings/profiles/ProfilesUtils.java [new file with mode: 0644]
src/com/android/settings/profiles/StreamVolumePreference.java [new file with mode: 0644]
src/com/android/settings/profiles/TriggersFragment.java [new file with mode: 0644]
src/com/android/settings/profiles/WifiTriggerAPPreference.java [new file with mode: 0644]

index 7923033..a7949a9 100644 (file)
             android:theme="@style/CryptKeeperBlankTheme"
         />
 
+        <!-- CyanogenMod activities Start -->
+        <activity android:name=".profiles.ProfilesSettings" />
+        <activity android:name="Settings$ProfilesSettingsActivity"
+                android:label="@string/profile_settings"
+                android:taskAffinity=""
+                android:excludeFromRecents="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <action android:name="android.settings.PROFILES_SETTINGS" />
+                <action android:name="com.android.settings.PROFILES_SETTINGS" />
+                <category android:name="android.intent.category.VOICE_LAUNCH" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.SHORTCUT" />
+                </intent-filter>
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                android:value="com.android.settings.profiles.ProfilesSettings" />
+            <meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
+                android:resource="@id/profiles_settings" />
+            </activity>
+
+        <!-- Keep compatibility with old shortcuts. -->
+        <activity-alias android:name="ProfileSettings"
+                android:label="@string/profile_settings"
+                android:exported="true"
+                android:targetActivity="Settings$ProfilesSettingsActivity">
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                android:value="com.android.settings.profiles.ProfilesSettings" />
+            <meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
+                android:resource="@id/profiles_settings" />
+        </activity-alias>
+
+        <activity android:name=".profiles.ProfileConfig" />
+
+        <activity android:name=".profiles.AppGroupList" />
+
+        <activity android:name=".profiles.AppGroupConfig" />
+
+        <activity android:name=".profiles.ProfileGroupConfig" />
+
+        <activity android:name=".profiles.WifiTriggers" />
+
+        <activity android:name=".profiles.NFCProfileWriter" />
+
+        <activity android:name=".profiles.NFCProfileSelect" />
+
+        <activity android:name=".profiles.NFCProfile"
+                android:theme="@android:style/Theme.NoDisplay">
+            <intent-filter>
+                <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <data android:mimeType="cm/profile" />
+            </intent-filter>
+        </activity>
+
+        <!-- CyanogenMod activities End -->
+
         <!-- Pseudo-activity used to provide an intent-filter entry point to encryption settings -->
         <activity android:name="Settings$CryptKeeperSettingsActivity"
                 android:label="@string/crypt_keeper_encrypt_title">
diff --git a/res/drawable-hdpi/ic_location.png b/res/drawable-hdpi/ic_location.png
new file mode 100644 (file)
index 0000000..be38c05
Binary files /dev/null and b/res/drawable-hdpi/ic_location.png differ
diff --git a/res/drawable-hdpi/ic_menu_add.png b/res/drawable-hdpi/ic_menu_add.png
new file mode 100644 (file)
index 0000000..4b68f52
Binary files /dev/null and b/res/drawable-hdpi/ic_menu_add.png differ
diff --git a/res/drawable-hdpi/ic_menu_nfc_writer_dark.png b/res/drawable-hdpi/ic_menu_nfc_writer_dark.png
new file mode 100644 (file)
index 0000000..88fe437
Binary files /dev/null and b/res/drawable-hdpi/ic_menu_nfc_writer_dark.png 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 (file)
index 0000000..c7a0832
Binary files /dev/null and b/res/drawable-hdpi/ic_menu_trash_holo_dark.png differ
diff --git a/res/drawable-hdpi/ic_settings_profiles.png b/res/drawable-hdpi/ic_settings_profiles.png
new file mode 100644 (file)
index 0000000..f47568d
Binary files /dev/null and b/res/drawable-hdpi/ic_settings_profiles.png differ
diff --git a/res/drawable-hdpi/nfc_writer.png b/res/drawable-hdpi/nfc_writer.png
new file mode 100644 (file)
index 0000000..873e721
Binary files /dev/null and b/res/drawable-hdpi/nfc_writer.png differ
diff --git a/res/drawable-mdpi/ic_location.png b/res/drawable-mdpi/ic_location.png
new file mode 100644 (file)
index 0000000..9bcee70
Binary files /dev/null and b/res/drawable-mdpi/ic_location.png differ
diff --git a/res/drawable-mdpi/ic_menu_add.png b/res/drawable-mdpi/ic_menu_add.png
new file mode 100644 (file)
index 0000000..15ffadd
Binary files /dev/null and b/res/drawable-mdpi/ic_menu_add.png 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 (file)
index 0000000..b9575aa
Binary files /dev/null and b/res/drawable-mdpi/ic_menu_trash_holo_dark.png differ
diff --git a/res/drawable-mdpi/ic_settings_profiles.png b/res/drawable-mdpi/ic_settings_profiles.png
new file mode 100644 (file)
index 0000000..cc740bd
Binary files /dev/null and b/res/drawable-mdpi/ic_settings_profiles.png differ
diff --git a/res/drawable-xhdpi/ic_location.png b/res/drawable-xhdpi/ic_location.png
new file mode 100644 (file)
index 0000000..0e0be6c
Binary files /dev/null and b/res/drawable-xhdpi/ic_location.png differ
diff --git a/res/drawable-xhdpi/ic_menu_add.png b/res/drawable-xhdpi/ic_menu_add.png
new file mode 100644 (file)
index 0000000..420510e
Binary files /dev/null and b/res/drawable-xhdpi/ic_menu_add.png differ
diff --git a/res/drawable-xhdpi/ic_menu_nfc_writer_dark.png b/res/drawable-xhdpi/ic_menu_nfc_writer_dark.png
new file mode 100644 (file)
index 0000000..417ec98
Binary files /dev/null and b/res/drawable-xhdpi/ic_menu_nfc_writer_dark.png 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 (file)
index 0000000..33add13
Binary files /dev/null and b/res/drawable-xhdpi/ic_menu_trash_holo_dark.png differ
diff --git a/res/drawable-xhdpi/ic_settings_profiles.png b/res/drawable-xhdpi/ic_settings_profiles.png
new file mode 100644 (file)
index 0000000..d6c6911
Binary files /dev/null and b/res/drawable-xhdpi/ic_settings_profiles.png differ
diff --git a/res/drawable-xhdpi/nfc_writer.png b/res/drawable-xhdpi/nfc_writer.png
new file mode 100644 (file)
index 0000000..60f4d6e
Binary files /dev/null and b/res/drawable-xhdpi/nfc_writer.png differ
diff --git a/res/layout/nfc_select.xml b/res/layout/nfc_select.xml
new file mode 100644 (file)
index 0000000..e72a979
--- /dev/null
@@ -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 (file)
index 0000000..959217c
--- /dev/null
@@ -0,0 +1,57 @@
+<?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="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:src="@drawable/nfc_writer" />
+
+        <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_name.xml b/res/layout/preference_name.xml
new file mode 100644 (file)
index 0000000..5b3ff86
--- /dev/null
@@ -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 (file)
index 0000000..916c2ee
--- /dev/null
@@ -0,0 +1,96 @@
+<?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/profiles_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:background="?android:attr/selectableItemBackground">
+
+        <LinearLayout
+            android:id="@android:id/widget_frame"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="center_vertical|end"
+            android:orientation="vertical" />
+
+        <RelativeLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginEnd="6dip"
+            android:layout_marginTop="6dip"
+            android:layout_marginBottom="6dip"
+            android:layout_weight="1">
+
+            <TextView
+                android:id="@+android:id/title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:singleLine="true"
+                android:textAppearance="?android:attr/textAppearanceMedium"
+                android:ellipsize="marquee"
+                android:fadingEdge="horizontal"/>
+
+            <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:paddingBottom="3dip"
+                android:visibility="gone"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textSize="13sp"
+                android:textColor="?android:attr/textColorSecondary"
+                android:focusable="false"
+                android:maxLines="4" />
+
+        </RelativeLayout>
+
+    </LinearLayout>
+
+    <View
+        android:layout_width="2dip"
+        android:layout_height="match_parent"
+        android:layout_marginTop="5dip"
+        android:layout_marginBottom="5dip"
+        android:background="@android:drawable/divider_horizontal_dark" />
+
+    <ImageView
+        android:id="@+id/profiles_settings"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:paddingStart="15dip"
+        android:paddingEnd="?android:attr/scrollbarSize"
+        android:src="@drawable/ic_sysbar_quicksettings"
+        android:contentDescription="@string/input_method_settings_button"
+        android:layout_gravity="center"
+        android:clickable="true"
+        android:focusable="true"
+        android:background="?android:attr/selectableItemBackground" />
+
+</LinearLayout>
diff --git a/res/layout/preference_profiles_widget.xml b/res/layout/preference_profiles_widget.xml
new file mode 100644 (file)
index 0000000..ab63a10
--- /dev/null
@@ -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/preference_streamvolume.xml b/res/layout/preference_streamvolume.xml
new file mode 100644 (file)
index 0000000..a6675f5
--- /dev/null
@@ -0,0 +1,86 @@
+<?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/text_layout"
+        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:dimen/preference_item_padding_inner"
+        android:background="?android:attr/selectableItemBackground">
+
+        <RelativeLayout
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:paddingTop="6dip"
+            android:paddingBottom="6dip"
+            android:paddingStart="@*android:dimen/preference_icon_minWidth" >
+
+            <TextView android:id="@+android:id/title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:singleLine="true"
+                android:textAppearance="?android:attr/textAppearanceMedium"
+                android:ellipsize="marquee"
+                android:fadingEdge="horizontal" />
+
+            <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/textAppearanceSmall"
+                android:textColor="?android:attr/textColorSecondary"
+                android:maxLines="2" />
+
+        </RelativeLayout>
+
+    </LinearLayout>
+
+    <View
+        android:layout_width="2dip"
+        android:layout_height="match_parent"
+        android:layout_marginTop="5dip"
+        android:layout_marginBottom="5dip"
+        android:background="@android:drawable/divider_horizontal_dark" />
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:minWidth="68dip"
+        android:gravity="center"
+        android:orientation="vertical" >
+
+        <CheckBox
+            xmlns:android="http://schemas.android.com/apk/res/android"
+            android:id="@+id/profile_checkbox"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:clickable="true"
+            android:gravity="center" />
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/res/layout/profile_name_dialog.xml b/res/layout/profile_name_dialog.xml
new file mode 100644 (file)
index 0000000..e7c2ed3
--- /dev/null
@@ -0,0 +1,38 @@
+<!--
+     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:paddingBottom="8dip"
+        android:textAppearance="?android:attr/textAppearanceSmall" />
+
+    <EditText
+        android:id="@+id/name"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:textAppearance="?android:attr/textAppearanceMedium" />
+
+</LinearLayout>
diff --git a/res/layout/profile_tabs.xml b/res/layout/profile_tabs.xml
new file mode 100644 (file)
index 0000000..7c8f6cb
--- /dev/null
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The CyanogenMod Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+    <android.support.v4.view.ViewPager
+            android:id="@+id/pager"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_weight="1">
+        <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.PagerTabStrip>
+    </android.support.v4.view.ViewPager>
+
+    <TextView
+        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" />
+
+</LinearLayout>
index 2692f43..3582200 100644 (file)
         <item>2</item>
         <item>3</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>
+    </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>
+    </string-array>
+
+    <!-- Profile mode options. -->
+    <string-array name="profile_entries">
+        <item>On</item>
+        <item>Off</item>
+        <item>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 mode options. -->
+    <string-array name="profile_connection_entries">
+        <item>Disable</item>
+        <item>Enable</item>
+    </string-array>
+
+    <!-- Profile 2G-3G mode options. -->
+    <string-array name="profile_networkmode_entries" translatable="false">
+        <item>@string/profile_networkmode_2g</item>
+        <item>@string/profile_networkmode_3g</item>
+        <item>@string/profile_networkmode_2g3g</item>
+    </string-array>
+
+    <!-- Values for profile connections. Do not translate. -->
+    <string-array name="profile_connection_values" translatable="false">
+        <item>0</item>
+        <item>1</item>
+        <item>2</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_notrigger</item>
+    </string-array>
+
+    <!--  Values for profile trigger types -->
+    <string-array name="profile_trigger_filters" translatable="false">
+        <item>@string/profile_trigger_filter_all</item>
+        <item>@string/profile_trigger_filter_wifi</item>
+        <item>@string/profile_trigger_filter_bluetooth</item>
+    </string-array>
+
+    <!-- Profile lock mode options. Do not translate. -->
+    <string-array name="profile_lockmode_entries" translatable="false">
+        <item>@string/profile_lockmode_default</item>
+        <item>@string/profile_lockmode_insecure</item>
+        <item>@string/profile_lockmode_disabled</item>
+    </string-array>
+
+    <!-- Profile lock mode summaries. Do not translate. -->
+    <string-array name="profile_lockmode_summaries" translatable="false">
+        <item>@string/profile_lockmode_default_summary</item>
+        <item>@string/profile_lockmode_insecure_summary</item>
+        <item>@string/profile_lockmode_disabled_summary</item>
+    </string-array>
+
+    <!-- Values for profile lock mode. Do not translate. -->
+    <string-array name="profile_lockmode_values" translatable="false">
+        <item>0</item>
+        <item>1</item>
+        <item>2</item>
+    </string-array>
 </resources>
index 5d9bd49..6008082 100644 (file)
     <!-- Themes Settings -->
     <!-- Settings main menu entry -->
     <string name="themes_settings_title">Themes</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>
+
+    <!-- Profiles settings  -->
+    <string name="profiles_settings_title">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_triggers">Triggers</string>
+    <string name="profile_trigger_filter_all">All triggers</string>
+    <string name="profile_trigger_filter_wifi">Wi-Fi triggers</string>
+    <string name="profile_trigger_filter_bluetooth">Bluetooth triggers</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>
+
+    <!-- Add Profile -->
+    <string name="add_profile_dialog_title">Create new profile</string>
+    <string name="profile_name_title">Name</string>
+    <string name="profile_profile_name_prompt">Enter a name for the new profile</string>
+    <string name="menu_new_profile">New profile</string>
+    <string name="new_profile_name">&lt;new profile&gt;</string>
+
+    <!-- Rename Dialog  -->
+    <string name="rename_dialog_title">Rename</string>
+    <string name="rename_dialog_message">Enter a new name</string>
+    <string name="duplicate_profile_name">Duplicate profile name!</string>
+    <string name="duplicate_appgroup_name">Duplicate app group 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_delete_confirm">Delete this profile?</string>
+    <string name="profile_app_delete_confirm">Remove this app?</string>
+    <string name="profile_cannot_delete">An active profile cannot be deleted</string>
+
+    <!-- Profile network mode -->
+    <string name="profile_networkmode_2g">2G</string>
+    <string name="profile_networkmode_3g">3G</string>
+    <string name="profile_networkmode_2g3g">2G/3G</string>
+
+    <!-- Profile Config screen PreferenceGroup titles -->
+    <string name="profile_connectionoverrides_title">Connection overrides</string>
+    <string name="profile_volumeoverrides_title">Volume overrides</string>
+    <string name="connection_state_disabled">Disable</string>
+    <string name="connection_state_enabled">Enable</string>
+    <string name="volume_override_summary">Set to</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>
+
+    <!-- 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_default">Default</string>
+    <string name="profile_lockmode_insecure">Insecure</string>
+    <string name="profile_lockmode_disabled">Disabled</string>
+    <string name="profile_lockmode_default_summary">Use system default</string>
+    <string name="profile_lockmode_insecure_summary">Use insecure lock screen</string>
+    <string name="profile_lockmode_disabled_summary">Lock screen is disabled</string>
+    <string name="profile_airplanemode_title">Airplane mode</string>
+
+    <!-- Connection override toggles (not all are used at this time ) -->
+    <string name="toggleWifi">Wi-Fi</string>
+    <string name="toggleWifiAp">Portable Wi-Fi hotspot</string>
+    <string name="toggleBluetooth">Bluetooth</string>
+    <string name="toggleGPS">GPS</string>
+    <string name="toggleData">Mobile Data</string>
+    <string name="toggleSync">Data Sync</string>
+    <string name="toggle2g3g">2G/3G</string>
+    <string name="toggleWimax">WiMAX</string>
+    <string name="toggleNfc">NFC</string>
 </resources>
index 387acf5..84501c3 100644 (file)
     <style name="SetupWizardStorageStyle" parent="@style/SuwThemeMaterial.Light">
         <item name="android:colorAccent">#ff009688</item>
     </style>
+
+    <style name="ProfilesPreferenceStyle">
+        <item name="android:layout">@layout/preference_profiles</item>
+        <item name="android:widgetLayout">@layout/preference_profiles_widget</item>
+    </style>
 </resources>
diff --git a/res/xml/appgroup_list.xml b/res/xml/appgroup_list.xml
new file mode 100644 (file)
index 0000000..a578a19
--- /dev/null
@@ -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>
diff --git a/res/xml/application_list.xml b/res/xml/application_list.xml
new file mode 100644 (file)
index 0000000..da8c201
--- /dev/null
@@ -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>
index 6c617d7..202e0ff 100644 (file)
             android:key="@string/category_key_personal"
             android:title="@string/header_category_personal" >
 
+        <!-- Profiles -->
+        <dashboard-tile
+                android:id="@+id/profile_settings"
+                android:fragment="com.android.settings.profiles.ProfilesSettings"
+                android:title="@string/profiles_settings_title"
+                android:icon="@drawable/ic_settings_location"
+                />
+
         <!-- Location -->
         <dashboard-tile
                 android:id="@+id/location_settings"
diff --git a/res/xml/profile_config.xml b/res/xml/profile_config.xml
new file mode 100644 (file)
index 0000000..6777a30
--- /dev/null
@@ -0,0 +1,42 @@
+<?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">
+
+    <PreferenceCategory
+        android:key="profile_general_section"
+        android:title="@string/profile_name_title">
+    </PreferenceCategory>
+
+    <PreferenceCategory
+        android:key="profile_connectionoverrides"
+        android:title="@string/profile_connectionoverrides_title" />
+
+    <PreferenceCategory
+        android:key="profile_volumeoverrides"
+        android:title="@string/profile_volumeoverrides_title" />
+
+    <PreferenceCategory
+        android:key="profile_system_settings"
+        android:title="@string/profile_system_settings_title" />
+
+    <PreferenceCategory
+        android:key="profile_appgroups"
+        android:title="@string/profile_appgroups_title" />
+
+</PreferenceScreen>
diff --git a/res/xml/profile_settings.xml b/res/xml/profile_settings.xml
new file mode 100644 (file)
index 0000000..8bce716
--- /dev/null
@@ -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 (file)
index 0000000..d3c477c
--- /dev/null
@@ -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" />
index b6d9414..0a09b15 100644 (file)
@@ -84,6 +84,9 @@ import android.view.ViewGroup;
 import android.view.animation.Animation;
 import android.view.animation.Animation.AnimationListener;
 import android.view.animation.AnimationUtils;
+import android.util.DisplayMetrics;
+import android.view.DisplayInfo;
+import android.view.WindowManager;
 import android.widget.ListView;
 import android.widget.TabWidget;
 
@@ -155,6 +158,14 @@ public final class Utils {
 
     private static SparseArray<Bitmap> sDarkDefaultUserBitmapCache = new SparseArray<Bitmap>();
 
+    // Device types
+    private static final int DEVICE_PHONE = 0;
+    private static final int DEVICE_HYBRID = 1;
+    private static final int DEVICE_TABLET = 2;
+
+    // Device type reference
+    private static int sDeviceType = -1;
+
     /**
      * Finds a matching activity for a preference's intent. If a matching
      * activity is not found, it will remove the preference.
@@ -609,6 +620,40 @@ public final class Utils {
                 .getUsers().size() > 1;
     }
 
+    private static int getScreenType(Context context) {
+        if (sDeviceType == -1) {
+            WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+           DisplayInfo outDisplayInfo = new DisplayInfo();
+            wm.getDefaultDisplay().getDisplayInfo(outDisplayInfo);
+            int shortSize = Math.min(outDisplayInfo.logicalHeight, outDisplayInfo.logicalWidth);
+            int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT
+                    / outDisplayInfo.logicalDensityDpi;
+            if (shortSizeDp < 600) {
+                // 0-599dp: "phone" UI with a separate status & navigation bar
+                sDeviceType =  DEVICE_PHONE;
+            } else if (shortSizeDp < 720) {
+                // 600-719dp: "phone" UI with modifications for larger screens
+                sDeviceType = DEVICE_HYBRID;
+            } else {
+                // 720dp: "tablet" UI with a single combined status & navigation bar
+                sDeviceType = DEVICE_TABLET;
+            }
+        }
+        return sDeviceType;
+    }
+
+    public static boolean isPhone(Context context) {
+        return getScreenType(context) == DEVICE_PHONE;
+    }
+
+    public static boolean isHybrid(Context context) {
+        return getScreenType(context) == DEVICE_HYBRID;
+    }
+
+    public static boolean isTablet(Context context) {
+        return getScreenType(context) == DEVICE_TABLET;
+    }
+
     /**
      * Start a new instance of the activity, showing only the given fragment.
      * When launched in this mode, the given preference fragment will be instantiated and fill the
diff --git a/src/com/android/settings/profiles/AbstractTriggerPreference.java b/src/com/android/settings/profiles/AbstractTriggerPreference.java
new file mode 100644 (file)
index 0000000..1bc2ec0
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.profiles;
+
+import android.app.Profile;
+import android.content.Context;
+import android.preference.Preference;
+
+public class AbstractTriggerPreference extends Preference {
+
+    public AbstractTriggerPreference(Context context) {
+        super(context);
+    }
+
+    private int mTriggerState = Profile.TriggerState.DISABLED;
+
+    public void setTriggerState(int trigger) {
+        mTriggerState = trigger;
+    }
+
+    public int getTriggerState() {
+        return mTriggerState;
+    }
+}
diff --git a/src/com/android/settings/profiles/AppGroupConfig.java b/src/com/android/settings/profiles/AppGroupConfig.java
new file mode 100644 (file)
index 0000000..8fbcfd0
--- /dev/null
@@ -0,0 +1,438 @@
+/*
+ * 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 com.android.settings.profiles;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.NotificationGroup;
+import android.app.ProfileManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.pm.ApplicationInfo;
+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.os.Handler;
+import android.preference.Preference;
+import android.preference.PreferenceGroup;
+import android.preference.PreferenceScreen;
+import android.util.Log;
+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.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+
+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;
+
+    PackageAdaptor mAppAdapter;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mProfileManager = (ProfileManager) getActivity().getSystemService(PROFILE_SERVICE);
+        addPreferencesFromResource(R.xml.application_list);
+
+        final Bundle args = getArguments();
+        if (args != null) {
+            mNotificationGroup = (NotificationGroup) args.getParcelable("NotificationGroup");
+            mPackageManager = getPackageManager();
+            mAppAdapter = new PackageAdaptor(mPackageManager.getInstalledPackages(0));
+            mAppAdapter.update();
+
+            updatePackages();
+
+            setHasOptionsMenu(true);
+        }
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        MenuItem delete = menu.add(0, MENU_DELETE, 0, R.string.profile_menu_delete)
+                .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 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, 0, R.string.profile_menu_delete);
+    }
+
+    @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:
+                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(PreferenceScreen preferenceScreen, Preference preference) {
+        if (preference instanceof Preference) {
+            String deleteItem = preference.getKey();
+            removeApp(deleteItem);
+            return true;
+        }
+        return super.onPreferenceTreeClick(preferenceScreen, 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);
+                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);
+                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();
+    }
+
+    class PackageItem implements Comparable<PackageItem> {
+        String title;
+        String packageName;
+        Drawable icon;
+        boolean enabled;
+
+        @Override
+        public int compareTo(PackageItem another) {
+            if (enabled != another.enabled) {
+                return enabled ? -1 : 1;
+            }
+            int titleResult = title.compareToIgnoreCase(another.title);
+            if (titleResult != 0) {
+                return titleResult;
+            }
+            return packageName.compareTo(another.packageName);
+        }
+    }
+
+    class PackageAdaptor extends BaseAdapter {
+
+        protected List<PackageInfo> mInstalledPackageInfo;
+
+        protected List<PackageItem> mInstalledPackages = new LinkedList<PackageItem>();
+
+        private void reloadList() {
+            final Handler handler = new Handler();
+            new Thread(new Runnable() {
+
+                @Override
+                public void run() {
+                    synchronized (mInstalledPackages) {
+                        mInstalledPackages.clear();
+                        for (PackageInfo info : mInstalledPackageInfo) {
+                            final PackageItem item = new PackageItem();
+                            ApplicationInfo applicationInfo = info.applicationInfo;
+                            item.title = applicationInfo.loadLabel(mPackageManager).toString();
+                            item.icon = applicationInfo.loadIcon(mPackageManager);
+                            item.packageName = applicationInfo.packageName;
+                            item.enabled = applicationInfo.enabled;
+                            handler.post(new Runnable() {
+                                @Override
+                                public void run() {
+                                    int index = Collections.binarySearch(mInstalledPackages, item);
+                                    if (index < 0) {
+                                        index = -index - 1;
+                                        mInstalledPackages.add(index, item);
+                                    }
+                                    notifyDataSetChanged();
+                                }
+                            });
+                        }
+                    }
+                }
+            }).start();
+        }
+
+        public PackageAdaptor(List<PackageInfo> installedPackagesInfo) {
+            mInstalledPackageInfo = installedPackagesInfo;
+        }
+
+        public void update() {
+            reloadList();
+        }
+
+        @Override
+        public int getCount() {
+            return mInstalledPackages.size();
+        }
+
+        @Override
+        public PackageItem getItem(int position) {
+            return mInstalledPackages.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return mInstalledPackages.get(position).packageName.hashCode();
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            ViewHolder holder;
+            if (convertView != null) {
+                holder = (ViewHolder) convertView.getTag();
+            } else {
+                final LayoutInflater layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+                convertView = layoutInflater.inflate(R.layout.preference_icon, null, false);
+                holder = new ViewHolder();
+                convertView.setTag(holder);
+                holder.title = (TextView) convertView.findViewById(com.android.internal.R.id.title);
+                holder.summary = (TextView) convertView
+                        .findViewById(com.android.internal.R.id.summary);
+                holder.icon = (ImageView) convertView.findViewById(R.id.icon);
+            }
+            PackageItem applicationInfo = getItem(position);
+
+            if (holder.title != null) {
+                holder.title.setText(applicationInfo.title);
+            }
+            if (holder.summary != null) {
+                holder.summary.setVisibility(View.GONE);
+            }
+            if (holder.icon != null) {
+                Drawable loadIcon = applicationInfo.icon;
+                holder.icon.setImageDrawable(loadIcon);
+            }
+            return convertView;
+        }
+    }
+
+    static class ViewHolder {
+        TextView title;
+        TextView summary;
+        ImageView icon;
+    }
+}
diff --git a/src/com/android/settings/profiles/AppGroupList.java b/src/com/android/settings/profiles/AppGroupList.java
new file mode 100644 (file)
index 0000000..088b344
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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 com.android.settings.profiles;
+
+import java.util.UUID;
+
+import android.app.NotificationGroup;
+import android.app.ProfileManager;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceScreen;
+import android.provider.Settings;
+
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.Utils;
+
+public class AppGroupList extends SettingsPreferenceFragment {
+
+    private static final String TAG = "AppGroupSettings";
+    public static final String PROFILE_SERVICE = "profile";
+
+    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) getActivity().getSystemService(PROFILE_SERVICE);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        refreshList();
+
+        // On tablet devices remove the padding
+        if (Utils.isTablet(getActivity())) {
+            getListView().setPadding(0, 0, 0, 0);
+        }
+    }
+
+    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(PreferenceScreen preferenceScreen, Preference preference) {
+        if (preference instanceof PreferenceScreen) {
+            NotificationGroup group = mProfileManager.getNotificationGroup(
+                    UUID.fromString(preference.getKey()));
+            editGroup(group);
+        }
+        return super.onPreferenceTreeClick(preferenceScreen, preference);
+    }
+
+    private void editGroup(NotificationGroup group) {
+        Bundle args = new Bundle();
+        args.putParcelable("NotificationGroup", group);
+
+        SettingsActivity pa = (SettingsActivity) getActivity();
+        pa.startPreferencePanel(AppGroupConfig.class.getName(), args,
+                R.string.profile_appgroup_manage, null, this, APP_GROUP_CONFIG);
+    }
+}
diff --git a/src/com/android/settings/profiles/ApplicationItemPreference.java b/src/com/android/settings/profiles/ApplicationItemPreference.java
new file mode 100644 (file)
index 0000000..1247e85
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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 com.android.settings.profiles;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.preference.Preference;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+import com.android.settings.R;
+
+public class ApplicationItemPreference extends Preference {
+
+    private static String TAG = "ApplicationItemPreference";
+    
+    private Drawable mIcon;
+
+    public ApplicationItemPreference(Context context) {
+        this(context, null, 0);
+    }
+
+    public ApplicationItemPreference(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        setLayoutResource(R.layout.preference_icon);
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.IconPreferenceScreen, defStyle, 0);
+        mIcon = a.getDrawable(R.styleable.IconPreferenceScreen_icon);
+    }
+
+    public void setIcon(Drawable icon){
+        mIcon = icon;
+    }
+
+    @Override
+    public void onBindView(View view) {
+        super.onBindView(view);
+
+        float valueDips = 36.0f;
+        final float scale = getContext().getResources().getDisplayMetrics().density;
+        int valuePixels = (int) (valueDips * scale + 0.5f);
+
+        ImageView imageView = (ImageView) view.findViewById(R.id.icon);
+        if (imageView != null && mIcon != null) {
+            imageView.setAdjustViewBounds(true);
+            imageView.setMaxHeight(valuePixels);
+            imageView.setMaxWidth(valuePixels);
+            imageView.setImageDrawable(mIcon);
+        }
+    }
+}
diff --git a/src/com/android/settings/profiles/BluetoothTriggerPreference.java b/src/com/android/settings/profiles/BluetoothTriggerPreference.java
new file mode 100644 (file)
index 0000000..848fc28
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.profiles;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+
+public class BluetoothTriggerPreference extends AbstractTriggerPreference {
+
+    private String mAddress;
+
+    BluetoothTriggerPreference(Context context, BluetoothDevice device) {
+        super(context);
+        mAddress = device.getAddress();
+        if (device.getAlias() != null) {
+            setTitle(device.getAlias());
+        } else {
+            setTitle(device.getName());
+        }
+    }
+
+    BluetoothTriggerPreference(Context context, String name, String address) {
+        super(context);
+        mAddress = address;
+        setTitle(name);
+    }
+
+    public String getAddress() {
+        return mAddress;
+    }
+}
diff --git a/src/com/android/settings/profiles/NFCProfile.java b/src/com/android/settings/profiles/NFCProfile.java
new file mode 100644 (file)
index 0000000..a6e2334
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * 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 com.android.settings.profiles;
+
+import java.util.UUID;
+
+import android.app.Activity;
+import android.app.Profile;
+import android.app.ProfileManager;
+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.provider.Settings;
+import android.widget.Toast;
+
+import com.android.settings.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) getSystemService(Context.PROFILE_SERVICE);
+    }
+
+    @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 = Settings.System.getInt(getContentResolver(),
+                Settings.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/com/android/settings/profiles/NFCProfileSelect.java b/src/com/android/settings/profiles/NFCProfileSelect.java
new file mode 100644 (file)
index 0000000..c66de64
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * 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 com.android.settings.profiles;
+
+import java.util.UUID;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.app.Profile;
+import android.app.ProfileManager;
+import android.app.AlertDialog.Builder;
+import android.content.Context;
+import android.content.DialogInterface;
+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.View;
+import android.view.View.OnClickListener;
+import android.widget.Toast;
+
+import com.android.settings.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) getSystemService(Context.PROFILE_SERVICE);
+
+        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/com/android/settings/profiles/NFCProfileUtils.java b/src/com/android/settings/profiles/NFCProfileUtils.java
new file mode 100644 (file)
index 0000000..f6461d1
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * 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 com.android.settings.profiles;
+
+import java.io.IOException;
+import java.util.UUID;
+
+import android.app.Profile;
+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;
+
+public class NFCProfileUtils {
+
+    private static final String TAG = "NFCUtils";
+
+    private static final long[] VIBRATION_PATTERN = {
+            0, 100, 10000
+    };
+
+    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
+     */
+    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
+     */
+    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/com/android/settings/profiles/NFCProfileWriter.java b/src/com/android/settings/profiles/NFCProfileWriter.java
new file mode 100644 (file)
index 0000000..72b8c13
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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 com.android.settings.profiles;
+
+import java.util.UUID;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.app.Profile;
+import android.app.ProfileManager;
+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 com.android.settings.R;
+
+/**
+ * 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) getSystemService(Context.PROFILE_SERVICE);
+
+        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/com/android/settings/profiles/NamePreference.java b/src/com/android/settings/profiles/NamePreference.java
new file mode 100644 (file)
index 0000000..7b48e0c
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * 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 com.android.settings.profiles;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.preference.Preference;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.settings.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 onBindView(View view) {
+        super.onBindView(view);
+
+        View namePref = view.findViewById(R.id.name_pref);
+        if ((namePref != null) && namePref instanceof LinearLayout) {
+            namePref.setOnClickListener(this);
+        }
+
+        mNameView = (TextView) view.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;
+    }
+}
diff --git a/src/com/android/settings/profiles/ProfileAirplaneModePreference.java b/src/com/android/settings/profiles/ProfileAirplaneModePreference.java
new file mode 100644 (file)
index 0000000..067e43b
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * 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 com.android.settings.profiles;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.preference.Preference;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.LinearLayout;
+
+import com.android.settings.R;
+
+public class ProfileAirplaneModePreference extends Preference implements
+        CompoundButton.OnCheckedChangeListener, View.OnClickListener {
+
+    private boolean mProtectFromCheckedChange = false;
+
+    private CheckBox mCheckBox;
+
+    final static String TAG = "ProfileSilentModePreference";
+
+    private ProfileConfig.AirplaneModeItem mAirplaneModeItem;
+
+    final static int defaultChoice = -1;
+
+    private int currentChoice;
+
+    /**
+     * @param context
+     * @param attrs
+     * @param defStyle
+     */
+    public ProfileAirplaneModePreference(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        init();
+    }
+
+    /**
+     * @param context
+     * @param attrs
+     */
+    public ProfileAirplaneModePreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    /**
+     * @param context
+     */
+    public ProfileAirplaneModePreference(Context context) {
+        super(context);
+        init();
+    }
+
+    @Override
+    public View getView(View convertView, ViewGroup parent) {
+        View view = super.getView(convertView, parent);
+
+        View widget = view.findViewById(R.id.profile_checkbox);
+        if ((widget != null) && widget instanceof CheckBox) {
+            mCheckBox = (CheckBox) widget;
+            mCheckBox.setOnCheckedChangeListener(this);
+
+            mProtectFromCheckedChange = true;
+            mCheckBox.setChecked(isChecked());
+            mProtectFromCheckedChange = false;
+        }
+
+        View textLayout = view.findViewById(R.id.text_layout);
+        if ((textLayout != null) && textLayout instanceof LinearLayout) {
+            textLayout.setOnClickListener(this);
+        }
+
+        return view;
+    }
+
+    private void init() {
+        setLayoutResource(R.layout.preference_streamvolume);
+    }
+
+    public boolean isChecked() {
+        return mAirplaneModeItem != null && mAirplaneModeItem.mSettings.isOverride();
+    }
+
+    public void setAirplaneModeItem(ProfileConfig.AirplaneModeItem airplaneModeItem) {
+        mAirplaneModeItem = airplaneModeItem;
+
+        if (mCheckBox != null) {
+            mCheckBox.setChecked(mAirplaneModeItem.mSettings.isOverride());
+        }
+    }
+
+    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+        if (mProtectFromCheckedChange) {
+            return;
+        }
+
+        mAirplaneModeItem.mSettings.setOverride(isChecked);
+
+        callChangeListener(isChecked);
+    }
+
+    protected Dialog createAirplaneModeDialog() {
+        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+        final String[] AirplaneModeValues = getContext().getResources().getStringArray(
+                R.array.profile_connection_values);
+
+        currentChoice = mAirplaneModeItem.mSettings.getValue();
+
+        builder.setTitle(R.string.profile_airplanemode_title);
+        builder.setSingleChoiceItems(R.array.profile_connection_entries, currentChoice,
+                new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int item) {
+                currentChoice = item;
+            }
+        });
+
+        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int item) {
+                if (currentChoice != defaultChoice) {
+                    int value = Integer.parseInt(AirplaneModeValues[currentChoice]);
+                    mAirplaneModeItem.mSettings.setValue(currentChoice);
+                    setSummary(value == 1 ? getContext().getString(R.string.connection_state_enabled) : getContext()
+                            .getString(R.string.connection_state_disabled));
+                }
+            }
+        });
+
+        builder.setNegativeButton(android.R.string.cancel, null);
+        return builder.create();
+    }
+
+    public ProfileConfig.AirplaneModeItem getAirplaneModeItem() {
+        return mAirplaneModeItem;
+    }
+
+    @Override
+    public void onClick(android.view.View v) {
+        if ((v != null) && (R.id.text_layout == v.getId())) {
+            createAirplaneModeDialog().show();
+        }
+    }
+
+    public void setSummary(Context context) {
+        int value = mAirplaneModeItem.mSettings.getValue();
+        setSummary(value == 1 ? getContext().getString(R.string.connection_state_enabled) : getContext().getString(
+                R.string.connection_state_disabled));
+    }
+}
diff --git a/src/com/android/settings/profiles/ProfileConfig.java b/src/com/android/settings/profiles/ProfileConfig.java
new file mode 100644 (file)
index 0000000..3532720
--- /dev/null
@@ -0,0 +1,479 @@
+/*
+ * 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 com.android.settings.profiles;
+
+import java.util.ArrayList;
+import java.util.UUID;
+
+import android.app.AirplaneModeSettings;
+import android.app.AlertDialog;
+import android.app.ConnectionSettings;
+import android.app.Profile;
+import android.app.ProfileGroup;
+import android.app.ProfileManager;
+import android.app.RingModeSettings;
+import android.app.StreamSettings;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.net.wimax.WimaxHelper;
+import android.os.Bundle;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceGroup;
+import android.preference.PreferenceScreen;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import static com.android.settings.profiles.ProfilesUtils.*;
+import com.android.settings.SettingsPreferenceFragment;
+
+public class ProfileConfig extends SettingsPreferenceFragment
+    implements Preference.OnPreferenceChangeListener {
+
+    static final String TAG = "ProfileConfig";
+
+    public static final String PROFILE_SERVICE = "profile";
+
+    private ProfileManager mProfileManager;
+
+    private static final int MENU_NFC_WRITE = Menu.FIRST;
+
+    private static final int MENU_DELETE = Menu.FIRST + 1;
+
+    private static final int MENU_TRIGGERS = Menu.FIRST + 2;
+
+    private Profile mProfile;
+
+    private NamePreference mNamePreference;
+
+    private ListPreference mScreenLockModePreference;
+
+    // constant value that can be used to check return code from sub activity.
+    private static final int PROFILE_GROUP_DETAILS = 1;
+
+    private StreamItem[] mStreams;
+
+    private ArrayList<ConnectionItem> mConnections;
+
+    private RingModeItem mRingMode;
+
+    private AirplaneModeItem mAirplaneMode;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        mStreams = new StreamItem[] {
+                new StreamItem(AudioManager.STREAM_ALARM, getString(R.string.alarm_volume_title)),
+                new StreamItem(AudioManager.STREAM_MUSIC, getString(R.string.media_volume_title)),
+                new StreamItem(AudioManager.STREAM_RING, getString(R.string.incoming_call_volume_title)),
+                new StreamItem(AudioManager.STREAM_NOTIFICATION, getString(R.string.notification_volume_title))
+        };
+
+        mConnections = new ArrayList<ConnectionItem>();
+        if (deviceSupportsBluetooth()) {
+            mConnections.add(new ConnectionItem(ConnectionSettings.PROFILE_CONNECTION_BLUETOOTH, getString(R.string.toggleBluetooth)));
+        }
+        mConnections.add(new ConnectionItem(ConnectionSettings.PROFILE_CONNECTION_GPS, getString(R.string.toggleGPS)));
+        mConnections.add(new ConnectionItem(ConnectionSettings.PROFILE_CONNECTION_WIFI, getString(R.string.toggleWifi)));
+        mConnections.add(new ConnectionItem(ConnectionSettings.PROFILE_CONNECTION_SYNC, getString(R.string.toggleSync)));
+        if (deviceSupportsMobileData(getActivity())) {
+            mConnections.add(new ConnectionItem(ConnectionSettings.PROFILE_CONNECTION_MOBILEDATA, getString(R.string.toggleData)));
+            mConnections.add(new ConnectionItem(ConnectionSettings.PROFILE_CONNECTION_WIFIAP, getString(R.string.toggleWifiAp)));
+            final TelephonyManager tm = (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
+            if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {
+                mConnections.add(new ConnectionItem(ConnectionSettings.PROFILE_CONNECTION_2G3G, getString(R.string.toggle2g3g), R.array.profile_networkmode_entries));
+            }
+        }
+        if (WimaxHelper.isWimaxSupported(getActivity())) {
+            mConnections.add(new ConnectionItem(ConnectionSettings.PROFILE_CONNECTION_WIMAX, getString(R.string.toggleWimax)));
+        }
+        if (deviceSupportsNfc(getActivity())) {
+            mConnections.add(new ConnectionItem(ConnectionSettings.PROFILE_CONNECTION_NFC, getString(R.string.toggleNfc)));
+        }
+
+        addPreferencesFromResource(R.xml.profile_config);
+
+        mProfileManager = (ProfileManager) getActivity().getSystemService(PROFILE_SERVICE);
+
+        final Bundle args = getArguments();
+        mProfile = (args != null) ? (Profile) args.getParcelable("Profile") : null;
+
+        if (mProfile == null) {
+            mProfile = new Profile(getString(R.string.new_profile_name));
+            mProfileManager.addProfile(mProfile);
+        }
+
+        setHasOptionsMenu(true);
+
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        if (deviceSupportsNfc(getActivity())) {
+            MenuItem nfc = menu.add(0, MENU_NFC_WRITE, 0, R.string.profile_write_nfc_tag)
+                .setIcon(R.drawable.ic_menu_nfc_writer_dark);
+            nfc.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM |
+                    MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+        }
+        MenuItem triggers = menu.add(0, MENU_TRIGGERS, 0, R.string.profile_triggers)
+                .setIcon(R.drawable.ic_location);
+        triggers.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM |
+                MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+        MenuItem delete = menu.add(0, MENU_DELETE, 1, R.string.profile_menu_delete)
+                .setIcon(R.drawable.ic_menu_trash_holo_dark);
+        delete.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM |
+                MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case MENU_DELETE:
+                deleteProfile();
+                return true;
+            case MENU_NFC_WRITE:
+                startNFCProfileWriter();
+                return true;
+            case MENU_TRIGGERS:
+                startTriggerFragment();
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mProfile = mProfileManager.getProfile(mProfile.getUuid());
+        fillList();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        // Save profile here
+        if (mProfile != null) {
+            mProfileManager.updateProfile(mProfile);
+        }
+    }
+
+    private void startNFCProfileWriter() {
+        SettingsActivity pa = (SettingsActivity) getActivity();
+        Intent i = new Intent(this.getActivity(), NFCProfileWriter.class);
+        i.putExtra(NFCProfileWriter.EXTRA_PROFILE_UUID, mProfile.getUuid().toString());
+        i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+        pa.startActivity(i);
+    }
+
+    private void startTriggerFragment() {
+        final SettingsActivity pa = (SettingsActivity) getActivity();
+        final Bundle args = new Bundle();
+        args.putParcelable("profile", mProfile);
+
+        pa.startPreferencePanel(TriggersFragment.class.getName(), args, 0, "", null, 0);
+    }
+
+    private void fillList() {
+        PreferenceScreen prefSet = getPreferenceScreen();
+
+        // Add the General section
+        PreferenceGroup generalPrefs = (PreferenceGroup) prefSet.findPreference("profile_general_section");
+        if (generalPrefs != null) {
+            generalPrefs.removeAll();
+
+            // Name preference
+            mNamePreference = new NamePreference(getActivity(), mProfile.getName());
+            mNamePreference.setOnPreferenceChangeListener(this);
+            generalPrefs.addPreference(mNamePreference);
+        }
+
+        // Populate system settings
+        PreferenceGroup systemPrefs = (PreferenceGroup) prefSet.findPreference("profile_system_settings");
+        if (systemPrefs != null) {
+            systemPrefs.removeAll();
+            // Ring mode preference
+            if (mRingMode == null) {
+                mRingMode = new RingModeItem();
+            }
+            RingModeSettings rms = mProfile.getRingMode();
+            if (rms == null) {
+                rms = new RingModeSettings();
+                mProfile.setRingMode(rms);
+            }
+            mRingMode.mSettings = rms;
+            ProfileRingModePreference rmp = new ProfileRingModePreference(getActivity());
+            rmp.setRingModeItem(mRingMode);
+            rmp.setTitle(R.string.ring_mode_title);
+            rmp.setPersistent(false);
+            rmp.setSummary(getActivity());
+            rmp.setOnPreferenceChangeListener(this);
+            mRingMode.mCheckbox = rmp;
+            systemPrefs.addPreference(rmp);
+
+            // Airplane mode preference
+            if (mAirplaneMode == null) {
+                mAirplaneMode = new AirplaneModeItem();
+            }
+            AirplaneModeSettings ams = mProfile.getAirplaneMode();
+            if (ams == null) {
+                ams = new AirplaneModeSettings();
+                mProfile.setAirplaneMode(ams);
+            }
+            mAirplaneMode.mSettings = ams;
+            ProfileAirplaneModePreference amp = new ProfileAirplaneModePreference(getActivity());
+            amp.setAirplaneModeItem(mAirplaneMode);
+            amp.setTitle(R.string.profile_airplanemode_title);
+            amp.setPersistent(false);
+            amp.setSummary(getActivity());
+            amp.setOnPreferenceChangeListener(this);
+            mAirplaneMode.mCheckbox = amp;
+            systemPrefs.addPreference(amp);
+
+            // Lockscreen mode preference
+            mScreenLockModePreference = new ListPreference(getActivity());
+            mScreenLockModePreference.setTitle(R.string.profile_lockmode_title);
+            mScreenLockModePreference.setEntries(R.array.profile_lockmode_entries);
+            mScreenLockModePreference.setEntryValues(R.array.profile_lockmode_values);
+            mScreenLockModePreference.setPersistent(false);
+            mScreenLockModePreference.setSummary(getResources().getStringArray(
+                    R.array.profile_lockmode_summaries)[mProfile.getScreenLockMode()]);
+            mScreenLockModePreference.setValue(String.valueOf(mProfile.getScreenLockMode()));
+            mScreenLockModePreference.setOnPreferenceChangeListener(this);
+
+            // DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
+            // if (dpm.requireSecureKeyguard()) {
+            //     mScreenLockModePreference.setEnabled(false);
+            //     mScreenLockModePreference.setSummary(R.string.unlock_set_unlock_disabled_summary);
+            // }
+
+            systemPrefs.addPreference(mScreenLockModePreference);
+        }
+
+        // Populate the audio streams list
+        final AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+        PreferenceGroup streamList = (PreferenceGroup) prefSet.findPreference("profile_volumeoverrides");
+        if (streamList != null) {
+            streamList.removeAll();
+            for (StreamItem stream : mStreams) {
+                StreamSettings settings = mProfile.getSettingsForStream(stream.mStreamId);
+                if (settings == null) {
+                    settings = new StreamSettings(stream.mStreamId);
+                    mProfile.setStreamSettings(settings);
+                }
+                stream.mSettings = settings;
+                StreamVolumePreference pref = new StreamVolumePreference(getActivity());
+                pref.setKey("stream_" + stream.mStreamId);
+                pref.setTitle(stream.mLabel);
+                pref.setSummary(getString(R.string.volume_override_summary) + " " + settings.getValue() 
+                        + "/" + am.getStreamMaxVolume(stream.mStreamId)); 
+                pref.setPersistent(false);
+                pref.setStreamItem(stream);
+                stream.mCheckbox = pref;
+                streamList.addPreference(pref);
+            }
+        }
+
+        // Populate Connections list
+        PreferenceGroup connectionList = (PreferenceGroup) prefSet.findPreference("profile_connectionoverrides");
+        if (connectionList != null) {
+            connectionList.removeAll();
+            for (ConnectionItem connection : mConnections) {
+                String[] connectionstrings = getResources().getStringArray(connection.mChoices);
+                ConnectionSettings settings = mProfile.getSettingsForConnection(connection.mConnectionId);
+                if (settings == null) {
+                    settings = new ConnectionSettings(connection.mConnectionId);
+                    mProfile.setConnectionSettings(settings);
+                }
+                connection.mSettings = settings;
+                ProfileConnectionPreference pref = new ProfileConnectionPreference(getActivity());
+                pref.setKey("connection_" + connection.mConnectionId);
+                pref.setTitle(connection.mLabel);
+                pref.setSummary(connectionstrings[settings.getValue()]);
+                pref.setPersistent(false);
+                pref.setConnectionItem(connection);
+                connection.mCheckbox = pref;
+                connectionList.addPreference(pref);
+            }
+        }
+
+        // Populate Application groups
+        PreferenceGroup groupList = (PreferenceGroup) prefSet.findPreference("profile_appgroups");
+        if (groupList != null) {
+            groupList.removeAll();
+            for (ProfileGroup profileGroup : mProfile.getProfileGroups()) {
+                PreferenceScreen pref = new PreferenceScreen(getActivity(), null);
+                UUID uuid = profileGroup.getUuid();
+                pref.setKey(uuid.toString());
+                pref.setTitle(mProfileManager.getNotificationGroup(uuid).getName());
+                //pref.setSummary(R.string.profile_summary);  // summary is repetitive, consider removing
+                pref.setPersistent(false);
+                pref.setSelectable(true);
+                groupList.addPreference(pref);
+            }
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        if (preference instanceof StreamVolumePreference) {
+            for (StreamItem stream : mStreams) {
+                if (preference == stream.mCheckbox) {
+                    stream.mSettings.setOverride((Boolean) newValue);
+                }
+            }
+        } else if (preference instanceof ProfileConnectionPreference) {
+            for (ConnectionItem connection : mConnections) {
+                if (preference == connection.mCheckbox) {
+                    connection.mSettings.setOverride((Boolean) newValue);
+                }
+            }
+        } else if (preference == mRingMode.mCheckbox) {
+            mRingMode.mSettings.setOverride((Boolean) newValue);
+        } else if (preference == mAirplaneMode.mCheckbox) {
+            mAirplaneMode.mSettings.setOverride((Boolean) newValue);
+        } else if (preference == mNamePreference) {
+            String name = mNamePreference.getName().toString();
+            if (!name.equals(mProfile.getName())) {
+                if (!mProfileManager.profileExists(name)) {
+                    mProfile.setName(name);
+                } else {
+                    mNamePreference.setName(mProfile.getName());
+                    Toast.makeText(getActivity(), R.string.duplicate_profile_name, Toast.LENGTH_LONG).show();
+                }
+            }
+        } else if (preference == mScreenLockModePreference) {
+            mProfile.setScreenLockMode(Integer.valueOf((String) newValue));
+            mScreenLockModePreference.setSummary(getResources().getStringArray(
+                    R.array.profile_lockmode_summaries)[mProfile.getScreenLockMode()]);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
+        Log.d(TAG, "onPreferenceTreeClick(): entered" + preferenceScreen.getKey() + preference.getKey());
+        if (preference instanceof PreferenceScreen) {
+            startProfileGroupActivity(preference.getKey(), preference.getTitle().toString());
+            return true;
+        }
+        return super.onPreferenceTreeClick(preferenceScreen, preference);
+    }
+
+    private void startProfileGroupActivity(String key, String title) {
+        Bundle args = new Bundle();
+        args.putString("ProfileGroup", key.toString());
+        args.putParcelable("Profile", mProfile);
+
+        String header = mProfile.getName().toString() + ": " + title.toString();
+        SettingsActivity pa = (SettingsActivity) getActivity();
+        pa.startPreferencePanel(ProfileGroupConfig.class.getName(), args,
+                0, header, this, PROFILE_GROUP_DETAILS);
+    }
+
+    
+    private void deleteProfile() {
+        if (mProfile.getUuid().equals(mProfileManager.getActiveProfile().getUuid())) {
+            Toast toast = Toast.makeText(getActivity(), getString(R.string.profile_cannot_delete),
+                    Toast.LENGTH_SHORT);
+            toast.show();
+        } else {
+            AlertDialog.Builder alert = new AlertDialog.Builder(getActivity());
+            alert.setTitle(R.string.profile_menu_delete);
+            alert.setIconAttribute(android.R.attr.alertDialogIcon);
+            alert.setMessage(R.string.profile_delete_confirm);
+            alert.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
+                public void onClick(DialogInterface dialog, int id) {
+                    doDelete();
+                }
+            });
+            alert.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+                public void onClick(DialogInterface dialog, int id) {
+                    dialog.cancel();
+                }
+            });
+            alert.create().show();
+        }
+    }
+
+    private void doDelete() {
+        mProfileManager.removeProfile(mProfile);
+        mProfile = null;
+        finish();
+    }
+
+    static class StreamItem {
+        int mStreamId;
+        String mLabel;
+        StreamSettings mSettings;
+        StreamVolumePreference mCheckbox;
+
+        public StreamItem(int streamId, String label) {
+            mStreamId = streamId;
+            mLabel = label;
+        }
+    }
+
+    static class ConnectionItem {
+        int mConnectionId;
+        String mLabel;
+        ConnectionSettings mSettings;
+        ProfileConnectionPreference mCheckbox;
+        int mChoices;
+
+        public ConnectionItem(int connectionId, String label) {
+            mConnectionId = connectionId;
+            mChoices = R.array.profile_connection_entries;
+            mLabel = label;
+        }
+
+        public ConnectionItem(int connectionId, String label, int choices) {
+            mConnectionId = connectionId;
+            mLabel = label;
+            mChoices = choices;
+        }
+    }
+
+    static class RingModeItem {
+        RingModeSettings mSettings;
+        ProfileRingModePreference mCheckbox;
+
+        public RingModeItem() {
+            // nothing to do
+        }
+    }
+
+    static class AirplaneModeItem {
+        AirplaneModeSettings mSettings;
+        ProfileAirplaneModePreference mCheckbox;
+
+        public AirplaneModeItem() {
+            // nothing to do
+        }
+    }
+}
diff --git a/src/com/android/settings/profiles/ProfileConnectionPreference.java b/src/com/android/settings/profiles/ProfileConnectionPreference.java
new file mode 100644 (file)
index 0000000..a5fc986
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * 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 com.android.settings.profiles;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.preference.Preference;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.LinearLayout;
+
+import com.android.settings.R;
+
+public class ProfileConnectionPreference extends Preference implements
+        CompoundButton.OnCheckedChangeListener, View.OnClickListener {
+
+    private boolean mProtectFromCheckedChange = false;
+
+    private CheckBox mCheckBox;
+
+    final static String TAG = "ProfileConnectionPreference";
+
+    private ProfileConfig.ConnectionItem mConnectionItem;
+
+    final static int defaultChoice = -1;
+
+    private int currentChoice;
+
+    /**
+     * @param context
+     * @param attrs
+     * @param defStyle
+     */
+    public ProfileConnectionPreference(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        init();
+    }
+
+    /**
+     * @param context
+     * @param attrs
+     */
+    public ProfileConnectionPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    /**
+     * @param context
+     */
+    public ProfileConnectionPreference(Context context) {
+        super(context);
+        init();
+    }
+
+    @Override
+    public View getView(View convertView, ViewGroup parent) {
+        View view = super.getView(convertView, parent);
+
+        View widget = view.findViewById(R.id.profile_checkbox);
+        if ((widget != null) && widget instanceof CheckBox) {
+            mCheckBox = (CheckBox) widget;
+            mCheckBox.setOnCheckedChangeListener(this);
+
+            mProtectFromCheckedChange = true;
+            mCheckBox.setChecked(isChecked());
+            mProtectFromCheckedChange = false;
+        }
+
+        View textLayout = view.findViewById(R.id.text_layout);
+        if ((textLayout != null) && textLayout instanceof LinearLayout) {
+            textLayout.setOnClickListener(this);
+        }
+
+        return view;
+    }
+
+    private void init() {
+        setLayoutResource(R.layout.preference_streamvolume);
+    }
+
+    public boolean isChecked() {
+        return mConnectionItem != null && mConnectionItem.mSettings.isOverride();
+    }
+
+    public void setConnectionItem(ProfileConfig.ConnectionItem connectionItem) {
+        mConnectionItem = connectionItem;
+
+        if (mCheckBox != null) {
+            mCheckBox.setChecked(mConnectionItem.mSettings.isOverride());
+        }
+    }
+
+    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+        if (mProtectFromCheckedChange) {
+            return;
+        }
+
+        mConnectionItem.mSettings.setOverride(isChecked);
+
+        callChangeListener(isChecked);
+    }
+
+    protected Dialog createConnectionDialog() {
+        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+        final String[] ConnectionValues = getContext().getResources().getStringArray(R.array.profile_connection_values);
+        final String[] connectionNames = getContext().getResources().getStringArray(mConnectionItem.mChoices);
+
+        currentChoice = mConnectionItem.mSettings.getValue();
+
+        builder.setTitle(mConnectionItem.mLabel);
+        builder.setSingleChoiceItems(mConnectionItem.mChoices, currentChoice, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int item) {
+                currentChoice = item;
+            }
+        });
+
+        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int item) {
+                if (currentChoice != defaultChoice) {
+                    int value = Integer.parseInt(ConnectionValues[currentChoice]);
+                    mConnectionItem.mSettings.setValue(value);
+                    setSummary(connectionNames[value]);
+                }
+            }
+        });
+
+        builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int item) {
+           }
+        });
+        return builder.create();
+    }
+
+    public ProfileConfig.ConnectionItem getConnectionItem() {
+        return mConnectionItem;
+    }
+
+    @Override
+    public void onClick(android.view.View v) {
+        if ((v != null) && (R.id.text_layout == v.getId())) {
+            createConnectionDialog().show();
+        }
+    }
+}
diff --git a/src/com/android/settings/profiles/ProfileEnabler.java b/src/com/android/settings/profiles/ProfileEnabler.java
new file mode 100644 (file)
index 0000000..0540dbe
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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 com.android.settings.profiles;
+
+import android.app.ProfileManager;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.Settings;
+import android.widget.CompoundButton;
+import android.widget.Switch;
+
+public class ProfileEnabler implements CompoundButton.OnCheckedChangeListener  {
+    private final Context mContext;
+    private Switch mSwitch;
+    private boolean mStateMachineEvent;
+
+    public ProfileEnabler(Context context, Switch switch_) {
+        mContext = context;
+        mSwitch = switch_;
+    }
+
+    public void resume() {
+        mSwitch.setOnCheckedChangeListener(this);
+        setSwitchState();
+    }
+
+    public void pause() {
+        mSwitch.setOnCheckedChangeListener(null);
+    }
+
+    public void setSwitch(Switch switch_) {
+        if (mSwitch == switch_) return;
+        mSwitch.setOnCheckedChangeListener(null);
+        mSwitch = switch_;
+        mSwitch.setOnCheckedChangeListener(this);
+        setSwitchState();
+    }
+
+    private void setSwitchState() {
+        boolean enabled = Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.SYSTEM_PROFILES_ENABLED, 1) == 1;
+        mStateMachineEvent = true;
+        mSwitch.setChecked(enabled);
+        mStateMachineEvent = false;
+    }
+
+    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+        if (mStateMachineEvent) {
+            return;
+        }
+        // Handle a switch change
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SYSTEM_PROFILES_ENABLED, isChecked ? 1 : 0);
+
+        // Send a broadcast intent to the world
+        // TODO Enabling or disabling profiles should be at ProfileManager, not here
+        Intent intent=new Intent(ProfileManager.PROFILES_STATE_CHANGED_ACTION);
+        intent.putExtra(
+                ProfileManager.EXTRA_PROFILES_STATE,
+                isChecked ?
+                          ProfileManager.PROFILES_STATE_ENABLED :
+                          ProfileManager.PROFILES_STATE_DISABLED);
+        mContext.sendBroadcast(intent);
+
+    }
+
+}
diff --git a/src/com/android/settings/profiles/ProfileGroupConfig.java b/src/com/android/settings/profiles/ProfileGroupConfig.java
new file mode 100644 (file)
index 0000000..f1d4e7b
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * 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 com.android.settings.profiles;
+
+import java.util.UUID;
+
+import android.app.Profile;
+import android.app.ProfileGroup;
+import android.app.ProfileGroup.Mode;
+import android.app.ProfileManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+
+public class ProfileGroupConfig extends SettingsPreferenceFragment implements
+    OnPreferenceChangeListener {
+
+    public static final String PROFILE_SERVICE = "profile";
+
+    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) getSystemService(PROFILE_SERVICE);
+            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/com/android/settings/profiles/ProfileRingModePreference.java b/src/com/android/settings/profiles/ProfileRingModePreference.java
new file mode 100644 (file)
index 0000000..d471998
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * 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 com.android.settings.profiles;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.preference.Preference;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.LinearLayout;
+
+import com.android.settings.R;
+
+public class ProfileRingModePreference extends Preference implements
+        CompoundButton.OnCheckedChangeListener, View.OnClickListener {
+
+    private boolean mProtectFromCheckedChange = false;
+
+    private CheckBox mCheckBox;
+
+    final static String TAG = "ProfileRingModePreference";
+
+    private ProfileConfig.RingModeItem mRingModeItem;
+
+    final static int defaultChoice = -1;
+
+    private int currentChoice;
+
+    /**
+     * @param context
+     * @param attrs
+     * @param defStyle
+     */
+    public ProfileRingModePreference(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        init();
+    }
+
+    /**
+     * @param context
+     * @param attrs
+     */
+    public ProfileRingModePreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    /**
+     * @param context
+     */
+    public ProfileRingModePreference(Context context) {
+        super(context);
+        init();
+    }
+
+    @Override
+    public View getView(View convertView, ViewGroup parent) {
+        View view = super.getView(convertView, parent);
+
+        View widget = view.findViewById(R.id.profile_checkbox);
+        if ((widget != null) && widget instanceof CheckBox) {
+            mCheckBox = (CheckBox) widget;
+            mCheckBox.setOnCheckedChangeListener(this);
+
+            mProtectFromCheckedChange = true;
+            mCheckBox.setChecked(isChecked());
+            mProtectFromCheckedChange = false;
+        }
+
+        View textLayout = view.findViewById(R.id.text_layout);
+        if ((textLayout != null) && textLayout instanceof LinearLayout) {
+            textLayout.setOnClickListener(this);
+        }
+
+        return view;
+    }
+
+    private void init() {
+        setLayoutResource(R.layout.preference_streamvolume);
+    }
+
+    public boolean isChecked() {
+        return mRingModeItem != null && mRingModeItem.mSettings.isOverride();
+    }
+
+    public void setRingModeItem(ProfileConfig.RingModeItem ringModeItem) {
+        mRingModeItem = ringModeItem;
+
+        if (mCheckBox != null) {
+            mCheckBox.setChecked(mRingModeItem.mSettings.isOverride());
+        }
+    }
+
+    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+        if (mProtectFromCheckedChange) {
+            return;
+        }
+
+        mRingModeItem.mSettings.setOverride(isChecked);
+
+        callChangeListener(isChecked);
+    }
+
+    protected Dialog createRingModeDialog() {
+        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+        final String[] ringModeValues = getContext().getResources().getStringArray(R.array.ring_mode_values);
+        String currentValue = mRingModeItem.mSettings.getValue();
+        if (currentValue != null) {
+            for (int i = 0; i < ringModeValues.length; i++) {
+                if (currentValue.equals(ringModeValues[i])) {
+                    currentChoice = i;
+                    break;
+                }
+            }
+        }
+
+        builder.setTitle(R.string.ring_mode_title);
+        builder.setSingleChoiceItems(R.array.ring_mode_entries, currentChoice, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int item) {
+                currentChoice = item;
+            }
+        });
+
+        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int item) {
+                if (currentChoice != defaultChoice) {
+                    String value = ringModeValues[currentChoice];
+                    mRingModeItem.mSettings.setValue(value);
+                    setSummary(getContext().getResources().getStringArray(R.array.ring_mode_entries)[currentChoice]);
+                }
+            }
+        });
+
+        builder.setNegativeButton(android.R.string.cancel, null);
+        return builder.create();
+    }
+
+    public ProfileConfig.RingModeItem getRingModeItem() {
+        return mRingModeItem;
+    }
+
+    @Override
+    public void onClick(android.view.View v) {
+        if ((v != null) && (R.id.text_layout == v.getId())) {
+            createRingModeDialog().show();
+        }
+    }
+
+    public void setSummary(Context context) {
+        String[] entries = context.getResources().getStringArray(R.array.ring_mode_entries);
+        String[] values = context.getResources().getStringArray(R.array.ring_mode_values);
+        int l = entries.length;
+        String value = mRingModeItem.mSettings.getValue();
+        for (int i = 0; i < l; i++) {
+            if (value.equals(values[i])) {
+                setSummary(entries[i]);
+                break;
+            }
+        }
+    }
+}
diff --git a/src/com/android/settings/profiles/ProfileRingtonePreference.java b/src/com/android/settings/profiles/ProfileRingtonePreference.java
new file mode 100644 (file)
index 0000000..91ccbe6
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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 com.android.settings.profiles;
+
+import android.content.Context;
+import android.content.Intent;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.preference.RingtonePreference;
+import android.util.AttributeSet;
+
+public class ProfileRingtonePreference extends RingtonePreference {
+    private static final String TAG = "ProfileRingtonePreference";
+
+    public ProfileRingtonePreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected 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/com/android/settings/profiles/ProfilesList.java b/src/com/android/settings/profiles/ProfilesList.java
new file mode 100644 (file)
index 0000000..13fa16b
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * 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 com.android.settings.profiles;
+
+import java.util.UUID;
+
+import android.app.Profile;
+import android.app.ProfileManager;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.Utils;
+
+public class ProfilesList extends SettingsPreferenceFragment implements
+        Preference.OnPreferenceChangeListener {
+    static final String TAG = "ProfilesSettings";
+    public static final String PROFILE_SERVICE = "profile";
+
+    private ProfileManager mProfileManager;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        addPreferencesFromResource(R.xml.profiles_settings);
+        mProfileManager = (ProfileManager) getActivity().getSystemService(PROFILE_SERVICE);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        refreshList();
+
+        // On tablet devices remove the padding
+        if (Utils.isTablet(getActivity())) {
+            getListView().setPadding(0, 0, 0, 0);
+        }
+    }
+
+    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("Profile", profile);
+
+            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/com/android/settings/profiles/ProfilesPreference.java b/src/com/android/settings/profiles/ProfilesPreference.java
new file mode 100644 (file)
index 0000000..91f57ad
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * 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 com.android.settings.profiles;
+
+import android.content.ActivityNotFoundException;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.PreferenceActivity;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settings.SettingsPreferenceFragment;
+
+public class ProfilesPreference extends CheckBoxPreference {
+    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;
+
+    private final OnClickListener mPrefOnclickListener = new OnClickListener() {
+        @Override
+        public void onClick(View arg0) {
+            if (!isEnabled() || isChecked()) {
+                return;
+            }
+            setChecked(true);
+            callChangeListener(getKey());
+        }
+    };
+
+    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
+    protected void onBindView(View view) {
+        super.onBindView(view);
+        mProfilesPref = view.findViewById(R.id.profiles_pref);
+        mProfilesPref.setOnClickListener(mPrefOnclickListener);
+        mProfilesSettingsButton = (ImageView)view.findViewById(R.id.profiles_settings);
+        mTitleText = (TextView)view.findViewById(android.R.id.title);
+        mSummaryText = (TextView)view.findViewById(android.R.id.summary);
+
+        if (mSettingsBundle != null) {
+            mProfilesSettingsButton.setOnClickListener(
+                    new OnClickListener() {
+                        @Override
+                        public void onClick(View arg0) {
+                            try {
+                                startProfileConfigActivity();
+                            } catch (ActivityNotFoundException e) {
+                                // If the settings activity does not exist, we can just
+                                // do nothing...
+                            }
+                        }
+                    });
+        }
+        if (mSettingsBundle == null) {
+            mProfilesSettingsButton.setVisibility(View.GONE);
+        } else {
+            updatePreferenceViews();
+        }
+    }
+
+    @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 ? mPrefOnclickListener : null);
+            if (!enabled) {
+                mProfilesPref.setBackgroundColor(0);
+            }
+        }
+    }
+
+    // utility method used to start sub activity
+    private void startProfileConfigActivity() {
+        SettingsActivity pa = (SettingsActivity) mFragment.getActivity();
+        pa.startPreferencePanel(ProfileConfig.class.getName(), mSettingsBundle,
+                R.string.profile_profile_manage, null, mFragment, PROFILE_DETAILS);
+    }
+
+    @Override
+    public void setChecked(boolean checked) {
+        super.setChecked(checked);
+    }
+}
diff --git a/src/com/android/settings/profiles/ProfilesSettings.java b/src/com/android/settings/profiles/ProfilesSettings.java
new file mode 100644 (file)
index 0000000..2576858
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * 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 com.android.settings.profiles;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.NotificationGroup;
+import android.app.Profile;
+import android.app.ProfileManager;
+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.preference.PreferenceActivity;
+import android.provider.Settings;
+import android.support.v4.view.PagerTabStrip;
+import android.support.v4.view.ViewPager;
+import android.support.v13.app.FragmentStatePagerAdapter;
+import android.view.Gravity;
+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.CompoundButton;
+import android.widget.EditText;
+import android.widget.Switch;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.Utils;
+
+import java.util.HashMap;
+
+public class ProfilesSettings extends SettingsPreferenceFragment {
+
+    private static final String TAG = "ProfilesSettings";
+    private static final String PROFILE_SERVICE = "profile";
+
+    private static final int MENU_RESET = Menu.FIRST;
+    private static final int MENU_ADD = Menu.FIRST + 1;
+
+    private final IntentFilter mFilter;
+    private final BroadcastReceiver mReceiver;
+
+    private ProfileManager mProfileManager;
+    private ProfileEnabler mProfileEnabler;
+
+    private Switch mActionBarSwitch;
+
+    private ViewPager mViewPager;
+    private TextView mEmptyText;
+    private ProfilesPagerAdapter mAdapter;
+    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();
+                }
+            }
+        };
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        mContainer = container;
+
+        View view = inflater.inflate(R.layout.profile_tabs, container, false);
+        mViewPager = (ViewPager) view.findViewById(R.id.pager);
+        mEmptyText = (TextView) view.findViewById(R.id.empty);
+
+        mAdapter = new ProfilesPagerAdapter(getFragmentManager());
+        mViewPager.setAdapter(mAdapter);
+
+        PagerTabStrip tabs = (PagerTabStrip) view.findViewById(R.id.tabs);
+        tabs.setTabIndicatorColorResource(android.R.color.holo_blue_light);
+
+        mProfileManager = (ProfileManager) getActivity().getSystemService(PROFILE_SERVICE);
+
+        setHasOptionsMenu(true);
+
+        return view;
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        // We don't call super.onActivityCreated() here, since it assumes we already set up
+        // Preference (probably in onCreate()), while ProfilesSettings exceptionally set it up in
+        // this method.
+        // On/off switch
+        Activity activity = getActivity();
+        //Switch
+        mActionBarSwitch = new Switch(activity);
+
+        if (activity instanceof PreferenceActivity) {
+            PreferenceActivity preferenceActivity = (PreferenceActivity) activity;
+            if (preferenceActivity.onIsHidingHeaders() || !preferenceActivity.onIsMultiPane()) {
+                final int padding = activity.getResources().getDimensionPixelSize(
+                        R.dimen.action_bar_switch_padding);
+                mActionBarSwitch.setPaddingRelative(0, 0, padding, 0);
+                activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
+                        ActionBar.DISPLAY_SHOW_CUSTOM);
+                activity.getActionBar().setCustomView(mActionBarSwitch, new ActionBar.LayoutParams(
+                        ActionBar.LayoutParams.WRAP_CONTENT,
+                        ActionBar.LayoutParams.WRAP_CONTENT,
+                        Gravity.CENTER_VERTICAL | Gravity.END));
+            }
+        }
+
+        mProfileEnabler = new ProfileEnabler(activity, mActionBarSwitch);
+
+        // After confirming PreferenceScreen is available, we call super.
+        super.onActivityCreated(savedInstanceState);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (mProfileEnabler != null) {
+            mProfileEnabler.resume();
+        }
+        getActivity().registerReceiver(mReceiver, mFilter);
+
+        // check if we are enabled
+        updateProfilesEnabledState();
+
+        // If running on a phone, remove padding around tabs
+        if (!Utils.isTablet(getActivity())) {
+            mContainer.setPadding(0, 0, 0, 0);
+        }
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        if (mProfileEnabler != null) {
+            mProfileEnabler.pause();
+        }
+        getActivity().unregisterReceiver(mReceiver);
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        menu.add(0, MENU_RESET, 0, R.string.profile_reset_title)
+                .setIcon(R.drawable.ic_settings_backup) // use the backup icon
+                .setAlphabeticShortcut('r')
+                .setEnabled(mEnabled)
+                .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM |
+                MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+
+        menu.add(0, MENU_ADD, 0, R.string.profiles_add)
+                .setIcon(R.drawable.ic_menu_add)
+                .setAlphabeticShortcut('a')
+                .setEnabled(mEnabled)
+                .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM |
+                MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case MENU_RESET:
+                resetAll();
+                return true;
+
+            case MENU_ADD:
+                // determine dialog to launch
+                if (mViewPager.getCurrentItem() == 0) {
+                    addProfile();
+                } else {
+                    addAppGroup();
+                }
+                return true;
+
+            default:
+                return false;
+        }
+    }
+
+    private void addProfile() {
+        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_profile_name_prompt);
+
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        builder.setTitle(R.string.menu_new_profile);
+        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.profileExists(name)) {
+                    Profile profile = new Profile(name);
+                    mProfileManager.addProfile(profile);
+                    mAdapter.refreshProfiles();
+                } else {
+                    Toast.makeText(getActivity(),
+                            R.string.duplicate_profile_name, Toast.LENGTH_LONG).show();
+                }
+            }
+        });
+        builder.setNegativeButton(android.R.string.cancel, null);
+
+        AlertDialog dialog = builder.create();
+        dialog.show();
+    }
+
+    private void resetAll() {
+        AlertDialog.Builder alert = new AlertDialog.Builder(getActivity());
+        alert.setTitle(R.string.profile_reset_title);
+        alert.setIconAttribute(android.R.attr.alertDialogIcon);
+        alert.setMessage(R.string.profile_reset_message);
+        alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+            public void onClick(DialogInterface dialog, int id) {
+                mProfileManager.resetAll();
+                mAdapter.refreshProfiles();
+                mAdapter.refreshAppGroups();
+            }
+        });
+        alert.setNegativeButton(R.string.cancel, null);
+        alert.show();
+    }
+
+    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);
+                    mAdapter.refreshAppGroups();
+                } 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 updateProfilesEnabledState() {
+        Activity activity = getActivity();
+
+        mEnabled = Settings.System.getInt(activity.getContentResolver(),
+                Settings.System.SYSTEM_PROFILES_ENABLED, 1) == 1;
+        activity.invalidateOptionsMenu();
+
+        mViewPager.setVisibility(mEnabled ? View.VISIBLE : View.GONE);
+        mEmptyText.setVisibility(mEnabled ? View.GONE : View.VISIBLE);
+    }
+
+    class ProfilesPagerAdapter extends FragmentStatePagerAdapter {
+        Fragment[] frags = { new ProfilesList(), new AppGroupList() };
+        String[] titles = { getString(R.string.profile_profiles_manage),
+                            getString(R.string.profile_appgroups_manage) };
+
+        ProfilesPagerAdapter(FragmentManager fm) {
+            super(fm);
+        }
+
+        @Override
+        public Fragment getItem(int position) {
+            return frags[position];
+        }
+
+        @Override
+        public int getCount() {
+            return frags.length;
+        }
+
+        @Override
+        public CharSequence getPageTitle(int position) {
+            return titles[position];
+        }
+
+        public void refreshProfiles() {
+            ((ProfilesList) frags[0]).refreshList();
+        }
+
+        public void refreshAppGroups() {
+            ((AppGroupList) frags[1]).refreshList();
+        }
+    }
+}
diff --git a/src/com/android/settings/profiles/ProfilesUtils.java b/src/com/android/settings/profiles/ProfilesUtils.java
new file mode 100644 (file)
index 0000000..4f59dd9
--- /dev/null
@@ -0,0 +1,68 @@
+package com.android.settings.profiles;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.hardware.Camera;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.WifiDisplayStatus;
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.net.ConnectivityManager;
+import android.nfc.NfcAdapter;
+import android.os.BatteryManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+
+import com.android.internal.telephony.PhoneConstants;
+
+public class ProfilesUtils {
+        public static boolean deviceSupportsUsbTether(Context ctx) {
+            ConnectivityManager cm = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
+            return (cm.getTetherableUsbRegexs().length != 0);
+        }
+
+        public static boolean deviceSupportsWifiDisplay(Context ctx) {
+            DisplayManager dm = (DisplayManager) ctx.getSystemService(Context.DISPLAY_SERVICE);
+            return (dm.getWifiDisplayStatus().getFeatureState() != WifiDisplayStatus.FEATURE_STATE_UNAVAILABLE);
+        }
+
+        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 systemProfilesEnabled(ContentResolver resolver) {
+            return (Settings.System.getInt(resolver, Settings.System.SYSTEM_PROFILES_ENABLED, 1) == 1);
+        }
+
+        public static boolean deviceSupportsNfc(Context ctx) {
+            return NfcAdapter.getDefaultAdapter(ctx) != null;
+        }
+
+        public static boolean deviceSupportsCamera() {
+            return Camera.getNumberOfCameras() > 0;
+        }
+
+        public static boolean deviceSupportsGps(Context context) {
+            return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS);
+        }
+
+        public static boolean adbEnabled(ContentResolver resolver) {
+            return (Settings.Global.getInt(resolver, Settings.Global.ADB_ENABLED, 0)) == 1;
+        }
+
+        public static boolean deviceSupportsCompass(Context context) {
+            SensorManager sm = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+            return (sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null
+                    && sm.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null);
+        }
+}
diff --git a/src/com/android/settings/profiles/StreamVolumePreference.java b/src/com/android/settings/profiles/StreamVolumePreference.java
new file mode 100644 (file)
index 0000000..6788aca
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * 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 com.android.settings.profiles;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.media.AudioManager;
+import android.preference.Preference;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+
+import com.android.settings.R;
+
+public class StreamVolumePreference extends Preference implements
+        CompoundButton.OnCheckedChangeListener, View.OnClickListener {
+
+    private boolean mProtectFromCheckedChange = false;
+
+    private CheckBox mCheckBox;
+
+    final static String TAG = "StreamVolumePreference";
+
+    private ProfileConfig.StreamItem mStreamItem;
+
+    private SeekBar mBar;
+
+    /**
+     * @param context
+     * @param attrs
+     * @param defStyle
+     */
+    public StreamVolumePreference(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        init();
+    }
+
+    /**
+     * @param context
+     * @param attrs
+     */
+    public StreamVolumePreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    /**
+     * @param context
+     */
+    public StreamVolumePreference(Context context) {
+        super(context);
+        init();
+    }
+
+    @Override
+    public View getView(View convertView, ViewGroup parent) {
+        View view = super.getView(convertView, parent);
+
+        View widget = view.findViewById(R.id.profile_checkbox);
+        if ((widget != null) && widget instanceof CheckBox) {
+            mCheckBox = (CheckBox) widget;
+            mCheckBox.setOnCheckedChangeListener(this);
+
+            mProtectFromCheckedChange = true;
+            mCheckBox.setChecked(isChecked());
+            mProtectFromCheckedChange = false;
+        }
+
+        View textLayout = view.findViewById(R.id.text_layout);
+        if ((textLayout != null) && textLayout instanceof LinearLayout) {
+            textLayout.setOnClickListener(this);
+        }
+
+        return view;
+    }
+
+    private void init() {
+        setLayoutResource(R.layout.preference_streamvolume);
+    }
+
+    public boolean isChecked() {
+        return mStreamItem != null && mStreamItem.mSettings.isOverride();
+    }
+
+    public void setStreamItem(ProfileConfig.StreamItem streamItem) {
+        mStreamItem = streamItem;
+        
+        if (mCheckBox != null) {
+            mCheckBox.setChecked(mStreamItem.mSettings.isOverride());
+        }
+    }
+
+    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+        if (mProtectFromCheckedChange) {
+            return;
+        }
+
+        mStreamItem.mSettings.setOverride(isChecked);
+
+        callChangeListener(isChecked);
+    }
+
+    protected Dialog createVolumeDialog() {
+        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+        final AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+        builder.setTitle(mStreamItem.mLabel);
+        mBar = new SeekBar(getContext());
+        mBar.setPaddingRelative(32, 16, 32, 16); // TODO: confirm appropriate padding
+        mBar.setMax(am.getStreamMaxVolume(mStreamItem.mStreamId));
+        mBar.setProgress(mStreamItem.mSettings.getValue());
+        builder.setView(mBar);
+        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                int value = mBar.getProgress();
+                mStreamItem.mSettings.setValue(value);
+                setSummary(getContext().getString(R.string.volume_override_summary) + " " + value + "/" + mBar.getMax());
+            }
+        });
+        builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+            }
+        });
+        return builder.create();
+    }
+
+    public ProfileConfig.StreamItem getStreamItem() {
+        return mStreamItem;
+    }
+
+    @Override
+    public void onClick(android.view.View v) {
+        if ((v != null) && (R.id.text_layout == v.getId())) {
+            createVolumeDialog().show();
+        }
+    }
+}
diff --git a/src/com/android/settings/profiles/TriggersFragment.java b/src/com/android/settings/profiles/TriggersFragment.java
new file mode 100644 (file)
index 0000000..5da8186
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.profiles;
+
+import android.app.ActionBar;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.Profile;
+import android.app.Profile.ProfileTrigger;
+import android.app.Profile.TriggerType;
+import android.app.ProfileManager;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.util.Log;
+import android.widget.ArrayAdapter;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Settings Preference to configure triggers to switch profiles base on Wi-Fi events
+ */
+public class TriggersFragment extends SettingsPreferenceFragment implements ActionBar.OnNavigationListener {
+    private Profile mProfile;
+    private Preference mSelectedTrigger;
+    private ProfileManager mProfileManager;
+    private WifiManager mWifiManager;
+    private BluetoothAdapter mBluetoothAdapter;
+
+    private int mTriggerFilter = 0;
+    private static final int WIFI_TRIGGER = 1;
+    private static final int BT_TRIGGER = 2;
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        mProfileManager = (ProfileManager) getSystemService(Context.PROFILE_SERVICE);
+        mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();;
+        addPreferencesFromResource(R.xml.wifi_settings);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        loadPreferences();
+        loadActionBar();
+    }
+
+    private void loadActionBar() {
+        final ActionBar actionBar = getActivity().getActionBar();
+        actionBar.setDisplayShowTitleEnabled(true);
+        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
+        Resources res = getResources();
+
+        String[] navItems = res.getStringArray(R.array.profile_trigger_filters);
+        ArrayAdapter<String> navAdapter = new ArrayAdapter<String>(
+                actionBar.getThemedContext(), android.R.layout.simple_list_item_1, navItems);
+
+        // Set up the dropdown list navigation in the action bar.
+        actionBar.setListNavigationCallbacks(navAdapter, this);
+        actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP
+                | ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE);
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Bundle args = getArguments();
+        if (args != null) {
+            mProfile = args.getParcelable("profile");
+        }
+    }
+
+    private void initPreference(AbstractTriggerPreference pref, int state, Resources res, int icon) {
+        String summary = res.getStringArray(R.array.profile_trigger_wifi_options)[state];
+        pref.setSummary(summary);
+        pref.setTriggerState(state);
+        pref.setIcon(icon);
+    }
+
+    private void loadPreferences() {
+        final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+        final Resources res = getResources();
+        final List<AbstractTriggerPreference> prefs = new ArrayList<AbstractTriggerPreference>();
+
+        getPreferenceScreen().removeAll();
+
+        if (mTriggerFilter == WIFI_TRIGGER || mTriggerFilter == 0) {
+            if (configs != null ) {
+                for (WifiConfiguration config : configs) {
+                    WifiTriggerAPPreference accessPoint =
+                            new WifiTriggerAPPreference(getActivity(), config);
+                    int state = mProfile.getTrigger(Profile.TriggerType.WIFI, accessPoint.getSSID());
+                    initPreference(accessPoint, state, res, R.drawable.ic_wifi_signal_4_dark);
+                    prefs.add(accessPoint);
+                }
+            } else {
+                final List<ProfileTrigger> triggers = mProfile.getTriggersFromType(TriggerType.WIFI);
+                for (ProfileTrigger trigger : triggers) {
+                    WifiTriggerAPPreference accessPoint =
+                            new WifiTriggerAPPreference(getActivity(), trigger.getName());
+                    initPreference(accessPoint, trigger.getState(), res, R.drawable.ic_wifi_signal_4_dark);
+                    prefs.add(accessPoint);
+                }
+            }
+        }
+
+        Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
+        if (mTriggerFilter == BT_TRIGGER || mTriggerFilter == 0) {
+            if (!pairedDevices.isEmpty()) {
+                for (BluetoothDevice device : pairedDevices) {
+                    BluetoothTriggerPreference bt =
+                            new BluetoothTriggerPreference(getActivity(), device);
+                    int state = mProfile.getTrigger(Profile.TriggerType.BLUETOOTH, bt.getAddress());
+                    initPreference(bt, state, res, R.drawable.ic_settings_bluetooth2);
+                    prefs.add(bt);
+                }
+            } else {
+                final List<ProfileTrigger> triggers = mProfile.getTriggersFromType(TriggerType.BLUETOOTH);
+                for (ProfileTrigger trigger : triggers) {
+                    BluetoothTriggerPreference bt = new BluetoothTriggerPreference(getActivity(),
+                            trigger.getName(), trigger.getId());
+                    initPreference(bt, trigger.getState(), res, R.drawable.ic_settings_bluetooth2);
+                    prefs.add(bt);
+                }
+            }
+        }
+
+        Collections.sort(prefs, new Comparator<AbstractTriggerPreference>() {
+            @Override
+            public int compare(AbstractTriggerPreference o1, AbstractTriggerPreference o2) {
+                if (o1.getTriggerState() == o2.getTriggerState()) {
+                    return o1.compareTo(o2);
+                }
+                return o1.getTriggerState() < o2.getTriggerState() ? -1 : 1;
+            }
+        });
+        for (Preference pref: prefs) {
+            getPreferenceScreen().addPreference(pref);
+        }
+
+    }
+
+    @Override
+    public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
+        mSelectedTrigger = preference;
+        if (preference instanceof WifiTriggerAPPreference) {
+            showDialog(WIFI_TRIGGER);
+            return true;
+        } else if (preference instanceof BluetoothTriggerPreference) {
+            showDialog(BT_TRIGGER);
+            return true;
+        }
+        return super.onPreferenceTreeClick(screen, preference);
+    }
+
+    @Override
+    public Dialog onCreateDialog(final int dialogId) {
+        final String id;
+        final String triggerName = mSelectedTrigger.getTitle().toString();
+        final int triggerType;
+
+        switch (dialogId) {
+            case WIFI_TRIGGER:
+                WifiTriggerAPPreference pref = (WifiTriggerAPPreference) mSelectedTrigger;
+                id = pref.getSSID();
+                triggerType = Profile.TriggerType.WIFI;
+                break;
+            case BT_TRIGGER:
+                BluetoothTriggerPreference btpref = (BluetoothTriggerPreference) mSelectedTrigger;
+                id = btpref.getAddress();
+                triggerType = Profile.TriggerType.BLUETOOTH;
+                break;
+            default:
+                return super.onCreateDialog(dialogId);
+        }
+
+        return new AlertDialog.Builder(getActivity())
+                .setTitle(R.string.profile_trigger_configure)
+                .setSingleChoiceItems(R.array.profile_trigger_wifi_options,
+                        mProfile.getTrigger(triggerType, id),
+                        new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        mProfile.setTrigger(triggerType, id, which, triggerName);
+                        mProfileManager.updateProfile(mProfile);
+                        loadPreferences();
+                        dialog.dismiss();
+                    }
+                })
+                .create();
+    }
+
+    @Override
+    public boolean onNavigationItemSelected(int itemPosition, long itemId) {
+        mTriggerFilter = itemPosition;
+        loadPreferences();
+        return true;
+    }
+}
diff --git a/src/com/android/settings/profiles/WifiTriggerAPPreference.java b/src/com/android/settings/profiles/WifiTriggerAPPreference.java
new file mode 100644 (file)
index 0000000..239509a
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.profiles;
+
+import android.content.Context;
+import android.net.wifi.WifiConfiguration;
+
+public class WifiTriggerAPPreference extends AbstractTriggerPreference {
+
+    private String mSSID;
+    private WifiConfiguration mConfig;
+
+    WifiTriggerAPPreference(Context context, WifiConfiguration config) {
+        super(context);
+        loadConfig(config);
+        setTitle(mSSID);
+    }
+
+    WifiTriggerAPPreference(Context context, String ssid) {
+        super(context);
+        mSSID = ssid;
+        setTitle(mSSID);
+    }
+
+     private void loadConfig(WifiConfiguration config) {
+        mSSID = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID));
+        mConfig = config;
+    }
+
+    public String getSSID() {
+        return mSSID;
+    }
+
+    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;
+    }
+}