Settings: New application-level notification settings.
- Convert the old application-level dialog to an activity. - Move the settings icon to the new activity (out of the list). - Add a custom application header, similar to the switch bar style. - Use the ubiquitous vector gear for the settings icon. - Migrate old checkboxes to switch prefs, add new summaries. - Remove obsolete artifacts. Bug:16396715 Change-Id: I857e3cf448b79f44fe1c242e6020f5214434c00c
This commit is contained in:
@@ -1839,25 +1839,30 @@
|
|||||||
android:resource="@id/notification_settings" />
|
android:resource="@id/notification_settings" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name="Settings$AppNotificationSettingsActivity"
|
<!-- Show apps for which application-level notification settings are applicable -->
|
||||||
|
<activity android:name="Settings$NotificationAppListActivity"
|
||||||
android:label="@string/app_notifications_title"
|
android:label="@string/app_notifications_title"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:taskAffinity="">
|
android:taskAffinity="">
|
||||||
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
||||||
android:value="com.android.settings.notification.AppNotificationSettings" />
|
android:value="com.android.settings.notification.NotificationAppList" />
|
||||||
<meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
|
<meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
|
||||||
android:resource="@id/notification_settings" />
|
android:resource="@id/notification_settings" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name=".notification.AppNotificationDialog"
|
<!-- Show application-level notification settings (app passed in as extras) -->
|
||||||
android:theme="@style/Theme.AlertDialog"
|
<activity android:name="Settings$AppNotificationSettingsActivity"
|
||||||
android:launchMode="singleTop"
|
android:label="@string/app_notifications_title"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<action android:name="android.settings.APP_NOTIFICATION_SETTINGS" />
|
<action android:name="android.settings.APP_NOTIFICATION_SETTINGS" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
|
||||||
|
android:value="com.android.settings.notification.AppNotificationSettings" />
|
||||||
|
<meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
|
||||||
|
android:resource="@id/notification_settings" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<!-- Show regulatory info (from settings item or dialing "*#07#") -->
|
<!-- Show regulatory info (from settings item or dialing "*#07#") -->
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
Before Width: | Height: | Size: 969 B |
Binary file not shown.
Before Width: | Height: | Size: 2.0 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.9 KiB |
23
res/drawable/ic_settings_32dp.xml
Normal file
23
res/drawable/ic_settings_32dp.xml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<!-- 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.
|
||||||
|
-->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="32dp"
|
||||||
|
android:height="32dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:pathData="M19.4,13.0c0.0,-0.3 0.1,-0.6 0.1,-1.0s0.0,-0.7 -0.1,-1.0l2.1,-1.7c0.2,-0.2 0.2,-0.4 0.1,-0.6l-2.0,-3.5C19.5,5.1 19.3,5.0 19.0,5.1l-2.5,1.0c-0.5,-0.4 -1.1,-0.7 -1.7,-1.0l-0.4,-2.6C14.5,2.2 14.2,2.0 14.0,2.0l-4.0,0.0C9.8,2.0 9.5,2.2 9.5,2.4L9.1,5.1C8.5,5.3 8.0,5.7 7.4,6.1L5.0,5.1C4.7,5.0 4.5,5.1 4.3,5.3l-2.0,3.5C2.2,8.9 2.3,9.2 2.5,9.4L4.6,11.0c0.0,0.3 -0.1,0.6 -0.1,1.0s0.0,0.7 0.1,1.0l-2.1,1.7c-0.2,0.2 -0.2,0.4 -0.1,0.6l2.0,3.5C4.5,18.9 4.7,19.0 5.0,18.9l2.5,-1.0c0.5,0.4 1.1,0.7 1.7,1.0l0.4,2.6c0.0,0.2 0.2,0.4 0.5,0.4l4.0,0.0c0.2,0.0 0.5,-0.2 0.5,-0.4l0.4,-2.6c0.6,-0.3 1.2,-0.6 1.7,-1.0l2.5,1.0c0.2,0.1 0.5,0.0 0.6,-0.2l2.0,-3.5c0.1,-0.2 0.1,-0.5 -0.1,-0.6L19.4,13.0zM12.0,15.5c-1.9,0.0 -3.5,-1.6 -3.5,-3.5s1.6,-3.5 3.5,-3.5s3.5,1.6 3.5,3.5S13.9,15.5 12.0,15.5z"
|
||||||
|
android:fillColor="#ffffffff" />
|
||||||
|
</vector>
|
63
res/layout/app_notification_header.xml
Normal file
63
res/layout/app_notification_header.xml
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<!--
|
||||||
|
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="?android:attr/actionBarSize"
|
||||||
|
android:background="@drawable/switchbar_background"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:theme="?attr/switchBarTheme" >
|
||||||
|
|
||||||
|
<ImageView android:id="@+id/app_icon"
|
||||||
|
android:layout_width="@dimen/switchbar_subsettings_margin_start"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:gravity="end"
|
||||||
|
android:layout_centerVertical="true" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/app_name"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_toStartOf="@+id/app_settings"
|
||||||
|
android:layout_marginStart="@dimen/switchbar_subsettings_margin_start"
|
||||||
|
android:layout_alignWithParentIfMissing="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:textAppearance="@style/TextAppearance.Switch"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:textAlignment="viewStart" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@id/app_settings"
|
||||||
|
android:layout_width="56dp"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_marginEnd="@dimen/switchbar_subsettings_margin_end"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:minHeight="0dp"
|
||||||
|
android:minWidth="0dp"
|
||||||
|
android:contentDescription="@string/notification_app_settings_button"
|
||||||
|
android:scaleType="center"
|
||||||
|
android:src="@drawable/ic_settings_32dp"
|
||||||
|
style="?android:attr/borderlessButtonStyle" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/row_divider"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?android:attr/listDivider" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
@@ -13,20 +13,14 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?android:attr/listChoiceBackgroundIndicator"
|
||||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
|
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:id="@android:id/button1"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignWithParentIfMissing="true"
|
|
||||||
android:layout_toStartOf="@+id/settings_divider"
|
|
||||||
android:background="?android:attr/listChoiceBackgroundIndicator">
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@android:id/icon"
|
android:id="@android:id/icon"
|
||||||
android:layout_width="@dimen/notification_app_icon_size"
|
android:layout_width="@dimen/notification_app_icon_size"
|
||||||
@@ -57,26 +51,6 @@
|
|||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
<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:background="?android:attr/listDivider" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@android:id/button2"
|
|
||||||
android:layout_width="@dimen/notification_app_icon_size"
|
|
||||||
android:layout_height="@dimen/notification_app_icon_size"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:background="?android:attr/listChoiceBackgroundIndicator"
|
|
||||||
android:contentDescription="@string/notification_app_settings_button"
|
|
||||||
android:scaleType="center"
|
|
||||||
android:src="@drawable/ic_settings_generic" />
|
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/row_divider"
|
android:id="@+id/row_divider"
|
||||||
|
@@ -1,65 +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.
|
|
||||||
-->
|
|
||||||
<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:padding="8dp"
|
|
||||||
android:layout_width="@dimen/notification_app_icon_size"
|
|
||||||
android:layout_height="@dimen/notification_app_icon_size"
|
|
||||||
android:contentDescription="@null" />
|
|
||||||
|
|
||||||
<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" />
|
|
||||||
|
|
||||||
<CheckBox
|
|
||||||
android:id="@android:id/button2"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="@dimen/notification_app_icon_size"
|
|
||||||
android:layout_below="@android:id/button1"
|
|
||||||
android:layout_marginStart="@dimen/content_margin_left"
|
|
||||||
android:text="@string/app_notifications_dialog_priority"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem" />
|
|
||||||
|
|
||||||
<CheckBox
|
|
||||||
android:id="@android:id/button3"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="@dimen/notification_app_icon_size"
|
|
||||||
android:layout_below="@android:id/button2"
|
|
||||||
android:layout_marginStart="@dimen/content_margin_left"
|
|
||||||
android:text="@string/app_notifications_dialog_visibility"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceListItem" />
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
@@ -87,7 +87,6 @@
|
|||||||
<dimen name="notification_app_icon_size">64dp</dimen>
|
<dimen name="notification_app_icon_size">64dp</dimen>
|
||||||
<dimen name="notification_app_icon_badge_size">20dp</dimen>
|
<dimen name="notification_app_icon_badge_size">20dp</dimen>
|
||||||
<dimen name="notification_app_icon_badge_margin">4dp</dimen>
|
<dimen name="notification_app_icon_badge_margin">4dp</dimen>
|
||||||
<dimen name="notification_app_settings_divider_height">48dp</dimen>
|
|
||||||
<dimen name="zen_mode_dropdown_width">160dp</dimen>
|
<dimen name="zen_mode_dropdown_width">160dp</dimen>
|
||||||
<dimen name="zen_downtime_checkbox_padding">7dp</dimen>
|
<dimen name="zen_downtime_checkbox_padding">7dp</dimen>
|
||||||
<dimen name="zen_downtime_margin">10dp</dimen>
|
<dimen name="zen_downtime_margin">10dp</dimen>
|
||||||
|
@@ -5739,27 +5739,33 @@
|
|||||||
<!-- [CHAR LIMIT=NONE] Text when loading app list in notification settings -->
|
<!-- [CHAR LIMIT=NONE] Text when loading app list in notification settings -->
|
||||||
<string name="loading_notification_apps">Loading apps...</string>
|
<string name="loading_notification_apps">Loading apps...</string>
|
||||||
|
|
||||||
<!-- [CHAR LIMIT=NONE] Notification settings: App notifications dialog show option -->
|
<!-- [CHAR LIMIT=NONE] App notification settings: Block option title -->
|
||||||
<string name="app_notifications_dialog_show">Show notifications</string>
|
<string name="app_notification_block_title">Block</string>
|
||||||
|
|
||||||
<!-- [CHAR LIMIT=NONE] Notification settings: App notifications dialog priority option -->
|
<!-- [CHAR LIMIT=NONE] App notification settings: Block option description-->
|
||||||
<string name="app_notifications_dialog_priority">Display at the top of the list</string>
|
<string name="app_notification_block_summary">Never show notifications from this app</string>
|
||||||
|
|
||||||
<!-- [CHAR LIMIT=NONE] Notification settings: App notifications dialog visibility option -->
|
<!-- [CHAR LIMIT=NONE] App notification settings: Priority option title -->
|
||||||
<string name="app_notifications_dialog_visibility">Hide sensitive content when device is locked</string>
|
<string name="app_notification_priority_title">Priority</string>
|
||||||
|
|
||||||
|
<!-- [CHAR LIMIT=NONE] App notification settings: Priority option description-->
|
||||||
|
<string name="app_notification_priority_summary">Show notifications at the top of the list and keep them coming when the device is set to priority interruptions only</string>
|
||||||
|
|
||||||
|
<!-- [CHAR LIMIT=NONE] App notification settings: Sensitive option title -->
|
||||||
|
<string name="app_notification_sensitive_title">Sensitive</string>
|
||||||
|
|
||||||
|
<!-- [CHAR LIMIT=NONE] App notification settings: Sensitive option description-->
|
||||||
|
<string name="app_notification_sensitive_summary">When the device is locked, hide any sensitive content from this app’s notifications</string>
|
||||||
|
|
||||||
<!-- [CHAR LIMIT=20] Notification settings: App notifications row summary when banned -->
|
<!-- [CHAR LIMIT=20] Notification settings: App notifications row summary when banned -->
|
||||||
<string name="app_notification_row_banned">Blocked</string>
|
<string name="app_notification_row_banned">Blocked</string>
|
||||||
|
|
||||||
<!-- [CHAR LIMIT=20] Notification settings: App notifications row summary when high priority -->
|
<!-- [CHAR LIMIT=40] Notification settings: App notifications row summary when high priority -->
|
||||||
<string name="app_notification_row_priority">Top of list</string>
|
<string name="app_notification_row_priority">Priority</string>
|
||||||
|
|
||||||
<!-- [CHAR LIMIT=20] Notification settings: App notifications row summary when sensitive -->
|
<!-- [CHAR LIMIT=20] Notification settings: App notifications row summary when sensitive -->
|
||||||
<string name="app_notification_row_sensitive">Sensitive</string>
|
<string name="app_notification_row_sensitive">Sensitive</string>
|
||||||
|
|
||||||
<!-- [CHAR LIMIT=20] Notification settings: App notifications dialog dismiss button caption -->
|
|
||||||
<string name="app_notifications_dialog_done">Done</string>
|
|
||||||
|
|
||||||
<!-- [CHAR LIMIT=30] Zen mode settings: Exit condition selection dialog, default option -->
|
<!-- [CHAR LIMIT=30] Zen mode settings: Exit condition selection dialog, default option -->
|
||||||
<string name="zen_mode_default_option">Until you turn this off</string>
|
<string name="zen_mode_default_option">Until you turn this off</string>
|
||||||
|
|
||||||
|
45
res/xml/app_notification_settings.xml
Normal file
45
res/xml/app_notification_settings.xml
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:title="@string/app_notifications_title"
|
||||||
|
android:key="app_notification_settings">
|
||||||
|
|
||||||
|
<!-- Block -->
|
||||||
|
<SwitchPreference
|
||||||
|
android:key="block"
|
||||||
|
android:title="@string/app_notification_block_title"
|
||||||
|
android:summary="@string/app_notification_block_summary"
|
||||||
|
android:disableDependentsState="true"
|
||||||
|
android:persistent="false" />
|
||||||
|
|
||||||
|
<!-- Priority -->
|
||||||
|
<SwitchPreference
|
||||||
|
android:key="priority"
|
||||||
|
android:title="@string/app_notification_priority_title"
|
||||||
|
android:summary="@string/app_notification_priority_summary"
|
||||||
|
android:dependency="block"
|
||||||
|
android:persistent="false" />
|
||||||
|
|
||||||
|
<!-- Sensitive -->
|
||||||
|
<SwitchPreference
|
||||||
|
android:key="sensitive"
|
||||||
|
android:title="@string/app_notification_sensitive_title"
|
||||||
|
android:summary="@string/app_notification_sensitive_summary"
|
||||||
|
android:enabled="false"
|
||||||
|
android:persistent="false" />
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
@@ -108,7 +108,7 @@
|
|||||||
<PreferenceScreen
|
<PreferenceScreen
|
||||||
android:key="app_notifications"
|
android:key="app_notifications"
|
||||||
android:title="@string/app_notifications_title"
|
android:title="@string/app_notifications_title"
|
||||||
android:fragment="com.android.settings.notification.AppNotificationSettings" />
|
android:fragment="com.android.settings.notification.NotificationAppList" />
|
||||||
|
|
||||||
<!-- Notification access -->
|
<!-- Notification access -->
|
||||||
<Preference
|
<Preference
|
||||||
|
@@ -92,6 +92,7 @@ public class Settings extends SettingsActivity {
|
|||||||
public static class PrintJobSettingsActivity extends SettingsActivity { /* empty */ }
|
public static class PrintJobSettingsActivity extends SettingsActivity { /* empty */ }
|
||||||
public static class ZenModeSettingsActivity extends SettingsActivity { /* empty */ }
|
public static class ZenModeSettingsActivity extends SettingsActivity { /* empty */ }
|
||||||
public static class NotificationSettingsActivity extends SettingsActivity { /* empty */ }
|
public static class NotificationSettingsActivity extends SettingsActivity { /* empty */ }
|
||||||
|
public static class NotificationAppListActivity extends SettingsActivity { /* empty */ }
|
||||||
public static class AppNotificationSettingsActivity extends SettingsActivity { /* empty */ }
|
public static class AppNotificationSettingsActivity extends SettingsActivity { /* empty */ }
|
||||||
public static class OtherSoundSettingsActivity extends SettingsActivity { /* empty */ }
|
public static class OtherSoundSettingsActivity extends SettingsActivity { /* empty */ }
|
||||||
public static class QuickLaunchSettingsActivity extends SettingsActivity { /* empty */ }
|
public static class QuickLaunchSettingsActivity extends SettingsActivity { /* empty */ }
|
||||||
|
@@ -82,7 +82,7 @@ import com.android.settings.deviceinfo.Memory;
|
|||||||
import com.android.settings.deviceinfo.UsbSettings;
|
import com.android.settings.deviceinfo.UsbSettings;
|
||||||
import com.android.settings.fuelgauge.BatterySaverSettings;
|
import com.android.settings.fuelgauge.BatterySaverSettings;
|
||||||
import com.android.settings.fuelgauge.PowerUsageSummary;
|
import com.android.settings.fuelgauge.PowerUsageSummary;
|
||||||
import com.android.settings.notification.AppNotificationSettings;
|
import com.android.settings.notification.NotificationAppList;
|
||||||
import com.android.settings.notification.OtherSoundSettings;
|
import com.android.settings.notification.OtherSoundSettings;
|
||||||
import com.android.settings.quicklaunch.QuickLaunchSettings;
|
import com.android.settings.quicklaunch.QuickLaunchSettings;
|
||||||
import com.android.settings.search.DynamicIndexableContentMonitor;
|
import com.android.settings.search.DynamicIndexableContentMonitor;
|
||||||
@@ -94,6 +94,7 @@ import com.android.settings.inputmethod.UserDictionaryList;
|
|||||||
import com.android.settings.location.LocationSettings;
|
import com.android.settings.location.LocationSettings;
|
||||||
import com.android.settings.nfc.AndroidBeam;
|
import com.android.settings.nfc.AndroidBeam;
|
||||||
import com.android.settings.nfc.PaymentSettings;
|
import com.android.settings.nfc.PaymentSettings;
|
||||||
|
import com.android.settings.notification.AppNotificationSettings;
|
||||||
import com.android.settings.notification.ConditionProviderSettings;
|
import com.android.settings.notification.ConditionProviderSettings;
|
||||||
import com.android.settings.notification.NotificationAccessSettings;
|
import com.android.settings.notification.NotificationAccessSettings;
|
||||||
import com.android.settings.notification.NotificationSettings;
|
import com.android.settings.notification.NotificationSettings;
|
||||||
@@ -286,6 +287,7 @@ public class SettingsActivity extends Activity
|
|||||||
ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
|
ChooseLockPattern.ChooseLockPatternFragment.class.getName(),
|
||||||
InstalledAppDetails.class.getName(),
|
InstalledAppDetails.class.getName(),
|
||||||
BatterySaverSettings.class.getName(),
|
BatterySaverSettings.class.getName(),
|
||||||
|
NotificationAppList.class.getName(),
|
||||||
AppNotificationSettings.class.getName(),
|
AppNotificationSettings.class.getName(),
|
||||||
OtherSoundSettings.class.getName(),
|
OtherSoundSettings.class.getName(),
|
||||||
QuickLaunchSettings.class.getName()
|
QuickLaunchSettings.class.getName()
|
||||||
|
@@ -1,180 +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.notification;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.PackageInfo;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.provider.Settings;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.CheckBox;
|
|
||||||
import android.widget.CompoundButton;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.Toast;
|
|
||||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
|
||||||
|
|
||||||
import com.android.internal.app.AlertActivity;
|
|
||||||
import com.android.internal.app.AlertController;
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.notification.AppNotificationSettings.Backend;
|
|
||||||
import com.android.settings.notification.AppNotificationSettings.AppRow;
|
|
||||||
|
|
||||||
public class AppNotificationDialog extends AlertActivity {
|
|
||||||
private static final String TAG = "AppNotificationDialog";
|
|
||||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show a checkbox in the per-app notification control dialog to allow the user to
|
|
||||||
* selectively redact this app's notifications on the lockscreen.
|
|
||||||
*/
|
|
||||||
private static final boolean ENABLE_APP_NOTIFICATION_PRIVACY_OPTION = false;
|
|
||||||
|
|
||||||
private final Context mContext = this;
|
|
||||||
private final Backend mBackend = new Backend();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
if (DEBUG) Log.d(TAG, "onCreate getIntent()=" + getIntent());
|
|
||||||
if (!buildDialog()) {
|
|
||||||
Toast.makeText(mContext, R.string.app_not_found_dlg_text, Toast.LENGTH_SHORT).show();
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean buildDialog() {
|
|
||||||
final Intent intent = getIntent();
|
|
||||||
if (intent != null) {
|
|
||||||
final int uid = intent.getIntExtra(Settings.EXTRA_APP_UID, -1);
|
|
||||||
final String pkg = intent.getStringExtra(Settings.EXTRA_APP_PACKAGE);
|
|
||||||
if (uid != -1 && !TextUtils.isEmpty(pkg)) {
|
|
||||||
if (DEBUG) Log.d(TAG, "Load details for pkg=" + pkg + " uid=" + uid);
|
|
||||||
final PackageManager pm = getPackageManager();
|
|
||||||
final PackageInfo info = findPackageInfo(pm, pkg, uid);
|
|
||||||
if (info != null) {
|
|
||||||
final AppRow row = AppNotificationSettings.loadAppRow(pm, info, mBackend);
|
|
||||||
final AlertController.AlertParams p = mAlertParams;
|
|
||||||
p.mView = getLayoutInflater().inflate(R.layout.notification_app_dialog,
|
|
||||||
null, false);
|
|
||||||
p.mPositiveButtonText = getString(R.string.app_notifications_dialog_done);
|
|
||||||
bindDialog(p.mView, row);
|
|
||||||
setupAlert();
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "Failed to find package info");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "Missing extras: " + Settings.EXTRA_APP_PACKAGE + " was " + pkg + ", "
|
|
||||||
+ Settings.EXTRA_APP_UID + " was " + uid);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "No intent");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static PackageInfo findPackageInfo(PackageManager pm, String pkg, int uid) {
|
|
||||||
final String[] packages = pm.getPackagesForUid(uid);
|
|
||||||
if (packages != null && pkg != null) {
|
|
||||||
final int N = packages.length;
|
|
||||||
for (int i = 0; i < N; i++) {
|
|
||||||
final String p = packages[i];
|
|
||||||
if (pkg.equals(p)) {
|
|
||||||
try {
|
|
||||||
return pm.getPackageInfo(pkg, 0);
|
|
||||||
} catch (NameNotFoundException e) {
|
|
||||||
Log.w(TAG, "Failed to load package " + pkg, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bindDialog(final View v, final AppRow row) {
|
|
||||||
final ImageView icon = (ImageView) v.findViewById(android.R.id.icon);
|
|
||||||
icon.setImageDrawable(row.icon);
|
|
||||||
final TextView title = (TextView) v.findViewById(android.R.id.title);
|
|
||||||
title.setText(row.label);
|
|
||||||
final CheckBox showNotifications = (CheckBox) v.findViewById(android.R.id.button1);
|
|
||||||
final CheckBox highPriority = (CheckBox) v.findViewById(android.R.id.button2);
|
|
||||||
final CheckBox sensitive = (CheckBox) v.findViewById(android.R.id.button3);
|
|
||||||
|
|
||||||
if (!ENABLE_APP_NOTIFICATION_PRIVACY_OPTION) {
|
|
||||||
sensitive.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
showNotifications.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;
|
|
||||||
highPriority.setEnabled(!row.banned);
|
|
||||||
sensitive.setEnabled(!row.banned);
|
|
||||||
} else {
|
|
||||||
showNotifications.setOnCheckedChangeListener(null);
|
|
||||||
showNotifications.setChecked(!isChecked);
|
|
||||||
showNotifications.setOnCheckedChangeListener(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
showNotifications.setOnCheckedChangeListener(showListener);
|
|
||||||
|
|
||||||
highPriority.setChecked(row.priority);
|
|
||||||
final OnCheckedChangeListener priListener = new OnCheckedChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
|
||||||
boolean success = mBackend.setHighPriority(row.pkg, row.uid, isChecked);
|
|
||||||
if (success) {
|
|
||||||
row.priority = isChecked;
|
|
||||||
} else {
|
|
||||||
highPriority.setOnCheckedChangeListener(null);
|
|
||||||
highPriority.setChecked(!isChecked);
|
|
||||||
highPriority.setOnCheckedChangeListener(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
highPriority.setOnCheckedChangeListener(priListener);
|
|
||||||
|
|
||||||
sensitive.setChecked(row.sensitive);
|
|
||||||
final OnCheckedChangeListener senListener = new OnCheckedChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
|
||||||
boolean success = mBackend.setSensitive(row.pkg, row.uid, isChecked);
|
|
||||||
if (success) {
|
|
||||||
row.sensitive = isChecked;
|
|
||||||
} else {
|
|
||||||
sensitive.setOnCheckedChangeListener(null);
|
|
||||||
sensitive.setChecked(!isChecked);
|
|
||||||
sensitive.setOnCheckedChangeListener(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
sensitive.setOnCheckedChangeListener(senListener);
|
|
||||||
|
|
||||||
highPriority.setEnabled(!row.banned);
|
|
||||||
sensitive.setEnabled(!row.banned);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -16,557 +16,186 @@
|
|||||||
|
|
||||||
package com.android.settings.notification;
|
package com.android.settings.notification;
|
||||||
|
|
||||||
import android.animation.LayoutTransition;
|
|
||||||
import android.app.INotificationManager;
|
|
||||||
import android.app.Notification;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ActivityInfo;
|
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
import android.content.pm.ResolveInfo;
|
|
||||||
import android.content.pm.Signature;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.preference.Preference;
|
||||||
import android.os.Parcelable;
|
import android.preference.Preference.OnPreferenceChangeListener;
|
||||||
import android.os.ServiceManager;
|
import android.preference.SwitchPreference;
|
||||||
import android.os.SystemClock;
|
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.os.UserHandle;
|
import android.text.TextUtils;
|
||||||
import android.os.UserManager;
|
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.TypedValue;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.AdapterView.OnItemSelectedListener;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.SectionIndexer;
|
|
||||||
import android.widget.Spinner;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.android.settings.Settings.AppNotificationSettingsActivity;
|
|
||||||
import com.android.settings.PinnedHeaderListFragment;
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.UserSpinnerAdapter;
|
import com.android.settings.SettingsPreferenceFragment;
|
||||||
import com.android.settings.Utils;
|
import com.android.settings.notification.NotificationAppList.AppRow;
|
||||||
|
import com.android.settings.notification.NotificationAppList.Backend;
|
||||||
|
|
||||||
import java.text.Collator;
|
/** These settings are per app, so should not be returned in global search results. */
|
||||||
import java.util.ArrayList;
|
public class AppNotificationSettings extends SettingsPreferenceFragment {
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/** Just a sectioned list of installed applications, nothing else to index **/
|
|
||||||
public class AppNotificationSettings extends PinnedHeaderListFragment
|
|
||||||
implements OnItemSelectedListener {
|
|
||||||
private static final String TAG = "AppNotificationSettings";
|
private static final String TAG = "AppNotificationSettings";
|
||||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||||
|
|
||||||
private static final String SECTION_BEFORE_A = "*";
|
private static final String KEY_BLOCK = "block";
|
||||||
private static final String SECTION_AFTER_Z = "**";
|
private static final String KEY_PRIORITY = "priority";
|
||||||
private static final Intent APP_NOTIFICATION_PREFS_CATEGORY_INTENT
|
private static final String KEY_SENSITIVE = "sensitive";
|
||||||
= new Intent(Intent.ACTION_MAIN)
|
|
||||||
.addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES);
|
|
||||||
|
|
||||||
private final Handler mHandler = new Handler();
|
static final String EXTRA_HAS_SETTINGS_INTENT = "has_settings_intent";
|
||||||
private final ArrayMap<String, AppRow> mRows = new ArrayMap<String, AppRow>();
|
static final String EXTRA_SETTINGS_INTENT = "settings_intent";
|
||||||
private final ArrayList<AppRow> mSortedRows = new ArrayList<AppRow>();
|
|
||||||
private final ArrayList<String> mSections = new ArrayList<String>();
|
private final Backend mBackend = new Backend();
|
||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private LayoutInflater mInflater;
|
private SwitchPreference mBlock;
|
||||||
private NotificationAppAdapter mAdapter;
|
private SwitchPreference mPriority;
|
||||||
private Signature[] mSystemSignature;
|
private SwitchPreference mSensitive;
|
||||||
private Parcelable mListViewState;
|
private AppRow mAppRow;
|
||||||
private Backend mBackend = new Backend();
|
private boolean mCreated;
|
||||||
private UserSpinnerAdapter mProfileSpinnerAdapter;
|
|
||||||
|
@Override
|
||||||
|
public void onActivityCreated(Bundle savedInstanceState) {
|
||||||
|
super.onActivityCreated(savedInstanceState);
|
||||||
|
if (DEBUG) Log.d(TAG, "onActivityCreated mCreated=" + mCreated);
|
||||||
|
if (mCreated) {
|
||||||
|
Log.w(TAG, "onActivityCreated: ignoring duplicate call");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mCreated = true;
|
||||||
|
if (mAppRow == null) return;
|
||||||
|
final View content = getActivity().findViewById(R.id.main_content);
|
||||||
|
final ViewGroup contentParent = (ViewGroup) content.getParent();
|
||||||
|
final View bar = getActivity().getLayoutInflater().inflate(R.layout.app_notification_header,
|
||||||
|
contentParent, false);
|
||||||
|
|
||||||
|
final ImageView appIcon = (ImageView) bar.findViewById(R.id.app_icon);
|
||||||
|
appIcon.setImageDrawable(mAppRow.icon);
|
||||||
|
|
||||||
|
final TextView appName = (TextView) bar.findViewById(R.id.app_name);
|
||||||
|
appName.setText(mAppRow.label);
|
||||||
|
|
||||||
|
final View appSettings = bar.findViewById(R.id.app_settings);
|
||||||
|
if (mAppRow.settingsIntent == null) {
|
||||||
|
appSettings.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
appSettings.setClickable(true);
|
||||||
|
appSettings.setOnClickListener(new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
mContext.startActivity(mAppRow.settingsIntent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
contentParent.addView(bar, 0);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
mContext = getActivity();
|
mContext = getActivity();
|
||||||
mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
Intent intent = getActivity().getIntent();
|
||||||
mAdapter = new NotificationAppAdapter(mContext);
|
if (DEBUG) Log.d(TAG, "onCreate getIntent()=" + intent);
|
||||||
getActivity().setTitle(R.string.app_notifications_title);
|
if (intent == null) {
|
||||||
}
|
Log.w(TAG, "No intent");
|
||||||
|
toastAndFinish();
|
||||||
@Override
|
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
|
||||||
Bundle savedInstanceState) {
|
|
||||||
return inflater.inflate(R.layout.notification_app_list, container, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
|
||||||
super.onViewCreated(view, savedInstanceState);
|
|
||||||
final UserManager um = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
|
|
||||||
mProfileSpinnerAdapter = Utils.createUserSpinnerAdapter(um, mContext);
|
|
||||||
if (mProfileSpinnerAdapter != null) {
|
|
||||||
Spinner spinner = (Spinner) getActivity().getLayoutInflater().inflate(
|
|
||||||
R.layout.spinner_view, null);
|
|
||||||
spinner.setAdapter(mProfileSpinnerAdapter);
|
|
||||||
spinner.setOnItemSelectedListener(this);
|
|
||||||
setPinnedHeaderView(spinner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@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 onDestroyView() {
|
|
||||||
super.onDestroyView();
|
|
||||||
mListViewState = null; // you're dead to me
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
loadAppsList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
UserHandle selectedUser = mProfileSpinnerAdapter.getUserHandle(position);
|
|
||||||
if (selectedUser.getIdentifier() != UserHandle.myUserId()) {
|
|
||||||
Intent intent = new Intent(getActivity(), AppNotificationSettingsActivity.class);
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
mContext.startActivityAsUser(intent, selectedUser);
|
|
||||||
getActivity().finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNothingSelected(AdapterView<?> parent) {
|
|
||||||
}
|
|
||||||
|
|
||||||
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 static class ViewHolder {
|
|
||||||
ViewGroup row;
|
|
||||||
ViewGroup appButton;
|
|
||||||
ImageView icon;
|
|
||||||
TextView title;
|
|
||||||
TextView subtitle;
|
|
||||||
View settingsDivider;
|
|
||||||
ImageView settingsButton;
|
|
||||||
View rowDivider;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class NotificationAppAdapter extends ArrayAdapter<Row> implements SectionIndexer {
|
|
||||||
public NotificationAppAdapter(Context context) {
|
|
||||||
super(context, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@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.title = (TextView) v.findViewById(android.R.id.title);
|
|
||||||
vh.subtitle = (TextView) v.findViewById(android.R.id.text1);
|
|
||||||
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)) {
|
|
||||||
// it's a section row
|
|
||||||
final TextView tv = (TextView)view.findViewById(android.R.id.title);
|
|
||||||
tv.setText(r.section);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final AppRow row = (AppRow)r;
|
final int uid = intent.getIntExtra(Settings.EXTRA_APP_UID, -1);
|
||||||
final ViewHolder vh = (ViewHolder) view.getTag();
|
final String pkg = intent.getStringExtra(Settings.EXTRA_APP_PACKAGE);
|
||||||
enableLayoutTransitions(vh.row, animate);
|
if (uid == -1 || TextUtils.isEmpty(pkg)) {
|
||||||
vh.rowDivider.setVisibility(row.first ? View.GONE : View.VISIBLE);
|
Log.w(TAG, "Missing extras: " + Settings.EXTRA_APP_PACKAGE + " was " + pkg + ", "
|
||||||
vh.appButton.setOnClickListener(new OnClickListener() {
|
+ Settings.EXTRA_APP_UID + " was " + uid);
|
||||||
|
toastAndFinish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DEBUG) Log.d(TAG, "Load details for pkg=" + pkg + " uid=" + uid);
|
||||||
|
final PackageManager pm = getPackageManager();
|
||||||
|
final PackageInfo info = findPackageInfo(pm, pkg, uid);
|
||||||
|
if (info == null) {
|
||||||
|
Log.w(TAG, "Failed to find package info: " + Settings.EXTRA_APP_PACKAGE + " was " + pkg
|
||||||
|
+ ", " + Settings.EXTRA_APP_UID + " was " + uid);
|
||||||
|
toastAndFinish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addPreferencesFromResource(R.xml.app_notification_settings);
|
||||||
|
mBlock = (SwitchPreference) findPreference(KEY_BLOCK);
|
||||||
|
mPriority = (SwitchPreference) findPreference(KEY_PRIORITY);
|
||||||
|
mSensitive = (SwitchPreference) findPreference(KEY_SENSITIVE);
|
||||||
|
|
||||||
|
mAppRow = NotificationAppList.loadAppRow(pm, info, mBackend);
|
||||||
|
if (intent.hasExtra(EXTRA_HAS_SETTINGS_INTENT)) {
|
||||||
|
// use settings intent from extra
|
||||||
|
if (intent.getBooleanExtra(EXTRA_HAS_SETTINGS_INTENT, false)) {
|
||||||
|
mAppRow.settingsIntent = intent.getParcelableExtra(EXTRA_SETTINGS_INTENT);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// load settings intent
|
||||||
|
ArrayMap<String, AppRow> rows = new ArrayMap<String, AppRow>();
|
||||||
|
rows.put(mAppRow.pkg, mAppRow);
|
||||||
|
NotificationAppList.collectConfigActivities(getPackageManager(), rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
mBlock.setChecked(mAppRow.banned);
|
||||||
|
mPriority.setChecked(mAppRow.priority);
|
||||||
|
mSensitive.setChecked(mAppRow.sensitive);
|
||||||
|
|
||||||
|
mBlock.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||||
mContext.startActivity(new Intent(mContext, AppNotificationDialog.class)
|
final boolean block = (Boolean) newValue;
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
return mBackend.setNotificationsBanned(pkg, uid, block);
|
||||||
.putExtra(Settings.EXTRA_APP_PACKAGE, row.pkg)
|
|
||||||
.putExtra(Settings.EXTRA_APP_UID, row.uid));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
enableLayoutTransitions(vh.appButton, animate);
|
|
||||||
vh.icon.setImageDrawable(row.icon);
|
mPriority.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
|
||||||
vh.title.setText(row.label);
|
|
||||||
final String sub = getSubtitle(row);
|
|
||||||
vh.subtitle.setText(sub);
|
|
||||||
vh.subtitle.setVisibility(!sub.isEmpty() ? View.VISIBLE : View.GONE);
|
|
||||||
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
|
@Override
|
||||||
public void onClick(View v) {
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||||
if (row.settingsIntent != null) {
|
final boolean priority = (Boolean) newValue;
|
||||||
getContext().startActivity(row.settingsIntent);
|
return mBackend.setHighPriority(pkg, uid, priority);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mSensitive.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||||
|
final boolean sensitive = (Boolean) newValue;
|
||||||
|
return mBackend.setSensitive(pkg, uid, sensitive);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getSubtitle(AppRow row) {
|
private void toastAndFinish() {
|
||||||
if (row.banned) return mContext.getString(R.string.app_notification_row_banned);
|
Toast.makeText(mContext, R.string.app_not_found_dlg_text, Toast.LENGTH_SHORT).show();
|
||||||
if (!row.priority && !row.sensitive) return "";
|
getActivity().finish();
|
||||||
final String priString = mContext.getString(R.string.app_notification_row_priority);
|
|
||||||
final String senString = mContext.getString(R.string.app_notification_row_sensitive);
|
|
||||||
if (row.priority != row.sensitive) {
|
|
||||||
return row.priority ? priString : senString;
|
|
||||||
}
|
|
||||||
return priString + mContext.getString(R.string.summary_divider_text) + senString;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private static PackageInfo findPackageInfo(PackageManager pm, String pkg, int uid) {
|
||||||
public Object[] getSections() {
|
final String[] packages = pm.getPackagesForUid(uid);
|
||||||
return mSections.toArray(new Object[mSections.size()]);
|
if (packages != null && pkg != null) {
|
||||||
}
|
final int N = packages.length;
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public 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 sensitive;
|
|
||||||
public boolean first; // first app in section
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static AppRow loadAppRow(PackageManager pm, PackageInfo pkg, Backend backend) {
|
|
||||||
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 = backend.getNotificationsBanned(row.pkg, row.uid);
|
|
||||||
row.priority = backend.getHighPriority(row.pkg, row.uid);
|
|
||||||
row.sensitive = backend.getSensitive(row.pkg, row.uid);
|
|
||||||
return row;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = loadAppRow(pm, pkg, mBackend);
|
|
||||||
mRows.put(row.pkg, row);
|
|
||||||
}
|
|
||||||
// collect config activities
|
|
||||||
if (DEBUG) 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++) {
|
for (int i = 0; i < N; i++) {
|
||||||
final AppRow row = mSortedRows.get(i);
|
final String p = packages[i];
|
||||||
if (!row.section.equals(section)) {
|
if (pkg.equals(p)) {
|
||||||
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 {
|
try {
|
||||||
nm.setNotificationsEnabledForPackage(pkg, uid, !banned);
|
return pm.getPackageInfo(pkg, 0);
|
||||||
return true;
|
} catch (NameNotFoundException e) {
|
||||||
} catch (Exception e) {
|
Log.w(TAG, "Failed to load package " + pkg, 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) {
|
|
||||||
INotificationManager nm = INotificationManager.Stub.asInterface(
|
|
||||||
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
|
|
||||||
try {
|
|
||||||
return nm.getPackagePriority(pkg, uid) == Notification.PRIORITY_MAX;
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.w(TAG, "Error calling NoMan", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean setHighPriority(String pkg, int uid, boolean highPriority) {
|
|
||||||
INotificationManager nm = INotificationManager.Stub.asInterface(
|
|
||||||
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
|
|
||||||
try {
|
|
||||||
nm.setPackagePriority(pkg, uid,
|
|
||||||
highPriority ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT);
|
|
||||||
return true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.w(TAG, "Error calling NoMan", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getSensitive(String pkg, int uid) {
|
|
||||||
// TODO get visibility state from NoMan
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean setSensitive(String pkg, int uid, boolean sensitive) {
|
|
||||||
// TODO save visibility state to NoMan
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
569
src/com/android/settings/notification/NotificationAppList.java
Normal file
569
src/com/android/settings/notification/NotificationAppList.java
Normal file
@@ -0,0 +1,569 @@
|
|||||||
|
/*
|
||||||
|
* 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 static com.android.settings.notification.AppNotificationSettings.EXTRA_HAS_SETTINGS_INTENT;
|
||||||
|
import static com.android.settings.notification.AppNotificationSettings.EXTRA_SETTINGS_INTENT;
|
||||||
|
|
||||||
|
import android.animation.LayoutTransition;
|
||||||
|
import android.app.INotificationManager;
|
||||||
|
import android.app.Notification;
|
||||||
|
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.drawable.Drawable;
|
||||||
|
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.os.UserHandle;
|
||||||
|
import android.os.UserManager;
|
||||||
|
import android.provider.Settings;
|
||||||
|
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.AdapterView;
|
||||||
|
import android.widget.AdapterView.OnItemSelectedListener;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.SectionIndexer;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.android.settings.PinnedHeaderListFragment;
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.Settings.NotificationAppListActivity;
|
||||||
|
import com.android.settings.UserSpinnerAdapter;
|
||||||
|
import com.android.settings.Utils;
|
||||||
|
|
||||||
|
import java.text.Collator;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** Just a sectioned list of installed applications, nothing else to index **/
|
||||||
|
public class NotificationAppList extends PinnedHeaderListFragment
|
||||||
|
implements OnItemSelectedListener {
|
||||||
|
private static final String TAG = "NotificationAppList";
|
||||||
|
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||||
|
|
||||||
|
private static final String EMPTY_SUBTITLE = "";
|
||||||
|
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(Notification.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();
|
||||||
|
private UserSpinnerAdapter mProfileSpinnerAdapter;
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
return inflater.inflate(R.layout.notification_app_list, container, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
final UserManager um = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
|
||||||
|
mProfileSpinnerAdapter = Utils.createUserSpinnerAdapter(um, mContext);
|
||||||
|
if (mProfileSpinnerAdapter != null) {
|
||||||
|
Spinner spinner = (Spinner) getActivity().getLayoutInflater().inflate(
|
||||||
|
R.layout.spinner_view, null);
|
||||||
|
spinner.setAdapter(mProfileSpinnerAdapter);
|
||||||
|
spinner.setOnItemSelectedListener(this);
|
||||||
|
setPinnedHeaderView(spinner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
mListViewState = null; // you're dead to me
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
loadAppsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
UserHandle selectedUser = mProfileSpinnerAdapter.getUserHandle(position);
|
||||||
|
if (selectedUser.getIdentifier() != UserHandle.myUserId()) {
|
||||||
|
Intent intent = new Intent(getActivity(), NotificationAppListActivity.class);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
mContext.startActivityAsUser(intent, selectedUser);
|
||||||
|
getActivity().finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNothingSelected(AdapterView<?> parent) {
|
||||||
|
}
|
||||||
|
|
||||||
|
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 static class ViewHolder {
|
||||||
|
ViewGroup row;
|
||||||
|
ImageView icon;
|
||||||
|
TextView title;
|
||||||
|
TextView subtitle;
|
||||||
|
View rowDivider;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NotificationAppAdapter extends ArrayAdapter<Row> implements SectionIndexer {
|
||||||
|
public NotificationAppAdapter(Context context) {
|
||||||
|
super(context, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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.row.setLayoutTransition(new LayoutTransition());
|
||||||
|
vh.icon = (ImageView) v.findViewById(android.R.id.icon);
|
||||||
|
vh.title = (TextView) v.findViewById(android.R.id.title);
|
||||||
|
vh.subtitle = (TextView) v.findViewById(android.R.id.text1);
|
||||||
|
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)) {
|
||||||
|
// it's a section row
|
||||||
|
final TextView tv = (TextView)view.findViewById(android.R.id.title);
|
||||||
|
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.row.setOnClickListener(new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||||
|
.putExtra(Settings.EXTRA_APP_PACKAGE, row.pkg)
|
||||||
|
.putExtra(Settings.EXTRA_APP_UID, row.uid)
|
||||||
|
.putExtra(EXTRA_HAS_SETTINGS_INTENT, row.settingsIntent != null)
|
||||||
|
.putExtra(EXTRA_SETTINGS_INTENT, row.settingsIntent));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
enableLayoutTransitions(vh.row, animate);
|
||||||
|
vh.icon.setImageDrawable(row.icon);
|
||||||
|
vh.title.setText(row.label);
|
||||||
|
final String sub = getSubtitle(row);
|
||||||
|
vh.subtitle.setText(sub);
|
||||||
|
vh.subtitle.setVisibility(!sub.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSubtitle(AppRow row) {
|
||||||
|
if (row.banned) {
|
||||||
|
return mContext.getString(R.string.app_notification_row_banned);
|
||||||
|
}
|
||||||
|
if (!row.priority && !row.sensitive) {
|
||||||
|
return EMPTY_SUBTITLE;
|
||||||
|
}
|
||||||
|
final String priString = mContext.getString(R.string.app_notification_row_priority);
|
||||||
|
final String senString = mContext.getString(R.string.app_notification_row_sensitive);
|
||||||
|
if (row.priority != row.sensitive) {
|
||||||
|
return row.priority ? priString : senString;
|
||||||
|
}
|
||||||
|
return priString + mContext.getString(R.string.summary_divider_text) + senString;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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 sensitive;
|
||||||
|
public boolean first; // first app in section
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static AppRow loadAppRow(PackageManager pm, PackageInfo pkg, Backend backend) {
|
||||||
|
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 = backend.getNotificationsBanned(row.pkg, row.uid);
|
||||||
|
row.priority = backend.getHighPriority(row.pkg, row.uid);
|
||||||
|
row.sensitive = backend.getSensitive(row.pkg, row.uid);
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void collectConfigActivities(PackageManager pm, ArrayMap<String, AppRow> rows) {
|
||||||
|
if (DEBUG) 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 = rows.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = loadAppRow(pm, pkg, mBackend);
|
||||||
|
mRows.put(row.pkg, row);
|
||||||
|
}
|
||||||
|
// collect config activities
|
||||||
|
collectConfigActivities(pm, mRows);
|
||||||
|
// 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) {
|
||||||
|
INotificationManager nm = INotificationManager.Stub.asInterface(
|
||||||
|
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
|
||||||
|
try {
|
||||||
|
return nm.getPackagePriority(pkg, uid) == Notification.PRIORITY_MAX;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "Error calling NoMan", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setHighPriority(String pkg, int uid, boolean highPriority) {
|
||||||
|
INotificationManager nm = INotificationManager.Stub.asInterface(
|
||||||
|
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
|
||||||
|
try {
|
||||||
|
nm.setPackagePriority(pkg, uid,
|
||||||
|
highPriority ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "Error calling NoMan", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getSensitive(String pkg, int uid) {
|
||||||
|
// TODO get visibility state from NoMan
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setSensitive(String pkg, int uid, boolean sensitive) {
|
||||||
|
// TODO save visibility state to NoMan
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user