Merge "Fix bug #14228591 NPE in settings app when it is launched by UiAutomator"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9a85dc0..4ee0b21 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -564,7 +564,7 @@
<category android:name="com.android.settings.SHORTCUT" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
- android:value="com.android.settings.ZenModeSettings" />
+ android:value="com.android.settings.notification.ZenModeSettings" />
<meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
android:resource="@id/notification_settings" />
</activity>
@@ -772,7 +772,7 @@
<category android:name="com.android.settings.SHORTCUT" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
- android:value="com.android.settings.NotificationStation" />
+ android:value="com.android.settings.notification.NotificationStation" />
</activity>
<!--
@@ -1690,7 +1690,7 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
- android:value="com.android.settings.NotificationAccessSettings" />
+ android:value="com.android.settings.notification.NotificationAccessSettings" />
<meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
android:resource="@id/security_settings" />
</activity>
@@ -1707,7 +1707,7 @@
<category android:name="com.android.settings.SHORTCUT" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
- android:value="com.android.settings.NotificationSettings" />
+ android:value="com.android.settings.notification.NotificationSettings" />
<meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
android:resource="@id/notification_settings" />
</activity>
diff --git a/res/drawable-hdpi/ic_settings_generic.png b/res/drawable-hdpi/ic_settings_generic.png
new file mode 100644
index 0000000..0e577bf
--- /dev/null
+++ b/res/drawable-hdpi/ic_settings_generic.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_settings_generic.png b/res/drawable-mdpi/ic_settings_generic.png
new file mode 100644
index 0000000..a7ede7e
--- /dev/null
+++ b/res/drawable-mdpi/ic_settings_generic.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_settings_generic.png b/res/drawable-xhdpi/ic_settings_generic.png
new file mode 100644
index 0000000..6c907f4
--- /dev/null
+++ b/res/drawable-xhdpi/ic_settings_generic.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_settings_generic.png b/res/drawable-xxhdpi/ic_settings_generic.png
new file mode 100644
index 0000000..9ce3b08
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_settings_generic.png
Binary files differ
diff --git a/res/layout/notification_app.xml b/res/layout/notification_app.xml
index 4f61c13..414165a 100644
--- a/res/layout/notification_app.xml
+++ b/res/layout/notification_app.xml
@@ -18,67 +18,76 @@
android:layout_height="wrap_content" >
<RelativeLayout
- android:layout_width="match_parent"
+ android:id="@android:id/button1"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_toStartOf="@+id/divider"
- >
+ android:layout_alignWithParentIfMissing="true"
+ android:layout_toStartOf="@+id/settings_divider"
+ android:background="?android:attr/listChoiceBackgroundIndicator" >
<ImageView
android:id="@android:id/icon"
- android:layout_width="@android:dimen/app_icon_size"
- android:layout_height="@android:dimen/app_icon_size"
+ android:layout_width="@dimen/notification_app_icon_size"
+ android:layout_height="@dimen/notification_app_icon_size"
android:layout_centerVertical="true"
- android:layout_marginBottom="10dp"
- android:layout_marginStart="0dp"
- android:layout_marginEnd="6dp"
- android:layout_marginTop="10dp"
android:contentDescription="@null"
- android:maxHeight="@android:dimen/app_icon_size"
- android:maxWidth="@android:dimen/app_icon_size"
- android:scaleType="fitCenter" />
+ android:scaleType="center" />
+
+ <ImageView
+ android:id="@android:id/icon1"
+ android:layout_width="@dimen/notification_app_icon_badge_size"
+ android:layout_height="@dimen/notification_app_icon_badge_size"
+ android:layout_marginLeft="@dimen/notification_app_icon_badge_margin"
+ android:layout_marginTop="@dimen/notification_app_icon_badge_margin"
+ android:background="#bb8b0000"
+ android:contentDescription="@null" />
+
+ <ImageView
+ android:id="@android:id/icon2"
+ android:layout_width="@dimen/notification_app_icon_badge_size"
+ android:layout_height="@dimen/notification_app_icon_badge_size"
+ android:layout_alignBottom="@android:id/icon"
+ android:layout_alignEnd="@android:id/icon"
+ android:layout_marginBottom="@dimen/notification_app_icon_badge_margin"
+ android:layout_marginEnd="@dimen/notification_app_icon_badge_margin"
+ android:background="#bb006400"
+ android:contentDescription="@null" />
<TextView
android:id="@android:id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
- android:layout_toStartOf="@android:id/button1"
android:layout_toEndOf="@android:id/icon"
android:ellipsize="end"
android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceMedium"
android:textAlignment="viewStart"
- android:labelFor="@android:id/button2" />
-
+ android:textAppearance="?android:attr/textAppearanceMedium" />
</RelativeLayout>
- <!-- Divider -->
-
- <ImageView
- android:id="@id/divider"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
+ <View
+ android:id="@+id/settings_divider"
+ android:layout_width="1dp"
+ android:layout_height="@dimen/notification_app_settings_divider_height"
android:layout_centerVertical="true"
android:layout_toStartOf="@android:id/button2"
- android:contentDescription="@null"
- android:src="@drawable/nav_divider" />
-
- <!-- Settings icon -->
+ android:background="?android:attr/listDivider" />
<ImageView
android:id="@android:id/button2"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_alignBottom="@android:id/widget_frame"
+ android:layout_width="@dimen/notification_app_icon_size"
+ android:layout_height="@dimen/notification_app_icon_size"
android:layout_alignParentEnd="true"
- android:layout_alignTop="@android:id/widget_frame"
android:layout_centerVertical="true"
- android:layout_margin="0dip"
- android:background="?android:attr/selectableItemBackground"
+ android:background="?android:attr/listChoiceBackgroundIndicator"
android:contentDescription="@string/notification_app_settings_button"
- android:padding="8dip"
- android:clickable="true"
- android:focusable="true"
- android:src="@drawable/ic_bt_config" />
+ android:scaleType="center"
+ android:src="@drawable/ic_settings_generic" />
-</RelativeLayout>
+ <View
+ android:id="@+id/row_divider"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/listDivider" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/res/layout/notification_app_dialog.xml b/res/layout/notification_app_dialog.xml
new file mode 100644
index 0000000..a8f7880
--- /dev/null
+++ b/res/layout/notification_app_dialog.xml
@@ -0,0 +1,47 @@
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" >
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="@dimen/notification_app_icon_size"
+ android:layout_height="@dimen/notification_app_icon_size"
+ android:contentDescription="@null"
+ android:scaleType="center" />
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_app_icon_size"
+ android:layout_toEndOf="@android:id/icon"
+ android:ellipsize="end"
+ android:gravity="center_vertical"
+ android:singleLine="true"
+ android:textAlignment="viewStart"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <CheckBox
+ android:id="@android:id/button1"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_app_icon_size"
+ android:layout_below="@android:id/icon"
+ android:layout_marginStart="@dimen/content_margin_left"
+ android:text="@string/app_notifications_dialog_show"
+ android:textAppearance="?android:attr/textAppearanceListItem" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/res/layout/notification_app_list.xml b/res/layout/notification_app_list.xml
new file mode 100644
index 0000000..9d23a54
--- /dev/null
+++ b/res/layout/notification_app_list.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+ <ListView
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:divider="#0000"
+ android:dividerHeight="0px"
+ android:fastScrollAlwaysVisible="true"
+ android:fastScrollEnabled="true"
+ android:listSelector="#0000"
+ android:scrollbarStyle="outsideInset" />
+
+ <TextView
+ android:id="@android:id/empty"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:text="@string/loading_notification_apps"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/notification_app_section.xml b/res/layout/notification_app_section.xml
new file mode 100644
index 0000000..628b524
--- /dev/null
+++ b/res/layout/notification_app_section.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ style="?android:attr/listSeparatorTextViewStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
diff --git a/res/layout/notification_info_row.xml b/res/layout/notification_info_row.xml
deleted file mode 100644
index bc71ef2..0000000
--- a/res/layout/notification_info_row.xml
+++ /dev/null
@@ -1,115 +0,0 @@
-<!--
- Copyright (C) 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" >
-
- <!-- Dream selectable row (icon, caption, radio button) -->
-
- <RelativeLayout
- android:id="@android:id/widget_frame"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_toStartOf="@+id/divider"
- android:background="?android:attr/selectableItemBackground" >
-
- <!-- Dream icon -->
-
- <ImageView
- android:id="@+id/pkgicon"
- android:layout_width="@*android:dimen/status_bar_icon_size"
- android:layout_height="@*android:dimen/status_bar_icon_size"
- android:layout_centerVertical="true"
- android:layout_marginBottom="6dp"
- android:layout_marginStart="0dp"
- android:layout_marginEnd="6dp"
- android:layout_marginTop="6dp"
- android:contentDescription="@null"
- android:maxHeight="@*android:dimen/status_bar_icon_size"
- android:maxWidth="@*android:dimen/status_bar_icon_size"
- android:scaleType="fitCenter" />
-
- <ImageView
- android:id="@android:id/icon"
- android:layout_width="@*android:dimen/status_bar_icon_size"
- android:layout_height="@*android:dimen/status_bar_icon_size"
- android:layout_centerVertical="true"
- android:layout_toEndOf="@id/pkgicon"
- android:layout_marginBottom="6dp"
- android:layout_marginStart="0dp"
- android:layout_marginEnd="8dp"
- android:layout_marginTop="6dp"
- android:contentDescription="@null"
- android:maxHeight="@*android:dimen/status_bar_icon_size"
- android:maxWidth="@*android:dimen/status_bar_icon_size"
- android:scaleType="fitCenter" />
-
- <!-- Dream caption -->
-
- <TextView
- android:id="@android:id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:layout_toStartOf="@android:id/button1"
- android:layout_toEndOf="@android:id/icon"
- android:ellipsize="end"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textAlignment="viewStart"
- android:labelFor="@android:id/button2" />
-
- <!-- Dream radio button -->
-
- <!--<RadioButton
- android:id="@android:id/button1"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_alignParentEnd="true"
- android:layout_centerVertical="true"
- android:duplicateParentState="true"
- android:clickable="false"
- android:focusable="false" />-->
- </RelativeLayout>
-
- <!-- Divider -->
-
- <ImageView
- android:id="@id/divider"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_centerVertical="true"
- android:layout_toStartOf="@android:id/button2"
- android:contentDescription="@null"
- android:src="@drawable/nav_divider" />
-
- <!-- Settings icon -->
-
- <ImageView
- android:id="@android:id/button2"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_alignBottom="@android:id/widget_frame"
- android:layout_alignParentEnd="true"
- android:layout_alignTop="@android:id/widget_frame"
- android:layout_centerVertical="true"
- android:layout_margin="0dip"
- android:background="?android:attr/selectableItemBackground"
- android:contentDescription="@string/screensaver_settings_button"
- android:padding="8dip"
- android:src="@drawable/ic_bt_config" />
-
-</RelativeLayout>
\ No newline at end of file
diff --git a/res/layout/notification_log_row.xml b/res/layout/notification_log_row.xml
index 284e9ea..702e6b8 100644
--- a/res/layout/notification_log_row.xml
+++ b/res/layout/notification_log_row.xml
@@ -33,7 +33,7 @@
android:layout_width="@*android:dimen/status_bar_icon_size"
android:layout_height="@*android:dimen/status_bar_icon_size"
android:layout_centerVertical="true"
- android:layout_toEndOf="@id/pkgicon"
+ android:layout_toEndOf="@+id/pkgicon"
android:layout_marginStart="0dp"
android:layout_marginEnd="8dp"
android:contentDescription="@null"
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 3581d6a..64d4851 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -78,4 +78,9 @@
<integer name="preferences_left_pane_weight">4</integer>
<!-- Weight of the right pane in a multi-pane preference layout. So the split is 40:60 -->
<integer name="preferences_right_pane_weight">6</integer>
+
+ <dimen name="notification_app_icon_size">64dp</dimen>
+ <dimen name="notification_app_icon_badge_size">20dp</dimen>
+ <dimen name="notification_app_icon_badge_margin">4dp</dimen>
+ <dimen name="notification_app_settings_divider_height">48dp</dimen>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 09bd6d6..c92bde2 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1767,9 +1767,7 @@
<!-- Sound settings screen, notification light repeat pulsing title -->
<string name="notification_pulse_title">Pulse notification light</string>
<!-- Display settings screen, notification popups are enabled [CHAR LIMIT=30] -->
- <string name="heads_up_enabled_title">Heads Up Notifications</string>
- <!-- Display settings screen, notification popups are explained [CHAR LIMIT=45]-->
- <string name="heads_up_enabled_summary">Important notifications will pop up</string>
+ <string name="heads_up_enabled_title">Pop up if high priority</string>
<!-- Sound settings screen, the title of the volume bar to adjust the incoming call volume -->
<string name="incoming_call_volume_title">Ringtone</string>
<!-- Sound settings screen, the title of the volume bar to adjust the notification volume -->
@@ -5027,38 +5025,31 @@
<!-- Notifications on lockscreen -->
<!-- Label for checkbox controlling the contents of notifications shown on
the secure lockscreen [CHAR LIMIT=25] -->
- <string name="lock_screen_notifications">Show when locked</string>
+ <string name="lock_screen_notifications">Show on lock screen</string>
<!-- Summary for lock_screen_notifications: sensitive information will be
hidden or redacted from notifications shown on a secure lock screen
[CHAR LIMIT=50] -->
- <string name="lock_screen_notifications_summary_off">Sensitive notification contents will be hidden on the lock screen</string>
+ <string name="lock_screen_notifications_summary_off">Unless content is sensitive</string>
<!-- Summary for lock_screen_notifications: all information will be
shown in notifications shown on a secure lock screen
[CHAR LIMIT=50] -->
- <string name="lock_screen_notifications_summary_on">All notification contents will be shown on the lock screen</string>
+ <string name="lock_screen_notifications_summary_on">All notifications</string>
- <!-- [CHAR LIMIT=30] Sound settings screen, setting option name to change zen mode -->
- <string name="title_zen_mode">Limited interruptions</string>
- <!-- [CHAR LIMIT=30] Sound settings screen, description for configuring zen mode -->
- <string name="configure_zen_mode_description">Configure limited interruptions</string>
+ <!-- [CHAR LIMIT=30] Notification settings screen, setting option name -->
+ <string name="title_zen_mode">Do not disturb</string>
+
<!-- [CHAR LIMIT=30] Zen mode settings screen, activity title -->
- <string name="zen_mode_settings_title">Limited Interruptions</string>
+ <string name="zen_mode_settings_title">Do not disturb</string>
<!-- [CHAR LIMIT=20] Notifications settings header -->
<string name="notification_settings">Notifications</string>
- <!-- [CHAR LIMIT=20] Notifications settings: General section header -->
- <string name="notification_settings_general">General</string>
-
- <!-- [CHAR LIMIT=20] Notifications settings: Security section header -->
- <string name="notification_settings_security">Security</string>
-
<!-- [CHAR LIMIT=20] Notifications settings: Tweaks section header (eng
builds only) -->
<string name="notification_settings_tweaks">Tweaks</string>
<!-- [CHAR LIMIT=20] Notifications settings: Apps section header -->
- <string name="notification_settings_apps">Apps</string>
+ <string name="notification_settings_apps_title">App notifications</string>
<!-- [CHAR LIMIT=NONE] Content description for per-app notification
settings button -->
@@ -5081,5 +5072,21 @@
<!-- Text displayed when tag is not writable -->
<string name="status_tag_not_writable">NFC tag is not writable. Please use a different tag.</string>
+ <!-- Option title for the default sound, context based on screen -->
+ <string name="default_sound">Default sound</string>
+ <!-- [CHAR LIMIT=NONE] Text when loading app list in notification settings -->
+ <string name="loading_notification_apps">Loading apps...</string>
+
+ <!-- [CHAR LIMIT=40] Notification settings: App notifications screen title -->
+ <string name="app_notifications_title">App notifications</string>
+
+ <!-- [CHAR LIMIT=30] Notification settings: App notifications dialog show option -->
+ <string name="app_notifications_dialog_show">Show notifications</string>
+
+ <!-- [CHAR LIMIT=30] Notification settings: App notifications dialog priority option -->
+ <string name="app_notifications_dialog_priority">High priority</string>
+
+ <!-- [CHAR LIMIT=20] Notification settings: App notifications dialog dismiss button caption -->
+ <string name="app_notifications_dialog_done">Done</string>
</resources>
diff --git a/res/xml/notification_settings.xml b/res/xml/notification_settings.xml
index 4979422..51b153e 100644
--- a/res/xml/notification_settings.xml
+++ b/res/xml/notification_settings.xml
@@ -19,67 +19,44 @@
android:key="notification_settings"
xmlns:settings="http://schemas.android.com/apk/res/com.android.settings">
- <PreferenceCategory
- android:key="category_general"
- android:title="@string/notification_settings_general">
+ <com.android.settings.DefaultRingtonePreference
+ android:key="notification_sound"
+ android:title="@string/default_sound"
+ android:dialogTitle="@string/notification_sound_dialog_title"
+ android:persistent="false"
+ android:ringtoneType="notification" />
- <com.android.settings.DefaultRingtonePreference
- android:key="notification_sound"
- android:title="@string/notification_sound_title"
- android:dialogTitle="@string/notification_sound_dialog_title"
- android:persistent="false"
- android:ringtoneType="notification" />
+ <CheckBoxPreference
+ android:key="notification_pulse"
+ android:title="@string/notification_pulse_title"
+ android:persistent="false" />
- <CheckBoxPreference
- android:key="notification_pulse"
- android:title="@string/notification_pulse_title"
- android:persistent="false" />
+ <CheckBoxPreference
+ android:key="heads_up"
+ android:title="@string/heads_up_enabled_title"
+ android:persistent="false" />
- <CheckBoxPreference
- android:key="heads_up"
- android:title="@string/heads_up_enabled_title"
- android:summary="@string/heads_up_enabled_summary"
- android:persistent="false" />
+ <CheckBoxPreference
+ android:key="toggle_lock_screen_notifications"
+ android:title="@string/lock_screen_notifications"
+ android:summaryOff="@string/lock_screen_notifications_summary_off"
+ android:summaryOn="@string/lock_screen_notifications_summary_on"
+ android:persistent="false" />
- <PreferenceScreen
- android:key="zen_mode"
- android:title="@string/title_zen_mode"
- android:fragment="com.android.settings.ZenModeSettings" />
+ <PreferenceScreen
+ android:key="configure"
+ android:title="@string/notification_settings_apps_title"
+ android:fragment="com.android.settings.notification.AppNotificationSettings" />
- </PreferenceCategory>
-
- <PreferenceCategory
- android:key="category_security"
- android:title="@string/notification_settings_security">
-
- <CheckBoxPreference
- android:key="toggle_lock_screen_notifications"
- android:title="@string/lock_screen_notifications"
- android:summaryOff="@string/lock_screen_notifications_summary_off"
- android:summaryOn="@string/lock_screen_notifications_summary_on"
- android:persistent="false" />
-
- <Preference
- android:key="manage_notification_access"
- android:title="@string/manage_notification_access"
- android:persistent="false"
- android:fragment="com.android.settings.NotificationAccessSettings"/>
-
- </PreferenceCategory>
-
-<!--
- <PreferenceCategory
- android:key="category_tweaks"
- android:title="@string/notification_settings_tweaks"/>
+ <PreferenceScreen
+ android:key="zen_mode"
+ android:title="@string/title_zen_mode"
+ android:fragment="com.android.settings.notification.ZenModeSettings" />
<Preference
- android:title="Coming soon"
- />
--->
-
- <PreferenceCategory
- android:key="category_apps"
- android:title="@string/notification_settings_apps">
- </PreferenceCategory>
+ android:key="manage_notification_access"
+ android:title="@string/manage_notification_access"
+ android:persistent="false"
+ android:fragment="com.android.settings.notification.NotificationAccessSettings" />
</PreferenceScreen>
diff --git a/res/xml/settings_headers.xml b/res/xml/settings_headers.xml
index 73665cd..bf44854 100644
--- a/res/xml/settings_headers.xml
+++ b/res/xml/settings_headers.xml
@@ -85,7 +85,7 @@
<header
android:id="@+id/notification_settings"
android:icon="@drawable/ic_settings_notifications"
- android:fragment="com.android.settings.NotificationSettings"
+ android:fragment="com.android.settings.notification.NotificationSettings"
android:title="@string/notification_settings" />
<!-- Storage -->
diff --git a/src/com/android/settings/NotificationSettings.java b/src/com/android/settings/NotificationSettings.java
deleted file mode 100644
index d0b64a6..0000000
--- a/src/com/android/settings/NotificationSettings.java
+++ /dev/null
@@ -1,412 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.database.ContentObserver;
-import android.graphics.drawable.Drawable;
-import android.media.RingtoneManager;
-import android.os.AsyncTask;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.preference.CheckBoxPreference;
-import android.preference.Preference;
-import android.preference.Preference.OnPreferenceClickListener;
-import android.preference.PreferenceGroup;
-import android.preference.PreferenceScreen;
-import android.provider.Settings;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import java.text.Collator;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-
-public class NotificationSettings extends SettingsPreferenceFragment implements
- Preference.OnPreferenceChangeListener, OnPreferenceClickListener {
- private static final String TAG = "NotificationSettings";
-
- private static final Intent APP_NOTIFICATION_PREFS_CATEGORY_INTENT
- = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_NOTIFICATION_PREFERENCES);
-
- private static final String KEY_NOTIFICATION_SOUND = "notification_sound";
- private static final String KEY_NOTIFICATION_ACCESS = "manage_notification_access";
- private static final String KEY_LOCK_SCREEN_NOTIFICATIONS = "toggle_lock_screen_notifications";
- private static final String KEY_HEADS_UP = "heads_up";
- private static final String KEY_NOTIFICATION_PULSE = "notification_pulse";
-
- private static final String KEY_SECURITY_CATEGORY = "category_security";
- private static final String KEY_APPS_CATEGORY = "category_apps";
- private static final String KEY_TWEAKS_CATEGORY = "category_tweaks"; // power toys, eng only
-
- private static final int MSG_UPDATE_SOUND_SUMMARY = 2;
-
- private Context mContext;
- private PackageManager mPM;
-
- private Preference mNotificationSoundPreference;
- private Preference mNotificationAccess;
- private CheckBoxPreference mLockscreenNotifications;
- private CheckBoxPreference mHeadsUp;
- private CheckBoxPreference mNotificationPulse;
- private PreferenceGroup mAppsPreference;
-
- private final Runnable mRingtoneLookupRunnable = new Runnable() {
- @Override
- public void run() {
- if (mNotificationSoundPreference != null) {
- final CharSequence summary = SoundSettings.updateRingtoneName(
- mContext, RingtoneManager.TYPE_NOTIFICATION);
- if (summary != null) {
- mHandler.sendMessage(
- mHandler.obtainMessage(MSG_UPDATE_SOUND_SUMMARY, summary));
- }
- }
- }
- };
-
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_UPDATE_SOUND_SUMMARY:
- mNotificationSoundPreference.setSummary((CharSequence) msg.obj);
- break;
- }
- }
- };
-
- private final ArrayList<AppNotificationInfo> mAppNotificationInfo
- = new ArrayList<AppNotificationInfo>();
- private final HashSet<String> mAppNotificationInfoPackages = new HashSet<String>();
- private final Comparator<AppNotificationInfo> mAppComparator = new Comparator<AppNotificationInfo>() {
- private final Collator sCollator = Collator.getInstance();
- @Override
- public int compare(AppNotificationInfo lhs, AppNotificationInfo rhs) {
- return sCollator.compare(lhs.label, rhs.label);
- }
- };
-
- private final Runnable mCollectAppsRunnable = new Runnable() {
- @Override
- public void run() {
- synchronized (mAppNotificationInfo) {
- mAppNotificationInfo.clear();
- mAppNotificationInfoPackages.clear();
-
- final PackageManager pm = getPackageManager();
-
- final List<ResolveInfo> resolveInfos = pm.queryIntentActivities(APP_NOTIFICATION_PREFS_CATEGORY_INTENT,
- PackageManager.MATCH_DEFAULT_ONLY);
-
- for (ResolveInfo ri : resolveInfos) {
- final ActivityInfo activityInfo = ri.activityInfo;
- final ApplicationInfo appInfo = activityInfo.applicationInfo;
- if (mAppNotificationInfoPackages.contains(activityInfo.packageName)) {
- Log.v(TAG, "Ignoring duplicate notification preference activity ("
- + activityInfo.name + ") for package "
- + activityInfo.packageName);
- continue;
- }
- final AppNotificationInfo info = new AppNotificationInfo();
- mAppNotificationInfoPackages.add(activityInfo.packageName);
-
- info.label = appInfo.loadLabel(pm);
- info.icon = appInfo.loadIcon(pm);
- info.name = activityInfo.name;
- info.pkg = activityInfo.packageName;
- mAppNotificationInfo.add(info);
- }
-
- Collections.sort(mAppNotificationInfo, mAppComparator);
- mHandler.post(mRefreshAppsListRunnable);
- }
- }
- };
-
- private final Runnable mRefreshAppsListRunnable = new Runnable() {
- @Override
- public void run() {
- synchronized (mAppNotificationInfo) {
- mAppsPreference.removeAll();
- Preference p = getPreferenceScreen().findPreference(mAppsPreference.getKey());
- final int N = mAppNotificationInfo.size();
- if (N == 0 && p != null) {
- getPreferenceScreen().removePreference(p);
- } else if (N > 0 && p == null) {
- getPreferenceScreen().addPreference(mAppsPreference);
- }
- for (int i = 0; i < N; i++) {
- final AppNotificationInfo info = mAppNotificationInfo.get(i);
- Preference pref = new AppNotificationPreference(mContext);
- pref.setTitle(info.label);
- pref.setIcon(info.icon);
- pref.setIntent(new Intent(Intent.ACTION_MAIN)
- .setClassName(info.pkg, info.name));
- mAppsPreference.addPreference(pref);
- }
- }
- }
- };
-
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mContext = getActivity();
- final ContentResolver resolver = mContext.getContentResolver();
-
- mPM = mContext.getPackageManager();
-
- addPreferencesFromResource(R.xml.notification_settings);
-
- final PreferenceScreen root = getPreferenceScreen();
- final PreferenceGroup securityCategory = (PreferenceGroup)
- root.findPreference(KEY_SECURITY_CATEGORY);
-
- PreferenceGroup tweaksCategory = (PreferenceGroup)
- root.findPreference(KEY_TWEAKS_CATEGORY);
-
- if (tweaksCategory != null
- && !(Build.TYPE.equals("eng") || Build.TYPE.equals("userdebug"))) {
- root.removePreference(tweaksCategory);
- tweaksCategory = null;
- }
-
- mNotificationSoundPreference = findPreference(KEY_NOTIFICATION_SOUND);
-
- mNotificationAccess = findPreference(KEY_NOTIFICATION_ACCESS);
- refreshNotificationListeners();
-
- mLockscreenNotifications
- = (CheckBoxPreference) root.findPreference(KEY_LOCK_SCREEN_NOTIFICATIONS);
- if (mLockscreenNotifications != null) {
- if (!getDeviceLockscreenNotificationsEnabled()) {
- if (securityCategory != null) {
- securityCategory.removePreference(mLockscreenNotifications);
- }
- } else {
- mLockscreenNotifications.setChecked(getLockscreenAllowPrivateNotifications());
- }
- }
-
- mHeadsUp = (CheckBoxPreference) findPreference(KEY_HEADS_UP);
- if (mHeadsUp != null) {
- updateHeadsUpMode(resolver);
- mHeadsUp.setOnPreferenceChangeListener(this);
- resolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED),
- false, new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange) {
- updateHeadsUpMode(resolver);
- }
- });
- }
- mNotificationPulse = (CheckBoxPreference) findPreference(KEY_NOTIFICATION_PULSE);
-
- if (mNotificationPulse != null
- && getResources().getBoolean(
- com.android.internal.R.bool.config_intrusiveNotificationLed) == false) {
- getPreferenceScreen().removePreference(mNotificationPulse);
- } else {
- try {
- mNotificationPulse.setChecked(Settings.System.getInt(resolver,
- Settings.System.NOTIFICATION_LIGHT_PULSE) == 1);
- mNotificationPulse.setOnPreferenceChangeListener(this);
- } catch (Settings.SettingNotFoundException snfe) {
- Log.e(TAG, Settings.System.NOTIFICATION_LIGHT_PULSE + " not found");
- }
- }
- mAppsPreference = (PreferenceGroup) root.findPreference(KEY_APPS_CATEGORY);
- root.removePreference(mAppsPreference);
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- refreshNotificationListeners();
- lookupRingtoneNames();
- loadAppsList();
- }
-
- private void loadAppsList() {
- AsyncTask.execute(mCollectAppsRunnable);
- }
-
- @Override
- public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
- final String key = preference.getKey();
-
- if (KEY_LOCK_SCREEN_NOTIFICATIONS.equals(key)) {
- Settings.Secure.putInt(getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
- mLockscreenNotifications.isChecked() ? 1 : 0);
- } else if (KEY_HEADS_UP.equals(key)) {
- setHeadsUpMode(getContentResolver(), mHeadsUp.isChecked());
- } else if (KEY_NOTIFICATION_PULSE.equals(key)) {
- Settings.System.putInt(getContentResolver(),
- Settings.System.NOTIFICATION_LIGHT_PULSE,
- mNotificationPulse.isChecked() ? 1 : 0);
- } else {
- return super.onPreferenceTreeClick(preferenceScreen, preference);
- }
-
- return true;
- }
-
- @Override
- public boolean onPreferenceChange(Preference preference, Object objValue) {
- return true;
- }
-
- @Override
- public boolean onPreferenceClick(Preference preference) {
- return false;
- }
-
- // === Heads-up notifications ===
-
- private void updateHeadsUpMode(ContentResolver resolver) {
- mHeadsUp.setChecked(Settings.Global.HEADS_UP_ON == Settings.Global.getInt(resolver,
- Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, Settings.Global.HEADS_UP_OFF));
- }
-
- private void setHeadsUpMode(ContentResolver resolver, boolean value) {
- Settings.Global.putInt(resolver, Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
- value ? Settings.Global.HEADS_UP_ON : Settings.Global.HEADS_UP_OFF);
- }
-
- // === Lockscreen (public / private) notifications ===
-
- private boolean getDeviceLockscreenNotificationsEnabled() {
- return 0 != Settings.Global.getInt(getContentResolver(),
- Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
- }
-
- private boolean getLockscreenAllowPrivateNotifications() {
- return 0 != Settings.Secure.getInt(getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
- }
-
- // === Notification listeners ===
-
- private int getNumEnabledNotificationListeners() {
- final String flat = Settings.Secure.getString(getContentResolver(),
- Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
- if (flat == null || "".equals(flat)) return 0;
- final String[] components = flat.split(":");
- return components.length;
- }
-
- private void refreshNotificationListeners() {
- if (mNotificationAccess != null) {
- final PreferenceGroup securityCategory
- = (PreferenceGroup) getPreferenceScreen().findPreference(KEY_SECURITY_CATEGORY);
-
- final int total = NotificationAccessSettings.getListenersCount(mPM);
- if (total == 0) {
- if (securityCategory != null) {
- securityCategory.removePreference(mNotificationAccess);
- }
- } else {
- final int n = getNumEnabledNotificationListeners();
- if (n == 0) {
- mNotificationAccess.setSummary(getResources().getString(
- R.string.manage_notification_access_summary_zero));
- } else {
- mNotificationAccess.setSummary(String.format(getResources().getQuantityString(
- R.plurals.manage_notification_access_summary_nonzero,
- n, n)));
- }
- }
- }
- }
-
- // === Ringtone ===
-
- private void lookupRingtoneNames() {
- new Thread(mRingtoneLookupRunnable).start();
- }
-
- // === Per-app notification settings row ==
-
- private static class AppNotificationPreference extends Preference {
- private Intent mIntent;
-
- public AppNotificationPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
-
- setLayoutResource(R.layout.notification_app);
- }
-
- public AppNotificationPreference(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public AppNotificationPreference(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public AppNotificationPreference(Context context) {
- this(context, null);
- }
-
- public void setIntent(Intent intent) {
- mIntent = intent;
- }
-
- @Override
- protected void onBindView(View view) {
- super.onBindView(view);
-
- ImageView icon = (ImageView) view.findViewById(android.R.id.icon);
- icon.setImageDrawable(getIcon());
- TextView title = (TextView) view.findViewById(android.R.id.title);
- title.setText(getTitle());
- ImageView settingsButton = (ImageView) view.findViewById(android.R.id.button2);
- settingsButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- getContext().startActivity(mIntent);
- }
- });
- }
- }
-
- private static class AppNotificationInfo {
- public Drawable icon;
- public CharSequence label;
- public String name;
- public String pkg;
- }
-}
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 9d6e740..964c444 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -93,6 +93,10 @@
import com.android.settings.location.LocationSettings;
import com.android.settings.nfc.AndroidBeam;
import com.android.settings.nfc.PaymentSettings;
+import com.android.settings.notification.NotificationAccessSettings;
+import com.android.settings.notification.NotificationSettings;
+import com.android.settings.notification.NotificationStation;
+import com.android.settings.notification.ZenModeSettings;
import com.android.settings.print.PrintJobSettingsFragment;
import com.android.settings.print.PrintSettingsFragment;
import com.android.settings.tts.TextToSpeechSettings;
diff --git a/src/com/android/settings/notification/AppNotificationSettings.java b/src/com/android/settings/notification/AppNotificationSettings.java
new file mode 100644
index 0000000..172557d
--- /dev/null
+++ b/src/com/android/settings/notification/AppNotificationSettings.java
@@ -0,0 +1,550 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.animation.LayoutTransition;
+import android.app.AlertDialog;
+import android.app.INotificationManager;
+import android.app.ListFragment;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.Signature;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.PathShape;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcelable;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.SectionIndexer;
+import android.widget.TextView;
+
+import com.android.settings.R;
+
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class AppNotificationSettings extends ListFragment {
+ private static final String TAG = "AppNotificationSettings";
+ private static final boolean DEBUG = true;
+
+ private static final String SECTION_BEFORE_A = "*";
+ private static final String SECTION_AFTER_Z = "**";
+ private static final Intent APP_NOTIFICATION_PREFS_CATEGORY_INTENT
+ = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_NOTIFICATION_PREFERENCES);
+
+ private final Handler mHandler = new Handler();
+ private final ArrayMap<String, AppRow> mRows = new ArrayMap<String, AppRow>();
+ private final ArrayList<AppRow> mSortedRows = new ArrayList<AppRow>();
+ private final ArrayList<String> mSections = new ArrayList<String>();
+
+ private Context mContext;
+ private LayoutInflater mInflater;
+ private NotificationAppAdapter mAdapter;
+ private Signature[] mSystemSignature;
+ private Parcelable mListViewState;
+ private Backend mBackend = new Backend();
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mContext = getActivity();
+ mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mAdapter = new NotificationAppAdapter(mContext);
+ getActivity().setTitle(R.string.app_notifications_title);
+ }
+
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.notification_app_list, container, false);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ repositionScrollbar();
+ getListView().setAdapter(mAdapter);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ if (DEBUG) Log.d(TAG, "Saving listView state");
+ mListViewState = getListView().onSaveInstanceState();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ loadAppsList();
+ }
+
+ public void setBackend(Backend backend) {
+ mBackend = backend;
+ }
+
+ private void loadAppsList() {
+ AsyncTask.execute(mCollectAppsRunnable);
+ }
+
+ private String getSection(CharSequence label) {
+ if (label == null || label.length() == 0) return SECTION_BEFORE_A;
+ final char c = Character.toUpperCase(label.charAt(0));
+ if (c < 'A') return SECTION_BEFORE_A;
+ if (c > 'Z') return SECTION_AFTER_Z;
+ return Character.toString(c);
+ }
+
+ private void repositionScrollbar() {
+ final int sbWidthPx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ getListView().getScrollBarSize(),
+ getResources().getDisplayMetrics());
+ final View parent = (View)getView().getParent();
+ final int eat = Math.min(sbWidthPx, parent.getPaddingEnd());
+ if (eat <= 0) return;
+ if (DEBUG) Log.d(TAG, String.format("Eating %dpx into %dpx padding for %dpx scroll, ld=%d",
+ eat, parent.getPaddingEnd(), sbWidthPx, getListView().getLayoutDirection()));
+ parent.setPaddingRelative(parent.getPaddingStart(), parent.getPaddingTop(),
+ parent.getPaddingEnd() - eat, parent.getPaddingBottom());
+ }
+
+ private boolean isSystemApp(PackageInfo pkg) {
+ if (mSystemSignature == null) {
+ mSystemSignature = new Signature[]{ getSystemSignature() };
+ }
+ return mSystemSignature[0] != null && mSystemSignature[0].equals(getFirstSignature(pkg));
+ }
+
+ private static Signature getFirstSignature(PackageInfo pkg) {
+ if (pkg != null && pkg.signatures != null && pkg.signatures.length > 0) {
+ return pkg.signatures[0];
+ }
+ return null;
+ }
+
+ private Signature getSystemSignature() {
+ final PackageManager pm = mContext.getPackageManager();
+ try {
+ final PackageInfo sys = pm.getPackageInfo("android", PackageManager.GET_SIGNATURES);
+ return getFirstSignature(sys);
+ } catch (NameNotFoundException e) {
+ }
+ return null;
+ }
+
+
+ private void showDialog(final View v, final AppRow row) {
+ final RelativeLayout layout = (RelativeLayout)
+ mInflater.inflate(R.layout.notification_app_dialog, null);
+ final ImageView icon = (ImageView) layout.findViewById(android.R.id.icon);
+ icon.setImageDrawable(row.icon);
+ final TextView title = (TextView) layout.findViewById(android.R.id.title);
+ title.setText(row.label);
+ final CheckBox showBox = (CheckBox) layout.findViewById(android.R.id.button1);
+ showBox.setChecked(!row.banned);
+ final OnCheckedChangeListener showListener = new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ boolean success = mBackend.setNotificationsBanned(row.pkg, row.uid, !isChecked);
+ if (success) {
+ row.banned = !isChecked;
+ mAdapter.bindView(v, row, true /*animate*/);
+ } else {
+ showBox.setOnCheckedChangeListener(null);
+ showBox.setChecked(!isChecked);
+ showBox.setOnCheckedChangeListener(this);
+ }
+ }
+ };
+ showBox.setOnCheckedChangeListener(showListener);
+ final AlertDialog d = new AlertDialog.Builder(mContext)
+ .setView(layout)
+ .setPositiveButton(R.string.app_notifications_dialog_done, null)
+ .create();
+ d.show();
+ }
+
+ private static class ViewHolder {
+ ViewGroup row;
+ ViewGroup appButton;
+ ImageView icon;
+ ImageView banBadge;
+ ImageView priBadge;
+ TextView title;
+ View settingsDivider;
+ ImageView settingsButton;
+ View rowDivider;
+ }
+
+ private class NotificationAppAdapter extends ArrayAdapter<Row> implements SectionIndexer {
+ private final ShapeDrawable mBanShape, mPriShape;
+
+ public NotificationAppAdapter(Context context) {
+ super(context, 0, 0);
+ final int s = context.getResources()
+ .getDimensionPixelSize(R.dimen.notification_app_icon_badge_size);
+ mBanShape = shape(banPath(s), s);
+ mPriShape = shape(priPath(s), s);
+ }
+
+ private ShapeDrawable shape(Path path, int s) {
+ final ShapeDrawable sd = new ShapeDrawable(new PathShape(path, s, s));
+ sd.getPaint().setStyle(Paint.Style.STROKE);
+ sd.getPaint().setColor(0xffffffff);
+ sd.getPaint().setStrokeWidth(s / 12);
+ sd.setIntrinsicWidth(s);
+ sd.setIntrinsicHeight(s);
+ return sd;
+ }
+
+ private Path banPath(int s) {
+ final Path p = new Path();
+ final int d = s / 5;
+ p.moveTo(d, d); p.lineTo(s - d, s - d);
+ p.moveTo(d, s - d); p.lineTo(s - d, d);
+ return p;
+ }
+
+ private Path priPath(int s) {
+ final Path p = new Path();
+ final int d = s / 5;
+ p.moveTo(s / 2, d); p.lineTo(s / 2, s - d);
+ return p;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 2;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ Row r = getItem(position);
+ return r instanceof AppRow ? 1 : 0;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ Row r = getItem(position);
+ View v;
+ if (convertView == null) {
+ v = newView(parent, r);
+ } else {
+ v = convertView;
+ }
+ bindView(v, r, false /*animate*/);
+ return v;
+ }
+
+ public View newView(ViewGroup parent, Row r) {
+ if (!(r instanceof AppRow)) {
+ return mInflater.inflate(R.layout.notification_app_section, parent, false);
+ }
+ final View v = mInflater.inflate(R.layout.notification_app, parent, false);
+ final ViewHolder vh = new ViewHolder();
+ vh.row = (ViewGroup) v;
+ vh.row.setLayoutTransition(new LayoutTransition());
+ vh.appButton = (ViewGroup) v.findViewById(android.R.id.button1);
+ vh.appButton.setLayoutTransition(new LayoutTransition());
+ vh.icon = (ImageView) v.findViewById(android.R.id.icon);
+ vh.banBadge = (ImageView) v.findViewById(android.R.id.icon1);
+ vh.banBadge.setImageDrawable(mBanShape);
+ vh.priBadge = (ImageView) v.findViewById(android.R.id.icon2);
+ vh.priBadge.setImageDrawable(mPriShape);
+ vh.title = (TextView) v.findViewById(android.R.id.title);
+ vh.settingsDivider = v.findViewById(R.id.settings_divider);
+ vh.settingsButton = (ImageView) v.findViewById(android.R.id.button2);
+ vh.rowDivider = v.findViewById(R.id.row_divider);
+ v.setTag(vh);
+ return v;
+ }
+
+ private void enableLayoutTransitions(ViewGroup vg, boolean enabled) {
+ if (enabled) {
+ vg.getLayoutTransition().enableTransitionType(LayoutTransition.APPEARING);
+ vg.getLayoutTransition().enableTransitionType(LayoutTransition.DISAPPEARING);
+ } else {
+ vg.getLayoutTransition().disableTransitionType(LayoutTransition.APPEARING);
+ vg.getLayoutTransition().disableTransitionType(LayoutTransition.DISAPPEARING);
+ }
+ }
+
+ public void bindView(final View view, Row r, boolean animate) {
+ if (!(r instanceof AppRow)) {
+ TextView tv = (TextView)view;
+ tv.setText(r.section);
+ return;
+ }
+
+ final AppRow row = (AppRow)r;
+ final ViewHolder vh = (ViewHolder) view.getTag();
+ enableLayoutTransitions(vh.row, animate);
+ vh.rowDivider.setVisibility(row.first ? View.GONE : View.VISIBLE);
+ vh.appButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showDialog(view, row);
+ }
+ });
+ enableLayoutTransitions(vh.appButton, animate);
+ vh.icon.setImageDrawable(row.icon);
+ vh.banBadge.setVisibility(row.banned ? View.VISIBLE : View.GONE);
+ vh.priBadge.setVisibility(row.priority ? View.VISIBLE : View.GONE);
+ vh.title.setText(row.label);
+ final boolean showSettings = !row.banned && row.settingsIntent != null;
+ vh.settingsDivider.setVisibility(showSettings ? View.VISIBLE : View.GONE);
+ vh.settingsButton.setVisibility(showSettings ? View.VISIBLE : View.GONE);
+ vh.settingsButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (row.settingsIntent != null) {
+ getContext().startActivity(row.settingsIntent);
+ }
+ }
+ });
+ }
+
+ @Override
+ public Object[] getSections() {
+ return mSections.toArray(new Object[mSections.size()]);
+ }
+
+ @Override
+ public int getPositionForSection(int sectionIndex) {
+ final String section = mSections.get(sectionIndex);
+ final int n = getCount();
+ for (int i = 0; i < n; i++) {
+ final Row r = getItem(i);
+ if (r.section.equals(section)) {
+ return i;
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public int getSectionForPosition(int position) {
+ Row row = getItem(position);
+ return mSections.indexOf(row.section);
+ }
+ }
+
+ private static class Row {
+ public String section;
+ }
+
+ private static class AppRow extends Row {
+ public String pkg;
+ public int uid;
+ public Drawable icon;
+ public CharSequence label;
+ public Intent settingsIntent;
+ public boolean banned;
+ public boolean priority;
+ public boolean first;
+ }
+
+ private static final Comparator<AppRow> mRowComparator = new Comparator<AppRow>() {
+ private final Collator sCollator = Collator.getInstance();
+ @Override
+ public int compare(AppRow lhs, AppRow rhs) {
+ return sCollator.compare(lhs.label, rhs.label);
+ }
+ };
+
+ private final Runnable mCollectAppsRunnable = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mRows) {
+ final long start = SystemClock.uptimeMillis();
+ if (DEBUG) Log.d(TAG, "Collecting apps...");
+ mRows.clear();
+ mSortedRows.clear();
+
+ // collect all non-system apps
+ final PackageManager pm = mContext.getPackageManager();
+ for (PackageInfo pkg : pm.getInstalledPackages(PackageManager.GET_SIGNATURES)) {
+ if (pkg.applicationInfo == null || isSystemApp(pkg)) {
+ if (DEBUG) Log.d(TAG, "Skipping " + pkg.packageName);
+ continue;
+ }
+ final AppRow row = new AppRow();
+ row.pkg = pkg.packageName;
+ row.uid = pkg.applicationInfo.uid;
+ try {
+ row.label = pkg.applicationInfo.loadLabel(pm);
+ } catch (Throwable t) {
+ Log.e(TAG, "Error loading application label for " + row.pkg, t);
+ row.label = row.pkg;
+ }
+ row.icon = pkg.applicationInfo.loadIcon(pm);
+ row.banned = mBackend.getNotificationsBanned(row.pkg, row.uid);
+ row.priority = mBackend.getHighPriority(row.pkg, row.uid);
+ mRows.put(row.pkg, row);
+ }
+ // collect config activities
+ Log.d(TAG, "APP_NOTIFICATION_PREFS_CATEGORY_INTENT is " + APP_NOTIFICATION_PREFS_CATEGORY_INTENT);
+ final List<ResolveInfo> resolveInfos = pm.queryIntentActivities(
+ APP_NOTIFICATION_PREFS_CATEGORY_INTENT,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ if (DEBUG) Log.d(TAG, "Found " + resolveInfos.size() + " preference activities");
+ for (ResolveInfo ri : resolveInfos) {
+ final ActivityInfo activityInfo = ri.activityInfo;
+ final ApplicationInfo appInfo = activityInfo.applicationInfo;
+ final AppRow row = mRows.get(appInfo.packageName);
+ if (row == null) {
+ Log.v(TAG, "Ignoring notification preference activity ("
+ + activityInfo.name + ") for unknown package "
+ + activityInfo.packageName);
+ continue;
+ }
+ if (row.settingsIntent != null) {
+ Log.v(TAG, "Ignoring duplicate notification preference activity ("
+ + activityInfo.name + ") for package "
+ + activityInfo.packageName);
+ continue;
+ }
+ row.settingsIntent = new Intent(Intent.ACTION_MAIN)
+ .setClassName(activityInfo.packageName, activityInfo.name);
+ }
+ // sort rows
+ mSortedRows.addAll(mRows.values());
+ Collections.sort(mSortedRows, mRowComparator);
+ // compute sections
+ mSections.clear();
+ String section = null;
+ for (AppRow r : mSortedRows) {
+ r.section = getSection(r.label);
+ if (!r.section.equals(section)) {
+ section = r.section;
+ mSections.add(section);
+ }
+ }
+ mHandler.post(mRefreshAppsListRunnable);
+ final long elapsed = SystemClock.uptimeMillis() - start;
+ if (DEBUG) Log.d(TAG, "Collected " + mRows.size() + " apps in " + elapsed + "ms");
+ }
+ }
+ };
+
+ private void refreshDisplayedItems() {
+ if (DEBUG) Log.d(TAG, "Refreshing apps...");
+ mAdapter.clear();
+ synchronized (mSortedRows) {
+ String section = null;
+ final int N = mSortedRows.size();
+ boolean first = true;
+ for (int i = 0; i < N; i++) {
+ final AppRow row = mSortedRows.get(i);
+ if (!row.section.equals(section)) {
+ section = row.section;
+ Row r = new Row();
+ r.section = section;
+ mAdapter.add(r);
+ first = true;
+ }
+ row.first = first;
+ mAdapter.add(row);
+ first = false;
+ }
+ }
+ if (mListViewState != null) {
+ if (DEBUG) Log.d(TAG, "Restoring listView state");
+ getListView().onRestoreInstanceState(mListViewState);
+ mListViewState = null;
+ }
+ if (DEBUG) Log.d(TAG, "Refreshed " + mSortedRows.size() + " displayed items");
+ }
+
+ private final Runnable mRefreshAppsListRunnable = new Runnable() {
+ @Override
+ public void run() {
+ refreshDisplayedItems();
+ }
+ };
+
+ public static class Backend {
+ public boolean setNotificationsBanned(String pkg, int uid, boolean banned) {
+ INotificationManager nm = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ try {
+ nm.setNotificationsEnabledForPackage(pkg, uid, !banned);
+ return true;
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ return false;
+ }
+ }
+
+ public boolean getNotificationsBanned(String pkg, int uid) {
+ INotificationManager nm = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ try {
+ final boolean enabled = nm.areNotificationsEnabledForPackage(pkg, uid);
+ return !enabled;
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ return false;
+ }
+ }
+
+ public boolean getHighPriority(String pkg, int uid) {
+ // TODO get high-pri state from NoMan
+ return false;
+ }
+
+ public boolean setHighPriority(String pkg, int uid, boolean priority) {
+ // TODO save high-pri state to NoMan
+ return true;
+ }
+ }
+}
diff --git a/src/com/android/settings/NotificationAccessSettings.java b/src/com/android/settings/notification/NotificationAccessSettings.java
similarity index 99%
rename from src/com/android/settings/NotificationAccessSettings.java
rename to src/com/android/settings/notification/NotificationAccessSettings.java
index 07d4353..78ea2d8 100644
--- a/src/com/android/settings/NotificationAccessSettings.java
+++ b/src/com/android/settings/notification/NotificationAccessSettings.java
@@ -14,41 +14,42 @@
* limitations under the License.
*/
-package com.android.settings;
+package com.android.settings.notification;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
+import android.app.ListFragment;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.DialogInterface;
+import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
+import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.util.Slog;
-import android.widget.ArrayAdapter;
-
-import android.app.ListFragment;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.Bundle;
-import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
+import com.android.settings.R;
+
import java.util.HashSet;
import java.util.List;
diff --git a/src/com/android/settings/notification/NotificationSettings.java b/src/com/android/settings/notification/NotificationSettings.java
new file mode 100644
index 0000000..24863cd
--- /dev/null
+++ b/src/com/android/settings/notification/NotificationSettings.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.media.RingtoneManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.preference.PreferenceGroup;
+import android.preference.PreferenceScreen;
+import android.preference.TwoStatePreference;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.SoundSettings;
+
+public class NotificationSettings extends SettingsPreferenceFragment implements
+ Preference.OnPreferenceChangeListener, OnPreferenceClickListener {
+ private static final String TAG = "NotificationSettings";
+
+ private static final String KEY_NOTIFICATION_SOUND = "notification_sound";
+ private static final String KEY_NOTIFICATION_PULSE = "notification_pulse";
+ private static final String KEY_HEADS_UP = "heads_up";
+ private static final String KEY_LOCK_SCREEN_NOTIFICATIONS = "toggle_lock_screen_notifications";
+ private static final String KEY_NOTIFICATION_ACCESS = "manage_notification_access";
+
+ private static final String KEY_TWEAKS_CATEGORY = "category_tweaks"; // power toys, eng only
+
+ private static final int MSG_UPDATE_SOUND_SUMMARY = 2;
+
+ private Context mContext;
+ private PackageManager mPM;
+
+ private Preference mNotificationSoundPreference;
+ private Preference mNotificationAccess;
+ private TwoStatePreference mLockscreenNotifications;
+ private TwoStatePreference mHeadsUp;
+ private TwoStatePreference mNotificationPulse;
+
+ private final Runnable mRingtoneLookupRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mNotificationSoundPreference != null) {
+ final CharSequence summary = SoundSettings.updateRingtoneName(
+ mContext, RingtoneManager.TYPE_NOTIFICATION);
+ if (summary != null) {
+ mHandler.sendMessage(
+ mHandler.obtainMessage(MSG_UPDATE_SOUND_SUMMARY, summary));
+ }
+ }
+ }
+ };
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_SOUND_SUMMARY:
+ mNotificationSoundPreference.setSummary((CharSequence) msg.obj);
+ break;
+ }
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mContext = getActivity();
+ final ContentResolver resolver = mContext.getContentResolver();
+
+ mPM = mContext.getPackageManager();
+
+ addPreferencesFromResource(R.xml.notification_settings);
+
+ final PreferenceScreen root = getPreferenceScreen();
+
+ PreferenceGroup tweaksCategory = (PreferenceGroup)
+ root.findPreference(KEY_TWEAKS_CATEGORY);
+
+ if (tweaksCategory != null
+ && !(Build.TYPE.equals("eng") || Build.TYPE.equals("userdebug"))) {
+ root.removePreference(tweaksCategory);
+ tweaksCategory = null;
+ }
+
+ mNotificationSoundPreference = findPreference(KEY_NOTIFICATION_SOUND);
+
+ mNotificationAccess = findPreference(KEY_NOTIFICATION_ACCESS);
+ refreshNotificationListeners();
+
+ mLockscreenNotifications
+ = (TwoStatePreference) root.findPreference(KEY_LOCK_SCREEN_NOTIFICATIONS);
+ if (mLockscreenNotifications != null) {
+ if (!getDeviceLockscreenNotificationsEnabled()) {
+ root.removePreference(mLockscreenNotifications);
+ } else {
+ mLockscreenNotifications.setChecked(getLockscreenAllowPrivateNotifications());
+ }
+ }
+
+ mHeadsUp = (TwoStatePreference) findPreference(KEY_HEADS_UP);
+ if (mHeadsUp != null) {
+ updateHeadsUpMode(resolver);
+ mHeadsUp.setOnPreferenceChangeListener(this);
+ resolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED),
+ false, new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateHeadsUpMode(resolver);
+ }
+ });
+ }
+ mNotificationPulse = (TwoStatePreference) findPreference(KEY_NOTIFICATION_PULSE);
+
+ if (mNotificationPulse != null
+ && getResources().getBoolean(
+ com.android.internal.R.bool.config_intrusiveNotificationLed) == false) {
+ getPreferenceScreen().removePreference(mNotificationPulse);
+ } else {
+ try {
+ mNotificationPulse.setChecked(Settings.System.getInt(resolver,
+ Settings.System.NOTIFICATION_LIGHT_PULSE) == 1);
+ mNotificationPulse.setOnPreferenceChangeListener(this);
+ } catch (Settings.SettingNotFoundException snfe) {
+ Log.e(TAG, Settings.System.NOTIFICATION_LIGHT_PULSE + " not found");
+ }
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ refreshNotificationListeners();
+ lookupRingtoneNames();
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
+ final String key = preference.getKey();
+
+ if (KEY_LOCK_SCREEN_NOTIFICATIONS.equals(key)) {
+ Settings.Secure.putInt(getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ mLockscreenNotifications.isChecked() ? 1 : 0);
+ } else if (KEY_HEADS_UP.equals(key)) {
+ setHeadsUpMode(getContentResolver(), mHeadsUp.isChecked());
+ } else if (KEY_NOTIFICATION_PULSE.equals(key)) {
+ Settings.System.putInt(getContentResolver(),
+ Settings.System.NOTIFICATION_LIGHT_PULSE,
+ mNotificationPulse.isChecked() ? 1 : 0);
+ } else {
+ return super.onPreferenceTreeClick(preferenceScreen, preference);
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object objValue) {
+ return true;
+ }
+
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ return false;
+ }
+
+ // === Heads-up notifications ===
+
+ private void updateHeadsUpMode(ContentResolver resolver) {
+ mHeadsUp.setChecked(Settings.Global.HEADS_UP_ON == Settings.Global.getInt(resolver,
+ Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, Settings.Global.HEADS_UP_OFF));
+ }
+
+ private void setHeadsUpMode(ContentResolver resolver, boolean value) {
+ Settings.Global.putInt(resolver, Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
+ value ? Settings.Global.HEADS_UP_ON : Settings.Global.HEADS_UP_OFF);
+ }
+
+ // === Lockscreen (public / private) notifications ===
+
+ private boolean getDeviceLockscreenNotificationsEnabled() {
+ return 0 != Settings.Global.getInt(getContentResolver(),
+ Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
+ }
+
+ private boolean getLockscreenAllowPrivateNotifications() {
+ return 0 != Settings.Secure.getInt(getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
+ }
+
+ // === Notification listeners ===
+
+ private int getNumEnabledNotificationListeners() {
+ final String flat = Settings.Secure.getString(getContentResolver(),
+ Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
+ if (flat == null || "".equals(flat)) return 0;
+ final String[] components = flat.split(":");
+ return components.length;
+ }
+
+ private void refreshNotificationListeners() {
+ if (mNotificationAccess != null) {
+ final int total = NotificationAccessSettings.getListenersCount(mPM);
+ if (total == 0) {
+ getPreferenceScreen().removePreference(mNotificationAccess);
+ } else {
+ final int n = getNumEnabledNotificationListeners();
+ if (n == 0) {
+ mNotificationAccess.setSummary(getResources().getString(
+ R.string.manage_notification_access_summary_zero));
+ } else {
+ mNotificationAccess.setSummary(String.format(getResources().getQuantityString(
+ R.plurals.manage_notification_access_summary_nonzero,
+ n, n)));
+ }
+ }
+ }
+ }
+
+ // === Ringtone ===
+
+ private void lookupRingtoneNames() {
+ new Thread(mRingtoneLookupRunnable).start();
+ }
+}
diff --git a/src/com/android/settings/NotificationStation.java b/src/com/android/settings/notification/NotificationStation.java
similarity index 98%
rename from src/com/android/settings/NotificationStation.java
rename to src/com/android/settings/notification/NotificationStation.java
index 5083e27..d8a3efb 100644
--- a/src/com/android/settings/NotificationStation.java
+++ b/src/com/android/settings/notification/NotificationStation.java
@@ -14,16 +14,14 @@
* limitations under the License.
*/
-package com.android.settings;
+package com.android.settings.notification;
import android.app.Activity;
import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.service.notification.INotificationListener;
import android.app.INotificationManager;
import android.app.Notification;
-import android.service.notification.StatusBarNotification;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -37,6 +35,8 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.service.notification.INotificationListener;
+import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -48,6 +48,9 @@
import android.widget.ListView;
import android.widget.TextView;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
diff --git a/src/com/android/settings/ZenModeSettings.java b/src/com/android/settings/notification/ZenModeSettings.java
similarity index 98%
rename from src/com/android/settings/ZenModeSettings.java
rename to src/com/android/settings/notification/ZenModeSettings.java
index f6c7b8c..de020db 100644
--- a/src/com/android/settings/ZenModeSettings.java
+++ b/src/com/android/settings/notification/ZenModeSettings.java
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package com.android.settings;
+package com.android.settings.notification;
import android.app.ActionBar;
import android.app.Activity;
import android.content.Context;
-import android.database.ContentObserver;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.net.Uri;
@@ -28,6 +28,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings.Global;
+import android.provider.SearchIndexableResource;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
@@ -45,6 +46,9 @@
import android.widget.Switch;
import android.widget.TextView;
import com.android.settings.search.BaseSearchIndexProvider;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.search.Indexable;
import com.android.settings.search.SearchIndexableRaw;
diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java
index 6f8efce..af04b5f 100644
--- a/src/com/android/settings/search/SearchIndexableResources.java
+++ b/src/com/android/settings/search/SearchIndexableResources.java
@@ -17,27 +17,27 @@
package com.android.settings.search;
import android.provider.SearchIndexableResource;
+
import com.android.settings.DataUsageSummary;
import com.android.settings.DateTimeSettings;
import com.android.settings.DevelopmentSettings;
import com.android.settings.DeviceInfoSettings;
import com.android.settings.DisplaySettings;
import com.android.settings.HomeSettings;
-import com.android.settings.NotificationSettings;
import com.android.settings.PrivacySettings;
import com.android.settings.R;
import com.android.settings.SecuritySettings;
import com.android.settings.SoundSettings;
import com.android.settings.WallpaperTypeSettings;
import com.android.settings.WirelessSettings;
-import com.android.settings.ZenModeSettings;
import com.android.settings.accessibility.AccessibilitySettings;
import com.android.settings.bluetooth.BluetoothSettings;
import com.android.settings.deviceinfo.Memory;
import com.android.settings.fuelgauge.PowerUsageSummary;
import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
import com.android.settings.location.LocationSettings;
-import com.android.settings.net.DataUsageMeteredSettings;
+import com.android.settings.notification.NotificationSettings;
+import com.android.settings.notification.ZenModeSettings;
import com.android.settings.print.PrintSettingsFragment;
import com.android.settings.users.UserSettings;
import com.android.settings.wifi.WifiSettings;