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