Refinements to notification controls in Settings.

- the switch is now a checkbox
- a dialog is posted to explain the ramifications of
  disabling notifications
- the user may no longer disable notifications from system
  apps (signed with the system cert); this is roughly the
  same as the constraint on disabling packages (with the
  exception that launchers may not be disabled entirely, but
  their notifications may still be disabled).

Bug: 6493120 (checkbox)
Bug: 6448988 (confirmation dialog & protect system apps)
Change-Id: I6c7a47ba2eb943936d7f59cfc807a4390acc980d
diff --git a/res/layout/installed_app_details.xml b/res/layout/installed_app_details.xml
index 099df64..c41bdb6 100644
--- a/res/layout/installed_app_details.xml
+++ b/res/layout/installed_app_details.xml
@@ -50,35 +50,12 @@
                 android:id="@+id/control_buttons_panel"/>
 
             <!-- Ban notifications for this package -->
-            <LinearLayout
-                android:orientation="horizontal"
-                android:layout_width="match_parent"
+            <CheckBox android:id="@+id/notification_switch"
+                android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:minHeight="48dp"
-                android:paddingLeft="6dip"
-                android:paddingTop="8dip"
-                android:gravity="center_vertical">
-
-                <TextView
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:paddingRight="14dip"
-                    android:text="@string/app_notifications_switch_label"
-                    android:singleLine="true"
-                    android:textAppearance="?android:attr/textAppearanceMedium"
-                    android:ellipsize="marquee"
-                    android:fadingEdge="horizontal" />
-                <Switch android:id="@+id/notification_switch"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="center"
-                    android:padding="8dip"
-                    android:switchTextAppearance="@style/TextAppearance.Switch"
-                    android:textOn="@string/app_notifications_switch_on"
-                    android:textOff="@string/app_notifications_switch_off"
-                    android:focusable="false"
-                    android:clickable="true" />
-            </LinearLayout>
+                android:layout_marginLeft="12dip"
+                android:layout_gravity="left"
+                android:text="@string/app_notifications_switch_label" />
 
         </LinearLayout>
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3dff4c8..df10d97 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2508,6 +2508,12 @@
     <!-- [CHAR LIMIT=200] Manage applications, text for dialog when disabling apps -->
     <string name="app_disable_dlg_text">If you disable a built-in app, other apps
         may misbehave.</string>
+    <!-- [CHAR LIMIT=30] Manage applications, title for dialog when disabling notifications for an app -->
+    <string name="app_disable_notifications_dlg_title">Turn off notifications?</string>
+    <!-- [CHAR LIMIT=200] Manage applications, text for dialog when disabling notifications for an app -->
+    <string name="app_disable_notifications_dlg_text">
+        If you turn off notifications for this app, you may miss important alerts and updates.
+    </string>
 
     <!-- [CHAR LIMIT=25] Services settings screen, setting option name for the user to go to the screen to view app storage use -->
     <string name="storageuse_settings_title">Storage use</string>
@@ -4025,13 +4031,8 @@
     <!-- User removal confirmation message [CHAR LIMIT=none] -->
     <string name="user_confirm_remove_message">Are you sure you want to remove the user and all associated data from the device?</string>
 
-    <!-- Label for "notifications enabled" switch in app details [CHAR LIMIT=20] -->
-    <string name="app_notifications_switch_label">Notifications</string>
-    <!-- Label for enabled state of "notifications enabled" switch in app details [CHAR LIMIT=10] -->
-    <string name="app_notifications_switch_on">Enabled</string>
-    <!-- Label for disabled state "notifications enabled" switch in app details [CHAR LIMIT=10] -->
-    <string name="app_notifications_switch_off">Disabled</string>
-
+    <!-- Label for are-notifications-enabled checkbox in app details [CHAR LIMIT=20] -->
+    <string name="app_notifications_switch_label">Show notifications</string>
 
     <!--  Help URLs for some screens. Not specified here. Specified in product overlays --><skip/>
     <!-- Help menu label [CHAR LIMIT=20] -->
diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java
index bd77118..c2e358c 100644
--- a/src/com/android/settings/applications/InstalledAppDetails.java
+++ b/src/com/android/settings/applications/InstalledAppDetails.java
@@ -125,7 +125,7 @@
     private Button mForceStopButton;
     private Button mClearDataButton;
     private Button mMoveAppButton;
-    private Switch mNotificationSwitch;
+    private CompoundButton mNotificationSwitch;
 
     private PackageMoveObserver mPackageMoveObserver;
     
@@ -161,6 +161,7 @@
     private static final int DLG_FORCE_STOP = DLG_BASE + 5;
     private static final int DLG_MOVE_FAILED = DLG_BASE + 6;
     private static final int DLG_DISABLE = DLG_BASE + 7;
+    private static final int DLG_DISABLE_NOTIFICATIONS = DLG_BASE + 8;
     
     private Handler mHandler = new Handler() {
         public void handleMessage(Message msg) {
@@ -279,6 +280,16 @@
         }
     }
 
+    private boolean isThisASystemPackage() {
+        try {
+            PackageInfo sys = mPm.getPackageInfo("android", PackageManager.GET_SIGNATURES);
+            return (mPackageInfo != null && mPackageInfo.signatures != null &&
+                    sys.signatures[0].equals(mPackageInfo.signatures[0]));
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+
     private void initUninstallButtons() {
         mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
         boolean enabled = true;
@@ -298,9 +309,7 @@
                         intent.addCategory(Intent.CATEGORY_HOME);
                         intent.setPackage(mAppEntry.info.packageName);
                         List<ResolveInfo> homes = mPm.queryIntentActivities(intent, 0);
-                        if ((homes != null && homes.size() > 0) ||
-                                (mPackageInfo != null && mPackageInfo.signatures != null &&
-                                        sys.signatures[0].equals(mPackageInfo.signatures[0]))) {
+                        if ((homes != null && homes.size() > 0) || isThisASystemPackage()) {
                             // Disable button for core system applications.
                             mUninstallButton.setText(R.string.disable_text);
                         } else if (mAppEntry.info.enabled) {
@@ -340,7 +349,12 @@
             // this does not bode well
         }
         mNotificationSwitch.setChecked(enabled);
-        mNotificationSwitch.setOnCheckedChangeListener(this);
+        if (isThisASystemPackage()) {
+            mNotificationSwitch.setEnabled(false);
+        } else {
+            mNotificationSwitch.setEnabled(true);
+            mNotificationSwitch.setOnCheckedChangeListener(this);
+        }
     }
 
     /** Called when the activity is first created. */
@@ -395,7 +409,7 @@
         mAskCompatibilityCB = (CheckBox)view.findViewById(R.id.ask_compatibility_cb);
         mEnableCompatibilityCB = (CheckBox)view.findViewById(R.id.enable_compatibility_cb);
         
-        mNotificationSwitch = (Switch) view.findViewById(R.id.notification_switch);
+        mNotificationSwitch = (CompoundButton) view.findViewById(R.id.notification_switch);
 
         return view;
     }
@@ -851,6 +865,26 @@
                     })
                     .setNegativeButton(R.string.dlg_cancel, null)
                     .create();
+                case DLG_DISABLE_NOTIFICATIONS:
+                    return new AlertDialog.Builder(getActivity())
+                    .setTitle(getActivity().getText(R.string.app_disable_notifications_dlg_title))
+                    .setIcon(android.R.drawable.ic_dialog_alert)
+                    .setMessage(getActivity().getText(R.string.app_disable_notifications_dlg_text))
+                    .setPositiveButton(R.string.dlg_ok,
+                        new DialogInterface.OnClickListener() {
+                        public void onClick(DialogInterface dialog, int which) {
+                            // Disable the package's notifications
+                            getOwner().setNotificationsEnabled(false);
+                        }
+                    })
+                    .setNegativeButton(R.string.dlg_cancel,
+                        new DialogInterface.OnClickListener() {
+                        public void onClick(DialogInterface dialog, int which) {
+                            // Re-enable the checkbox
+                            getOwner().mNotificationSwitch.setChecked(true);
+                        }
+                    })
+                    .create();
             }
             throw new IllegalArgumentException("unknown id " + id);
         }
@@ -905,7 +939,7 @@
                     Activity.RESULT_CANCELED, null, null);
         }
     }
-    
+
     static class DisableChanger extends AsyncTask<Object, Object, Object> {
         final PackageManager mPm;
         final WeakReference<InstalledAppDetails> mActivity;
@@ -926,6 +960,18 @@
         }
     }
 
+    private void setNotificationsEnabled(boolean enabled) {
+        String packageName = mAppEntry.info.packageName;
+        INotificationManager nm = INotificationManager.Stub.asInterface(
+                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        try {
+            final boolean enable = mNotificationSwitch.isChecked();
+            nm.setNotificationsEnabledForPackage(packageName, enabled);
+        } catch (android.os.RemoteException ex) {
+            mNotificationSwitch.setChecked(!enabled); // revert
+        }
+    }
+
     /*
      * Method implementing functionality of buttons clicked
      * @see android.view.View.OnClickListener#onClick(android.view.View)
@@ -1003,12 +1049,10 @@
             am.setPackageScreenCompatMode(packageName, isChecked ?
                     ActivityManager.COMPAT_MODE_ENABLED : ActivityManager.COMPAT_MODE_DISABLED);
         } else if (buttonView == mNotificationSwitch) {
-            INotificationManager nm = INotificationManager.Stub.asInterface(
-                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
-            try {
-                nm.setNotificationsEnabledForPackage(packageName, isChecked);
-            } catch (android.os.RemoteException ex) {
-                mNotificationSwitch.setChecked(!isChecked); // revert
+            if (!isChecked) {
+                showDialogInner(DLG_DISABLE_NOTIFICATIONS, 0);
+            } else {
+                setNotificationsEnabled(true);
             }
         }
     }