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