Home | History | Annotate | Download | only in statusbar
      1 /*
      2  * Copyright (C) 2010 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.systemui.statusbar;
     18 
     19 import android.animation.Animator;
     20 import android.animation.AnimatorListenerAdapter;
     21 import android.animation.TimeInterpolator;
     22 import android.app.ActivityManager;
     23 import android.app.ActivityManagerNative;
     24 import android.app.Notification;
     25 import android.app.NotificationManager;
     26 import android.app.PendingIntent;
     27 import android.app.TaskStackBuilder;
     28 import android.app.admin.DevicePolicyManager;
     29 import android.content.BroadcastReceiver;
     30 import android.content.ComponentName;
     31 import android.content.Context;
     32 import android.content.Intent;
     33 import android.content.IntentFilter;
     34 import android.content.pm.ApplicationInfo;
     35 import android.content.pm.PackageManager;
     36 import android.content.pm.PackageManager.NameNotFoundException;
     37 import android.content.pm.ResolveInfo;
     38 import android.content.pm.UserInfo;
     39 import android.content.res.Configuration;
     40 import android.content.res.Resources;
     41 import android.database.ContentObserver;
     42 import android.graphics.PorterDuff;
     43 import android.graphics.drawable.Drawable;
     44 import android.os.AsyncTask;
     45 import android.os.Build;
     46 import android.os.Handler;
     47 import android.os.IBinder;
     48 import android.os.Message;
     49 import android.os.PowerManager;
     50 import android.os.RemoteException;
     51 import android.os.ServiceManager;
     52 import android.os.UserHandle;
     53 import android.os.UserManager;
     54 import android.provider.Settings;
     55 import android.service.dreams.DreamService;
     56 import android.service.dreams.IDreamManager;
     57 import android.service.notification.NotificationListenerService;
     58 import android.service.notification.NotificationListenerService.RankingMap;
     59 import android.service.notification.StatusBarNotification;
     60 import android.text.TextUtils;
     61 import android.util.Log;
     62 import android.util.SparseArray;
     63 import android.util.SparseBooleanArray;
     64 import android.view.Display;
     65 import android.view.IWindowManager;
     66 import android.view.LayoutInflater;
     67 import android.view.MotionEvent;
     68 import android.view.View;
     69 import android.view.ViewAnimationUtils;
     70 import android.view.ViewGroup;
     71 import android.view.ViewGroup.LayoutParams;
     72 import android.view.ViewStub;
     73 import android.view.WindowManager;
     74 import android.view.WindowManagerGlobal;
     75 import android.view.accessibility.AccessibilityManager;
     76 import android.view.animation.AnimationUtils;
     77 import android.widget.DateTimeView;
     78 import android.widget.ImageView;
     79 import android.widget.LinearLayout;
     80 import android.widget.RemoteViews;
     81 import android.widget.TextView;
     82 
     83 import com.android.internal.statusbar.IStatusBarService;
     84 import com.android.internal.statusbar.StatusBarIcon;
     85 import com.android.internal.statusbar.StatusBarIconList;
     86 import com.android.internal.util.NotificationColorUtil;
     87 import com.android.internal.widget.LockPatternUtils;
     88 import com.android.systemui.R;
     89 import com.android.systemui.RecentsComponent;
     90 import com.android.systemui.SearchPanelView;
     91 import com.android.systemui.SwipeHelper;
     92 import com.android.systemui.SystemUI;
     93 import com.android.systemui.statusbar.NotificationData.Entry;
     94 import com.android.systemui.statusbar.phone.KeyguardTouchDelegate;
     95 import com.android.systemui.statusbar.phone.NavigationBarView;
     96 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
     97 import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
     98 import com.android.systemui.statusbar.policy.PreviewInflater;
     99 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
    100 
    101 import java.util.ArrayList;
    102 import java.util.List;
    103 import java.util.Locale;
    104 
    105 import static com.android.keyguard.KeyguardHostView.OnDismissAction;
    106 
    107 public abstract class BaseStatusBar extends SystemUI implements
    108         CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
    109         RecentsComponent.Callbacks, ExpandableNotificationRow.ExpansionLogger,
    110         NotificationData.Environment {
    111     public static final String TAG = "StatusBar";
    112     public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    113     public static final boolean MULTIUSER_DEBUG = false;
    114 
    115     protected static final int MSG_SHOW_RECENT_APPS = 1019;
    116     protected static final int MSG_HIDE_RECENT_APPS = 1020;
    117     protected static final int MSG_TOGGLE_RECENTS_APPS = 1021;
    118     protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
    119     protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
    120     protected static final int MSG_SHOW_NEXT_AFFILIATED_TASK = 1024;
    121     protected static final int MSG_SHOW_PREV_AFFILIATED_TASK = 1025;
    122     protected static final int MSG_CLOSE_SEARCH_PANEL = 1027;
    123     protected static final int MSG_SHOW_HEADS_UP = 1028;
    124     protected static final int MSG_HIDE_HEADS_UP = 1029;
    125     protected static final int MSG_ESCALATE_HEADS_UP = 1030;
    126     protected static final int MSG_DECAY_HEADS_UP = 1031;
    127 
    128     protected static final boolean ENABLE_HEADS_UP = true;
    129     // scores above this threshold should be displayed in heads up mode.
    130     protected static final int INTERRUPTION_THRESHOLD = 10;
    131     protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
    132 
    133     // Should match the value in PhoneWindowManager
    134     public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
    135 
    136     public static final int EXPANDED_LEAVE_ALONE = -10000;
    137     public static final int EXPANDED_FULL_OPEN = -10001;
    138 
    139     private static final int HIDDEN_NOTIFICATION_ID = 10000;
    140     private static final String BANNER_ACTION_CANCEL =
    141             "com.android.systemui.statusbar.banner_action_cancel";
    142     private static final String BANNER_ACTION_SETUP =
    143             "com.android.systemui.statusbar.banner_action_setup";
    144 
    145     protected CommandQueue mCommandQueue;
    146     protected IStatusBarService mBarService;
    147     protected H mHandler = createHandler();
    148 
    149     // all notifications
    150     protected NotificationData mNotificationData;
    151     protected NotificationStackScrollLayout mStackScroller;
    152 
    153     // for heads up notifications
    154     protected HeadsUpNotificationView mHeadsUpNotificationView;
    155     protected int mHeadsUpNotificationDecay;
    156 
    157     // used to notify status bar for suppressing notification LED
    158     protected boolean mPanelSlightlyVisible;
    159 
    160     // Search panel
    161     protected SearchPanelView mSearchPanelView;
    162 
    163     protected int mCurrentUserId = 0;
    164     final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
    165 
    166     protected int mLayoutDirection = -1; // invalid
    167     protected AccessibilityManager mAccessibilityManager;
    168 
    169     // on-screen navigation buttons
    170     protected NavigationBarView mNavigationBarView = null;
    171     private Locale mLocale;
    172     private float mFontScale;
    173 
    174     protected boolean mUseHeadsUp = false;
    175     protected boolean mHeadsUpTicker = false;
    176     protected boolean mDisableNotificationAlerts = false;
    177 
    178     protected DevicePolicyManager mDevicePolicyManager;
    179     protected IDreamManager mDreamManager;
    180     PowerManager mPowerManager;
    181     protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
    182     protected int mRowMinHeight;
    183     protected int mRowMaxHeight;
    184 
    185     // public mode, private notifications, etc
    186     private boolean mLockscreenPublicMode = false;
    187     private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
    188     private NotificationColorUtil mNotificationColorUtil;
    189 
    190     private UserManager mUserManager;
    191 
    192     // UI-specific methods
    193 
    194     /**
    195      * Create all windows necessary for the status bar (including navigation, overlay panels, etc)
    196      * and add them to the window manager.
    197      */
    198     protected abstract void createAndAddWindows();
    199 
    200     protected WindowManager mWindowManager;
    201     protected IWindowManager mWindowManagerService;
    202 
    203     protected abstract void refreshLayout(int layoutDirection);
    204 
    205     protected Display mDisplay;
    206 
    207     private boolean mDeviceProvisioned = false;
    208 
    209     private RecentsComponent mRecents;
    210 
    211     protected int mZenMode;
    212 
    213     // which notification is currently being longpress-examined by the user
    214     private NotificationGuts mNotificationGutsExposed;
    215 
    216     private TimeInterpolator mLinearOutSlowIn, mFastOutLinearIn;
    217 
    218     /**
    219      * The {@link StatusBarState} of the status bar.
    220      */
    221     protected int mState;
    222     protected boolean mBouncerShowing;
    223     protected boolean mShowLockscreenNotifications;
    224 
    225     protected NotificationOverflowContainer mKeyguardIconOverflowContainer;
    226     protected DismissView mDismissView;
    227     protected EmptyShadeView mEmptyShadeView;
    228 
    229     @Override  // NotificationData.Environment
    230     public boolean isDeviceProvisioned() {
    231         return mDeviceProvisioned;
    232     }
    233 
    234     protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
    235         @Override
    236         public void onChange(boolean selfChange) {
    237             final boolean provisioned = 0 != Settings.Global.getInt(
    238                     mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
    239             if (provisioned != mDeviceProvisioned) {
    240                 mDeviceProvisioned = provisioned;
    241                 updateNotifications();
    242             }
    243             final int mode = Settings.Global.getInt(mContext.getContentResolver(),
    244                     Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
    245             setZenMode(mode);
    246 
    247             updateLockscreenNotificationSetting();
    248         }
    249     };
    250 
    251     private final ContentObserver mLockscreenSettingsObserver = new ContentObserver(mHandler) {
    252         @Override
    253         public void onChange(boolean selfChange) {
    254             // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
    255             // so we just dump our cache ...
    256             mUsersAllowingPrivateNotifications.clear();
    257             // ... and refresh all the notifications
    258             updateNotifications();
    259         }
    260     };
    261 
    262     private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
    263         @Override
    264         public boolean onClickHandler(
    265                 final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
    266             if (DEBUG) {
    267                 Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
    268             }
    269             // The intent we are sending is for the application, which
    270             // won't have permission to immediately start an activity after
    271             // the user switches to home.  We know it is safe to do at this
    272             // point, so make sure new activity switches are now allowed.
    273             try {
    274                 ActivityManagerNative.getDefault().resumeAppSwitches();
    275             } catch (RemoteException e) {
    276             }
    277             final boolean isActivity = pendingIntent.isActivity();
    278             if (isActivity) {
    279                 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
    280                 final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
    281                         mContext, pendingIntent.getIntent(), mCurrentUserId);
    282                 dismissKeyguardThenExecute(new OnDismissAction() {
    283                     @Override
    284                     public boolean onDismiss() {
    285                         if (keyguardShowing && !afterKeyguardGone) {
    286                             try {
    287                                 ActivityManagerNative.getDefault()
    288                                         .keyguardWaitingForActivityDrawn();
    289                             } catch (RemoteException e) {
    290                             }
    291                         }
    292 
    293                         boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
    294                         overrideActivityPendingAppTransition(keyguardShowing && !afterKeyguardGone);
    295 
    296                         // close the shade if it was open
    297                         if (handled) {
    298                             animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
    299                             visibilityChanged(false);
    300                         }
    301                         // Wait for activity start.
    302                         return handled;
    303                     }
    304                 }, afterKeyguardGone);
    305                 return true;
    306             } else {
    307                 return super.onClickHandler(view, pendingIntent, fillInIntent);
    308             }
    309         }
    310 
    311         private boolean superOnClickHandler(View view, PendingIntent pendingIntent,
    312                 Intent fillInIntent) {
    313             return super.onClickHandler(view, pendingIntent, fillInIntent);
    314         }
    315     };
    316 
    317     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
    318         @Override
    319         public void onReceive(Context context, Intent intent) {
    320             String action = intent.getAction();
    321             if (Intent.ACTION_USER_SWITCHED.equals(action)) {
    322                 mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
    323                 updateCurrentProfilesCache();
    324                 if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
    325 
    326                 updateLockscreenNotificationSetting();
    327 
    328                 userSwitched(mCurrentUserId);
    329             } else if (Intent.ACTION_USER_ADDED.equals(action)) {
    330                 updateCurrentProfilesCache();
    331             } else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(
    332                     action)) {
    333                 mUsersAllowingPrivateNotifications.clear();
    334                 updateLockscreenNotificationSetting();
    335                 updateNotifications();
    336             } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) {
    337                 NotificationManager noMan = (NotificationManager)
    338                         mContext.getSystemService(Context.NOTIFICATION_SERVICE);
    339                 noMan.cancel(HIDDEN_NOTIFICATION_ID);
    340 
    341                 Settings.Secure.putInt(mContext.getContentResolver(),
    342                         Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
    343                 if (BANNER_ACTION_SETUP.equals(action)) {
    344                     animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
    345                     mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION)
    346                             .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    347 
    348                     );
    349                 }
    350             }
    351         }
    352     };
    353 
    354     private final NotificationListenerService mNotificationListener =
    355             new NotificationListenerService() {
    356         @Override
    357         public void onListenerConnected() {
    358             if (DEBUG) Log.d(TAG, "onListenerConnected");
    359             final StatusBarNotification[] notifications = getActiveNotifications();
    360             final RankingMap currentRanking = getCurrentRanking();
    361             mHandler.post(new Runnable() {
    362                 @Override
    363                 public void run() {
    364                     for (StatusBarNotification sbn : notifications) {
    365                         addNotification(sbn, currentRanking);
    366                     }
    367                 }
    368             });
    369         }
    370 
    371         @Override
    372         public void onNotificationPosted(final StatusBarNotification sbn,
    373                 final RankingMap rankingMap) {
    374             if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
    375             mHandler.post(new Runnable() {
    376                 @Override
    377                 public void run() {
    378                     Notification n = sbn.getNotification();
    379                     boolean isUpdate = mNotificationData.get(sbn.getKey()) != null
    380                             || isHeadsUp(sbn.getKey());
    381 
    382                     // Ignore children of notifications that have a summary, since we're not
    383                     // going to show them anyway. This is true also when the summary is canceled,
    384                     // because children are automatically canceled by NoMan in that case.
    385                     if (n.isGroupChild() &&
    386                             mNotificationData.isGroupWithSummary(sbn.getGroupKey())) {
    387                         if (DEBUG) {
    388                             Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
    389                         }
    390 
    391                         // Remove existing notification to avoid stale data.
    392                         if (isUpdate) {
    393                             removeNotification(sbn.getKey(), rankingMap);
    394                         } else {
    395                             mNotificationData.updateRanking(rankingMap);
    396                         }
    397                         return;
    398                     }
    399                     if (isUpdate) {
    400                         updateNotification(sbn, rankingMap);
    401                     } else {
    402                         addNotification(sbn, rankingMap);
    403                     }
    404                 }
    405             });
    406         }
    407 
    408         @Override
    409         public void onNotificationRemoved(final StatusBarNotification sbn,
    410                 final RankingMap rankingMap) {
    411             if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
    412             mHandler.post(new Runnable() {
    413                 @Override
    414                 public void run() {
    415                     removeNotification(sbn.getKey(), rankingMap);
    416                 }
    417             });
    418         }
    419 
    420         @Override
    421         public void onNotificationRankingUpdate(final RankingMap rankingMap) {
    422             if (DEBUG) Log.d(TAG, "onRankingUpdate");
    423             mHandler.post(new Runnable() {
    424                 @Override
    425                 public void run() {
    426                     updateNotificationRanking(rankingMap);
    427                 }
    428             });
    429         }
    430 
    431     };
    432 
    433     private void updateCurrentProfilesCache() {
    434         synchronized (mCurrentProfiles) {
    435             mCurrentProfiles.clear();
    436             if (mUserManager != null) {
    437                 for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
    438                     mCurrentProfiles.put(user.id, user);
    439                 }
    440             }
    441         }
    442     }
    443 
    444     public void start() {
    445         mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    446         mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
    447         mDisplay = mWindowManager.getDefaultDisplay();
    448         mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService(
    449                 Context.DEVICE_POLICY_SERVICE);
    450 
    451         mNotificationColorUtil = NotificationColorUtil.getInstance(mContext);
    452 
    453         mNotificationData = new NotificationData(this);
    454 
    455         mAccessibilityManager = (AccessibilityManager)
    456                 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
    457 
    458         mDreamManager = IDreamManager.Stub.asInterface(
    459                 ServiceManager.checkService(DreamService.DREAM_SERVICE));
    460         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
    461 
    462         mSettingsObserver.onChange(false); // set up
    463         mContext.getContentResolver().registerContentObserver(
    464                 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
    465                 mSettingsObserver);
    466         mContext.getContentResolver().registerContentObserver(
    467                 Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
    468                 mSettingsObserver);
    469         mContext.getContentResolver().registerContentObserver(
    470                 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
    471                 mSettingsObserver,
    472                 UserHandle.USER_ALL);
    473 
    474         mContext.getContentResolver().registerContentObserver(
    475                 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
    476                 true,
    477                 mLockscreenSettingsObserver,
    478                 UserHandle.USER_ALL);
    479 
    480         mBarService = IStatusBarService.Stub.asInterface(
    481                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
    482 
    483         mRecents = getComponent(RecentsComponent.class);
    484         mRecents.setCallback(this);
    485 
    486         final Configuration currentConfig = mContext.getResources().getConfiguration();
    487         mLocale = currentConfig.locale;
    488         mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
    489         mFontScale = currentConfig.fontScale;
    490 
    491         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
    492 
    493         mLinearOutSlowIn = AnimationUtils.loadInterpolator(mContext,
    494                 android.R.interpolator.linear_out_slow_in);
    495         mFastOutLinearIn = AnimationUtils.loadInterpolator(mContext,
    496                 android.R.interpolator.fast_out_linear_in);
    497 
    498         // Connect in to the status bar manager service
    499         StatusBarIconList iconList = new StatusBarIconList();
    500         mCommandQueue = new CommandQueue(this, iconList);
    501 
    502         int[] switches = new int[8];
    503         ArrayList<IBinder> binders = new ArrayList<IBinder>();
    504         try {
    505             mBarService.registerStatusBar(mCommandQueue, iconList, switches, binders);
    506         } catch (RemoteException ex) {
    507             // If the system process isn't there we're doomed anyway.
    508         }
    509 
    510         createAndAddWindows();
    511 
    512         disable(switches[0], false /* animate */);
    513         setSystemUiVisibility(switches[1], 0xffffffff);
    514         topAppWindowChanged(switches[2] != 0);
    515         // StatusBarManagerService has a back up of IME token and it's restored here.
    516         setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
    517 
    518         // Set up the initial icon state
    519         int N = iconList.size();
    520         int viewIndex = 0;
    521         for (int i=0; i<N; i++) {
    522             StatusBarIcon icon = iconList.getIcon(i);
    523             if (icon != null) {
    524                 addIcon(iconList.getSlot(i), i, viewIndex, icon);
    525                 viewIndex++;
    526             }
    527         }
    528 
    529         // Set up the initial notification state.
    530         try {
    531             mNotificationListener.registerAsSystemService(mContext,
    532                     new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
    533                     UserHandle.USER_ALL);
    534         } catch (RemoteException e) {
    535             Log.e(TAG, "Unable to register notification listener", e);
    536         }
    537 
    538 
    539         if (DEBUG) {
    540             Log.d(TAG, String.format(
    541                     "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
    542                    iconList.size(),
    543                    switches[0],
    544                    switches[1],
    545                    switches[2],
    546                    switches[3]
    547                    ));
    548         }
    549 
    550         mCurrentUserId = ActivityManager.getCurrentUser();
    551 
    552         IntentFilter filter = new IntentFilter();
    553         filter.addAction(Intent.ACTION_USER_SWITCHED);
    554         filter.addAction(Intent.ACTION_USER_ADDED);
    555         filter.addAction(BANNER_ACTION_CANCEL);
    556         filter.addAction(BANNER_ACTION_SETUP);
    557         filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
    558         mContext.registerReceiver(mBroadcastReceiver, filter);
    559 
    560         updateCurrentProfilesCache();
    561     }
    562 
    563     protected void notifyUserAboutHiddenNotifications() {
    564         if (0 != Settings.Secure.getInt(mContext.getContentResolver(),
    565                 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) {
    566             Log.d(TAG, "user hasn't seen notification about hidden notifications");
    567             final LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
    568             if (!lockPatternUtils.isSecure()) {
    569                 Log.d(TAG, "insecure lockscreen, skipping notification");
    570                 Settings.Secure.putInt(mContext.getContentResolver(),
    571                         Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
    572                 return;
    573             }
    574             Log.d(TAG, "disabling lockecreen notifications and alerting the user");
    575             // disable lockscreen notifications until user acts on the banner.
    576             Settings.Secure.putInt(mContext.getContentResolver(),
    577                     Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
    578             Settings.Secure.putInt(mContext.getContentResolver(),
    579                     Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
    580 
    581             final String packageName = mContext.getPackageName();
    582             PendingIntent cancelIntent = PendingIntent.getBroadcast(mContext, 0,
    583                     new Intent(BANNER_ACTION_CANCEL).setPackage(packageName),
    584                     PendingIntent.FLAG_CANCEL_CURRENT);
    585             PendingIntent setupIntent = PendingIntent.getBroadcast(mContext, 0,
    586                     new Intent(BANNER_ACTION_SETUP).setPackage(packageName),
    587                     PendingIntent.FLAG_CANCEL_CURRENT);
    588 
    589             final Resources res = mContext.getResources();
    590             final int colorRes = com.android.internal.R.color.system_notification_accent_color;
    591             Notification.Builder note = new Notification.Builder(mContext)
    592                     .setSmallIcon(R.drawable.ic_android)
    593                     .setContentTitle(mContext.getString(R.string.hidden_notifications_title))
    594                     .setContentText(mContext.getString(R.string.hidden_notifications_text))
    595                     .setPriority(Notification.PRIORITY_HIGH)
    596                     .setOngoing(true)
    597                     .setColor(res.getColor(colorRes))
    598                     .setContentIntent(setupIntent)
    599                     .addAction(R.drawable.ic_close,
    600                             mContext.getString(R.string.hidden_notifications_cancel),
    601                             cancelIntent)
    602                     .addAction(R.drawable.ic_settings,
    603                             mContext.getString(R.string.hidden_notifications_setup),
    604                             setupIntent);
    605 
    606             NotificationManager noMan =
    607                     (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
    608             noMan.notify(HIDDEN_NOTIFICATION_ID, note.build());
    609         }
    610     }
    611 
    612     public void userSwitched(int newUserId) {
    613         // should be overridden
    614     }
    615 
    616     public boolean isHeadsUp(String key) {
    617       return mHeadsUpNotificationView != null && mHeadsUpNotificationView.isShowing(key);
    618     }
    619 
    620     @Override  // NotificationData.Environment
    621     public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
    622         final int thisUserId = mCurrentUserId;
    623         final int notificationUserId = n.getUserId();
    624         if (DEBUG && MULTIUSER_DEBUG) {
    625             Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
    626                     n, thisUserId, notificationUserId));
    627         }
    628         synchronized (mCurrentProfiles) {
    629             return notificationUserId == UserHandle.USER_ALL
    630                     || mCurrentProfiles.get(notificationUserId) != null;
    631         }
    632     }
    633 
    634     @Override
    635     public String getCurrentMediaNotificationKey() {
    636         return null;
    637     }
    638 
    639     /**
    640      * Takes the necessary steps to prepare the status bar for starting an activity, then starts it.
    641      * @param action A dismiss action that is called if it's safe to start the activity.
    642      * @param afterKeyguardGone Whether the action should be executed after the Keyguard is gone.
    643      */
    644     protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) {
    645         action.onDismiss();
    646     }
    647 
    648     @Override
    649     protected void onConfigurationChanged(Configuration newConfig) {
    650         final Locale locale = mContext.getResources().getConfiguration().locale;
    651         final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
    652         final float fontScale = newConfig.fontScale;
    653 
    654         if (! locale.equals(mLocale) || ld != mLayoutDirection || fontScale != mFontScale) {
    655             if (DEBUG) {
    656                 Log.v(TAG, String.format(
    657                         "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
    658                         locale, ld));
    659             }
    660             mLocale = locale;
    661             mLayoutDirection = ld;
    662             refreshLayout(ld);
    663         }
    664     }
    665 
    666     protected View updateNotificationVetoButton(View row, StatusBarNotification n) {
    667         View vetoButton = row.findViewById(R.id.veto);
    668         if (n.isClearable() || (mHeadsUpNotificationView.getEntry() != null
    669                 && mHeadsUpNotificationView.getEntry().row == row)) {
    670             final String _pkg = n.getPackageName();
    671             final String _tag = n.getTag();
    672             final int _id = n.getId();
    673             final int _userId = n.getUserId();
    674             vetoButton.setOnClickListener(new View.OnClickListener() {
    675                     public void onClick(View v) {
    676                         // Accessibility feedback
    677                         v.announceForAccessibility(
    678                                 mContext.getString(R.string.accessibility_notification_dismissed));
    679                         try {
    680                             mBarService.onNotificationClear(_pkg, _tag, _id, _userId);
    681 
    682                         } catch (RemoteException ex) {
    683                             // system process is dead if we're here.
    684                         }
    685                     }
    686                 });
    687             vetoButton.setVisibility(View.VISIBLE);
    688         } else {
    689             vetoButton.setVisibility(View.GONE);
    690         }
    691         vetoButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
    692         return vetoButton;
    693     }
    694 
    695 
    696     protected void applyColorsAndBackgrounds(StatusBarNotification sbn,
    697             NotificationData.Entry entry) {
    698 
    699         if (entry.expanded.getId() != com.android.internal.R.id.status_bar_latest_event_content) {
    700             // Using custom RemoteViews
    701             if (entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
    702                     && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP) {
    703                 entry.row.setShowingLegacyBackground(true);
    704                 entry.legacy = true;
    705             }
    706         } else {
    707             // Using platform templates
    708             final int color = sbn.getNotification().color;
    709             if (isMediaNotification(entry)) {
    710                 entry.row.setTintColor(color == Notification.COLOR_DEFAULT
    711                         ? mContext.getResources().getColor(
    712                                 R.color.notification_material_background_media_default_color)
    713                         : color);
    714             }
    715         }
    716 
    717         if (entry.icon != null) {
    718             if (entry.targetSdk >= Build.VERSION_CODES.LOLLIPOP) {
    719                 entry.icon.setColorFilter(mContext.getResources().getColor(android.R.color.white));
    720             } else {
    721                 entry.icon.setColorFilter(null);
    722             }
    723         }
    724     }
    725 
    726     public boolean isMediaNotification(NotificationData.Entry entry) {
    727         // TODO: confirm that there's a valid media key
    728         return entry.expandedBig != null &&
    729                entry.expandedBig.findViewById(com.android.internal.R.id.media_actions) != null;
    730     }
    731 
    732     // The gear button in the guts that links to the app's own notification settings
    733     private void startAppOwnNotificationSettingsActivity(Intent intent,
    734             final int notificationId, final String notificationTag, final int appUid) {
    735         intent.putExtra("notification_id", notificationId);
    736         intent.putExtra("notification_tag", notificationTag);
    737         startNotificationGutsIntent(intent, appUid);
    738     }
    739 
    740     // The (i) button in the guts that links to the system notification settings for that app
    741     private void startAppNotificationSettingsActivity(String packageName, final int appUid) {
    742         final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
    743         intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
    744         intent.putExtra(Settings.EXTRA_APP_UID, appUid);
    745         startNotificationGutsIntent(intent, appUid);
    746     }
    747 
    748     private void startNotificationGutsIntent(final Intent intent, final int appUid) {
    749         final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
    750         dismissKeyguardThenExecute(new OnDismissAction() {
    751             @Override
    752             public boolean onDismiss() {
    753                 AsyncTask.execute(new Runnable() {
    754                     public void run() {
    755                         try {
    756                             if (keyguardShowing) {
    757                                 ActivityManagerNative.getDefault()
    758                                         .keyguardWaitingForActivityDrawn();
    759                             }
    760                             TaskStackBuilder.create(mContext)
    761                                     .addNextIntentWithParentStack(intent)
    762                                     .startActivities(null,
    763                                             new UserHandle(UserHandle.getUserId(appUid)));
    764                             overrideActivityPendingAppTransition(keyguardShowing);
    765                         } catch (RemoteException e) {
    766                         }
    767                     }
    768                 });
    769                 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
    770                 return true;
    771             }
    772         }, false /* afterKeyguardGone */);
    773     }
    774 
    775     private void inflateGuts(ExpandableNotificationRow row) {
    776         ViewStub stub = (ViewStub) row.findViewById(R.id.notification_guts_stub);
    777         if (stub != null) {
    778             stub.inflate();
    779         }
    780         final StatusBarNotification sbn = row.getStatusBarNotification();
    781         PackageManager pmUser = getPackageManagerForUser(
    782                 sbn.getUser().getIdentifier());
    783         row.setTag(sbn.getPackageName());
    784         final View guts = row.findViewById(R.id.notification_guts);
    785         final String pkg = sbn.getPackageName();
    786         String appname = pkg;
    787         Drawable pkgicon = null;
    788         int appUid = -1;
    789         try {
    790             final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
    791                     PackageManager.GET_UNINSTALLED_PACKAGES
    792                             | PackageManager.GET_DISABLED_COMPONENTS);
    793             if (info != null) {
    794                 appname = String.valueOf(pmUser.getApplicationLabel(info));
    795                 pkgicon = pmUser.getApplicationIcon(info);
    796                 appUid = info.uid;
    797             }
    798         } catch (NameNotFoundException e) {
    799             // app is gone, just show package name and generic icon
    800             pkgicon = pmUser.getDefaultActivityIcon();
    801         }
    802         ((ImageView) row.findViewById(android.R.id.icon)).setImageDrawable(pkgicon);
    803         ((DateTimeView) row.findViewById(R.id.timestamp)).setTime(sbn.getPostTime());
    804         ((TextView) row.findViewById(R.id.pkgname)).setText(appname);
    805         final View settingsButton = guts.findViewById(R.id.notification_inspect_item);
    806         final View appSettingsButton
    807                 = guts.findViewById(R.id.notification_inspect_app_provided_settings);
    808         if (appUid >= 0) {
    809             final int appUidF = appUid;
    810             settingsButton.setOnClickListener(new View.OnClickListener() {
    811                 public void onClick(View v) {
    812                     startAppNotificationSettingsActivity(pkg, appUidF);
    813                 }
    814             });
    815 
    816             final Intent appSettingsQueryIntent
    817                     = new Intent(Intent.ACTION_MAIN)
    818                     .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
    819                     .setPackage(pkg);
    820             List<ResolveInfo> infos = pmUser.queryIntentActivities(appSettingsQueryIntent, 0);
    821             if (infos.size() > 0) {
    822                 appSettingsButton.setVisibility(View.VISIBLE);
    823                 appSettingsButton.setContentDescription(
    824                         mContext.getResources().getString(
    825                                 R.string.status_bar_notification_app_settings_title,
    826                                 appname
    827                         ));
    828                 final Intent appSettingsLaunchIntent = new Intent(appSettingsQueryIntent)
    829                         .setClassName(pkg, infos.get(0).activityInfo.name);
    830                 appSettingsButton.setOnClickListener(new View.OnClickListener() {
    831                     public void onClick(View v) {
    832                         startAppOwnNotificationSettingsActivity(appSettingsLaunchIntent,
    833                                 sbn.getId(),
    834                                 sbn.getTag(),
    835                                 appUidF);
    836                     }
    837                 });
    838             } else {
    839                 appSettingsButton.setVisibility(View.GONE);
    840             }
    841         } else {
    842             settingsButton.setVisibility(View.GONE);
    843             appSettingsButton.setVisibility(View.GONE);
    844         }
    845 
    846     }
    847 
    848     protected SwipeHelper.LongPressListener getNotificationLongClicker() {
    849         return new SwipeHelper.LongPressListener() {
    850             @Override
    851             public boolean onLongPress(View v, int x, int y) {
    852                 dismissPopups();
    853 
    854                 if (!(v instanceof ExpandableNotificationRow)) {
    855                     return false;
    856                 }
    857                 if (v.getWindowToken() == null) {
    858                     Log.e(TAG, "Trying to show notification guts, but not attached to window");
    859                     return false;
    860                 }
    861 
    862                 inflateGuts((ExpandableNotificationRow) v);
    863 
    864                 // Assume we are a status_bar_notification_row
    865                 final NotificationGuts guts = (NotificationGuts) v.findViewById(
    866                         R.id.notification_guts);
    867                 if (guts == null) {
    868                     // This view has no guts. Examples are the more card or the dismiss all view
    869                     return false;
    870                 }
    871 
    872                 // Already showing?
    873                 if (guts.getVisibility() == View.VISIBLE) {
    874                     Log.e(TAG, "Trying to show notification guts, but already visible");
    875                     return false;
    876                 }
    877 
    878                 guts.setVisibility(View.VISIBLE);
    879                 final double horz = Math.max(guts.getWidth() - x, x);
    880                 final double vert = Math.max(guts.getActualHeight() - y, y);
    881                 final float r = (float) Math.hypot(horz, vert);
    882                 final Animator a
    883                         = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
    884                 a.setDuration(400);
    885                 a.setInterpolator(mLinearOutSlowIn);
    886                 a.start();
    887 
    888                 mNotificationGutsExposed = guts;
    889 
    890                 return true;
    891             }
    892         };
    893     }
    894 
    895     public void dismissPopups() {
    896         if (mNotificationGutsExposed != null) {
    897             final NotificationGuts v = mNotificationGutsExposed;
    898             mNotificationGutsExposed = null;
    899 
    900             if (v.getWindowToken() == null) return;
    901 
    902             final int x = (v.getLeft() + v.getRight()) / 2;
    903             final int y = (v.getTop() + v.getActualHeight() / 2);
    904             final Animator a = ViewAnimationUtils.createCircularReveal(v,
    905                     x, y, x, 0);
    906             a.setDuration(200);
    907             a.setInterpolator(mFastOutLinearIn);
    908             a.addListener(new AnimatorListenerAdapter() {
    909                 @Override
    910                 public void onAnimationEnd(Animator animation) {
    911                     super.onAnimationEnd(animation);
    912                     v.setVisibility(View.GONE);
    913                 }
    914             });
    915             a.start();
    916         }
    917     }
    918 
    919     public void onHeadsUpDismissed() {
    920     }
    921 
    922     @Override
    923     public void showRecentApps(boolean triggeredFromAltTab) {
    924         int msg = MSG_SHOW_RECENT_APPS;
    925         mHandler.removeMessages(msg);
    926         mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0, 0).sendToTarget();
    927     }
    928 
    929     @Override
    930     public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
    931         int msg = MSG_HIDE_RECENT_APPS;
    932         mHandler.removeMessages(msg);
    933         mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0,
    934                 triggeredFromHomeKey ? 1 : 0).sendToTarget();
    935     }
    936 
    937     @Override
    938     public void toggleRecentApps() {
    939         int msg = MSG_TOGGLE_RECENTS_APPS;
    940         mHandler.removeMessages(msg);
    941         mHandler.sendEmptyMessage(msg);
    942     }
    943 
    944     @Override
    945     public void preloadRecentApps() {
    946         int msg = MSG_PRELOAD_RECENT_APPS;
    947         mHandler.removeMessages(msg);
    948         mHandler.sendEmptyMessage(msg);
    949     }
    950 
    951     @Override
    952     public void cancelPreloadRecentApps() {
    953         int msg = MSG_CANCEL_PRELOAD_RECENT_APPS;
    954         mHandler.removeMessages(msg);
    955         mHandler.sendEmptyMessage(msg);
    956     }
    957 
    958     /** Jumps to the next affiliated task in the group. */
    959     public void showNextAffiliatedTask() {
    960         int msg = MSG_SHOW_NEXT_AFFILIATED_TASK;
    961         mHandler.removeMessages(msg);
    962         mHandler.sendEmptyMessage(msg);
    963     }
    964 
    965     /** Jumps to the previous affiliated task in the group. */
    966     public void showPreviousAffiliatedTask() {
    967         int msg = MSG_SHOW_PREV_AFFILIATED_TASK;
    968         mHandler.removeMessages(msg);
    969         mHandler.sendEmptyMessage(msg);
    970     }
    971 
    972     @Override
    973     public void showSearchPanel() {
    974         if (mSearchPanelView != null && mSearchPanelView.isAssistantAvailable()) {
    975             mSearchPanelView.show(true, true);
    976         }
    977     }
    978 
    979     @Override
    980     public void hideSearchPanel() {
    981         int msg = MSG_CLOSE_SEARCH_PANEL;
    982         mHandler.removeMessages(msg);
    983         mHandler.sendEmptyMessage(msg);
    984     }
    985 
    986     protected abstract WindowManager.LayoutParams getSearchLayoutParams(
    987             LayoutParams layoutParams);
    988 
    989     protected void updateSearchPanel() {
    990         // Search Panel
    991         boolean visible = false;
    992         if (mSearchPanelView != null) {
    993             visible = mSearchPanelView.isShowing();
    994             mWindowManager.removeView(mSearchPanelView);
    995         }
    996 
    997         // Provide SearchPanel with a temporary parent to allow layout params to work.
    998         LinearLayout tmpRoot = new LinearLayout(mContext);
    999         mSearchPanelView = (SearchPanelView) LayoutInflater.from(mContext).inflate(
   1000                  R.layout.status_bar_search_panel, tmpRoot, false);
   1001         mSearchPanelView.setOnTouchListener(
   1002                  new TouchOutsideListener(MSG_CLOSE_SEARCH_PANEL, mSearchPanelView));
   1003         mSearchPanelView.setVisibility(View.GONE);
   1004         boolean vertical = mNavigationBarView != null && mNavigationBarView.isVertical();
   1005         mSearchPanelView.setHorizontal(vertical);
   1006 
   1007         WindowManager.LayoutParams lp = getSearchLayoutParams(mSearchPanelView.getLayoutParams());
   1008 
   1009         mWindowManager.addView(mSearchPanelView, lp);
   1010         mSearchPanelView.setBar(this);
   1011         if (visible) {
   1012             mSearchPanelView.show(true, false);
   1013         }
   1014     }
   1015 
   1016     protected H createHandler() {
   1017          return new H();
   1018     }
   1019 
   1020     static void sendCloseSystemWindows(Context context, String reason) {
   1021         if (ActivityManagerNative.isSystemReady()) {
   1022             try {
   1023                 ActivityManagerNative.getDefault().closeSystemDialogs(reason);
   1024             } catch (RemoteException e) {
   1025             }
   1026         }
   1027     }
   1028 
   1029     protected abstract View getStatusBarView();
   1030 
   1031     protected View.OnTouchListener mRecentsPreloadOnTouchListener = new View.OnTouchListener() {
   1032         // additional optimization when we have software system buttons - start loading the recent
   1033         // tasks on touch down
   1034         @Override
   1035         public boolean onTouch(View v, MotionEvent event) {
   1036             int action = event.getAction() & MotionEvent.ACTION_MASK;
   1037             if (action == MotionEvent.ACTION_DOWN) {
   1038                 preloadRecents();
   1039             } else if (action == MotionEvent.ACTION_CANCEL) {
   1040                 cancelPreloadingRecents();
   1041             } else if (action == MotionEvent.ACTION_UP) {
   1042                 if (!v.isPressed()) {
   1043                     cancelPreloadingRecents();
   1044                 }
   1045 
   1046             }
   1047             return false;
   1048         }
   1049     };
   1050 
   1051     /** Proxy for RecentsComponent */
   1052 
   1053     protected void showRecents(boolean triggeredFromAltTab) {
   1054         if (mRecents != null) {
   1055             sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
   1056             mRecents.showRecents(triggeredFromAltTab, getStatusBarView());
   1057         }
   1058     }
   1059 
   1060     protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
   1061         if (mRecents != null) {
   1062             mRecents.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
   1063         }
   1064     }
   1065 
   1066     protected void toggleRecents() {
   1067         if (mRecents != null) {
   1068             sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
   1069             mRecents.toggleRecents(mDisplay, mLayoutDirection, getStatusBarView());
   1070         }
   1071     }
   1072 
   1073     protected void preloadRecents() {
   1074         if (mRecents != null) {
   1075             mRecents.preloadRecents();
   1076         }
   1077     }
   1078 
   1079     protected void cancelPreloadingRecents() {
   1080         if (mRecents != null) {
   1081             mRecents.cancelPreloadingRecents();
   1082         }
   1083     }
   1084 
   1085     protected void showRecentsNextAffiliatedTask() {
   1086         if (mRecents != null) {
   1087             mRecents.showNextAffiliatedTask();
   1088         }
   1089     }
   1090 
   1091     protected void showRecentsPreviousAffiliatedTask() {
   1092         if (mRecents != null) {
   1093             mRecents.showPrevAffiliatedTask();
   1094         }
   1095     }
   1096 
   1097     @Override
   1098     public void onVisibilityChanged(boolean visible) {
   1099         // Do nothing
   1100     }
   1101 
   1102     public abstract void resetHeadsUpDecayTimer();
   1103 
   1104     public abstract void scheduleHeadsUpOpen();
   1105 
   1106     public abstract void scheduleHeadsUpClose();
   1107 
   1108     public abstract void scheduleHeadsUpEscalation();
   1109 
   1110     /**
   1111      * Save the current "public" (locked and secure) state of the lockscreen.
   1112      */
   1113     public void setLockscreenPublicMode(boolean publicMode) {
   1114         mLockscreenPublicMode = publicMode;
   1115     }
   1116 
   1117     public boolean isLockscreenPublicMode() {
   1118         return mLockscreenPublicMode;
   1119     }
   1120 
   1121     /**
   1122      * Has the given user chosen to allow their private (full) notifications to be shown even
   1123      * when the lockscreen is in "public" (secure & locked) mode?
   1124      */
   1125     public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
   1126         if (userHandle == UserHandle.USER_ALL) {
   1127             return true;
   1128         }
   1129 
   1130         if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
   1131             final boolean allowed = 0 != Settings.Secure.getIntForUser(
   1132                     mContext.getContentResolver(),
   1133                     Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
   1134             final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */,
   1135                     userHandle);
   1136             final boolean allowedByDpm = (dpmFlags
   1137                     & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0;
   1138             mUsersAllowingPrivateNotifications.append(userHandle, allowed && allowedByDpm);
   1139             return allowed;
   1140         }
   1141 
   1142         return mUsersAllowingPrivateNotifications.get(userHandle);
   1143     }
   1144 
   1145     /**
   1146      * Returns true if we're on a secure lockscreen and the user wants to hide "sensitive"
   1147      * notification data. If so, private notifications should show their (possibly
   1148      * auto-generated) publicVersion, and secret notifications should be totally invisible.
   1149      */
   1150     @Override  // NotificationData.Environment
   1151     public boolean shouldHideSensitiveContents(int userid) {
   1152         return isLockscreenPublicMode() && !userAllowsPrivateNotificationsInPublic(userid);
   1153     }
   1154 
   1155     public void onNotificationClear(StatusBarNotification notification) {
   1156         try {
   1157             mBarService.onNotificationClear(
   1158                     notification.getPackageName(),
   1159                     notification.getTag(),
   1160                     notification.getId(),
   1161                     notification.getUserId());
   1162         } catch (android.os.RemoteException ex) {
   1163             // oh well
   1164         }
   1165     }
   1166 
   1167     protected class H extends Handler {
   1168         public void handleMessage(Message m) {
   1169             switch (m.what) {
   1170              case MSG_SHOW_RECENT_APPS:
   1171                  showRecents(m.arg1 > 0);
   1172                  break;
   1173              case MSG_HIDE_RECENT_APPS:
   1174                  hideRecents(m.arg1 > 0, m.arg2 > 0);
   1175                  break;
   1176              case MSG_TOGGLE_RECENTS_APPS:
   1177                  toggleRecents();
   1178                  break;
   1179              case MSG_PRELOAD_RECENT_APPS:
   1180                   preloadRecents();
   1181                   break;
   1182              case MSG_CANCEL_PRELOAD_RECENT_APPS:
   1183                   cancelPreloadingRecents();
   1184                   break;
   1185              case MSG_SHOW_NEXT_AFFILIATED_TASK:
   1186                   showRecentsNextAffiliatedTask();
   1187                   break;
   1188              case MSG_SHOW_PREV_AFFILIATED_TASK:
   1189                   showRecentsPreviousAffiliatedTask();
   1190                   break;
   1191              case MSG_CLOSE_SEARCH_PANEL:
   1192                  if (DEBUG) Log.d(TAG, "closing search panel");
   1193                  if (mSearchPanelView != null && mSearchPanelView.isShowing()) {
   1194                      mSearchPanelView.show(false, true);
   1195                  }
   1196                  break;
   1197             }
   1198         }
   1199     }
   1200 
   1201     public class TouchOutsideListener implements View.OnTouchListener {
   1202         private int mMsg;
   1203         private StatusBarPanel mPanel;
   1204 
   1205         public TouchOutsideListener(int msg, StatusBarPanel panel) {
   1206             mMsg = msg;
   1207             mPanel = panel;
   1208         }
   1209 
   1210         public boolean onTouch(View v, MotionEvent ev) {
   1211             final int action = ev.getAction();
   1212             if (action == MotionEvent.ACTION_OUTSIDE
   1213                 || (action == MotionEvent.ACTION_DOWN
   1214                     && !mPanel.isInContentArea((int)ev.getX(), (int)ev.getY()))) {
   1215                 mHandler.removeMessages(mMsg);
   1216                 mHandler.sendEmptyMessage(mMsg);
   1217                 return true;
   1218             }
   1219             return false;
   1220         }
   1221     }
   1222 
   1223     protected void workAroundBadLayerDrawableOpacity(View v) {
   1224     }
   1225 
   1226     private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
   1227             return inflateViews(entry, parent, false);
   1228     }
   1229 
   1230     protected boolean inflateViewsForHeadsUp(NotificationData.Entry entry, ViewGroup parent) {
   1231             return inflateViews(entry, parent, true);
   1232     }
   1233 
   1234     private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent, boolean isHeadsUp) {
   1235         PackageManager pmUser = getPackageManagerForUser(
   1236                 entry.notification.getUser().getIdentifier());
   1237 
   1238         int maxHeight = mRowMaxHeight;
   1239         final StatusBarNotification sbn = entry.notification;
   1240         RemoteViews contentView = sbn.getNotification().contentView;
   1241         RemoteViews bigContentView = sbn.getNotification().bigContentView;
   1242 
   1243         if (isHeadsUp) {
   1244             maxHeight =
   1245                     mContext.getResources().getDimensionPixelSize(R.dimen.notification_mid_height);
   1246             bigContentView = sbn.getNotification().headsUpContentView;
   1247         }
   1248 
   1249         if (contentView == null) {
   1250             return false;
   1251         }
   1252 
   1253         if (DEBUG) {
   1254             Log.v(TAG, "publicNotification: " + sbn.getNotification().publicVersion);
   1255         }
   1256 
   1257         Notification publicNotification = sbn.getNotification().publicVersion;
   1258 
   1259         ExpandableNotificationRow row;
   1260 
   1261         // Stash away previous user expansion state so we can restore it at
   1262         // the end.
   1263         boolean hasUserChangedExpansion = false;
   1264         boolean userExpanded = false;
   1265         boolean userLocked = false;
   1266 
   1267         if (entry.row != null) {
   1268             row = entry.row;
   1269             hasUserChangedExpansion = row.hasUserChangedExpansion();
   1270             userExpanded = row.isUserExpanded();
   1271             userLocked = row.isUserLocked();
   1272             entry.reset();
   1273             if (hasUserChangedExpansion) {
   1274                 row.setUserExpanded(userExpanded);
   1275             }
   1276         } else {
   1277             // create the row view
   1278             LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
   1279                     Context.LAYOUT_INFLATER_SERVICE);
   1280             row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row,
   1281                     parent, false);
   1282             row.setExpansionLogger(this, entry.notification.getKey());
   1283         }
   1284 
   1285         workAroundBadLayerDrawableOpacity(row);
   1286         View vetoButton = updateNotificationVetoButton(row, sbn);
   1287         vetoButton.setContentDescription(mContext.getString(
   1288                 R.string.accessibility_remove_notification));
   1289 
   1290         // NB: the large icon is now handled entirely by the template
   1291 
   1292         // bind the click event to the content area
   1293         NotificationContentView expanded =
   1294                 (NotificationContentView) row.findViewById(R.id.expanded);
   1295         NotificationContentView expandedPublic =
   1296                 (NotificationContentView) row.findViewById(R.id.expandedPublic);
   1297 
   1298         row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
   1299 
   1300         PendingIntent contentIntent = sbn.getNotification().contentIntent;
   1301         if (contentIntent != null) {
   1302             final View.OnClickListener listener = makeClicker(contentIntent, sbn.getKey(),
   1303                     isHeadsUp);
   1304             row.setOnClickListener(listener);
   1305         } else {
   1306             row.setOnClickListener(null);
   1307         }
   1308 
   1309         // set up the adaptive layout
   1310         View contentViewLocal = null;
   1311         View bigContentViewLocal = null;
   1312         try {
   1313             contentViewLocal = contentView.apply(mContext, expanded,
   1314                     mOnClickHandler);
   1315             if (bigContentView != null) {
   1316                 bigContentViewLocal = bigContentView.apply(mContext, expanded,
   1317                         mOnClickHandler);
   1318             }
   1319         }
   1320         catch (RuntimeException e) {
   1321             final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
   1322             Log.e(TAG, "couldn't inflate view for notification " + ident, e);
   1323             return false;
   1324         }
   1325 
   1326         if (contentViewLocal != null) {
   1327             contentViewLocal.setIsRootNamespace(true);
   1328             expanded.setContractedChild(contentViewLocal);
   1329         }
   1330         if (bigContentViewLocal != null) {
   1331             bigContentViewLocal.setIsRootNamespace(true);
   1332             expanded.setExpandedChild(bigContentViewLocal);
   1333         }
   1334 
   1335         // now the public version
   1336         View publicViewLocal = null;
   1337         if (publicNotification != null) {
   1338             try {
   1339                 publicViewLocal = publicNotification.contentView.apply(mContext, expandedPublic,
   1340                         mOnClickHandler);
   1341 
   1342                 if (publicViewLocal != null) {
   1343                     publicViewLocal.setIsRootNamespace(true);
   1344                     expandedPublic.setContractedChild(publicViewLocal);
   1345                 }
   1346             }
   1347             catch (RuntimeException e) {
   1348                 final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
   1349                 Log.e(TAG, "couldn't inflate public view for notification " + ident, e);
   1350                 publicViewLocal = null;
   1351             }
   1352         }
   1353 
   1354         // Extract target SDK version.
   1355         try {
   1356             ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
   1357             entry.targetSdk = info.targetSdkVersion;
   1358         } catch (NameNotFoundException ex) {
   1359             Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
   1360         }
   1361 
   1362         if (publicViewLocal == null) {
   1363             // Add a basic notification template
   1364             publicViewLocal = LayoutInflater.from(mContext).inflate(
   1365                     R.layout.notification_public_default,
   1366                     expandedPublic, false);
   1367             publicViewLocal.setIsRootNamespace(true);
   1368             expandedPublic.setContractedChild(publicViewLocal);
   1369 
   1370             final TextView title = (TextView) publicViewLocal.findViewById(R.id.title);
   1371             try {
   1372                 title.setText(pmUser.getApplicationLabel(
   1373                         pmUser.getApplicationInfo(entry.notification.getPackageName(), 0)));
   1374             } catch (NameNotFoundException e) {
   1375                 title.setText(entry.notification.getPackageName());
   1376             }
   1377 
   1378             final ImageView icon = (ImageView) publicViewLocal.findViewById(R.id.icon);
   1379             final ImageView profileBadge = (ImageView) publicViewLocal.findViewById(
   1380                     R.id.profile_badge_line3);
   1381 
   1382             final StatusBarIcon ic = new StatusBarIcon(entry.notification.getPackageName(),
   1383                     entry.notification.getUser(),
   1384                     entry.notification.getNotification().icon,
   1385                     entry.notification.getNotification().iconLevel,
   1386                     entry.notification.getNotification().number,
   1387                     entry.notification.getNotification().tickerText);
   1388 
   1389             Drawable iconDrawable = StatusBarIconView.getIcon(mContext, ic);
   1390             icon.setImageDrawable(iconDrawable);
   1391             if (entry.targetSdk >= Build.VERSION_CODES.LOLLIPOP
   1392                     || mNotificationColorUtil.isGrayscaleIcon(iconDrawable)) {
   1393                 icon.setBackgroundResource(
   1394                         com.android.internal.R.drawable.notification_icon_legacy_bg);
   1395                 int padding = mContext.getResources().getDimensionPixelSize(
   1396                         com.android.internal.R.dimen.notification_large_icon_circle_padding);
   1397                 icon.setPadding(padding, padding, padding, padding);
   1398                 if (sbn.getNotification().color != Notification.COLOR_DEFAULT) {
   1399                     icon.getBackground().setColorFilter(
   1400                             sbn.getNotification().color, PorterDuff.Mode.SRC_ATOP);
   1401                 }
   1402             }
   1403 
   1404             if (profileBadge != null) {
   1405                 Drawable profileDrawable = mContext.getPackageManager().getUserBadgeForDensity(
   1406                         entry.notification.getUser(), 0);
   1407                 if (profileDrawable != null) {
   1408                     profileBadge.setImageDrawable(profileDrawable);
   1409                     profileBadge.setVisibility(View.VISIBLE);
   1410                 } else {
   1411                     profileBadge.setVisibility(View.GONE);
   1412                 }
   1413             }
   1414 
   1415             final View privateTime = contentViewLocal.findViewById(com.android.internal.R.id.time);
   1416             final DateTimeView time = (DateTimeView) publicViewLocal.findViewById(R.id.time);
   1417             if (privateTime != null && privateTime.getVisibility() == View.VISIBLE) {
   1418                 time.setVisibility(View.VISIBLE);
   1419                 time.setTime(entry.notification.getNotification().when);
   1420             }
   1421 
   1422             final TextView text = (TextView) publicViewLocal.findViewById(R.id.text);
   1423             if (text != null) {
   1424                 text.setText(R.string.notification_hidden_text);
   1425                 text.setTextAppearance(mContext,
   1426                         R.style.TextAppearance_Material_Notification_Parenthetical);
   1427             }
   1428 
   1429             int topPadding = Notification.Builder.calculateTopPadding(mContext,
   1430                     false /* hasThreeLines */,
   1431                     mContext.getResources().getConfiguration().fontScale);
   1432             title.setPadding(0, topPadding, 0, 0);
   1433 
   1434             entry.autoRedacted = true;
   1435         }
   1436 
   1437         row.setClearable(sbn.isClearable());
   1438 
   1439         if (MULTIUSER_DEBUG) {
   1440             TextView debug = (TextView) row.findViewById(R.id.debug_info);
   1441             if (debug != null) {
   1442                 debug.setVisibility(View.VISIBLE);
   1443                 debug.setText("CU " + mCurrentUserId +" NU " + entry.notification.getUserId());
   1444             }
   1445         }
   1446         entry.row = row;
   1447         entry.row.setHeightRange(mRowMinHeight, maxHeight);
   1448         entry.row.setOnActivatedListener(this);
   1449         entry.expanded = contentViewLocal;
   1450         entry.expandedPublic = publicViewLocal;
   1451         entry.setBigContentView(bigContentViewLocal);
   1452 
   1453         applyColorsAndBackgrounds(sbn, entry);
   1454 
   1455         // Restore previous flags.
   1456         if (hasUserChangedExpansion) {
   1457             // Note: setUserExpanded() conveniently ignores calls with
   1458             //       userExpanded=true if !isExpandable().
   1459             row.setUserExpanded(userExpanded);
   1460         }
   1461         row.setUserLocked(userLocked);
   1462         row.setStatusBarNotification(entry.notification);
   1463         return true;
   1464     }
   1465 
   1466     public NotificationClicker makeClicker(PendingIntent intent, String notificationKey,
   1467             boolean forHun) {
   1468         return new NotificationClicker(intent, notificationKey, forHun);
   1469     }
   1470 
   1471     protected class NotificationClicker implements View.OnClickListener {
   1472         private PendingIntent mIntent;
   1473         private final String mNotificationKey;
   1474         private boolean mIsHeadsUp;
   1475 
   1476         public NotificationClicker(PendingIntent intent, String notificationKey, boolean forHun) {
   1477             mIntent = intent;
   1478             mNotificationKey = notificationKey;
   1479             mIsHeadsUp = forHun;
   1480         }
   1481 
   1482         public void onClick(final View v) {
   1483             final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
   1484             final boolean afterKeyguardGone = mIntent.isActivity()
   1485                     && PreviewInflater.wouldLaunchResolverActivity(mContext, mIntent.getIntent(),
   1486                             mCurrentUserId);
   1487             dismissKeyguardThenExecute(new OnDismissAction() {
   1488                 public boolean onDismiss() {
   1489                     if (mIsHeadsUp) {
   1490                         mHeadsUpNotificationView.clear();
   1491                     }
   1492                     new Thread() {
   1493                         @Override
   1494                         public void run() {
   1495                             try {
   1496                                 if (keyguardShowing && !afterKeyguardGone) {
   1497                                     ActivityManagerNative.getDefault()
   1498                                             .keyguardWaitingForActivityDrawn();
   1499                                 }
   1500 
   1501                                 // The intent we are sending is for the application, which
   1502                                 // won't have permission to immediately start an activity after
   1503                                 // the user switches to home.  We know it is safe to do at this
   1504                                 // point, so make sure new activity switches are now allowed.
   1505                                 ActivityManagerNative.getDefault().resumeAppSwitches();
   1506                             } catch (RemoteException e) {
   1507                             }
   1508 
   1509                             if (mIntent != null) {
   1510                                 try {
   1511                                     mIntent.send();
   1512                                 } catch (PendingIntent.CanceledException e) {
   1513                                     // the stack trace isn't very helpful here.
   1514                                     // Just log the exception message.
   1515                                     Log.w(TAG, "Sending contentIntent failed: " + e);
   1516 
   1517                                     // TODO: Dismiss Keyguard.
   1518                                 }
   1519                                 if (mIntent.isActivity()) {
   1520                                     overrideActivityPendingAppTransition(keyguardShowing
   1521                                             && !afterKeyguardGone);
   1522                                 }
   1523                             }
   1524 
   1525                             try {
   1526                                 mBarService.onNotificationClick(mNotificationKey);
   1527                             } catch (RemoteException ex) {
   1528                                 // system process is dead if we're here.
   1529                             }
   1530                         }
   1531                     }.start();
   1532 
   1533                     // close the shade if it was open
   1534                     animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
   1535                     visibilityChanged(false);
   1536 
   1537                     return mIntent != null && mIntent.isActivity();
   1538                 }
   1539             }, afterKeyguardGone);
   1540         }
   1541     }
   1542 
   1543     public void animateCollapsePanels(int flags, boolean force) {
   1544     }
   1545 
   1546     public void overrideActivityPendingAppTransition(boolean keyguardShowing) {
   1547         if (keyguardShowing) {
   1548             try {
   1549                 mWindowManagerService.overridePendingAppTransition(null, 0, 0, null);
   1550             } catch (RemoteException e) {
   1551                 Log.w(TAG, "Error overriding app transition: " + e);
   1552             }
   1553         }
   1554     }
   1555 
   1556     /**
   1557      * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
   1558      * This was added last-minute and is inconsistent with the way the rest of the notifications
   1559      * are handled, because the notification isn't really cancelled.  The lights are just
   1560      * turned off.  If any other notifications happen, the lights will turn back on.  Steve says
   1561      * this is what he wants. (see bug 1131461)
   1562      */
   1563     protected void visibilityChanged(boolean visible) {
   1564         if (mPanelSlightlyVisible != visible) {
   1565             mPanelSlightlyVisible = visible;
   1566             if (!visible) {
   1567                 dismissPopups();
   1568             }
   1569             try {
   1570                 if (visible) {
   1571                     mBarService.onPanelRevealed();
   1572                 } else {
   1573                     mBarService.onPanelHidden();
   1574                 }
   1575             } catch (RemoteException ex) {
   1576                 // Won't fail unless the world has ended.
   1577             }
   1578         }
   1579     }
   1580 
   1581     /**
   1582      * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
   1583      * about the failure.
   1584      *
   1585      * WARNING: this will call back into us.  Don't hold any locks.
   1586      */
   1587     void handleNotificationError(StatusBarNotification n, String message) {
   1588         removeNotification(n.getKey(), null);
   1589         try {
   1590             mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
   1591                     n.getInitialPid(), message, n.getUserId());
   1592         } catch (RemoteException ex) {
   1593             // The end is nigh.
   1594         }
   1595     }
   1596 
   1597     protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) {
   1598         NotificationData.Entry entry = mNotificationData.remove(key, ranking);
   1599         if (entry == null) {
   1600             Log.w(TAG, "removeNotification for unknown key: " + key);
   1601             return null;
   1602         }
   1603         updateNotifications();
   1604         return entry.notification;
   1605     }
   1606 
   1607     protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) {
   1608         if (DEBUG) {
   1609             Log.d(TAG, "createNotificationViews(notification=" + sbn);
   1610         }
   1611         // Construct the icon.
   1612         Notification n = sbn.getNotification();
   1613         final StatusBarIconView iconView = new StatusBarIconView(mContext,
   1614                 sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), n);
   1615         iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
   1616 
   1617         final StatusBarIcon ic = new StatusBarIcon(sbn.getPackageName(),
   1618                 sbn.getUser(),
   1619                     n.icon,
   1620                     n.iconLevel,
   1621                     n.number,
   1622                     n.tickerText);
   1623         if (!iconView.set(ic)) {
   1624             handleNotificationError(sbn, "Couldn't create icon: " + ic);
   1625             return null;
   1626         }
   1627         // Construct the expanded view.
   1628         NotificationData.Entry entry = new NotificationData.Entry(sbn, iconView);
   1629         if (!inflateViews(entry, mStackScroller)) {
   1630             handleNotificationError(sbn, "Couldn't expand RemoteViews for: " + sbn);
   1631             return null;
   1632         }
   1633         return entry;
   1634     }
   1635 
   1636     protected void addNotificationViews(Entry entry, RankingMap ranking) {
   1637         if (entry == null) {
   1638             return;
   1639         }
   1640         // Add the expanded view and icon.
   1641         mNotificationData.add(entry, ranking);
   1642         updateNotifications();
   1643     }
   1644 
   1645     /**
   1646      * @return The number of notifications we show on Keyguard.
   1647      */
   1648     protected abstract int getMaxKeyguardNotifications();
   1649 
   1650     /**
   1651      * Updates expanded, dimmed and locked states of notification rows.
   1652      */
   1653     protected void updateRowStates() {
   1654         int maxKeyguardNotifications = getMaxKeyguardNotifications();
   1655         mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
   1656 
   1657         ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
   1658         final int N = activeNotifications.size();
   1659 
   1660         int visibleNotifications = 0;
   1661         boolean onKeyguard = mState == StatusBarState.KEYGUARD;
   1662         for (int i = 0; i < N; i++) {
   1663             NotificationData.Entry entry = activeNotifications.get(i);
   1664             if (onKeyguard) {
   1665                 entry.row.setExpansionDisabled(true);
   1666             } else {
   1667                 entry.row.setExpansionDisabled(false);
   1668                 if (!entry.row.isUserLocked()) {
   1669                     boolean top = (i == 0);
   1670                     entry.row.setSystemExpanded(top);
   1671                 }
   1672             }
   1673             boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
   1674             if ((isLockscreenPublicMode() && !mShowLockscreenNotifications) ||
   1675                     (onKeyguard && (visibleNotifications >= maxKeyguardNotifications
   1676                             || !showOnKeyguard))) {
   1677                 entry.row.setVisibility(View.GONE);
   1678                 if (onKeyguard && showOnKeyguard) {
   1679                     mKeyguardIconOverflowContainer.getIconsView().addNotification(entry);
   1680                 }
   1681             } else {
   1682                 boolean wasGone = entry.row.getVisibility() == View.GONE;
   1683                 entry.row.setVisibility(View.VISIBLE);
   1684                 if (wasGone) {
   1685                     // notify the scroller of a child addition
   1686                     mStackScroller.generateAddAnimation(entry.row, true /* fromMoreCard */);
   1687                 }
   1688                 visibleNotifications++;
   1689             }
   1690         }
   1691 
   1692         if (onKeyguard && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0) {
   1693             mKeyguardIconOverflowContainer.setVisibility(View.VISIBLE);
   1694         } else {
   1695             mKeyguardIconOverflowContainer.setVisibility(View.GONE);
   1696         }
   1697 
   1698         mStackScroller.changeViewPosition(mKeyguardIconOverflowContainer,
   1699                 mStackScroller.getChildCount() - 3);
   1700         mStackScroller.changeViewPosition(mEmptyShadeView, mStackScroller.getChildCount() - 2);
   1701         mStackScroller.changeViewPosition(mDismissView, mStackScroller.getChildCount() - 1);
   1702     }
   1703 
   1704     private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
   1705         return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey());
   1706     }
   1707 
   1708     protected void setZenMode(int mode) {
   1709         if (!isDeviceProvisioned()) return;
   1710         mZenMode = mode;
   1711         updateNotifications();
   1712     }
   1713 
   1714     // extended in PhoneStatusBar
   1715     protected void setShowLockscreenNotifications(boolean show) {
   1716         mShowLockscreenNotifications = show;
   1717     }
   1718 
   1719     private void updateLockscreenNotificationSetting() {
   1720         final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
   1721                 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
   1722                 1,
   1723                 mCurrentUserId) != 0;
   1724         final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
   1725                 null /* admin */, mCurrentUserId);
   1726         final boolean allowedByDpm = (dpmFlags
   1727                 & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
   1728         setShowLockscreenNotifications(show && allowedByDpm);
   1729     }
   1730 
   1731     protected abstract void haltTicker();
   1732     protected abstract void setAreThereNotifications();
   1733     protected abstract void updateNotifications();
   1734     protected abstract void tick(StatusBarNotification n, boolean firstTime);
   1735     protected abstract void updateExpandedViewPos(int expandedPosition);
   1736     protected abstract boolean shouldDisableNavbarGestures();
   1737 
   1738     public abstract void addNotification(StatusBarNotification notification,
   1739             RankingMap ranking);
   1740     protected abstract void updateNotificationRanking(RankingMap ranking);
   1741     public abstract void removeNotification(String key, RankingMap ranking);
   1742 
   1743     public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
   1744         if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
   1745 
   1746         final String key = notification.getKey();
   1747         boolean wasHeadsUp = isHeadsUp(key);
   1748         Entry oldEntry;
   1749         if (wasHeadsUp) {
   1750             oldEntry = mHeadsUpNotificationView.getEntry();
   1751         } else {
   1752             oldEntry = mNotificationData.get(key);
   1753         }
   1754         if (oldEntry == null) {
   1755             return;
   1756         }
   1757 
   1758         final StatusBarNotification oldNotification = oldEntry.notification;
   1759 
   1760         // XXX: modify when we do something more intelligent with the two content views
   1761         final RemoteViews oldContentView = oldNotification.getNotification().contentView;
   1762         Notification n = notification.getNotification();
   1763         final RemoteViews contentView = n.contentView;
   1764         final RemoteViews oldBigContentView = oldNotification.getNotification().bigContentView;
   1765         final RemoteViews bigContentView = n.bigContentView;
   1766         final RemoteViews oldHeadsUpContentView = oldNotification.getNotification().headsUpContentView;
   1767         final RemoteViews headsUpContentView = n.headsUpContentView;
   1768         final Notification oldPublicNotification = oldNotification.getNotification().publicVersion;
   1769         final RemoteViews oldPublicContentView = oldPublicNotification != null
   1770                 ? oldPublicNotification.contentView : null;
   1771         final Notification publicNotification = n.publicVersion;
   1772         final RemoteViews publicContentView = publicNotification != null
   1773                 ? publicNotification.contentView : null;
   1774 
   1775         if (DEBUG) {
   1776             Log.d(TAG, "old notification: when=" + oldNotification.getNotification().when
   1777                     + " ongoing=" + oldNotification.isOngoing()
   1778                     + " expanded=" + oldEntry.expanded
   1779                     + " contentView=" + oldContentView
   1780                     + " bigContentView=" + oldBigContentView
   1781                     + " publicView=" + oldPublicContentView
   1782                     + " rowParent=" + oldEntry.row.getParent());
   1783             Log.d(TAG, "new notification: when=" + n.when
   1784                     + " ongoing=" + oldNotification.isOngoing()
   1785                     + " contentView=" + contentView
   1786                     + " bigContentView=" + bigContentView
   1787                     + " publicView=" + publicContentView);
   1788         }
   1789 
   1790         // Can we just reapply the RemoteViews in place?
   1791 
   1792         // 1U is never null
   1793         boolean contentsUnchanged = oldEntry.expanded != null
   1794                 && contentView.getPackage() != null
   1795                 && oldContentView.getPackage() != null
   1796                 && oldContentView.getPackage().equals(contentView.getPackage())
   1797                 && oldContentView.getLayoutId() == contentView.getLayoutId();
   1798         // large view may be null
   1799         boolean bigContentsUnchanged =
   1800                 (oldEntry.getBigContentView() == null && bigContentView == null)
   1801                 || ((oldEntry.getBigContentView() != null && bigContentView != null)
   1802                     && bigContentView.getPackage() != null
   1803                     && oldBigContentView.getPackage() != null
   1804                     && oldBigContentView.getPackage().equals(bigContentView.getPackage())
   1805                     && oldBigContentView.getLayoutId() == bigContentView.getLayoutId());
   1806         boolean headsUpContentsUnchanged =
   1807                 (oldHeadsUpContentView == null && headsUpContentView == null)
   1808                 || ((oldHeadsUpContentView != null && headsUpContentView != null)
   1809                     && headsUpContentView.getPackage() != null
   1810                     && oldHeadsUpContentView.getPackage() != null
   1811                     && oldHeadsUpContentView.getPackage().equals(headsUpContentView.getPackage())
   1812                     && oldHeadsUpContentView.getLayoutId() == headsUpContentView.getLayoutId());
   1813         boolean publicUnchanged  =
   1814                 (oldPublicContentView == null && publicContentView == null)
   1815                 || ((oldPublicContentView != null && publicContentView != null)
   1816                         && publicContentView.getPackage() != null
   1817                         && oldPublicContentView.getPackage() != null
   1818                         && oldPublicContentView.getPackage().equals(publicContentView.getPackage())
   1819                         && oldPublicContentView.getLayoutId() == publicContentView.getLayoutId());
   1820         boolean updateTicker = n.tickerText != null
   1821                 && !TextUtils.equals(n.tickerText,
   1822                 oldEntry.notification.getNotification().tickerText);
   1823 
   1824         final boolean shouldInterrupt = shouldInterrupt(notification);
   1825         final boolean alertAgain = alertAgain(oldEntry, n);
   1826         boolean updateSuccessful = false;
   1827         if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged
   1828                 && publicUnchanged) {
   1829             if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
   1830             oldEntry.notification = notification;
   1831             try {
   1832                 if (oldEntry.icon != null) {
   1833                     // Update the icon
   1834                     final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
   1835                             notification.getUser(),
   1836                             n.icon,
   1837                             n.iconLevel,
   1838                             n.number,
   1839                             n.tickerText);
   1840                     oldEntry.icon.setNotification(n);
   1841                     if (!oldEntry.icon.set(ic)) {
   1842                         handleNotificationError(notification, "Couldn't update icon: " + ic);
   1843                         return;
   1844                     }
   1845                 }
   1846 
   1847                 if (wasHeadsUp) {
   1848                     if (shouldInterrupt) {
   1849                         updateHeadsUpViews(oldEntry, notification);
   1850                         if (alertAgain) {
   1851                             resetHeadsUpDecayTimer();
   1852                         }
   1853                     } else {
   1854                         // we updated the notification above, so release to build a new shade entry
   1855                         mHeadsUpNotificationView.releaseAndClose();
   1856                         return;
   1857                     }
   1858                 } else {
   1859                     if (shouldInterrupt && alertAgain) {
   1860                         removeNotificationViews(key, ranking);
   1861                         addNotification(notification, ranking);  //this will pop the headsup
   1862                     } else {
   1863                         updateNotificationViews(oldEntry, notification);
   1864                     }
   1865                 }
   1866                 mNotificationData.updateRanking(ranking);
   1867                 updateNotifications();
   1868                 updateSuccessful = true;
   1869             }
   1870             catch (RuntimeException e) {
   1871                 // It failed to add cleanly.  Log, and remove the view from the panel.
   1872                 Log.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
   1873             }
   1874         }
   1875         if (!updateSuccessful) {
   1876             if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);
   1877             if (wasHeadsUp) {
   1878                 if (shouldInterrupt) {
   1879                     if (DEBUG) Log.d(TAG, "rebuilding heads up for key: " + key);
   1880                     Entry newEntry = new Entry(notification, null);
   1881                     ViewGroup holder = mHeadsUpNotificationView.getHolder();
   1882                     if (inflateViewsForHeadsUp(newEntry, holder)) {
   1883                         mHeadsUpNotificationView.showNotification(newEntry);
   1884                         if (alertAgain) {
   1885                             resetHeadsUpDecayTimer();
   1886                         }
   1887                     } else {
   1888                         Log.w(TAG, "Couldn't create new updated headsup for package "
   1889                                 + contentView.getPackage());
   1890                     }
   1891                 } else {
   1892                     if (DEBUG) Log.d(TAG, "releasing heads up for key: " + key);
   1893                     oldEntry.notification = notification;
   1894                     mHeadsUpNotificationView.releaseAndClose();
   1895                     return;
   1896                 }
   1897             } else {
   1898                 if (shouldInterrupt && alertAgain) {
   1899                     if (DEBUG) Log.d(TAG, "reposting to invoke heads up for key: " + key);
   1900                     removeNotificationViews(key, ranking);
   1901                     addNotification(notification, ranking);  //this will pop the headsup
   1902                 } else {
   1903                     if (DEBUG) Log.d(TAG, "rebuilding update in place for key: " + key);
   1904                     oldEntry.notification = notification;
   1905                     final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
   1906                             notification.getUser(),
   1907                             n.icon,
   1908                             n.iconLevel,
   1909                             n.number,
   1910                             n.tickerText);
   1911                     oldEntry.icon.setNotification(n);
   1912                     oldEntry.icon.set(ic);
   1913                     inflateViews(oldEntry, mStackScroller, wasHeadsUp);
   1914                     mNotificationData.updateRanking(ranking);
   1915                     updateNotifications();
   1916                 }
   1917             }
   1918         }
   1919 
   1920         // Update the veto button accordingly (and as a result, whether this row is
   1921         // swipe-dismissable)
   1922         updateNotificationVetoButton(oldEntry.row, notification);
   1923 
   1924         // Is this for you?
   1925         boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);
   1926         if (DEBUG) Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
   1927 
   1928         // Restart the ticker if it's still running
   1929         if (updateTicker && isForCurrentUser) {
   1930             haltTicker();
   1931             tick(notification, false);
   1932         }
   1933 
   1934         // Recalculate the position of the sliding windows and the titles.
   1935         setAreThereNotifications();
   1936         updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
   1937     }
   1938 
   1939     private void updateNotificationViews(NotificationData.Entry entry,
   1940             StatusBarNotification notification) {
   1941         updateNotificationViews(entry, notification, false);
   1942     }
   1943 
   1944     private void updateHeadsUpViews(NotificationData.Entry entry,
   1945             StatusBarNotification notification) {
   1946         updateNotificationViews(entry, notification, true);
   1947     }
   1948 
   1949     private void updateNotificationViews(NotificationData.Entry entry,
   1950             StatusBarNotification notification, boolean isHeadsUp) {
   1951         final RemoteViews contentView = notification.getNotification().contentView;
   1952         final RemoteViews bigContentView = isHeadsUp
   1953                 ? notification.getNotification().headsUpContentView
   1954                 : notification.getNotification().bigContentView;
   1955         final Notification publicVersion = notification.getNotification().publicVersion;
   1956         final RemoteViews publicContentView = publicVersion != null ? publicVersion.contentView
   1957                 : null;
   1958 
   1959         // Reapply the RemoteViews
   1960         contentView.reapply(mContext, entry.expanded, mOnClickHandler);
   1961         if (bigContentView != null && entry.getBigContentView() != null) {
   1962             bigContentView.reapply(mContext, entry.getBigContentView(),
   1963                     mOnClickHandler);
   1964         }
   1965         if (publicContentView != null && entry.getPublicContentView() != null) {
   1966             publicContentView.reapply(mContext, entry.getPublicContentView(), mOnClickHandler);
   1967         }
   1968         // update the contentIntent
   1969         final PendingIntent contentIntent = notification.getNotification().contentIntent;
   1970         if (contentIntent != null) {
   1971             final View.OnClickListener listener = makeClicker(contentIntent, notification.getKey(),
   1972                     isHeadsUp);
   1973             entry.row.setOnClickListener(listener);
   1974         } else {
   1975             entry.row.setOnClickListener(null);
   1976         }
   1977         entry.row.setStatusBarNotification(notification);
   1978         entry.row.notifyContentUpdated();
   1979         entry.row.resetHeight();
   1980     }
   1981 
   1982     protected void notifyHeadsUpScreenOn(boolean screenOn) {
   1983         if (!screenOn) {
   1984             scheduleHeadsUpEscalation();
   1985         }
   1986     }
   1987 
   1988     private boolean alertAgain(Entry oldEntry, Notification newNotification) {
   1989         return oldEntry == null || !oldEntry.hasInterrupted()
   1990                 || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
   1991     }
   1992 
   1993     protected boolean shouldInterrupt(StatusBarNotification sbn) {
   1994         if (mNotificationData.shouldFilterOut(sbn)) {
   1995             if (DEBUG) {
   1996                 Log.d(TAG, "Skipping HUN check for " + sbn.getKey() + " since it's filtered out.");
   1997             }
   1998             return false;
   1999         }
   2000 
   2001         Notification notification = sbn.getNotification();
   2002         // some predicates to make the boolean logic legible
   2003         boolean isNoisy = (notification.defaults & Notification.DEFAULT_SOUND) != 0
   2004                 || (notification.defaults & Notification.DEFAULT_VIBRATE) != 0
   2005                 || notification.sound != null
   2006                 || notification.vibrate != null;
   2007         boolean isHighPriority = sbn.getScore() >= INTERRUPTION_THRESHOLD;
   2008         boolean isFullscreen = notification.fullScreenIntent != null;
   2009         boolean hasTicker = mHeadsUpTicker && !TextUtils.isEmpty(notification.tickerText);
   2010         boolean isAllowed = notification.extras.getInt(Notification.EXTRA_AS_HEADS_UP,
   2011                 Notification.HEADS_UP_ALLOWED) != Notification.HEADS_UP_NEVER;
   2012         boolean accessibilityForcesLaunch = isFullscreen
   2013                 && mAccessibilityManager.isTouchExplorationEnabled();
   2014 
   2015         final KeyguardTouchDelegate keyguard = KeyguardTouchDelegate.getInstance(mContext);
   2016         boolean interrupt = (isFullscreen || (isHighPriority && (isNoisy || hasTicker)))
   2017                 && isAllowed
   2018                 && !accessibilityForcesLaunch
   2019                 && mPowerManager.isScreenOn()
   2020                 && !keyguard.isShowingAndNotOccluded()
   2021                 && !keyguard.isInputRestricted();
   2022         try {
   2023             interrupt = interrupt && !mDreamManager.isDreaming();
   2024         } catch (RemoteException e) {
   2025             Log.d(TAG, "failed to query dream manager", e);
   2026         }
   2027         if (DEBUG) Log.d(TAG, "interrupt: " + interrupt);
   2028         return interrupt;
   2029     }
   2030 
   2031     public boolean inKeyguardRestrictedInputMode() {
   2032         return KeyguardTouchDelegate.getInstance(mContext).isInputRestricted();
   2033     }
   2034 
   2035     public void setInteracting(int barWindow, boolean interacting) {
   2036         // hook for subclasses
   2037     }
   2038 
   2039     public void setBouncerShowing(boolean bouncerShowing) {
   2040         mBouncerShowing = bouncerShowing;
   2041     }
   2042 
   2043     /**
   2044      * @return Whether the security bouncer from Keyguard is showing.
   2045      */
   2046     public boolean isBouncerShowing() {
   2047         return mBouncerShowing;
   2048     }
   2049 
   2050     public void destroy() {
   2051         if (mSearchPanelView != null) {
   2052             mWindowManager.removeViewImmediate(mSearchPanelView);
   2053         }
   2054         mContext.unregisterReceiver(mBroadcastReceiver);
   2055         try {
   2056             mNotificationListener.unregisterAsSystemService();
   2057         } catch (RemoteException e) {
   2058             // Ignore.
   2059         }
   2060     }
   2061 
   2062     /**
   2063      * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then
   2064      *         return PackageManager for mContext
   2065      */
   2066     protected PackageManager getPackageManagerForUser(int userId) {
   2067         Context contextForUser = mContext;
   2068         // UserHandle defines special userId as negative values, e.g. USER_ALL
   2069         if (userId >= 0) {
   2070             try {
   2071                 // Create a context for the correct user so if a package isn't installed
   2072                 // for user 0 we can still load information about the package.
   2073                 contextForUser =
   2074                         mContext.createPackageContextAsUser(mContext.getPackageName(),
   2075                         Context.CONTEXT_RESTRICTED,
   2076                         new UserHandle(userId));
   2077             } catch (NameNotFoundException e) {
   2078                 // Shouldn't fail to find the package name for system ui.
   2079             }
   2080         }
   2081         return contextForUser.getPackageManager();
   2082     }
   2083 
   2084     @Override
   2085     public void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
   2086         try {
   2087             mBarService.onNotificationExpansionChanged(key, userAction, expanded);
   2088         } catch (RemoteException e) {
   2089             // Ignore.
   2090         }
   2091     }
   2092 }
   2093