diff --git a/res/layout/notification_app.xml b/res/layout/notification_app.xml
new file mode 100644
index 00000000000..4f61c132993
--- /dev/null
+++ b/res/layout/notification_app.xml
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1d6c7511027..5376bb44cf5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -5041,4 +5041,8 @@
Apps
+
+
+ Notification settings
diff --git a/res/xml/notification_settings.xml b/res/xml/notification_settings.xml
index edea08ffdee..49794225e73 100644
--- a/res/xml/notification_settings.xml
+++ b/res/xml/notification_settings.xml
@@ -75,14 +75,11 @@
+-->
-
-
--->
+ android:title="@string/notification_settings_apps">
+
diff --git a/src/com/android/settings/NotificationSettings.java b/src/com/android/settings/NotificationSettings.java
index c8ba39a00a7..303ec240f71 100644
--- a/src/com/android/settings/NotificationSettings.java
+++ b/src/com/android/settings/NotificationSettings.java
@@ -17,9 +17,16 @@
package com.android.settings;
import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
+import android.graphics.drawable.Drawable;
import android.media.RingtoneManager;
+import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -30,12 +37,26 @@ import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.provider.Settings;
+import android.util.AttributeSet;
import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
public class NotificationSettings extends SettingsPreferenceFragment implements
Preference.OnPreferenceChangeListener, OnPreferenceClickListener {
private static final String TAG = "NotificationSettings";
+ private static final Intent APP_NOTIFICATION_PREFS_CATEGORY_INTENT
+ = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_NOTIFICATION_PREFERENCES);
+
private static final String KEY_NOTIFICATION_SOUND = "notification_sound";
private static final String KEY_ZEN_MODE = "zen_mode";
private static final String KEY_NOTIFICATION_ACCESS = "manage_notification_access";
@@ -44,6 +65,7 @@ public class NotificationSettings extends SettingsPreferenceFragment implements
private static final String KEY_NOTIFICATION_PULSE = "notification_pulse";
private static final String KEY_SECURITY_CATEGORY = "category_security";
+ private static final String KEY_APPS_CATEGORY = "category_apps";
private static final String KEY_TWEAKS_CATEGORY = "category_tweaks"; // power toys, eng only
private static final int MSG_UPDATE_SOUND_SUMMARY = 2;
@@ -81,6 +103,84 @@ public class NotificationSettings extends SettingsPreferenceFragment implements
}
};
+ private final ArrayList mAppNotificationInfo
+ = new ArrayList();
+ private final HashSet mAppNotificationInfoPackages = new HashSet();
+ private final Comparator mAppComparator = new Comparator() {
+ 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 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() {
+ final PreferenceScreen root = getPreferenceScreen();
+ final PreferenceGroup appsCategory = (PreferenceGroup)
+ root.findPreference(KEY_APPS_CATEGORY);
+
+ appsCategory.removeAll();
+
+ synchronized (mAppNotificationInfo) {
+ if (mAppNotificationInfo.size() == 0) {
+ root.removePreference(appsCategory);
+ return;
+ }
+
+ final int N = mAppNotificationInfo.size();
+ for (int i = 0; i < N; i++) {
+ final AppNotificationInfo info = mAppNotificationInfo.get(i);
+ Preference pref = new AppNotificationPreference(root.getContext());
+ pref.setTitle(info.label);
+ pref.setIcon(info.icon);
+ pref.setIntent(new Intent(Intent.ACTION_MAIN)
+ .setClassName(info.pkg, info.name));
+ appsCategory.addPreference(pref);
+ }
+ }
+ }
+ };
+
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -156,6 +256,11 @@ public class NotificationSettings extends SettingsPreferenceFragment implements
refreshNotificationListeners();
lookupRingtoneNames();
+ loadAppsList();
+ }
+
+ private void loadAppsList() {
+ AsyncTask.execute(mCollectAppsRunnable);
}
@Override
@@ -252,4 +357,56 @@ 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;
+ }
}