Home | History | Annotate | Download | only in notification
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.settings.notification;
     18 
     19 import android.app.Notification;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.content.pm.ActivityInfo;
     23 import android.content.pm.ApplicationInfo;
     24 import android.content.pm.PackageInfo;
     25 import android.content.pm.PackageManager;
     26 import android.content.pm.PackageManager.NameNotFoundException;
     27 import android.content.pm.ResolveInfo;
     28 import android.os.Bundle;
     29 import android.os.UserHandle;
     30 import android.preference.Preference;
     31 import android.preference.Preference.OnPreferenceChangeListener;
     32 import android.preference.Preference.OnPreferenceClickListener;
     33 import android.preference.SwitchPreference;
     34 import android.provider.Settings;
     35 import android.text.TextUtils;
     36 import android.util.ArrayMap;
     37 import android.util.Log;
     38 import android.widget.Toast;
     39 
     40 import com.android.internal.logging.MetricsLogger;
     41 import com.android.internal.widget.LockPatternUtils;
     42 import com.android.settings.AppHeader;
     43 import com.android.settings.R;
     44 import com.android.settings.SettingsPreferenceFragment;
     45 import com.android.settings.Utils;
     46 import com.android.settings.applications.AppInfoBase;
     47 import com.android.settings.applications.AppInfoWithHeader;
     48 import com.android.settings.notification.NotificationBackend.AppRow;
     49 
     50 import java.util.List;
     51 
     52 /** These settings are per app, so should not be returned in global search results. */
     53 public class AppNotificationSettings extends SettingsPreferenceFragment {
     54     private static final String TAG = "AppNotificationSettings";
     55     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     56 
     57     private static final String KEY_BLOCK = "block";
     58     private static final String KEY_PRIORITY = "priority";
     59     private static final String KEY_PEEKABLE = "peekable";
     60     private static final String KEY_SENSITIVE = "sensitive";
     61     private static final String KEY_APP_SETTINGS = "app_settings";
     62 
     63     private static final Intent APP_NOTIFICATION_PREFS_CATEGORY_INTENT
     64             = new Intent(Intent.ACTION_MAIN)
     65                 .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES);
     66 
     67     private final NotificationBackend mBackend = new NotificationBackend();
     68 
     69     private Context mContext;
     70     private SwitchPreference mBlock;
     71     private SwitchPreference mPriority;
     72     private SwitchPreference mPeekable;
     73     private SwitchPreference mSensitive;
     74     private AppRow mAppRow;
     75     private boolean mCreated;
     76     private boolean mIsSystemPackage;
     77     private int mUid;
     78 
     79     @Override
     80     public void onActivityCreated(Bundle savedInstanceState) {
     81         super.onActivityCreated(savedInstanceState);
     82         if (DEBUG) Log.d(TAG, "onActivityCreated mCreated=" + mCreated);
     83         if (mCreated) {
     84             Log.w(TAG, "onActivityCreated: ignoring duplicate call");
     85             return;
     86         }
     87         mCreated = true;
     88         if (mAppRow == null) return;
     89         AppHeader.createAppHeader(this, mAppRow.icon, mAppRow.label,
     90                 AppInfoWithHeader.getInfoIntent(this, mAppRow.pkg));
     91     }
     92 
     93     @Override
     94     protected int getMetricsCategory() {
     95         return MetricsLogger.NOTIFICATION_APP_NOTIFICATION;
     96     }
     97 
     98     @Override
     99     public void onCreate(Bundle savedInstanceState) {
    100         super.onCreate(savedInstanceState);
    101         mContext = getActivity();
    102         Intent intent = getActivity().getIntent();
    103         Bundle args = getArguments();
    104         if (DEBUG) Log.d(TAG, "onCreate getIntent()=" + intent);
    105         if (intent == null && args == null) {
    106             Log.w(TAG, "No intent");
    107             toastAndFinish();
    108             return;
    109         }
    110 
    111         final String pkg = args != null && args.containsKey(AppInfoBase.ARG_PACKAGE_NAME)
    112                 ? args.getString(AppInfoBase.ARG_PACKAGE_NAME)
    113                 : intent.getStringExtra(Settings.EXTRA_APP_PACKAGE);
    114         mUid = args != null && args.containsKey(AppInfoBase.ARG_PACKAGE_UID)
    115                 ? args.getInt(AppInfoBase.ARG_PACKAGE_UID)
    116                 : intent.getIntExtra(Settings.EXTRA_APP_UID, -1);
    117         if (mUid == -1 || TextUtils.isEmpty(pkg)) {
    118             Log.w(TAG, "Missing extras: " + Settings.EXTRA_APP_PACKAGE + " was " + pkg + ", "
    119                     + Settings.EXTRA_APP_UID + " was " + mUid);
    120             toastAndFinish();
    121             return;
    122         }
    123 
    124         if (DEBUG) Log.d(TAG, "Load details for pkg=" + pkg + " uid=" + mUid);
    125         final PackageManager pm = getPackageManager();
    126         final PackageInfo info = findPackageInfo(pm, pkg, mUid);
    127         if (info == null) {
    128             Log.w(TAG, "Failed to find package info: " + Settings.EXTRA_APP_PACKAGE + " was " + pkg
    129                     + ", " + Settings.EXTRA_APP_UID + " was " + mUid);
    130             toastAndFinish();
    131             return;
    132         }
    133         mIsSystemPackage = Utils.isSystemPackage(pm, info);
    134 
    135         addPreferencesFromResource(R.xml.app_notification_settings);
    136         mBlock = (SwitchPreference) findPreference(KEY_BLOCK);
    137         mPriority = (SwitchPreference) findPreference(KEY_PRIORITY);
    138         mPeekable = (SwitchPreference) findPreference(KEY_PEEKABLE);
    139         mSensitive = (SwitchPreference) findPreference(KEY_SENSITIVE);
    140 
    141         mAppRow = mBackend.loadAppRow(pm, info.applicationInfo);
    142 
    143         // load settings intent
    144         ArrayMap<String, AppRow> rows = new ArrayMap<String, AppRow>();
    145         rows.put(mAppRow.pkg, mAppRow);
    146         collectConfigActivities(getPackageManager(), rows);
    147 
    148         mBlock.setChecked(mAppRow.banned);
    149         updateDependents(mAppRow.banned);
    150         mPriority.setChecked(mAppRow.priority);
    151         mPeekable.setChecked(mAppRow.peekable);
    152         mSensitive.setChecked(mAppRow.sensitive);
    153 
    154         mBlock.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
    155             @Override
    156             public boolean onPreferenceChange(Preference preference, Object newValue) {
    157                 final boolean banned = (Boolean) newValue;
    158                 if (banned) {
    159                     MetricsLogger.action(getActivity(), MetricsLogger.ACTION_BAN_APP_NOTES, pkg);
    160                 }
    161                 final boolean success =  mBackend.setNotificationsBanned(pkg, mUid, banned);
    162                 if (success) {
    163                     updateDependents(banned);
    164                 }
    165                 return success;
    166             }
    167         });
    168 
    169         mPriority.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
    170             @Override
    171             public boolean onPreferenceChange(Preference preference, Object newValue) {
    172                 final boolean priority = (Boolean) newValue;
    173                 return mBackend.setHighPriority(pkg, mUid, priority);
    174             }
    175         });
    176 
    177         mPeekable.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
    178             @Override
    179             public boolean onPreferenceChange(Preference preference, Object newValue) {
    180                 final boolean peekable = (Boolean) newValue;
    181                 return mBackend.setPeekable(pkg, mUid, peekable);
    182             }
    183         });
    184 
    185         mSensitive.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
    186             @Override
    187             public boolean onPreferenceChange(Preference preference, Object newValue) {
    188                 final boolean sensitive = (Boolean) newValue;
    189                 return mBackend.setSensitive(pkg, mUid, sensitive);
    190             }
    191         });
    192 
    193         if (mAppRow.settingsIntent != null) {
    194             findPreference(KEY_APP_SETTINGS).setOnPreferenceClickListener(
    195                     new OnPreferenceClickListener() {
    196                 @Override
    197                 public boolean onPreferenceClick(Preference preference) {
    198                     mContext.startActivity(mAppRow.settingsIntent);
    199                     return true;
    200                 }
    201             });
    202         } else {
    203             removePreference(KEY_APP_SETTINGS);
    204         }
    205     }
    206 
    207     @Override
    208     public void onResume() {
    209         super.onResume();
    210         if (mUid != -1 && getPackageManager().getPackagesForUid(mUid) == null) {
    211             // App isn't around anymore, must have been removed.
    212             finish();
    213         }
    214     }
    215 
    216     private void updateDependents(boolean banned) {
    217         final boolean lockscreenSecure = new LockPatternUtils(getActivity()).isSecure(
    218                 UserHandle.myUserId());
    219         final boolean lockscreenNotificationsEnabled = getLockscreenNotificationsEnabled();
    220         final boolean allowPrivate = getLockscreenAllowPrivateNotifications();
    221 
    222         setVisible(mBlock, !mIsSystemPackage);
    223         setVisible(mPriority, mIsSystemPackage || !banned);
    224         setVisible(mPeekable, mIsSystemPackage || !banned);
    225         setVisible(mSensitive, mIsSystemPackage || !banned && lockscreenSecure
    226                 && lockscreenNotificationsEnabled && allowPrivate);
    227     }
    228 
    229     private void setVisible(Preference p, boolean visible) {
    230         final boolean isVisible = getPreferenceScreen().findPreference(p.getKey()) != null;
    231         if (isVisible == visible) return;
    232         if (visible) {
    233             getPreferenceScreen().addPreference(p);
    234         } else {
    235             getPreferenceScreen().removePreference(p);
    236         }
    237     }
    238 
    239     private boolean getLockscreenNotificationsEnabled() {
    240         return Settings.Secure.getInt(getContentResolver(),
    241                 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0) != 0;
    242     }
    243 
    244     private boolean getLockscreenAllowPrivateNotifications() {
    245         return Settings.Secure.getInt(getContentResolver(),
    246                 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0) != 0;
    247     }
    248 
    249     private void toastAndFinish() {
    250         Toast.makeText(mContext, R.string.app_not_found_dlg_text, Toast.LENGTH_SHORT).show();
    251         getActivity().finish();
    252     }
    253 
    254     private static PackageInfo findPackageInfo(PackageManager pm, String pkg, int uid) {
    255         final String[] packages = pm.getPackagesForUid(uid);
    256         if (packages != null && pkg != null) {
    257             final int N = packages.length;
    258             for (int i = 0; i < N; i++) {
    259                 final String p = packages[i];
    260                 if (pkg.equals(p)) {
    261                     try {
    262                         return pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES);
    263                     } catch (NameNotFoundException e) {
    264                         Log.w(TAG, "Failed to load package " + pkg, e);
    265                     }
    266                 }
    267             }
    268         }
    269         return null;
    270     }
    271 
    272     public static List<ResolveInfo> queryNotificationConfigActivities(PackageManager pm) {
    273         if (DEBUG) Log.d(TAG, "APP_NOTIFICATION_PREFS_CATEGORY_INTENT is "
    274                 + APP_NOTIFICATION_PREFS_CATEGORY_INTENT);
    275         final List<ResolveInfo> resolveInfos = pm.queryIntentActivities(
    276                 APP_NOTIFICATION_PREFS_CATEGORY_INTENT,
    277                 0 //PackageManager.MATCH_DEFAULT_ONLY
    278         );
    279         return resolveInfos;
    280     }
    281 
    282     public static void collectConfigActivities(PackageManager pm, ArrayMap<String, AppRow> rows) {
    283         final List<ResolveInfo> resolveInfos = queryNotificationConfigActivities(pm);
    284         applyConfigActivities(pm, rows, resolveInfos);
    285     }
    286 
    287     public static void applyConfigActivities(PackageManager pm, ArrayMap<String, AppRow> rows,
    288             List<ResolveInfo> resolveInfos) {
    289         if (DEBUG) Log.d(TAG, "Found " + resolveInfos.size() + " preference activities"
    290                 + (resolveInfos.size() == 0 ? " ;_;" : ""));
    291         for (ResolveInfo ri : resolveInfos) {
    292             final ActivityInfo activityInfo = ri.activityInfo;
    293             final ApplicationInfo appInfo = activityInfo.applicationInfo;
    294             final AppRow row = rows.get(appInfo.packageName);
    295             if (row == null) {
    296                 if (DEBUG) Log.v(TAG, "Ignoring notification preference activity ("
    297                         + activityInfo.name + ") for unknown package "
    298                         + activityInfo.packageName);
    299                 continue;
    300             }
    301             if (row.settingsIntent != null) {
    302                 if (DEBUG) Log.v(TAG, "Ignoring duplicate notification preference activity ("
    303                         + activityInfo.name + ") for package "
    304                         + activityInfo.packageName);
    305                 continue;
    306             }
    307             row.settingsIntent = new Intent(APP_NOTIFICATION_PREFS_CATEGORY_INTENT)
    308                     .setClassName(activityInfo.packageName, activityInfo.name);
    309         }
    310     }
    311 }
    312