OSDN Git Service

App-level notification configuration settings page.
authorJohn Spurlock <jspurlock@google.com>
Tue, 8 Apr 2014 18:08:21 +0000 (14:08 -0400)
committerJohn Spurlock <jspurlock@google.com>
Mon, 21 Apr 2014 16:19:14 +0000 (12:19 -0400)
New sub-page off of notification settings to ban/unban
apps and navigate to an app's notification configuration
activity (if configured).

Centralized all notification settings artifacts under a new
settings subpackage.

Bug: 13935172
Change-Id: I53b75c02f0091900734d17dc9217035d0df9b466

22 files changed:
AndroidManifest.xml
res/drawable-hdpi/ic_settings_generic.png [new file with mode: 0644]
res/drawable-mdpi/ic_settings_generic.png [new file with mode: 0644]
res/drawable-xhdpi/ic_settings_generic.png [new file with mode: 0644]
res/drawable-xxhdpi/ic_settings_generic.png [new file with mode: 0644]
res/layout/notification_app.xml
res/layout/notification_app_dialog.xml [new file with mode: 0644]
res/layout/notification_app_list.xml [new file with mode: 0644]
res/layout/notification_app_section.xml [new file with mode: 0644]
res/layout/notification_info_row.xml [deleted file]
res/layout/notification_log_row.xml
res/values/dimens.xml
res/values/strings.xml
res/xml/notification_settings.xml
res/xml/settings_headers.xml
src/com/android/settings/SettingsActivity.java
src/com/android/settings/notification/AppNotificationSettings.java [new file with mode: 0644]
src/com/android/settings/notification/NotificationAccessSettings.java [moved from src/com/android/settings/NotificationAccessSettings.java with 99% similarity]
src/com/android/settings/notification/NotificationSettings.java [moved from src/com/android/settings/NotificationSettings.java with 55% similarity]
src/com/android/settings/notification/NotificationStation.java [moved from src/com/android/settings/NotificationStation.java with 99% similarity]
src/com/android/settings/notification/ZenModeSettings.java [moved from src/com/android/settings/ZenModeSettings.java with 98% similarity]
src/com/android/settings/search/SearchIndexableResources.java

index 9a85dc0..4ee0b21 100644 (file)
                 <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>
                 <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>
 
         <!--
                 <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>
                 <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 (file)
index 0000000..0e577bf
Binary files /dev/null and b/res/drawable-hdpi/ic_settings_generic.png differ
diff --git a/res/drawable-mdpi/ic_settings_generic.png b/res/drawable-mdpi/ic_settings_generic.png
new file mode 100644 (file)
index 0000000..a7ede7e
Binary files /dev/null and b/res/drawable-mdpi/ic_settings_generic.png differ
diff --git a/res/drawable-xhdpi/ic_settings_generic.png b/res/drawable-xhdpi/ic_settings_generic.png
new file mode 100644 (file)
index 0000000..6c907f4
Binary files /dev/null and b/res/drawable-xhdpi/ic_settings_generic.png differ
diff --git a/res/drawable-xxhdpi/ic_settings_generic.png b/res/drawable-xxhdpi/ic_settings_generic.png
new file mode 100644 (file)
index 0000000..9ce3b08
Binary files /dev/null and b/res/drawable-xxhdpi/ic_settings_generic.png differ
index 4f61c13..414165a 100644 (file)
     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" />
+
+    <View
+        android:id="@+id/row_divider"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="?android:attr/listDivider" />
 
-</RelativeLayout>
+</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 (file)
index 0000000..a8f7880
--- /dev/null
@@ -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 (file)
index 0000000..9d23a54
--- /dev/null
@@ -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 (file)
index 0000000..628b524
--- /dev/null
@@ -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 (file)
index bc71ef2..0000000
+++ /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
index 284e9ea..702e6b8 100644 (file)
@@ -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"
index 3581d6a..64d4851 100755 (executable)
@@ -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>
index 09bd6d6..c92bde2 100644 (file)
     <!-- 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 -->
     <!-- 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] Notification settings screen, setting option name -->
+    <string name="title_zen_mode">Do not disturb</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] 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 -->
     <!-- 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>
index 4979422..51b153e 100644 (file)
         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/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="heads_up"
-                android:title="@string/heads_up_enabled_title"
-                android:summary="@string/heads_up_enabled_summary"
-                android:persistent="false" />
-
-        <PreferenceScreen
-                android:key="zen_mode"
-                android:title="@string/title_zen_mode"
-                android:fragment="com.android.settings.ZenModeSettings" />
-
-    </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"/>
+    <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" />
+
+    <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="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="configure"
+            android:title="@string/notification_settings_apps_title"
+            android:fragment="com.android.settings.notification.AppNotificationSettings" />
+
+    <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>
index 73665cd..bf44854 100644 (file)
@@ -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 -->
index b42f2d0..4bf0a14 100644 (file)
@@ -93,6 +93,10 @@ import com.android.settings.inputmethod.UserDictionaryList;
 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 (file)
index 0000000..172557d
--- /dev/null
@@ -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;
+        }
+    }
+}
  * 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;
 
  * limitations under the License.
  */
 
-package com.android.settings;
+package com.android.settings.notification;
 
 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.preference.TwoStatePreference;
 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;
+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 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_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_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;
@@ -74,10 +57,9 @@ public class NotificationSettings extends SettingsPreferenceFragment implements
 
     private Preference mNotificationSoundPreference;
     private Preference mNotificationAccess;
-    private CheckBoxPreference mLockscreenNotifications;
-    private CheckBoxPreference mHeadsUp;
-    private CheckBoxPreference mNotificationPulse;
-    private PreferenceGroup mAppsPreference;
+    private TwoStatePreference mLockscreenNotifications;
+    private TwoStatePreference mHeadsUp;
+    private TwoStatePreference mNotificationPulse;
 
     private final Runnable mRingtoneLookupRunnable = new Runnable() {
         @Override
@@ -104,80 +86,6 @@ public class NotificationSettings extends SettingsPreferenceFragment implements
         }
     };
 
-    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);
@@ -189,8 +97,6 @@ public class NotificationSettings extends SettingsPreferenceFragment implements
         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);
@@ -207,18 +113,16 @@ public class NotificationSettings extends SettingsPreferenceFragment implements
         refreshNotificationListeners();
 
         mLockscreenNotifications
-                = (CheckBoxPreference) root.findPreference(KEY_LOCK_SCREEN_NOTIFICATIONS);
+                = (TwoStatePreference) root.findPreference(KEY_LOCK_SCREEN_NOTIFICATIONS);
         if (mLockscreenNotifications != null) {
             if (!getDeviceLockscreenNotificationsEnabled()) {
-                if (securityCategory != null) {
-                    securityCategory.removePreference(mLockscreenNotifications);
-                }
+                root.removePreference(mLockscreenNotifications);
             } else {
                 mLockscreenNotifications.setChecked(getLockscreenAllowPrivateNotifications());
             }
         }
 
-        mHeadsUp = (CheckBoxPreference) findPreference(KEY_HEADS_UP);
+        mHeadsUp = (TwoStatePreference) findPreference(KEY_HEADS_UP);
         if (mHeadsUp != null) {
             updateHeadsUpMode(resolver);
             mHeadsUp.setOnPreferenceChangeListener(this);
@@ -231,7 +135,7 @@ public class NotificationSettings extends SettingsPreferenceFragment implements
                 }
             });
         }
-        mNotificationPulse = (CheckBoxPreference) findPreference(KEY_NOTIFICATION_PULSE);
+        mNotificationPulse = (TwoStatePreference) findPreference(KEY_NOTIFICATION_PULSE);
 
         if (mNotificationPulse != null
                 && getResources().getBoolean(
@@ -246,8 +150,6 @@ public class NotificationSettings extends SettingsPreferenceFragment implements
                 Log.e(TAG, Settings.System.NOTIFICATION_LIGHT_PULSE + " not found");
             }
         }
-        mAppsPreference = (PreferenceGroup) root.findPreference(KEY_APPS_CATEGORY);
-        root.removePreference(mAppsPreference);
     }
 
     @Override
@@ -256,11 +158,6 @@ public class NotificationSettings extends SettingsPreferenceFragment implements
 
         refreshNotificationListeners();
         lookupRingtoneNames();
-        loadAppsList();
-    }
-
-    private void loadAppsList() {
-        AsyncTask.execute(mCollectAppsRunnable);
     }
 
     @Override
@@ -330,14 +227,9 @@ public class NotificationSettings extends SettingsPreferenceFragment implements
 
     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);
-                }
+                getPreferenceScreen().removePreference(mNotificationAccess);
             } else {
                 final int n = getNumEnabledNotificationListeners();
                 if (n == 0) {
@@ -357,56 +249,4 @@ public class NotificationSettings extends SettingsPreferenceFragment implements
     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;
-    }
 }
  * 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.Handler;
 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.ImageView;
 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;
  * 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.AsyncTask;
 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.ScrollView;
 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;
 
index 6f8efce..af04b5f 100644 (file)
 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;