diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9a85dc0f9e9..4ee0b217c91 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -564,7 +564,7 @@
+ android:value="com.android.settings.notification.ZenModeSettings" />
@@ -772,7 +772,7 @@
+ android:value="com.android.settings.notification.NotificationStation" />
-
-
-
-
+ android:background="?android:attr/listDivider" />
+ android:scaleType="center"
+ android:src="@drawable/ic_settings_generic" />
-
+
+
+
\ No newline at end of file
diff --git a/res/layout/notification_app_dialog.xml b/res/layout/notification_app_dialog.xml
new file mode 100644
index 00000000000..a8f78803bd2
--- /dev/null
+++ b/res/layout/notification_app_dialog.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/notification_app_list.xml b/res/layout/notification_app_list.xml
new file mode 100644
index 00000000000..9d23a54daf5
--- /dev/null
+++ b/res/layout/notification_app_list.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/notification_app_section.xml b/res/layout/notification_app_section.xml
new file mode 100644
index 00000000000..628b52452e8
--- /dev/null
+++ b/res/layout/notification_app_section.xml
@@ -0,0 +1,19 @@
+
+
diff --git a/res/layout/notification_info_row.xml b/res/layout/notification_info_row.xml
deleted file mode 100644
index bc71ef25948..00000000000
--- a/res/layout/notification_info_row.xml
+++ /dev/null
@@ -1,115 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/res/layout/notification_log_row.xml b/res/layout/notification_log_row.xml
index 284e9ea42b8..702e6b8c31b 100644
--- a/res/layout/notification_log_row.xml
+++ b/res/layout/notification_log_row.xml
@@ -33,7 +33,7 @@
android:layout_width="@*android:dimen/status_bar_icon_size"
android:layout_height="@*android:dimen/status_bar_icon_size"
android:layout_centerVertical="true"
- android:layout_toEndOf="@id/pkgicon"
+ android:layout_toEndOf="@+id/pkgicon"
android:layout_marginStart="0dp"
android:layout_marginEnd="8dp"
android:contentDescription="@null"
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 3581d6a1cfd..64d4851cb0d 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -78,4 +78,9 @@
4
6
+
+ 64dp
+ 20dp
+ 4dp
+ 48dp
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 09bd6d64b3d..c92bde23f94 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1767,9 +1767,7 @@
Pulse notification light
- Heads Up Notifications
-
- Important notifications will pop up
+ Pop up if high priority
Ringtone
@@ -5027,38 +5025,31 @@
- Show when locked
+ Show on lock screen
- Sensitive notification contents will be hidden on the lock screen
+ Unless content is sensitive
- All notification contents will be shown on the lock screen
+ All notifications
+
+
+ Do not disturb
-
- Limited interruptions
-
- Configure limited interruptions
- Limited Interruptions
+ Do not disturb
Notifications
-
- General
-
-
- Security
-
Tweaks
- Apps
+ App notifications
@@ -5081,5 +5072,21 @@
NFC tag is not writable. Please use a different tag.
+
+ Default sound
+
+ Loading apps...
+
+
+ App notifications
+
+
+ Show notifications
+
+
+ High priority
+
+
+ Done
diff --git a/res/xml/notification_settings.xml b/res/xml/notification_settings.xml
index 49794225e73..51b153e838d 100644
--- a/res/xml/notification_settings.xml
+++ b/res/xml/notification_settings.xml
@@ -19,67 +19,44 @@
android:key="notification_settings"
xmlns:settings="http://schemas.android.com/apk/res/com.android.settings">
-
+
-
+
-
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:key="manage_notification_access"
+ android:title="@string/manage_notification_access"
+ android:persistent="false"
+ android:fragment="com.android.settings.notification.NotificationAccessSettings" />
diff --git a/res/xml/settings_headers.xml b/res/xml/settings_headers.xml
index 73665cdca1e..bf44854f7a1 100644
--- a/res/xml/settings_headers.xml
+++ b/res/xml/settings_headers.xml
@@ -85,7 +85,7 @@
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index b42f2d0e6eb..4bf0a143560 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -93,6 +93,10 @@ import com.android.settings.inputmethod.UserDictionaryList;
import com.android.settings.location.LocationSettings;
import com.android.settings.nfc.AndroidBeam;
import com.android.settings.nfc.PaymentSettings;
+import com.android.settings.notification.NotificationAccessSettings;
+import com.android.settings.notification.NotificationSettings;
+import com.android.settings.notification.NotificationStation;
+import com.android.settings.notification.ZenModeSettings;
import com.android.settings.print.PrintJobSettingsFragment;
import com.android.settings.print.PrintSettingsFragment;
import com.android.settings.tts.TextToSpeechSettings;
diff --git a/src/com/android/settings/notification/AppNotificationSettings.java b/src/com/android/settings/notification/AppNotificationSettings.java
new file mode 100644
index 00000000000..172557df401
--- /dev/null
+++ b/src/com/android/settings/notification/AppNotificationSettings.java
@@ -0,0 +1,550 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.notification;
+
+import android.animation.LayoutTransition;
+import android.app.AlertDialog;
+import android.app.INotificationManager;
+import android.app.ListFragment;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.Signature;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.PathShape;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcelable;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.SectionIndexer;
+import android.widget.TextView;
+
+import com.android.settings.R;
+
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class AppNotificationSettings extends ListFragment {
+ private static final String TAG = "AppNotificationSettings";
+ private static final boolean DEBUG = true;
+
+ private static final String SECTION_BEFORE_A = "*";
+ private static final String SECTION_AFTER_Z = "**";
+ private static final Intent APP_NOTIFICATION_PREFS_CATEGORY_INTENT
+ = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_NOTIFICATION_PREFERENCES);
+
+ private final Handler mHandler = new Handler();
+ private final ArrayMap mRows = new ArrayMap();
+ private final ArrayList mSortedRows = new ArrayList();
+ private final ArrayList mSections = new ArrayList();
+
+ private Context mContext;
+ private LayoutInflater mInflater;
+ private NotificationAppAdapter mAdapter;
+ private Signature[] mSystemSignature;
+ private Parcelable mListViewState;
+ private Backend mBackend = new Backend();
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mContext = getActivity();
+ mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mAdapter = new NotificationAppAdapter(mContext);
+ getActivity().setTitle(R.string.app_notifications_title);
+ }
+
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.notification_app_list, container, false);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ repositionScrollbar();
+ getListView().setAdapter(mAdapter);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ if (DEBUG) Log.d(TAG, "Saving listView state");
+ mListViewState = getListView().onSaveInstanceState();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ loadAppsList();
+ }
+
+ public void setBackend(Backend backend) {
+ mBackend = backend;
+ }
+
+ private void loadAppsList() {
+ AsyncTask.execute(mCollectAppsRunnable);
+ }
+
+ private String getSection(CharSequence label) {
+ if (label == null || label.length() == 0) return SECTION_BEFORE_A;
+ final char c = Character.toUpperCase(label.charAt(0));
+ if (c < 'A') return SECTION_BEFORE_A;
+ if (c > 'Z') return SECTION_AFTER_Z;
+ return Character.toString(c);
+ }
+
+ private void repositionScrollbar() {
+ final int sbWidthPx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ getListView().getScrollBarSize(),
+ getResources().getDisplayMetrics());
+ final View parent = (View)getView().getParent();
+ final int eat = Math.min(sbWidthPx, parent.getPaddingEnd());
+ if (eat <= 0) return;
+ if (DEBUG) Log.d(TAG, String.format("Eating %dpx into %dpx padding for %dpx scroll, ld=%d",
+ eat, parent.getPaddingEnd(), sbWidthPx, getListView().getLayoutDirection()));
+ parent.setPaddingRelative(parent.getPaddingStart(), parent.getPaddingTop(),
+ parent.getPaddingEnd() - eat, parent.getPaddingBottom());
+ }
+
+ private boolean isSystemApp(PackageInfo pkg) {
+ if (mSystemSignature == null) {
+ mSystemSignature = new Signature[]{ getSystemSignature() };
+ }
+ return mSystemSignature[0] != null && mSystemSignature[0].equals(getFirstSignature(pkg));
+ }
+
+ private static Signature getFirstSignature(PackageInfo pkg) {
+ if (pkg != null && pkg.signatures != null && pkg.signatures.length > 0) {
+ return pkg.signatures[0];
+ }
+ return null;
+ }
+
+ private Signature getSystemSignature() {
+ final PackageManager pm = mContext.getPackageManager();
+ try {
+ final PackageInfo sys = pm.getPackageInfo("android", PackageManager.GET_SIGNATURES);
+ return getFirstSignature(sys);
+ } catch (NameNotFoundException e) {
+ }
+ return null;
+ }
+
+
+ private void showDialog(final View v, final AppRow row) {
+ final RelativeLayout layout = (RelativeLayout)
+ mInflater.inflate(R.layout.notification_app_dialog, null);
+ final ImageView icon = (ImageView) layout.findViewById(android.R.id.icon);
+ icon.setImageDrawable(row.icon);
+ final TextView title = (TextView) layout.findViewById(android.R.id.title);
+ title.setText(row.label);
+ final CheckBox showBox = (CheckBox) layout.findViewById(android.R.id.button1);
+ showBox.setChecked(!row.banned);
+ final OnCheckedChangeListener showListener = new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ boolean success = mBackend.setNotificationsBanned(row.pkg, row.uid, !isChecked);
+ if (success) {
+ row.banned = !isChecked;
+ mAdapter.bindView(v, row, true /*animate*/);
+ } else {
+ showBox.setOnCheckedChangeListener(null);
+ showBox.setChecked(!isChecked);
+ showBox.setOnCheckedChangeListener(this);
+ }
+ }
+ };
+ showBox.setOnCheckedChangeListener(showListener);
+ final AlertDialog d = new AlertDialog.Builder(mContext)
+ .setView(layout)
+ .setPositiveButton(R.string.app_notifications_dialog_done, null)
+ .create();
+ d.show();
+ }
+
+ private static class ViewHolder {
+ ViewGroup row;
+ ViewGroup appButton;
+ ImageView icon;
+ ImageView banBadge;
+ ImageView priBadge;
+ TextView title;
+ View settingsDivider;
+ ImageView settingsButton;
+ View rowDivider;
+ }
+
+ private class NotificationAppAdapter extends ArrayAdapter implements SectionIndexer {
+ private final ShapeDrawable mBanShape, mPriShape;
+
+ public NotificationAppAdapter(Context context) {
+ super(context, 0, 0);
+ final int s = context.getResources()
+ .getDimensionPixelSize(R.dimen.notification_app_icon_badge_size);
+ mBanShape = shape(banPath(s), s);
+ mPriShape = shape(priPath(s), s);
+ }
+
+ private ShapeDrawable shape(Path path, int s) {
+ final ShapeDrawable sd = new ShapeDrawable(new PathShape(path, s, s));
+ sd.getPaint().setStyle(Paint.Style.STROKE);
+ sd.getPaint().setColor(0xffffffff);
+ sd.getPaint().setStrokeWidth(s / 12);
+ sd.setIntrinsicWidth(s);
+ sd.setIntrinsicHeight(s);
+ return sd;
+ }
+
+ private Path banPath(int s) {
+ final Path p = new Path();
+ final int d = s / 5;
+ p.moveTo(d, d); p.lineTo(s - d, s - d);
+ p.moveTo(d, s - d); p.lineTo(s - d, d);
+ return p;
+ }
+
+ private Path priPath(int s) {
+ final Path p = new Path();
+ final int d = s / 5;
+ p.moveTo(s / 2, d); p.lineTo(s / 2, s - d);
+ return p;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 2;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ Row r = getItem(position);
+ return r instanceof AppRow ? 1 : 0;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ Row r = getItem(position);
+ View v;
+ if (convertView == null) {
+ v = newView(parent, r);
+ } else {
+ v = convertView;
+ }
+ bindView(v, r, false /*animate*/);
+ return v;
+ }
+
+ public View newView(ViewGroup parent, Row r) {
+ if (!(r instanceof AppRow)) {
+ return mInflater.inflate(R.layout.notification_app_section, parent, false);
+ }
+ final View v = mInflater.inflate(R.layout.notification_app, parent, false);
+ final ViewHolder vh = new ViewHolder();
+ vh.row = (ViewGroup) v;
+ vh.row.setLayoutTransition(new LayoutTransition());
+ vh.appButton = (ViewGroup) v.findViewById(android.R.id.button1);
+ vh.appButton.setLayoutTransition(new LayoutTransition());
+ vh.icon = (ImageView) v.findViewById(android.R.id.icon);
+ vh.banBadge = (ImageView) v.findViewById(android.R.id.icon1);
+ vh.banBadge.setImageDrawable(mBanShape);
+ vh.priBadge = (ImageView) v.findViewById(android.R.id.icon2);
+ vh.priBadge.setImageDrawable(mPriShape);
+ vh.title = (TextView) v.findViewById(android.R.id.title);
+ vh.settingsDivider = v.findViewById(R.id.settings_divider);
+ vh.settingsButton = (ImageView) v.findViewById(android.R.id.button2);
+ vh.rowDivider = v.findViewById(R.id.row_divider);
+ v.setTag(vh);
+ return v;
+ }
+
+ private void enableLayoutTransitions(ViewGroup vg, boolean enabled) {
+ if (enabled) {
+ vg.getLayoutTransition().enableTransitionType(LayoutTransition.APPEARING);
+ vg.getLayoutTransition().enableTransitionType(LayoutTransition.DISAPPEARING);
+ } else {
+ vg.getLayoutTransition().disableTransitionType(LayoutTransition.APPEARING);
+ vg.getLayoutTransition().disableTransitionType(LayoutTransition.DISAPPEARING);
+ }
+ }
+
+ public void bindView(final View view, Row r, boolean animate) {
+ if (!(r instanceof AppRow)) {
+ TextView tv = (TextView)view;
+ tv.setText(r.section);
+ return;
+ }
+
+ final AppRow row = (AppRow)r;
+ final ViewHolder vh = (ViewHolder) view.getTag();
+ enableLayoutTransitions(vh.row, animate);
+ vh.rowDivider.setVisibility(row.first ? View.GONE : View.VISIBLE);
+ vh.appButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showDialog(view, row);
+ }
+ });
+ enableLayoutTransitions(vh.appButton, animate);
+ vh.icon.setImageDrawable(row.icon);
+ vh.banBadge.setVisibility(row.banned ? View.VISIBLE : View.GONE);
+ vh.priBadge.setVisibility(row.priority ? View.VISIBLE : View.GONE);
+ vh.title.setText(row.label);
+ final boolean showSettings = !row.banned && row.settingsIntent != null;
+ vh.settingsDivider.setVisibility(showSettings ? View.VISIBLE : View.GONE);
+ vh.settingsButton.setVisibility(showSettings ? View.VISIBLE : View.GONE);
+ vh.settingsButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (row.settingsIntent != null) {
+ getContext().startActivity(row.settingsIntent);
+ }
+ }
+ });
+ }
+
+ @Override
+ public Object[] getSections() {
+ return mSections.toArray(new Object[mSections.size()]);
+ }
+
+ @Override
+ public int getPositionForSection(int sectionIndex) {
+ final String section = mSections.get(sectionIndex);
+ final int n = getCount();
+ for (int i = 0; i < n; i++) {
+ final Row r = getItem(i);
+ if (r.section.equals(section)) {
+ return i;
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public int getSectionForPosition(int position) {
+ Row row = getItem(position);
+ return mSections.indexOf(row.section);
+ }
+ }
+
+ private static class Row {
+ public String section;
+ }
+
+ private static class AppRow extends Row {
+ public String pkg;
+ public int uid;
+ public Drawable icon;
+ public CharSequence label;
+ public Intent settingsIntent;
+ public boolean banned;
+ public boolean priority;
+ public boolean first;
+ }
+
+ private static final Comparator mRowComparator = new Comparator() {
+ private final Collator sCollator = Collator.getInstance();
+ @Override
+ public int compare(AppRow lhs, AppRow rhs) {
+ return sCollator.compare(lhs.label, rhs.label);
+ }
+ };
+
+ private final Runnable mCollectAppsRunnable = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mRows) {
+ final long start = SystemClock.uptimeMillis();
+ if (DEBUG) Log.d(TAG, "Collecting apps...");
+ mRows.clear();
+ mSortedRows.clear();
+
+ // collect all non-system apps
+ final PackageManager pm = mContext.getPackageManager();
+ for (PackageInfo pkg : pm.getInstalledPackages(PackageManager.GET_SIGNATURES)) {
+ if (pkg.applicationInfo == null || isSystemApp(pkg)) {
+ if (DEBUG) Log.d(TAG, "Skipping " + pkg.packageName);
+ continue;
+ }
+ final AppRow row = new AppRow();
+ row.pkg = pkg.packageName;
+ row.uid = pkg.applicationInfo.uid;
+ try {
+ row.label = pkg.applicationInfo.loadLabel(pm);
+ } catch (Throwable t) {
+ Log.e(TAG, "Error loading application label for " + row.pkg, t);
+ row.label = row.pkg;
+ }
+ row.icon = pkg.applicationInfo.loadIcon(pm);
+ row.banned = mBackend.getNotificationsBanned(row.pkg, row.uid);
+ row.priority = mBackend.getHighPriority(row.pkg, row.uid);
+ mRows.put(row.pkg, row);
+ }
+ // collect config activities
+ Log.d(TAG, "APP_NOTIFICATION_PREFS_CATEGORY_INTENT is " + APP_NOTIFICATION_PREFS_CATEGORY_INTENT);
+ final List resolveInfos = pm.queryIntentActivities(
+ APP_NOTIFICATION_PREFS_CATEGORY_INTENT,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ if (DEBUG) Log.d(TAG, "Found " + resolveInfos.size() + " preference activities");
+ for (ResolveInfo ri : resolveInfos) {
+ final ActivityInfo activityInfo = ri.activityInfo;
+ final ApplicationInfo appInfo = activityInfo.applicationInfo;
+ final AppRow row = mRows.get(appInfo.packageName);
+ if (row == null) {
+ Log.v(TAG, "Ignoring notification preference activity ("
+ + activityInfo.name + ") for unknown package "
+ + activityInfo.packageName);
+ continue;
+ }
+ if (row.settingsIntent != null) {
+ Log.v(TAG, "Ignoring duplicate notification preference activity ("
+ + activityInfo.name + ") for package "
+ + activityInfo.packageName);
+ continue;
+ }
+ row.settingsIntent = new Intent(Intent.ACTION_MAIN)
+ .setClassName(activityInfo.packageName, activityInfo.name);
+ }
+ // sort rows
+ mSortedRows.addAll(mRows.values());
+ Collections.sort(mSortedRows, mRowComparator);
+ // compute sections
+ mSections.clear();
+ String section = null;
+ for (AppRow r : mSortedRows) {
+ r.section = getSection(r.label);
+ if (!r.section.equals(section)) {
+ section = r.section;
+ mSections.add(section);
+ }
+ }
+ mHandler.post(mRefreshAppsListRunnable);
+ final long elapsed = SystemClock.uptimeMillis() - start;
+ if (DEBUG) Log.d(TAG, "Collected " + mRows.size() + " apps in " + elapsed + "ms");
+ }
+ }
+ };
+
+ private void refreshDisplayedItems() {
+ if (DEBUG) Log.d(TAG, "Refreshing apps...");
+ mAdapter.clear();
+ synchronized (mSortedRows) {
+ String section = null;
+ final int N = mSortedRows.size();
+ boolean first = true;
+ for (int i = 0; i < N; i++) {
+ final AppRow row = mSortedRows.get(i);
+ if (!row.section.equals(section)) {
+ section = row.section;
+ Row r = new Row();
+ r.section = section;
+ mAdapter.add(r);
+ first = true;
+ }
+ row.first = first;
+ mAdapter.add(row);
+ first = false;
+ }
+ }
+ if (mListViewState != null) {
+ if (DEBUG) Log.d(TAG, "Restoring listView state");
+ getListView().onRestoreInstanceState(mListViewState);
+ mListViewState = null;
+ }
+ if (DEBUG) Log.d(TAG, "Refreshed " + mSortedRows.size() + " displayed items");
+ }
+
+ private final Runnable mRefreshAppsListRunnable = new Runnable() {
+ @Override
+ public void run() {
+ refreshDisplayedItems();
+ }
+ };
+
+ public static class Backend {
+ public boolean setNotificationsBanned(String pkg, int uid, boolean banned) {
+ INotificationManager nm = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ try {
+ nm.setNotificationsEnabledForPackage(pkg, uid, !banned);
+ return true;
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ return false;
+ }
+ }
+
+ public boolean getNotificationsBanned(String pkg, int uid) {
+ INotificationManager nm = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ try {
+ final boolean enabled = nm.areNotificationsEnabledForPackage(pkg, uid);
+ return !enabled;
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ return false;
+ }
+ }
+
+ public boolean getHighPriority(String pkg, int uid) {
+ // TODO get high-pri state from NoMan
+ return false;
+ }
+
+ public boolean setHighPriority(String pkg, int uid, boolean priority) {
+ // TODO save high-pri state to NoMan
+ return true;
+ }
+ }
+}
diff --git a/src/com/android/settings/NotificationAccessSettings.java b/src/com/android/settings/notification/NotificationAccessSettings.java
similarity index 99%
rename from src/com/android/settings/NotificationAccessSettings.java
rename to src/com/android/settings/notification/NotificationAccessSettings.java
index 07d43537cd4..78ea2d8907a 100644
--- a/src/com/android/settings/NotificationAccessSettings.java
+++ b/src/com/android/settings/notification/NotificationAccessSettings.java
@@ -14,41 +14,42 @@
* limitations under the License.
*/
-package com.android.settings;
+package com.android.settings.notification;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
+import android.app.ListFragment;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.DialogInterface;
+import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
+import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.util.Slog;
-import android.widget.ArrayAdapter;
-
-import android.app.ListFragment;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.Bundle;
-import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
+import com.android.settings.R;
+
import java.util.HashSet;
import java.util.List;
diff --git a/src/com/android/settings/NotificationSettings.java b/src/com/android/settings/notification/NotificationSettings.java
similarity index 55%
rename from src/com/android/settings/NotificationSettings.java
rename to src/com/android/settings/notification/NotificationSettings.java
index d0b64a61cb3..24863cda183 100644
--- a/src/com/android/settings/NotificationSettings.java
+++ b/src/com/android/settings/notification/NotificationSettings.java
@@ -14,57 +14,40 @@
* limitations under the License.
*/
-package com.android.settings;
+package com.android.settings.notification;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.database.ContentObserver;
-import android.graphics.drawable.Drawable;
import android.media.RingtoneManager;
-import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
-import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
+import android.preference.TwoStatePreference;
import android.provider.Settings;
-import android.util.AttributeSet;
import android.util.Log;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-import java.text.Collator;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.SoundSettings;
public class NotificationSettings extends SettingsPreferenceFragment implements
Preference.OnPreferenceChangeListener, OnPreferenceClickListener {
private static final String TAG = "NotificationSettings";
- private static final Intent APP_NOTIFICATION_PREFS_CATEGORY_INTENT
- = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_NOTIFICATION_PREFERENCES);
-
private static final String KEY_NOTIFICATION_SOUND = "notification_sound";
- private static final String KEY_NOTIFICATION_ACCESS = "manage_notification_access";
- private static final String KEY_LOCK_SCREEN_NOTIFICATIONS = "toggle_lock_screen_notifications";
- private static final String KEY_HEADS_UP = "heads_up";
private static final String KEY_NOTIFICATION_PULSE = "notification_pulse";
+ private static final String KEY_HEADS_UP = "heads_up";
+ private static final String KEY_LOCK_SCREEN_NOTIFICATIONS = "toggle_lock_screen_notifications";
+ private static final String KEY_NOTIFICATION_ACCESS = "manage_notification_access";
- private static final String KEY_SECURITY_CATEGORY = "category_security";
- private static final String KEY_APPS_CATEGORY = "category_apps";
private static final String KEY_TWEAKS_CATEGORY = "category_tweaks"; // power toys, eng only
private static final int MSG_UPDATE_SOUND_SUMMARY = 2;
@@ -74,10 +57,9 @@ public class NotificationSettings extends SettingsPreferenceFragment implements
private Preference mNotificationSoundPreference;
private Preference mNotificationAccess;
- private CheckBoxPreference mLockscreenNotifications;
- private CheckBoxPreference mHeadsUp;
- private CheckBoxPreference mNotificationPulse;
- private PreferenceGroup mAppsPreference;
+ private TwoStatePreference mLockscreenNotifications;
+ private TwoStatePreference mHeadsUp;
+ private TwoStatePreference mNotificationPulse;
private final Runnable mRingtoneLookupRunnable = new Runnable() {
@Override
@@ -104,80 +86,6 @@ public class NotificationSettings extends SettingsPreferenceFragment implements
}
};
- private final ArrayList 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() {
- synchronized (mAppNotificationInfo) {
- mAppsPreference.removeAll();
- Preference p = getPreferenceScreen().findPreference(mAppsPreference.getKey());
- final int N = mAppNotificationInfo.size();
- if (N == 0 && p != null) {
- getPreferenceScreen().removePreference(p);
- } else if (N > 0 && p == null) {
- getPreferenceScreen().addPreference(mAppsPreference);
- }
- for (int i = 0; i < N; i++) {
- final AppNotificationInfo info = mAppNotificationInfo.get(i);
- Preference pref = new AppNotificationPreference(mContext);
- pref.setTitle(info.label);
- pref.setIcon(info.icon);
- pref.setIntent(new Intent(Intent.ACTION_MAIN)
- .setClassName(info.pkg, info.name));
- mAppsPreference.addPreference(pref);
- }
- }
- }
- };
-
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -189,8 +97,6 @@ public class NotificationSettings extends SettingsPreferenceFragment implements
addPreferencesFromResource(R.xml.notification_settings);
final PreferenceScreen root = getPreferenceScreen();
- final PreferenceGroup securityCategory = (PreferenceGroup)
- root.findPreference(KEY_SECURITY_CATEGORY);
PreferenceGroup tweaksCategory = (PreferenceGroup)
root.findPreference(KEY_TWEAKS_CATEGORY);
@@ -207,18 +113,16 @@ public class NotificationSettings extends SettingsPreferenceFragment implements
refreshNotificationListeners();
mLockscreenNotifications
- = (CheckBoxPreference) root.findPreference(KEY_LOCK_SCREEN_NOTIFICATIONS);
+ = (TwoStatePreference) root.findPreference(KEY_LOCK_SCREEN_NOTIFICATIONS);
if (mLockscreenNotifications != null) {
if (!getDeviceLockscreenNotificationsEnabled()) {
- if (securityCategory != null) {
- securityCategory.removePreference(mLockscreenNotifications);
- }
+ root.removePreference(mLockscreenNotifications);
} else {
mLockscreenNotifications.setChecked(getLockscreenAllowPrivateNotifications());
}
}
- mHeadsUp = (CheckBoxPreference) findPreference(KEY_HEADS_UP);
+ mHeadsUp = (TwoStatePreference) findPreference(KEY_HEADS_UP);
if (mHeadsUp != null) {
updateHeadsUpMode(resolver);
mHeadsUp.setOnPreferenceChangeListener(this);
@@ -231,7 +135,7 @@ public class NotificationSettings extends SettingsPreferenceFragment implements
}
});
}
- mNotificationPulse = (CheckBoxPreference) findPreference(KEY_NOTIFICATION_PULSE);
+ mNotificationPulse = (TwoStatePreference) findPreference(KEY_NOTIFICATION_PULSE);
if (mNotificationPulse != null
&& getResources().getBoolean(
@@ -246,8 +150,6 @@ public class NotificationSettings extends SettingsPreferenceFragment implements
Log.e(TAG, Settings.System.NOTIFICATION_LIGHT_PULSE + " not found");
}
}
- mAppsPreference = (PreferenceGroup) root.findPreference(KEY_APPS_CATEGORY);
- root.removePreference(mAppsPreference);
}
@Override
@@ -256,11 +158,6 @@ public class NotificationSettings extends SettingsPreferenceFragment implements
refreshNotificationListeners();
lookupRingtoneNames();
- loadAppsList();
- }
-
- private void loadAppsList() {
- AsyncTask.execute(mCollectAppsRunnable);
}
@Override
@@ -330,14 +227,9 @@ public class NotificationSettings extends SettingsPreferenceFragment implements
private void refreshNotificationListeners() {
if (mNotificationAccess != null) {
- final PreferenceGroup securityCategory
- = (PreferenceGroup) getPreferenceScreen().findPreference(KEY_SECURITY_CATEGORY);
-
final int total = NotificationAccessSettings.getListenersCount(mPM);
if (total == 0) {
- if (securityCategory != null) {
- securityCategory.removePreference(mNotificationAccess);
- }
+ getPreferenceScreen().removePreference(mNotificationAccess);
} else {
final int n = getNumEnabledNotificationListeners();
if (n == 0) {
@@ -357,56 +249,4 @@ public class NotificationSettings extends SettingsPreferenceFragment implements
private void lookupRingtoneNames() {
new Thread(mRingtoneLookupRunnable).start();
}
-
- // === Per-app notification settings row ==
-
- private static class AppNotificationPreference extends Preference {
- private Intent mIntent;
-
- public AppNotificationPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
-
- setLayoutResource(R.layout.notification_app);
- }
-
- public AppNotificationPreference(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public AppNotificationPreference(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public AppNotificationPreference(Context context) {
- this(context, null);
- }
-
- public void setIntent(Intent intent) {
- mIntent = intent;
- }
-
- @Override
- protected void onBindView(View view) {
- super.onBindView(view);
-
- ImageView icon = (ImageView) view.findViewById(android.R.id.icon);
- icon.setImageDrawable(getIcon());
- TextView title = (TextView) view.findViewById(android.R.id.title);
- title.setText(getTitle());
- ImageView settingsButton = (ImageView) view.findViewById(android.R.id.button2);
- settingsButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- getContext().startActivity(mIntent);
- }
- });
- }
- }
-
- private static class AppNotificationInfo {
- public Drawable icon;
- public CharSequence label;
- public String name;
- public String pkg;
- }
}
diff --git a/src/com/android/settings/NotificationStation.java b/src/com/android/settings/notification/NotificationStation.java
similarity index 99%
rename from src/com/android/settings/NotificationStation.java
rename to src/com/android/settings/notification/NotificationStation.java
index 5083e27d810..d8a3efb8dfb 100644
--- a/src/com/android/settings/NotificationStation.java
+++ b/src/com/android/settings/notification/NotificationStation.java
@@ -14,16 +14,14 @@
* limitations under the License.
*/
-package com.android.settings;
+package com.android.settings.notification;
import android.app.Activity;
import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.service.notification.INotificationListener;
import android.app.INotificationManager;
import android.app.Notification;
-import android.service.notification.StatusBarNotification;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -37,6 +35,8 @@ import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.service.notification.INotificationListener;
+import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -48,6 +48,9 @@ import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
diff --git a/src/com/android/settings/ZenModeSettings.java b/src/com/android/settings/notification/ZenModeSettings.java
similarity index 98%
rename from src/com/android/settings/ZenModeSettings.java
rename to src/com/android/settings/notification/ZenModeSettings.java
index f6c7b8c5c70..de020db66f1 100644
--- a/src/com/android/settings/ZenModeSettings.java
+++ b/src/com/android/settings/notification/ZenModeSettings.java
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package com.android.settings;
+package com.android.settings.notification;
import android.app.ActionBar;
import android.app.Activity;
import android.content.Context;
-import android.database.ContentObserver;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.net.Uri;
@@ -28,6 +28,7 @@ import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings.Global;
+import android.provider.SearchIndexableResource;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
@@ -45,6 +46,9 @@ import android.widget.ScrollView;
import android.widget.Switch;
import android.widget.TextView;
import com.android.settings.search.BaseSearchIndexProvider;
+
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.search.Indexable;
import com.android.settings.search.SearchIndexableRaw;
diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java
index 6f8efce96f0..af04b5f5282 100644
--- a/src/com/android/settings/search/SearchIndexableResources.java
+++ b/src/com/android/settings/search/SearchIndexableResources.java
@@ -17,27 +17,27 @@
package com.android.settings.search;
import android.provider.SearchIndexableResource;
+
import com.android.settings.DataUsageSummary;
import com.android.settings.DateTimeSettings;
import com.android.settings.DevelopmentSettings;
import com.android.settings.DeviceInfoSettings;
import com.android.settings.DisplaySettings;
import com.android.settings.HomeSettings;
-import com.android.settings.NotificationSettings;
import com.android.settings.PrivacySettings;
import com.android.settings.R;
import com.android.settings.SecuritySettings;
import com.android.settings.SoundSettings;
import com.android.settings.WallpaperTypeSettings;
import com.android.settings.WirelessSettings;
-import com.android.settings.ZenModeSettings;
import com.android.settings.accessibility.AccessibilitySettings;
import com.android.settings.bluetooth.BluetoothSettings;
import com.android.settings.deviceinfo.Memory;
import com.android.settings.fuelgauge.PowerUsageSummary;
import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
import com.android.settings.location.LocationSettings;
-import com.android.settings.net.DataUsageMeteredSettings;
+import com.android.settings.notification.NotificationSettings;
+import com.android.settings.notification.ZenModeSettings;
import com.android.settings.print.PrintSettingsFragment;
import com.android.settings.users.UserSettings;
import com.android.settings.wifi.WifiSettings;