OSDN Git Service

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