OSDN Git Service

b50555d817519163222e309b4a25be7a3d11a905
[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.app.ActivityManager;
22 import android.app.ActivityManager.StackId;
23 import android.app.ActivityManagerNative;
24 import android.app.ActivityOptions;
25 import android.app.KeyguardManager;
26 import android.app.Notification;
27 import android.app.NotificationManager;
28 import android.app.PendingIntent;
29 import android.app.RemoteInput;
30 import android.app.TaskStackBuilder;
31 import android.app.admin.DevicePolicyManager;
32 import android.content.BroadcastReceiver;
33 import android.content.ComponentName;
34 import android.content.Context;
35 import android.content.Intent;
36 import android.content.IntentFilter;
37 import android.content.IntentSender;
38 import android.content.pm.ApplicationInfo;
39 import android.content.pm.PackageManager;
40 import android.content.pm.PackageManager.NameNotFoundException;
41 import android.content.pm.UserInfo;
42 import android.content.res.Configuration;
43 import android.content.res.Resources;
44 import android.database.ContentObserver;
45 import android.graphics.Rect;
46 import android.graphics.drawable.Drawable;
47 import android.graphics.drawable.Icon;
48 import android.os.AsyncTask;
49 import android.os.Build;
50 import android.os.Bundle;
51 import android.os.Handler;
52 import android.os.IBinder;
53 import android.os.Message;
54 import android.os.PowerManager;
55 import android.os.RemoteException;
56 import android.os.ServiceManager;
57 import android.os.SystemProperties;
58 import android.os.UserHandle;
59 import android.os.UserManager;
60 import android.provider.Settings;
61 import android.service.dreams.DreamService;
62 import android.service.dreams.IDreamManager;
63 import android.service.notification.NotificationListenerService;
64 import android.service.notification.NotificationListenerService.RankingMap;
65 import android.service.notification.StatusBarNotification;
66 import android.service.vr.IVrManager;
67 import android.service.vr.IVrStateCallbacks;
68 import android.text.TextUtils;
69 import android.util.ArraySet;
70 import android.util.Log;
71 import android.util.Slog;
72 import android.util.SparseArray;
73 import android.util.SparseBooleanArray;
74 import android.view.Display;
75 import android.view.IWindowManager;
76 import android.view.LayoutInflater;
77 import android.view.MotionEvent;
78 import android.view.View;
79 import android.view.ViewAnimationUtils;
80 import android.view.ViewGroup;
81 import android.view.ViewParent;
82 import android.view.WindowManager;
83 import android.view.WindowManagerGlobal;
84 import android.view.accessibility.AccessibilityManager;
85 import android.widget.ImageView;
86 import android.widget.RemoteViews;
87 import android.widget.TextView;
88 import android.widget.Toast;
89
90 import com.android.internal.logging.MetricsLogger;
91 import com.android.internal.logging.MetricsProto.MetricsEvent;
92 import com.android.internal.statusbar.IStatusBarService;
93 import com.android.internal.statusbar.StatusBarIcon;
94 import com.android.internal.widget.LockPatternUtils;
95 import com.android.keyguard.KeyguardHostView.OnDismissAction;
96 import com.android.keyguard.KeyguardUpdateMonitor;
97 import com.android.systemui.DejankUtils;
98 import com.android.systemui.Interpolators;
99 import com.android.systemui.R;
100 import com.android.systemui.RecentsComponent;
101 import com.android.systemui.SwipeHelper;
102 import com.android.systemui.SystemUI;
103 import com.android.systemui.assist.AssistManager;
104 import com.android.systemui.recents.Recents;
105 import com.android.systemui.statusbar.NotificationData.Entry;
106 import com.android.systemui.statusbar.NotificationGuts.OnGutsClosedListener;
107 import com.android.systemui.statusbar.phone.NavigationBarView;
108 import com.android.systemui.statusbar.phone.NotificationGroupManager;
109 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
110 import com.android.systemui.statusbar.policy.HeadsUpManager;
111 import com.android.systemui.statusbar.policy.PreviewInflater;
112 import com.android.systemui.statusbar.policy.RemoteInputView;
113 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
114 import com.android.systemui.statusbar.stack.StackStateAnimator;
115
116 import java.util.ArrayList;
117 import java.util.List;
118 import java.util.Locale;
119
120 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
121
122 public abstract class BaseStatusBar extends SystemUI implements
123         CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
124         ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
125         ExpandableNotificationRow.OnExpandClickListener,
126         OnGutsClosedListener {
127     public static final String TAG = "StatusBar";
128     public static final boolean DEBUG = false;
129     public static final boolean MULTIUSER_DEBUG = false;
130
131     public static final boolean ENABLE_REMOTE_INPUT =
132             SystemProperties.getBoolean("debug.enable_remote_input", true);
133     public static final boolean ENABLE_CHILD_NOTIFICATIONS
134             = SystemProperties.getBoolean("debug.child_notifs", true);
135     public static final boolean FORCE_REMOTE_INPUT_HISTORY =
136             SystemProperties.getBoolean("debug.force_remoteinput_history", false);
137     private static boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false;
138
139     protected static final int MSG_SHOW_RECENT_APPS = 1019;
140     protected static final int MSG_HIDE_RECENT_APPS = 1020;
141     protected static final int MSG_TOGGLE_RECENTS_APPS = 1021;
142     protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
143     protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
144     protected static final int MSG_SHOW_NEXT_AFFILIATED_TASK = 1024;
145     protected static final int MSG_SHOW_PREV_AFFILIATED_TASK = 1025;
146     protected static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU = 1026;
147     protected static final int MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU = 1027;
148
149     protected static final boolean ENABLE_HEADS_UP = true;
150     protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
151
152     private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
153
154     // Should match the values in PhoneWindowManager
155     public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
156     public static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
157
158     private static final String BANNER_ACTION_CANCEL =
159             "com.android.systemui.statusbar.banner_action_cancel";
160     private static final String BANNER_ACTION_SETUP =
161             "com.android.systemui.statusbar.banner_action_setup";
162     private static final String WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION
163             = "com.android.systemui.statusbar.work_challenge_unlocked_notification_action";
164
165     protected CommandQueue mCommandQueue;
166     protected IStatusBarService mBarService;
167     protected H mHandler = createHandler();
168
169     // all notifications
170     protected NotificationData mNotificationData;
171     protected NotificationStackScrollLayout mStackScroller;
172
173     protected NotificationGroupManager mGroupManager = new NotificationGroupManager();
174
175     protected RemoteInputController mRemoteInputController;
176
177     // for heads up notifications
178     protected HeadsUpManager mHeadsUpManager;
179
180     protected int mCurrentUserId = 0;
181     final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
182
183     protected int mLayoutDirection = -1; // invalid
184     protected AccessibilityManager mAccessibilityManager;
185
186     // on-screen navigation buttons
187     protected NavigationBarView mNavigationBarView = null;
188
189     protected boolean mDeviceInteractive;
190
191     protected boolean mVisible;
192     protected ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>();
193     protected ArraySet<Entry> mRemoteInputEntriesToRemoveOnCollapse = new ArraySet<>();
194
195     /**
196      * Notifications with keys in this set are not actually around anymore. We kept them around
197      * when they were canceled in response to a remote input interaction. This allows us to show
198      * what you replied and allows you to continue typing into it.
199      */
200     protected ArraySet<String> mKeysKeptForRemoteInput = new ArraySet<>();
201
202     // mScreenOnFromKeyguard && mVisible.
203     private boolean mVisibleToUser;
204
205     private Locale mLocale;
206     private float mFontScale;
207
208     protected boolean mUseHeadsUp = false;
209     protected boolean mHeadsUpTicker = false;
210     protected boolean mDisableNotificationAlerts = false;
211
212     protected DevicePolicyManager mDevicePolicyManager;
213     protected IDreamManager mDreamManager;
214     protected PowerManager mPowerManager;
215     protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
216
217     // public mode, private notifications, etc
218     private boolean mLockscreenPublicMode = false;
219     private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
220     private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray();
221
222     private UserManager mUserManager;
223     private int mDensity;
224
225     private KeyguardManager mKeyguardManager;
226     private LockPatternUtils mLockPatternUtils;
227
228     // UI-specific methods
229
230     /**
231      * Create all windows necessary for the status bar (including navigation, overlay panels, etc)
232      * and add them to the window manager.
233      */
234     protected abstract void createAndAddWindows();
235
236     protected WindowManager mWindowManager;
237     protected IWindowManager mWindowManagerService;
238
239     protected abstract void refreshLayout(int layoutDirection);
240
241     protected Display mDisplay;
242
243     private boolean mDeviceProvisioned = false;
244
245     protected RecentsComponent mRecents;
246
247     protected int mZenMode;
248
249     // which notification is currently being longpress-examined by the user
250     private NotificationGuts mNotificationGutsExposed;
251
252     private KeyboardShortcuts mKeyboardShortcuts;
253
254     /**
255      * The {@link StatusBarState} of the status bar.
256      */
257     protected int mState;
258     protected boolean mBouncerShowing;
259     protected boolean mShowLockscreenNotifications;
260     protected boolean mAllowLockscreenRemoteInput;
261
262     protected NotificationOverflowContainer mKeyguardIconOverflowContainer;
263     protected DismissView mDismissView;
264     protected EmptyShadeView mEmptyShadeView;
265
266     private NotificationClicker mNotificationClicker = new NotificationClicker();
267
268     protected AssistManager mAssistManager;
269
270     protected boolean mVrMode;
271
272     @Override  // NotificationData.Environment
273     public boolean isDeviceProvisioned() {
274         return mDeviceProvisioned;
275     }
276
277     private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
278         @Override
279         public void onVrStateChanged(boolean enabled) {
280             mVrMode = enabled;
281         }
282     };
283
284     public boolean isDeviceInVrMode() {
285         return mVrMode;
286     }
287
288     protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
289         @Override
290         public void onChange(boolean selfChange) {
291             final boolean provisioned = 0 != Settings.Global.getInt(
292                     mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
293             if (provisioned != mDeviceProvisioned) {
294                 mDeviceProvisioned = provisioned;
295                 updateNotifications();
296             }
297             final int mode = Settings.Global.getInt(mContext.getContentResolver(),
298                     Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
299             setZenMode(mode);
300
301             updateLockscreenNotificationSetting();
302         }
303     };
304
305     private final ContentObserver mLockscreenSettingsObserver = new ContentObserver(mHandler) {
306         @Override
307         public void onChange(boolean selfChange) {
308             // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or
309             // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ...
310             mUsersAllowingPrivateNotifications.clear();
311             mUsersAllowingNotifications.clear();
312             // ... and refresh all the notifications
313             updateNotifications();
314         }
315     };
316
317     private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
318         @Override
319         public boolean onClickHandler(
320                 final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
321             if (handleRemoteInput(view, pendingIntent, fillInIntent)) {
322                 return true;
323             }
324
325             if (DEBUG) {
326                 Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
327             }
328             logActionClick(view);
329             // The intent we are sending is for the application, which
330             // won't have permission to immediately start an activity after
331             // the user switches to home.  We know it is safe to do at this
332             // point, so make sure new activity switches are now allowed.
333             try {
334                 ActivityManagerNative.getDefault().resumeAppSwitches();
335             } catch (RemoteException e) {
336             }
337             final boolean isActivity = pendingIntent.isActivity();
338             if (isActivity) {
339                 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
340                 final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
341                         mContext, pendingIntent.getIntent(), mCurrentUserId);
342                 dismissKeyguardThenExecute(new OnDismissAction() {
343                     @Override
344                     public boolean onDismiss() {
345                         if (keyguardShowing && !afterKeyguardGone) {
346                             try {
347                                 ActivityManagerNative.getDefault()
348                                         .keyguardWaitingForActivityDrawn();
349                                 ActivityManagerNative.getDefault().resumeAppSwitches();
350                             } catch (RemoteException e) {
351                             }
352                         }
353
354                         boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
355                         overrideActivityPendingAppTransition(keyguardShowing && !afterKeyguardGone);
356
357                         // close the shade if it was open
358                         if (handled) {
359                             animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
360                                     true /* force */);
361                             visibilityChanged(false);
362                             mAssistManager.hideAssist();
363                         }
364
365                         // Wait for activity start.
366                         return handled;
367                     }
368                 }, afterKeyguardGone);
369                 return true;
370             } else {
371                 return superOnClickHandler(view, pendingIntent, fillInIntent);
372             }
373         }
374
375         private void logActionClick(View view) {
376             ViewParent parent = view.getParent();
377             String key = getNotificationKeyForParent(parent);
378             if (key == null) {
379                 Log.w(TAG, "Couldn't determine notification for click.");
380                 return;
381             }
382             int index = -1;
383             // If this is a default template, determine the index of the button.
384             if (view.getId() == com.android.internal.R.id.action0 &&
385                     parent != null && parent instanceof ViewGroup) {
386                 ViewGroup actionGroup = (ViewGroup) parent;
387                 index = actionGroup.indexOfChild(view);
388             }
389             try {
390                 mBarService.onNotificationActionClick(key, index);
391             } catch (RemoteException e) {
392                 // Ignore
393             }
394         }
395
396         private String getNotificationKeyForParent(ViewParent parent) {
397             while (parent != null) {
398                 if (parent instanceof ExpandableNotificationRow) {
399                     return ((ExpandableNotificationRow) parent).getStatusBarNotification().getKey();
400                 }
401                 parent = parent.getParent();
402             }
403             return null;
404         }
405
406         private boolean superOnClickHandler(View view, PendingIntent pendingIntent,
407                 Intent fillInIntent) {
408             return super.onClickHandler(view, pendingIntent, fillInIntent,
409                     StackId.FULLSCREEN_WORKSPACE_STACK_ID);
410         }
411
412         private boolean handleRemoteInput(View view, PendingIntent pendingIntent, Intent fillInIntent) {
413             Object tag = view.getTag(com.android.internal.R.id.remote_input_tag);
414             RemoteInput[] inputs = null;
415             if (tag instanceof RemoteInput[]) {
416                 inputs = (RemoteInput[]) tag;
417             }
418
419             if (inputs == null) {
420                 return false;
421             }
422
423             RemoteInput input = null;
424
425             for (RemoteInput i : inputs) {
426                 if (i.getAllowFreeFormInput()) {
427                     input = i;
428                 }
429             }
430
431             if (input == null) {
432                 return false;
433             }
434
435             ViewParent p = view.getParent();
436             RemoteInputView riv = null;
437             while (p != null) {
438                 if (p instanceof View) {
439                     View pv = (View) p;
440                     if (pv.isRootNamespace()) {
441                         riv = (RemoteInputView) pv.findViewWithTag(RemoteInputView.VIEW_TAG);
442                         break;
443                     }
444                 }
445                 p = p.getParent();
446             }
447             ExpandableNotificationRow row = null;
448             while (p != null) {
449                 if (p instanceof ExpandableNotificationRow) {
450                     row = (ExpandableNotificationRow) p;
451                     break;
452                 }
453                 p = p.getParent();
454             }
455
456             if (riv == null || row == null) {
457                 return false;
458             }
459
460             row.setUserExpanded(true);
461
462             if (!mAllowLockscreenRemoteInput) {
463                 if (isLockscreenPublicMode()) {
464                     onLockedRemoteInput(row, view);
465                     return true;
466                 }
467                 final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
468                 if (mUserManager.getUserInfo(userId).isManagedProfile()
469                         && mKeyguardManager.isDeviceLocked(userId)) {
470                     onLockedWorkRemoteInput(userId, row, view);
471                     return true;
472                 }
473             }
474
475             riv.setVisibility(View.VISIBLE);
476             int cx = view.getLeft() + view.getWidth() / 2;
477             int cy = view.getTop() + view.getHeight() / 2;
478             int w = riv.getWidth();
479             int h = riv.getHeight();
480             int r = Math.max(
481                     Math.max(cx + cy, cx + (h - cy)),
482                     Math.max((w - cx) + cy, (w - cx) + (h - cy)));
483             ViewAnimationUtils.createCircularReveal(riv, cx, cy, 0, r)
484                     .start();
485
486             riv.setPendingIntent(pendingIntent);
487             riv.setRemoteInput(inputs, input);
488             riv.focus();
489
490             return true;
491         }
492
493     };
494
495     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
496         @Override
497         public void onReceive(Context context, Intent intent) {
498             String action = intent.getAction();
499             if (Intent.ACTION_USER_SWITCHED.equals(action)) {
500                 mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
501                 updateCurrentProfilesCache();
502                 if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
503
504                 updateLockscreenNotificationSetting();
505
506                 userSwitched(mCurrentUserId);
507             } else if (Intent.ACTION_USER_ADDED.equals(action)) {
508                 updateCurrentProfilesCache();
509             } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
510                 List<ActivityManager.RecentTaskInfo> recentTask = null;
511                 try {
512                     recentTask = ActivityManagerNative.getDefault().getRecentTasks(1,
513                             ActivityManager.RECENT_WITH_EXCLUDED
514                             | ActivityManager.RECENT_INCLUDE_PROFILES,
515                             mCurrentUserId);
516                 } catch (RemoteException e) {
517                     // Abandon hope activity manager not running.
518                 }
519                 if (recentTask != null && recentTask.size() > 0) {
520                     UserInfo user = mUserManager.getUserInfo(recentTask.get(0).userId);
521                     if (user != null && user.isManagedProfile()) {
522                         Toast toast = Toast.makeText(mContext,
523                                 R.string.managed_profile_foreground_toast,
524                                 Toast.LENGTH_SHORT);
525                         TextView text = (TextView) toast.getView().findViewById(
526                                 android.R.id.message);
527                         text.setCompoundDrawablesRelativeWithIntrinsicBounds(
528                                 R.drawable.stat_sys_managed_profile_status, 0, 0, 0);
529                         int paddingPx = mContext.getResources().getDimensionPixelSize(
530                                 R.dimen.managed_profile_toast_padding);
531                         text.setCompoundDrawablePadding(paddingPx);
532                         toast.show();
533                     }
534                 }
535             } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) {
536                 NotificationManager noMan = (NotificationManager)
537                         mContext.getSystemService(Context.NOTIFICATION_SERVICE);
538                 noMan.cancel(R.id.notification_hidden);
539
540                 Settings.Secure.putInt(mContext.getContentResolver(),
541                         Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
542                 if (BANNER_ACTION_SETUP.equals(action)) {
543                     animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
544                             true /* force */);
545                     mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION)
546                             .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
547
548                     );
549                 }
550             } else if (WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION.equals(action)) {
551                 final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT);
552                 final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
553                 if (intentSender != null) {
554                     try {
555                         mContext.startIntentSender(intentSender, null, 0, 0, 0);
556                     } catch (IntentSender.SendIntentException e) {
557                         /* ignore */
558                     }
559                 }
560                 if (notificationKey != null) {
561                     try {
562                         mBarService.onNotificationClick(notificationKey);
563                     } catch (RemoteException e) {
564                         /* ignore */
565                     }
566                 }
567                 onWorkChallengeUnlocked();
568             }
569         }
570     };
571
572     private final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
573         @Override
574         public void onReceive(Context context, Intent intent) {
575             String action = intent.getAction();
576             if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) &&
577                     isCurrentProfile(getSendingUserId())) {
578                 mUsersAllowingPrivateNotifications.clear();
579                 updateLockscreenNotificationSetting();
580                 updateNotifications();
581             }
582         }
583     };
584
585     private final NotificationListenerService mNotificationListener =
586             new NotificationListenerService() {
587         @Override
588         public void onListenerConnected() {
589             if (DEBUG) Log.d(TAG, "onListenerConnected");
590             final StatusBarNotification[] notifications = getActiveNotifications();
591             final RankingMap currentRanking = getCurrentRanking();
592             mHandler.post(new Runnable() {
593                 @Override
594                 public void run() {
595                     for (StatusBarNotification sbn : notifications) {
596                         addNotification(sbn, currentRanking, null /* oldEntry */);
597                     }
598                 }
599             });
600         }
601
602         @Override
603         public void onNotificationPosted(final StatusBarNotification sbn,
604                 final RankingMap rankingMap) {
605             if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
606             if (sbn != null) {
607                 mHandler.post(new Runnable() {
608                     @Override
609                     public void run() {
610                         processForRemoteInput(sbn.getNotification());
611                         String key = sbn.getKey();
612                         mKeysKeptForRemoteInput.remove(key);
613                         boolean isUpdate = mNotificationData.get(key) != null;
614                         // In case we don't allow child notifications, we ignore children of
615                         // notifications that have a summary, since we're not going to show them
616                         // anyway. This is true also when the summary is canceled,
617                         // because children are automatically canceled by NoMan in that case.
618                         if (!ENABLE_CHILD_NOTIFICATIONS
619                             && mGroupManager.isChildInGroupWithSummary(sbn)) {
620                             if (DEBUG) {
621                                 Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
622                             }
623
624                             // Remove existing notification to avoid stale data.
625                             if (isUpdate) {
626                                 removeNotification(key, rankingMap);
627                             } else {
628                                 mNotificationData.updateRanking(rankingMap);
629                             }
630                             return;
631                         }
632                         if (isUpdate) {
633                             updateNotification(sbn, rankingMap);
634                         } else {
635                             addNotification(sbn, rankingMap, null /* oldEntry */);
636                         }
637                     }
638                 });
639             }
640         }
641
642         @Override
643         public void onNotificationRemoved(StatusBarNotification sbn,
644                 final RankingMap rankingMap) {
645             if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
646             if (sbn != null) {
647                 final String key = sbn.getKey();
648                 mHandler.post(new Runnable() {
649                     @Override
650                     public void run() {
651                         removeNotification(key, rankingMap);
652                     }
653                 });
654             }
655         }
656
657         @Override
658         public void onNotificationRankingUpdate(final RankingMap rankingMap) {
659             if (DEBUG) Log.d(TAG, "onRankingUpdate");
660             if (rankingMap != null) {
661             mHandler.post(new Runnable() {
662                 @Override
663                 public void run() {
664                     updateNotificationRanking(rankingMap);
665                 }
666             });
667         }                            }
668
669     };
670
671     private void updateCurrentProfilesCache() {
672         synchronized (mCurrentProfiles) {
673             mCurrentProfiles.clear();
674             if (mUserManager != null) {
675                 for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
676                     mCurrentProfiles.put(user.id, user);
677                 }
678             }
679         }
680     }
681
682     public void start() {
683         mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
684         mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
685         mDisplay = mWindowManager.getDefaultDisplay();
686         mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService(
687                 Context.DEVICE_POLICY_SERVICE);
688
689         mNotificationData = new NotificationData(this);
690
691         mAccessibilityManager = (AccessibilityManager)
692                 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
693
694         mDreamManager = IDreamManager.Stub.asInterface(
695                 ServiceManager.checkService(DreamService.DREAM_SERVICE));
696         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
697
698         mContext.getContentResolver().registerContentObserver(
699                 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
700                 mSettingsObserver);
701         mContext.getContentResolver().registerContentObserver(
702                 Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
703                 mSettingsObserver);
704         mContext.getContentResolver().registerContentObserver(
705                 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
706                 mSettingsObserver,
707                 UserHandle.USER_ALL);
708         if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
709             mContext.getContentResolver().registerContentObserver(
710                     Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT),
711                     false,
712                     mSettingsObserver,
713                     UserHandle.USER_ALL);
714         }
715
716         mContext.getContentResolver().registerContentObserver(
717                 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
718                 true,
719                 mLockscreenSettingsObserver,
720                 UserHandle.USER_ALL);
721
722         mBarService = IStatusBarService.Stub.asInterface(
723                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
724
725         mRecents = getComponent(Recents.class);
726
727         final Configuration currentConfig = mContext.getResources().getConfiguration();
728         mLocale = currentConfig.locale;
729         mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
730         mFontScale = currentConfig.fontScale;
731         mDensity = currentConfig.densityDpi;
732
733         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
734         mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
735         mLockPatternUtils = new LockPatternUtils(mContext);
736
737         // Connect in to the status bar manager service
738         mCommandQueue = new CommandQueue(this);
739
740         int[] switches = new int[9];
741         ArrayList<IBinder> binders = new ArrayList<IBinder>();
742         ArrayList<String> iconSlots = new ArrayList<>();
743         ArrayList<StatusBarIcon> icons = new ArrayList<>();
744         Rect fullscreenStackBounds = new Rect();
745         Rect dockedStackBounds = new Rect();
746         try {
747             mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
748                     fullscreenStackBounds, dockedStackBounds);
749         } catch (RemoteException ex) {
750             // If the system process isn't there we're doomed anyway.
751         }
752
753         createAndAddWindows();
754
755         mSettingsObserver.onChange(false); // set up
756         disable(switches[0], switches[6], false /* animate */);
757         setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,
758                 fullscreenStackBounds, dockedStackBounds);
759         topAppWindowChanged(switches[2] != 0);
760         // StatusBarManagerService has a back up of IME token and it's restored here.
761         setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
762
763         // Set up the initial icon state
764         int N = iconSlots.size();
765         int viewIndex = 0;
766         for (int i=0; i < N; i++) {
767             setIcon(iconSlots.get(i), icons.get(i));
768         }
769
770         // Set up the initial notification state.
771         try {
772             mNotificationListener.registerAsSystemService(mContext,
773                     new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
774                     UserHandle.USER_ALL);
775         } catch (RemoteException e) {
776             Log.e(TAG, "Unable to register notification listener", e);
777         }
778
779
780         if (DEBUG) {
781             Log.d(TAG, String.format(
782                     "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
783                    icons.size(),
784                    switches[0],
785                    switches[1],
786                    switches[2],
787                    switches[3]
788                    ));
789         }
790
791         mCurrentUserId = ActivityManager.getCurrentUser();
792         setHeadsUpUser(mCurrentUserId);
793
794         IntentFilter filter = new IntentFilter();
795         filter.addAction(Intent.ACTION_USER_SWITCHED);
796         filter.addAction(Intent.ACTION_USER_ADDED);
797         filter.addAction(Intent.ACTION_USER_PRESENT);
798         mContext.registerReceiver(mBroadcastReceiver, filter);
799
800         IntentFilter internalFilter = new IntentFilter();
801         internalFilter.addAction(WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION);
802         internalFilter.addAction(BANNER_ACTION_CANCEL);
803         internalFilter.addAction(BANNER_ACTION_SETUP);
804         mContext.registerReceiver(mBroadcastReceiver, internalFilter, PERMISSION_SELF, null);
805
806         IntentFilter allUsersFilter = new IntentFilter();
807         allUsersFilter.addAction(
808                 DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
809         mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter,
810                 null, null);
811         updateCurrentProfilesCache();
812
813         IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService("vrmanager"));
814         try {
815             vrManager.registerListener(mVrStateCallbacks);
816         } catch (RemoteException e) {
817             Slog.e(TAG, "Failed to register VR mode state listener: " + e);
818         }
819
820     }
821
822     protected void notifyUserAboutHiddenNotifications() {
823         if (0 != Settings.Secure.getInt(mContext.getContentResolver(),
824                 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) {
825             Log.d(TAG, "user hasn't seen notification about hidden notifications");
826             if (!mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
827                 Log.d(TAG, "insecure lockscreen, skipping notification");
828                 Settings.Secure.putInt(mContext.getContentResolver(),
829                         Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
830                 return;
831             }
832             Log.d(TAG, "disabling lockecreen notifications and alerting the user");
833             // disable lockscreen notifications until user acts on the banner.
834             Settings.Secure.putInt(mContext.getContentResolver(),
835                     Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
836             Settings.Secure.putInt(mContext.getContentResolver(),
837                     Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
838
839             final String packageName = mContext.getPackageName();
840             PendingIntent cancelIntent = PendingIntent.getBroadcast(mContext, 0,
841                     new Intent(BANNER_ACTION_CANCEL).setPackage(packageName),
842                     PendingIntent.FLAG_CANCEL_CURRENT);
843             PendingIntent setupIntent = PendingIntent.getBroadcast(mContext, 0,
844                     new Intent(BANNER_ACTION_SETUP).setPackage(packageName),
845                     PendingIntent.FLAG_CANCEL_CURRENT);
846
847             final int colorRes = com.android.internal.R.color.system_notification_accent_color;
848             Notification.Builder note = new Notification.Builder(mContext)
849                     .setSmallIcon(R.drawable.ic_android)
850                     .setContentTitle(mContext.getString(R.string.hidden_notifications_title))
851                     .setContentText(mContext.getString(R.string.hidden_notifications_text))
852                     .setPriority(Notification.PRIORITY_HIGH)
853                     .setOngoing(true)
854                     .setColor(mContext.getColor(colorRes))
855                     .setContentIntent(setupIntent)
856                     .addAction(R.drawable.ic_close,
857                             mContext.getString(R.string.hidden_notifications_cancel),
858                             cancelIntent)
859                     .addAction(R.drawable.ic_settings,
860                             mContext.getString(R.string.hidden_notifications_setup),
861                             setupIntent);
862             overrideNotificationAppName(mContext, note);
863
864             NotificationManager noMan =
865                     (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
866             noMan.notify(R.id.notification_hidden, note.build());
867         }
868     }
869
870     public void userSwitched(int newUserId) {
871         setHeadsUpUser(newUserId);
872     }
873
874     protected abstract void setHeadsUpUser(int newUserId);
875
876     @Override  // NotificationData.Environment
877     public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
878         final int thisUserId = mCurrentUserId;
879         final int notificationUserId = n.getUserId();
880         if (DEBUG && MULTIUSER_DEBUG) {
881             Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
882                     n, thisUserId, notificationUserId));
883         }
884         return isCurrentProfile(notificationUserId);
885     }
886
887     protected void setNotificationShown(StatusBarNotification n) {
888         setNotificationsShown(new String[]{n.getKey()});
889     }
890
891     protected void setNotificationsShown(String[] keys) {
892         try {
893             mNotificationListener.setNotificationsShown(keys);
894         } catch (RuntimeException e) {
895             Log.d(TAG, "failed setNotificationsShown: ", e);
896         }
897     }
898
899     protected boolean isCurrentProfile(int userId) {
900         synchronized (mCurrentProfiles) {
901             return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
902         }
903     }
904
905     @Override
906     public String getCurrentMediaNotificationKey() {
907         return null;
908     }
909
910     @Override
911     public NotificationGroupManager getGroupManager() {
912         return mGroupManager;
913     }
914
915     /**
916      * Takes the necessary steps to prepare the status bar for starting an activity, then starts it.
917      * @param action A dismiss action that is called if it's safe to start the activity.
918      * @param afterKeyguardGone Whether the action should be executed after the Keyguard is gone.
919      */
920     protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) {
921         action.onDismiss();
922     }
923
924     @Override
925     protected void onConfigurationChanged(Configuration newConfig) {
926         final Locale locale = mContext.getResources().getConfiguration().locale;
927         final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
928         final float fontScale = newConfig.fontScale;
929         final int density = newConfig.densityDpi;
930         if (density != mDensity || mFontScale != fontScale) {
931             onDensityOrFontScaleChanged();
932             mDensity = density;
933             mFontScale = fontScale;
934         }
935         if (! locale.equals(mLocale) || ld != mLayoutDirection) {
936             if (DEBUG) {
937                 Log.v(TAG, String.format(
938                         "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
939                         locale, ld));
940             }
941             mLocale = locale;
942             mLayoutDirection = ld;
943             refreshLayout(ld);
944         }
945     }
946
947     protected void onDensityOrFontScaleChanged() {
948         ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
949         for (int i = 0; i < activeNotifications.size(); i++) {
950             Entry entry = activeNotifications.get(i);
951             boolean exposedGuts = entry.row.getGuts() == mNotificationGutsExposed;
952             entry.row.reInflateViews();
953             if (exposedGuts) {
954                 mNotificationGutsExposed = entry.row.getGuts();
955                 bindGuts(entry.row);
956             }
957             entry.cacheContentViews(mContext, null /* updatedNotification */);
958             inflateViews(entry, mStackScroller);
959         }
960     }
961
962     protected View bindVetoButtonClickListener(View row, final StatusBarNotification n) {
963         View vetoButton = row.findViewById(R.id.veto);
964         vetoButton.setOnClickListener(new View.OnClickListener() {
965             public void onClick(View v) {
966                 // Accessibility feedback
967                 v.announceForAccessibility(
968                         mContext.getString(R.string.accessibility_notification_dismissed));
969                 performRemoveNotification(n, false /* removeView */);
970             }
971         });
972         vetoButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
973         return vetoButton;
974     }
975
976     protected void performRemoveNotification(StatusBarNotification n, boolean removeView) {
977         final String pkg = n.getPackageName();
978         final String tag = n.getTag();
979         final int id = n.getId();
980         final int userId = n.getUserId();
981         try {
982             mBarService.onNotificationClear(pkg, tag, id, userId);
983             if (FORCE_REMOTE_INPUT_HISTORY
984                     && mKeysKeptForRemoteInput.contains(n.getKey())) {
985                 mKeysKeptForRemoteInput.remove(n.getKey());
986                 removeView = true;
987             }
988             if (mRemoteInputEntriesToRemoveOnCollapse.remove(mNotificationData.get(n.getKey()))) {
989                 removeView = true;
990             }
991             if (removeView) {
992                 removeNotification(n.getKey(), null);
993             }
994
995         } catch (RemoteException ex) {
996             // system process is dead if we're here.
997         }
998     }
999
1000
1001     protected void applyColorsAndBackgrounds(StatusBarNotification sbn,
1002             NotificationData.Entry entry) {
1003
1004         if (entry.getContentView().getId()
1005                 != com.android.internal.R.id.status_bar_latest_event_content) {
1006             // Using custom RemoteViews
1007             if (entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
1008                     && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP) {
1009                 entry.row.setShowingLegacyBackground(true);
1010                 entry.legacy = true;
1011             }
1012         }
1013
1014         if (entry.icon != null) {
1015             entry.icon.setTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
1016         }
1017     }
1018
1019     public boolean isMediaNotification(NotificationData.Entry entry) {
1020         // TODO: confirm that there's a valid media key
1021         return entry.getExpandedContentView() != null &&
1022                entry.getExpandedContentView()
1023                        .findViewById(com.android.internal.R.id.media_actions) != null;
1024     }
1025
1026     // The (i) button in the guts that links to the system notification settings for that app
1027     private void startAppNotificationSettingsActivity(String packageName, final int appUid) {
1028         final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
1029         intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
1030         intent.putExtra(Settings.EXTRA_APP_UID, appUid);
1031         startNotificationGutsIntent(intent, appUid);
1032     }
1033
1034     private void startNotificationGutsIntent(final Intent intent, final int appUid) {
1035         final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
1036         dismissKeyguardThenExecute(new OnDismissAction() {
1037             @Override
1038             public boolean onDismiss() {
1039                 AsyncTask.execute(new Runnable() {
1040                     public void run() {
1041                         try {
1042                             if (keyguardShowing) {
1043                                 ActivityManagerNative.getDefault()
1044                                         .keyguardWaitingForActivityDrawn();
1045                             }
1046                             TaskStackBuilder.create(mContext)
1047                                     .addNextIntentWithParentStack(intent)
1048                                     .startActivities(getActivityOptions(),
1049                                             new UserHandle(UserHandle.getUserId(appUid)));
1050                             overrideActivityPendingAppTransition(keyguardShowing);
1051                         } catch (RemoteException e) {
1052                         }
1053                     }
1054                 });
1055                 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
1056                 return true;
1057             }
1058         }, false /* afterKeyguardGone */);
1059     }
1060
1061     private void bindGuts(final ExpandableNotificationRow row) {
1062         row.inflateGuts();
1063         final StatusBarNotification sbn = row.getStatusBarNotification();
1064         PackageManager pmUser = getPackageManagerForUser(mContext, sbn.getUser().getIdentifier());
1065         row.setTag(sbn.getPackageName());
1066         final NotificationGuts guts = row.getGuts();
1067         guts.setClosedListener(this);
1068         final String pkg = sbn.getPackageName();
1069         String appname = pkg;
1070         Drawable pkgicon = null;
1071         int appUid = -1;
1072         try {
1073             final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
1074                     PackageManager.GET_UNINSTALLED_PACKAGES
1075                             | PackageManager.GET_DISABLED_COMPONENTS);
1076             if (info != null) {
1077                 appname = String.valueOf(pmUser.getApplicationLabel(info));
1078                 pkgicon = pmUser.getApplicationIcon(info);
1079                 appUid = info.uid;
1080             }
1081         } catch (NameNotFoundException e) {
1082             // app is gone, just show package name and generic icon
1083             pkgicon = pmUser.getDefaultActivityIcon();
1084         }
1085
1086         ((ImageView) guts.findViewById(R.id.app_icon)).setImageDrawable(pkgicon);
1087         ((TextView) guts.findViewById(R.id.pkgname)).setText(appname);
1088
1089         final View settingsButton = guts.findViewById(R.id.more_settings);
1090         if (appUid >= 0) {
1091             final int appUidF = appUid;
1092             settingsButton.setOnClickListener(new View.OnClickListener() {
1093                 public void onClick(View v) {
1094                     MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_INFO);
1095                     guts.resetFalsingCheck();
1096                     startAppNotificationSettingsActivity(pkg, appUidF);
1097                 }
1098             });
1099         } else {
1100             settingsButton.setVisibility(View.GONE);
1101         }
1102
1103         guts.bindImportance(pmUser, sbn, mNotificationData.getImportance(sbn.getKey()));
1104
1105         guts.findViewById(R.id.done).setOnClickListener(new View.OnClickListener() {
1106             @Override
1107             public void onClick(View v) {
1108                 // If the user has security enabled, show challenge if the setting is changed.
1109                 if (guts.hasImportanceChanged() && isLockscreenPublicMode() &&
1110                         (mState == StatusBarState.KEYGUARD
1111                         || mState == StatusBarState.SHADE_LOCKED)) {
1112                     OnDismissAction dismissAction = new OnDismissAction() {
1113                         @Override
1114                         public boolean onDismiss() {
1115                             saveImportanceCloseControls(sbn, row, guts, v);
1116                             return true;
1117                         }
1118                     };
1119                     onLockedNotificationImportanceChange(dismissAction);
1120                 } else {
1121                     saveImportanceCloseControls(sbn, row, guts, v);
1122                 }
1123             }
1124         });
1125     }
1126
1127     private void saveImportanceCloseControls(StatusBarNotification sbn,
1128             ExpandableNotificationRow row, NotificationGuts guts, View done) {
1129         guts.resetFalsingCheck();
1130         guts.saveImportance(sbn);
1131
1132         int[] rowLocation = new int[2];
1133         int[] doneLocation = new int[2];
1134         row.getLocationOnScreen(rowLocation);
1135         done.getLocationOnScreen(doneLocation);
1136
1137         final int centerX = done.getWidth() / 2;
1138         final int centerY = done.getHeight() / 2;
1139         final int x = doneLocation[0] - rowLocation[0] + centerX;
1140         final int y = doneLocation[1] - rowLocation[1] + centerY;
1141         dismissPopups(x, y);
1142     }
1143
1144     protected SwipeHelper.LongPressListener getNotificationLongClicker() {
1145         return new SwipeHelper.LongPressListener() {
1146             @Override
1147             public boolean onLongPress(View v, final int x, final int y) {
1148                 if (!(v instanceof ExpandableNotificationRow)) {
1149                     return false;
1150                 }
1151                 if (v.getWindowToken() == null) {
1152                     Log.e(TAG, "Trying to show notification guts, but not attached to window");
1153                     return false;
1154                 }
1155
1156                 final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1157                 bindGuts(row);
1158
1159                 // Assume we are a status_bar_notification_row
1160                 final NotificationGuts guts = row.getGuts();
1161                 if (guts == null) {
1162                     // This view has no guts. Examples are the more card or the dismiss all view
1163                     return false;
1164                 }
1165
1166                 // Already showing?
1167                 if (guts.getVisibility() == View.VISIBLE) {
1168                     dismissPopups(x, y);
1169                     return false;
1170                 }
1171
1172                 MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_CONTROLS);
1173
1174                 // ensure that it's laid but not visible until actually laid out
1175                 guts.setVisibility(View.INVISIBLE);
1176                 // Post to ensure the the guts are properly laid out.
1177                 guts.post(new Runnable() {
1178                     public void run() {
1179                         dismissPopups(-1 /* x */, -1 /* y */, false /* resetGear */,
1180                                 false /* animate */);
1181                         guts.setVisibility(View.VISIBLE);
1182                         final double horz = Math.max(guts.getWidth() - x, x);
1183                         final double vert = Math.max(guts.getHeight() - y, y);
1184                         final float r = (float) Math.hypot(horz, vert);
1185                         final Animator a
1186                                 = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
1187                         a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
1188                         a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
1189                         a.addListener(new AnimatorListenerAdapter() {
1190                             @Override
1191                             public void onAnimationEnd(Animator animation) {
1192                                 super.onAnimationEnd(animation);
1193                                 // Move the notification view back over the gear
1194                                 row.resetTranslation();
1195                             }
1196                         });
1197                         a.start();
1198                         guts.setExposed(true /* exposed */,
1199                                 mState == StatusBarState.KEYGUARD /* needsFalsingProtection */);
1200                         row.closeRemoteInput();
1201                         mStackScroller.onHeightChanged(null, true /* needsAnimation */);
1202                         mNotificationGutsExposed = guts;
1203                     }
1204                 });
1205                 return true;
1206             }
1207         };
1208     }
1209
1210     /**
1211      * Returns the exposed NotificationGuts or null if none are exposed.
1212      */
1213     public NotificationGuts getExposedGuts() {
1214         return mNotificationGutsExposed;
1215     }
1216
1217     public void dismissPopups() {
1218         dismissPopups(-1 /* x */, -1 /* y */, true /* resetGear */, false /* animate */);
1219     }
1220
1221     private void dismissPopups(int x, int y) {
1222         dismissPopups(x, y, true /* resetGear */, false /* animate */);
1223     }
1224
1225     public void dismissPopups(int x, int y, boolean resetGear, boolean animate) {
1226         if (mNotificationGutsExposed != null) {
1227             mNotificationGutsExposed.closeControls(x, y, true /* notify */);
1228         }
1229         if (resetGear) {
1230             mStackScroller.resetExposedGearView(animate, true /* force */);
1231         }
1232     }
1233
1234     @Override
1235     public void onGutsClosed(NotificationGuts guts) {
1236         mStackScroller.onHeightChanged(null, true /* needsAnimation */);
1237         mNotificationGutsExposed = null;
1238     }
1239
1240     @Override
1241     public void showRecentApps(boolean triggeredFromAltTab, boolean fromHome) {
1242         int msg = MSG_SHOW_RECENT_APPS;
1243         mHandler.removeMessages(msg);
1244         mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0, fromHome ? 1 : 0).sendToTarget();
1245     }
1246
1247     @Override
1248     public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
1249         int msg = MSG_HIDE_RECENT_APPS;
1250         mHandler.removeMessages(msg);
1251         mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0,
1252                 triggeredFromHomeKey ? 1 : 0).sendToTarget();
1253     }
1254
1255     @Override
1256     public void toggleRecentApps() {
1257         toggleRecents();
1258     }
1259
1260     @Override
1261     public void toggleSplitScreen() {
1262         toggleSplitScreenMode(-1 /* metricsDockAction */, -1 /* metricsUndockAction */);
1263     }
1264
1265     @Override
1266     public void preloadRecentApps() {
1267         int msg = MSG_PRELOAD_RECENT_APPS;
1268         mHandler.removeMessages(msg);
1269         mHandler.sendEmptyMessage(msg);
1270     }
1271
1272     @Override
1273     public void cancelPreloadRecentApps() {
1274         int msg = MSG_CANCEL_PRELOAD_RECENT_APPS;
1275         mHandler.removeMessages(msg);
1276         mHandler.sendEmptyMessage(msg);
1277     }
1278
1279     @Override
1280     public void dismissKeyboardShortcutsMenu() {
1281         int msg = MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU;
1282         mHandler.removeMessages(msg);
1283         mHandler.sendEmptyMessage(msg);
1284     }
1285
1286     @Override
1287     public void toggleKeyboardShortcutsMenu(int deviceId) {
1288         int msg = MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU;
1289         mHandler.removeMessages(msg);
1290         mHandler.obtainMessage(msg, deviceId, 0).sendToTarget();
1291     }
1292
1293     /** Jumps to the next affiliated task in the group. */
1294     public void showNextAffiliatedTask() {
1295         int msg = MSG_SHOW_NEXT_AFFILIATED_TASK;
1296         mHandler.removeMessages(msg);
1297         mHandler.sendEmptyMessage(msg);
1298     }
1299
1300     /** Jumps to the previous affiliated task in the group. */
1301     public void showPreviousAffiliatedTask() {
1302         int msg = MSG_SHOW_PREV_AFFILIATED_TASK;
1303         mHandler.removeMessages(msg);
1304         mHandler.sendEmptyMessage(msg);
1305     }
1306
1307     protected H createHandler() {
1308          return new H();
1309     }
1310
1311     protected void sendCloseSystemWindows(String reason) {
1312         if (ActivityManagerNative.isSystemReady()) {
1313             try {
1314                 ActivityManagerNative.getDefault().closeSystemDialogs(reason);
1315             } catch (RemoteException e) {
1316             }
1317         }
1318     }
1319
1320     protected abstract View getStatusBarView();
1321
1322     protected View.OnTouchListener mRecentsPreloadOnTouchListener = new View.OnTouchListener() {
1323         // additional optimization when we have software system buttons - start loading the recent
1324         // tasks on touch down
1325         @Override
1326         public boolean onTouch(View v, MotionEvent event) {
1327             int action = event.getAction() & MotionEvent.ACTION_MASK;
1328             if (action == MotionEvent.ACTION_DOWN) {
1329                 preloadRecents();
1330             } else if (action == MotionEvent.ACTION_CANCEL) {
1331                 cancelPreloadingRecents();
1332             } else if (action == MotionEvent.ACTION_UP) {
1333                 if (!v.isPressed()) {
1334                     cancelPreloadingRecents();
1335                 }
1336
1337             }
1338             return false;
1339         }
1340     };
1341
1342     /**
1343      * Toggle docking the app window
1344      *
1345      * @param metricsDockAction the action to log when docking is successful, or -1 to not log
1346      *                          anything on successful docking
1347      * @param metricsUndockAction the action to log when undocking, or -1 to not log anything when
1348      *                            undocking
1349      */
1350     protected abstract void toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction);
1351
1352     /** Proxy for RecentsComponent */
1353
1354     protected void showRecents(boolean triggeredFromAltTab, boolean fromHome) {
1355         if (mRecents != null) {
1356             sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
1357             mRecents.showRecents(triggeredFromAltTab, fromHome);
1358         }
1359     }
1360
1361     protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
1362         if (mRecents != null) {
1363             mRecents.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
1364         }
1365     }
1366
1367     protected void toggleRecents() {
1368         if (mRecents != null) {
1369             mRecents.toggleRecents(mDisplay);
1370         }
1371     }
1372
1373     protected void preloadRecents() {
1374         if (mRecents != null) {
1375             mRecents.preloadRecents();
1376         }
1377     }
1378
1379     protected void toggleKeyboardShortcuts(int deviceId) {
1380         KeyboardShortcuts.toggle(mContext, deviceId);
1381     }
1382
1383     protected void dismissKeyboardShortcuts() {
1384         KeyboardShortcuts.dismiss();
1385     }
1386
1387     protected void cancelPreloadingRecents() {
1388         if (mRecents != null) {
1389             mRecents.cancelPreloadingRecents();
1390         }
1391     }
1392
1393     protected void showRecentsNextAffiliatedTask() {
1394         if (mRecents != null) {
1395             mRecents.showNextAffiliatedTask();
1396         }
1397     }
1398
1399     protected void showRecentsPreviousAffiliatedTask() {
1400         if (mRecents != null) {
1401             mRecents.showPrevAffiliatedTask();
1402         }
1403     }
1404
1405     /**
1406      * If there is an active heads-up notification and it has a fullscreen intent, fire it now.
1407      */
1408     public abstract void maybeEscalateHeadsUp();
1409
1410     /**
1411      * Save the current "public" (locked and secure) state of the lockscreen.
1412      */
1413     public void setLockscreenPublicMode(boolean publicMode) {
1414         mLockscreenPublicMode = publicMode;
1415     }
1416
1417     public boolean isLockscreenPublicMode() {
1418         return mLockscreenPublicMode;
1419     }
1420
1421     protected void onWorkChallengeUnlocked() {}
1422
1423     /**
1424      * Has the given user chosen to allow notifications to be shown even when the lockscreen is in
1425      * "public" (secure & locked) mode?
1426      */
1427     public boolean userAllowsNotificationsInPublic(int userHandle) {
1428         if (userHandle == UserHandle.USER_ALL) {
1429             return true;
1430         }
1431
1432         if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
1433             final boolean allowed = 0 != Settings.Secure.getIntForUser(
1434                     mContext.getContentResolver(),
1435                     Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle);
1436             mUsersAllowingNotifications.append(userHandle, allowed);
1437             return allowed;
1438         }
1439
1440         return mUsersAllowingNotifications.get(userHandle);
1441     }
1442
1443     /**
1444      * Has the given user chosen to allow their private (full) notifications to be shown even
1445      * when the lockscreen is in "public" (secure & locked) mode?
1446      */
1447     public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
1448         if (userHandle == UserHandle.USER_ALL) {
1449             return true;
1450         }
1451
1452         if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
1453             final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
1454                     mContext.getContentResolver(),
1455                     Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
1456             final boolean allowedByDpm = adminAllowsUnredactedNotifications(userHandle);
1457             final boolean allowed = allowedByUser && allowedByDpm;
1458             mUsersAllowingPrivateNotifications.append(userHandle, allowed);
1459             return allowed;
1460         }
1461
1462         return mUsersAllowingPrivateNotifications.get(userHandle);
1463     }
1464
1465     private boolean adminAllowsUnredactedNotifications(int userHandle) {
1466         if (userHandle == UserHandle.USER_ALL) {
1467             return true;
1468         }
1469         final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */,
1470                     userHandle);
1471         return (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0;
1472     }
1473
1474     /**
1475      * Returns true if we're on a secure lockscreen and the user wants to hide notification data.
1476      * If so, notifications should be hidden.
1477      */
1478     @Override  // NotificationData.Environment
1479     public boolean shouldHideNotifications(int userid) {
1480         return isLockscreenPublicMode() && !userAllowsNotificationsInPublic(userid);
1481     }
1482
1483     /**
1484      * Returns true if we're on a secure lockscreen and the user wants to hide notifications via
1485      * package-specific override.
1486      */
1487     @Override // NotificationDate.Environment
1488     public boolean shouldHideNotifications(String key) {
1489         return isLockscreenPublicMode()
1490                 && mNotificationData.getVisibilityOverride(key) == Notification.VISIBILITY_SECRET;
1491     }
1492
1493     /**
1494      * Returns true if we're on a secure lockscreen.
1495      */
1496     @Override  // NotificationData.Environment
1497     public boolean onSecureLockScreen() {
1498         return isLockscreenPublicMode();
1499     }
1500
1501     public void onNotificationClear(StatusBarNotification notification) {
1502         try {
1503             mBarService.onNotificationClear(
1504                     notification.getPackageName(),
1505                     notification.getTag(),
1506                     notification.getId(),
1507                     notification.getUserId());
1508         } catch (android.os.RemoteException ex) {
1509             // oh well
1510         }
1511     }
1512
1513     /**
1514      * Called when the notification panel layouts
1515      */
1516     public void onPanelLaidOut() {
1517         if (mState == StatusBarState.KEYGUARD) {
1518             // Since the number of notifications is determined based on the height of the view, we
1519             // need to update them.
1520             int maxBefore = getMaxKeyguardNotifications(false /* recompute */);
1521             int maxNotifications = getMaxKeyguardNotifications(true /* recompute */);
1522             if (maxBefore != maxNotifications) {
1523                 updateRowStates();
1524             }
1525         }
1526     }
1527
1528     protected void onLockedNotificationImportanceChange(OnDismissAction dismissAction) {}
1529
1530     protected void onLockedRemoteInput(ExpandableNotificationRow row, View clickedView) {}
1531
1532     protected void onLockedWorkRemoteInput(int userId, ExpandableNotificationRow row,
1533             View clicked) {}
1534
1535     @Override
1536     public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
1537     }
1538
1539     protected class H extends Handler {
1540         public void handleMessage(Message m) {
1541             switch (m.what) {
1542              case MSG_SHOW_RECENT_APPS:
1543                  showRecents(m.arg1 > 0, m.arg2 != 0);
1544                  break;
1545              case MSG_HIDE_RECENT_APPS:
1546                  hideRecents(m.arg1 > 0, m.arg2 > 0);
1547                  break;
1548              case MSG_TOGGLE_RECENTS_APPS:
1549                  toggleRecents();
1550                  break;
1551              case MSG_PRELOAD_RECENT_APPS:
1552                   preloadRecents();
1553                   break;
1554              case MSG_CANCEL_PRELOAD_RECENT_APPS:
1555                   cancelPreloadingRecents();
1556                   break;
1557              case MSG_SHOW_NEXT_AFFILIATED_TASK:
1558                   showRecentsNextAffiliatedTask();
1559                   break;
1560              case MSG_SHOW_PREV_AFFILIATED_TASK:
1561                   showRecentsPreviousAffiliatedTask();
1562                   break;
1563              case MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU:
1564                   toggleKeyboardShortcuts(m.arg1);
1565                   break;
1566              case MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU:
1567                   dismissKeyboardShortcuts();
1568                   break;
1569             }
1570         }
1571     }
1572
1573     protected void workAroundBadLayerDrawableOpacity(View v) {
1574     }
1575
1576     protected boolean inflateViews(Entry entry, ViewGroup parent) {
1577         PackageManager pmUser = getPackageManagerForUser(mContext,
1578                 entry.notification.getUser().getIdentifier());
1579
1580         final StatusBarNotification sbn = entry.notification;
1581         entry.cacheContentViews(mContext, null);
1582
1583         final RemoteViews contentView = entry.cachedContentView;
1584         final RemoteViews bigContentView = entry.cachedBigContentView;
1585         final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView;
1586         final RemoteViews publicContentView = entry.cachedPublicContentView;
1587
1588         if (contentView == null) {
1589             Log.v(TAG, "no contentView for: " + sbn.getNotification());
1590             return false;
1591         }
1592
1593         if (DEBUG) {
1594             Log.v(TAG, "publicContentView: " + publicContentView);
1595         }
1596
1597         ExpandableNotificationRow row;
1598
1599         // Stash away previous user expansion state so we can restore it at
1600         // the end.
1601         boolean hasUserChangedExpansion = false;
1602         boolean userExpanded = false;
1603         boolean userLocked = false;
1604
1605         if (entry.row != null) {
1606             row = entry.row;
1607             hasUserChangedExpansion = row.hasUserChangedExpansion();
1608             userExpanded = row.isUserExpanded();
1609             userLocked = row.isUserLocked();
1610             entry.reset();
1611             if (hasUserChangedExpansion) {
1612                 row.setUserExpanded(userExpanded);
1613             }
1614         } else {
1615             // create the row view
1616             LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
1617                     Context.LAYOUT_INFLATER_SERVICE);
1618             row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row,
1619                     parent, false);
1620             row.setExpansionLogger(this, entry.notification.getKey());
1621             row.setGroupManager(mGroupManager);
1622             row.setHeadsUpManager(mHeadsUpManager);
1623             row.setRemoteInputController(mRemoteInputController);
1624             row.setOnExpandClickListener(this);
1625
1626             // Get the app name.
1627             // Note that Notification.Builder#bindHeaderAppName has similar logic
1628             // but since this field is used in the guts, it must be accurate.
1629             // Therefore we will only show the application label, or, failing that, the
1630             // package name. No substitutions.
1631             final String pkg = sbn.getPackageName();
1632             String appname = pkg;
1633             try {
1634                 final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
1635                         PackageManager.GET_UNINSTALLED_PACKAGES
1636                                 | PackageManager.GET_DISABLED_COMPONENTS);
1637                 if (info != null) {
1638                     appname = String.valueOf(pmUser.getApplicationLabel(info));
1639                 }
1640             } catch (NameNotFoundException e) {
1641                 // Do nothing
1642             }
1643             row.setAppName(appname);
1644         }
1645
1646         workAroundBadLayerDrawableOpacity(row);
1647         View vetoButton = bindVetoButtonClickListener(row, sbn);
1648         vetoButton.setContentDescription(mContext.getString(
1649                 R.string.accessibility_remove_notification));
1650
1651         // NB: the large icon is now handled entirely by the template
1652
1653         // bind the click event to the content area
1654         NotificationContentView contentContainer = row.getPrivateLayout();
1655         NotificationContentView contentContainerPublic = row.getPublicLayout();
1656
1657         row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
1658         if (ENABLE_REMOTE_INPUT) {
1659             row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
1660         }
1661
1662         mNotificationClicker.register(row, sbn);
1663
1664         // set up the adaptive layout
1665         View contentViewLocal = null;
1666         View bigContentViewLocal = null;
1667         View headsUpContentViewLocal = null;
1668         View publicViewLocal = null;
1669         try {
1670             contentViewLocal = contentView.apply(
1671                     sbn.getPackageContext(mContext),
1672                     contentContainer,
1673                     mOnClickHandler);
1674             if (bigContentView != null) {
1675                 bigContentViewLocal = bigContentView.apply(
1676                         sbn.getPackageContext(mContext),
1677                         contentContainer,
1678                         mOnClickHandler);
1679             }
1680             if (headsUpContentView != null) {
1681                 headsUpContentViewLocal = headsUpContentView.apply(
1682                         sbn.getPackageContext(mContext),
1683                         contentContainer,
1684                         mOnClickHandler);
1685             }
1686             if (publicContentView != null) {
1687                 publicViewLocal = publicContentView.apply(
1688                         sbn.getPackageContext(mContext),
1689                         contentContainerPublic, mOnClickHandler);
1690             }
1691         }
1692         catch (RuntimeException e) {
1693             final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
1694             Log.e(TAG, "couldn't inflate view for notification " + ident, e);
1695             return false;
1696         }
1697
1698         if (contentViewLocal != null) {
1699             contentViewLocal.setIsRootNamespace(true);
1700             contentContainer.setContractedChild(contentViewLocal);
1701         }
1702         if (bigContentViewLocal != null) {
1703             bigContentViewLocal.setIsRootNamespace(true);
1704             contentContainer.setExpandedChild(bigContentViewLocal);
1705         }
1706         if (headsUpContentViewLocal != null) {
1707             headsUpContentViewLocal.setIsRootNamespace(true);
1708             contentContainer.setHeadsUpChild(headsUpContentViewLocal);
1709         }
1710         if (publicViewLocal != null) {
1711             publicViewLocal.setIsRootNamespace(true);
1712             contentContainerPublic.setContractedChild(publicViewLocal);
1713         }
1714
1715         // Extract target SDK version.
1716         try {
1717             ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
1718             entry.targetSdk = info.targetSdkVersion;
1719         } catch (NameNotFoundException ex) {
1720             Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
1721         }
1722         entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
1723
1724         if (MULTIUSER_DEBUG) {
1725             TextView debug = (TextView) row.findViewById(R.id.debug_info);
1726             if (debug != null) {
1727                 debug.setVisibility(View.VISIBLE);
1728                 debug.setText("CU " + mCurrentUserId +" NU " + entry.notification.getUserId());
1729             }
1730         }
1731         entry.row = row;
1732         entry.row.setOnActivatedListener(this);
1733         entry.row.setExpandable(bigContentViewLocal != null);
1734
1735         applyColorsAndBackgrounds(sbn, entry);
1736
1737         // Restore previous flags.
1738         if (hasUserChangedExpansion) {
1739             // Note: setUserExpanded() conveniently ignores calls with
1740             //       userExpanded=true if !isExpandable().
1741             row.setUserExpanded(userExpanded);
1742         }
1743         row.setUserLocked(userLocked);
1744         row.onNotificationUpdated(entry);
1745         return true;
1746     }
1747
1748     /**
1749      * Adds RemoteInput actions from the WearableExtender; to be removed once more apps support this
1750      * via first-class API.
1751      *
1752      * TODO: Remove once enough apps specify remote inputs on their own.
1753      */
1754     private void processForRemoteInput(Notification n) {
1755         if (!ENABLE_REMOTE_INPUT) return;
1756
1757         if (n.extras != null && n.extras.containsKey("android.wearable.EXTENSIONS") &&
1758                 (n.actions == null || n.actions.length == 0)) {
1759             Notification.Action viableAction = null;
1760             Notification.WearableExtender we = new Notification.WearableExtender(n);
1761
1762             List<Notification.Action> actions = we.getActions();
1763             final int numActions = actions.size();
1764
1765             for (int i = 0; i < numActions; i++) {
1766                 Notification.Action action = actions.get(i);
1767                 if (action == null) {
1768                     continue;
1769                 }
1770                 RemoteInput[] remoteInputs = action.getRemoteInputs();
1771                 if (remoteInputs == null) {
1772                     continue;
1773                 }
1774                 for (RemoteInput ri : remoteInputs) {
1775                     if (ri.getAllowFreeFormInput()) {
1776                         viableAction = action;
1777                         break;
1778                     }
1779                 }
1780                 if (viableAction != null) {
1781                     break;
1782                 }
1783             }
1784
1785             if (viableAction != null) {
1786                 Notification.Builder rebuilder = Notification.Builder.recoverBuilder(mContext, n);
1787                 rebuilder.setActions(viableAction);
1788                 rebuilder.build(); // will rewrite n
1789             }
1790         }
1791     }
1792
1793     public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
1794         if (!isDeviceProvisioned()) return;
1795
1796         final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
1797         final boolean afterKeyguardGone = intent.isActivity()
1798                 && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
1799                 mCurrentUserId);
1800         dismissKeyguardThenExecute(new OnDismissAction() {
1801             public boolean onDismiss() {
1802                 new Thread() {
1803                     @Override
1804                     public void run() {
1805                         try {
1806                             if (keyguardShowing && !afterKeyguardGone) {
1807                                 ActivityManagerNative.getDefault()
1808                                         .keyguardWaitingForActivityDrawn();
1809                             }
1810
1811                             // The intent we are sending is for the application, which
1812                             // won't have permission to immediately start an activity after
1813                             // the user switches to home.  We know it is safe to do at this
1814                             // point, so make sure new activity switches are now allowed.
1815                             ActivityManagerNative.getDefault().resumeAppSwitches();
1816                         } catch (RemoteException e) {
1817                         }
1818                         try {
1819                             intent.send(null, 0, null, null, null, null, getActivityOptions());
1820                         } catch (PendingIntent.CanceledException e) {
1821                             // the stack trace isn't very helpful here.
1822                             // Just log the exception message.
1823                             Log.w(TAG, "Sending intent failed: " + e);
1824
1825                             // TODO: Dismiss Keyguard.
1826                         }
1827                         if (intent.isActivity()) {
1828                             mAssistManager.hideAssist();
1829                             overrideActivityPendingAppTransition(keyguardShowing
1830                                     && !afterKeyguardGone);
1831                         }
1832                     }
1833                 }.start();
1834
1835                 // close the shade if it was open
1836                 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
1837                         true /* force */, true /* delayed */);
1838                 visibilityChanged(false);
1839
1840                 return true;
1841             }
1842         }, afterKeyguardGone);
1843     }
1844
1845     public void addPostCollapseAction(Runnable r) {
1846     }
1847
1848     public boolean isCollapsing() {
1849         return false;
1850     }
1851
1852     private final class NotificationClicker implements View.OnClickListener {
1853         public void onClick(final View v) {
1854             if (!(v instanceof ExpandableNotificationRow)) {
1855                 Log.e(TAG, "NotificationClicker called on a view that is not a notification row.");
1856                 return;
1857             }
1858
1859             final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1860             final StatusBarNotification sbn = row.getStatusBarNotification();
1861             if (sbn == null) {
1862                 Log.e(TAG, "NotificationClicker called on an unclickable notification,");
1863                 return;
1864             }
1865
1866             // Check if the notification is displaying the gear, if so slide notification back
1867             if (row.getSettingsRow() != null && row.getSettingsRow().isVisible()) {
1868                 row.animateTranslateNotification(0);
1869                 return;
1870             }
1871
1872             Notification notification = sbn.getNotification();
1873             final PendingIntent intent = notification.contentIntent != null
1874                     ? notification.contentIntent
1875                     : notification.fullScreenIntent;
1876             final String notificationKey = sbn.getKey();
1877
1878             // Mark notification for one frame.
1879             row.setJustClicked(true);
1880             DejankUtils.postAfterTraversal(new Runnable() {
1881                 @Override
1882                 public void run() {
1883                     row.setJustClicked(false);
1884                 }
1885             });
1886
1887             final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
1888             final boolean afterKeyguardGone = intent.isActivity()
1889                     && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
1890                             mCurrentUserId);
1891             dismissKeyguardThenExecute(new OnDismissAction() {
1892                 public boolean onDismiss() {
1893                     if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) {
1894                         // Release the HUN notification to the shade.
1895
1896                         if (isPanelFullyCollapsed()) {
1897                             HeadsUpManager.setIsClickedNotification(row, true);
1898                         }
1899                         //
1900                         // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
1901                         // become canceled shortly by NoMan, but we can't assume that.
1902                         mHeadsUpManager.releaseImmediately(notificationKey);
1903                     }
1904                     StatusBarNotification parentToCancel = null;
1905                     if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) {
1906                         StatusBarNotification summarySbn = mGroupManager.getLogicalGroupSummary(sbn)
1907                                         .getStatusBarNotification();
1908                         if (shouldAutoCancel(summarySbn)) {
1909                             parentToCancel = summarySbn;
1910                         }
1911                     }
1912                     final StatusBarNotification parentToCancelFinal = parentToCancel;
1913                     new Thread() {
1914                         @Override
1915                         public void run() {
1916                             try {
1917                                 if (keyguardShowing && !afterKeyguardGone) {
1918                                     ActivityManagerNative.getDefault()
1919                                             .keyguardWaitingForActivityDrawn();
1920                                 }
1921
1922                                 // The intent we are sending is for the application, which
1923                                 // won't have permission to immediately start an activity after
1924                                 // the user switches to home.  We know it is safe to do at this
1925                                 // point, so make sure new activity switches are now allowed.
1926                                 ActivityManagerNative.getDefault().resumeAppSwitches();
1927                             } catch (RemoteException e) {
1928                             }
1929                             if (intent != null) {
1930                                 // If we are launching a work activity and require to launch
1931                                 // separate work challenge, we defer the activity action and cancel
1932                                 // notification until work challenge is unlocked.
1933                                 if (intent.isActivity()) {
1934                                     final int userId = intent.getCreatorUserHandle()
1935                                             .getIdentifier();
1936                                     if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
1937                                             && mKeyguardManager.isDeviceLocked(userId)) {
1938                                         if (startWorkChallengeIfNecessary(userId,
1939                                                 intent.getIntentSender(), notificationKey)) {
1940                                             // Show work challenge, do not run pendingintent and
1941                                             // remove notification
1942                                             return;
1943                                         }
1944                                     }
1945                                 }
1946                                 try {
1947                                     intent.send(null, 0, null, null, null, null,
1948                                             getActivityOptions());
1949                                 } catch (PendingIntent.CanceledException e) {
1950                                     // the stack trace isn't very helpful here.
1951                                     // Just log the exception message.
1952                                     Log.w(TAG, "Sending contentIntent failed: " + e);
1953
1954                                     // TODO: Dismiss Keyguard.
1955                                 }
1956                                 if (intent.isActivity()) {
1957                                     mAssistManager.hideAssist();
1958                                     overrideActivityPendingAppTransition(keyguardShowing
1959                                             && !afterKeyguardGone);
1960                                 }
1961                             }
1962
1963                             try {
1964                                 mBarService.onNotificationClick(notificationKey);
1965                             } catch (RemoteException ex) {
1966                                 // system process is dead if we're here.
1967                             }
1968                             if (parentToCancelFinal != null) {
1969                                 // We have to post it to the UI thread for synchronization
1970                                 mHandler.post(new Runnable() {
1971                                     @Override
1972                                     public void run() {
1973                                         Runnable removeRunnable = new Runnable() {
1974                                             @Override
1975                                             public void run() {
1976                                                 performRemoveNotification(parentToCancelFinal,
1977                                                         true);
1978                                             }
1979                                         };
1980                                         if (isCollapsing()) {
1981                                             // To avoid lags we're only performing the remove
1982                                             // after the shade was collapsed
1983                                             addPostCollapseAction(removeRunnable);
1984                                         } else {
1985                                             removeRunnable.run();
1986                                         }
1987                                     }
1988                                 });
1989                             }
1990                         }
1991                     }.start();
1992
1993                     // close the shade if it was open
1994                     animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
1995                             true /* force */, true /* delayed */);
1996                     visibilityChanged(false);
1997
1998                     return true;
1999                 }
2000             }, afterKeyguardGone);
2001         }
2002
2003         private boolean shouldAutoCancel(StatusBarNotification sbn) {
2004             int flags = sbn.getNotification().flags;
2005             if ((flags & Notification.FLAG_AUTO_CANCEL) != Notification.FLAG_AUTO_CANCEL) {
2006                 return false;
2007             }
2008             if ((flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
2009                 return false;
2010             }
2011             return true;
2012         }
2013
2014         public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
2015             Notification notification = sbn.getNotification();
2016             if (notification.contentIntent != null || notification.fullScreenIntent != null) {
2017                 row.setOnClickListener(this);
2018             } else {
2019                 row.setOnClickListener(null);
2020             }
2021         }
2022     }
2023
2024     public void animateCollapsePanels(int flags, boolean force) {
2025     }
2026
2027     public void animateCollapsePanels(int flags, boolean force, boolean delayed) {
2028     }
2029
2030     public void overrideActivityPendingAppTransition(boolean keyguardShowing) {
2031         if (keyguardShowing) {
2032             try {
2033                 mWindowManagerService.overridePendingAppTransition(null, 0, 0, null);
2034             } catch (RemoteException e) {
2035                 Log.w(TAG, "Error overriding app transition: " + e);
2036             }
2037         }
2038     }
2039
2040     protected boolean startWorkChallengeIfNecessary(int userId, IntentSender intendSender,
2041             String notificationKey) {
2042         final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null,
2043                 null, userId);
2044         if (newIntent == null) {
2045             return false;
2046         }
2047         final Intent callBackIntent = new Intent(
2048                 WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION);
2049         callBackIntent.putExtra(Intent.EXTRA_INTENT, intendSender);
2050         callBackIntent.putExtra(Intent.EXTRA_INDEX, notificationKey);
2051         callBackIntent.setPackage(mContext.getPackageName());
2052
2053         PendingIntent callBackPendingIntent = PendingIntent.getBroadcast(
2054                 mContext,
2055                 0,
2056                 callBackIntent,
2057                 PendingIntent.FLAG_CANCEL_CURRENT |
2058                         PendingIntent.FLAG_ONE_SHOT |
2059                         PendingIntent.FLAG_IMMUTABLE);
2060         newIntent.putExtra(
2061                 Intent.EXTRA_INTENT,
2062                 callBackPendingIntent.getIntentSender());
2063         try {
2064             ActivityManagerNative.getDefault().startConfirmDeviceCredentialIntent(newIntent);
2065         } catch (RemoteException ex) {
2066             // ignore
2067         }
2068         return true;
2069     }
2070
2071     protected Bundle getActivityOptions() {
2072         // Anything launched from the notification shade should always go into the
2073         // fullscreen stack.
2074         ActivityOptions options = ActivityOptions.makeBasic();
2075         options.setLaunchStackId(StackId.FULLSCREEN_WORKSPACE_STACK_ID);
2076         return options.toBundle();
2077     }
2078
2079     protected void visibilityChanged(boolean visible) {
2080         if (mVisible != visible) {
2081             mVisible = visible;
2082             if (!visible) {
2083                 dismissPopups();
2084             }
2085         }
2086         updateVisibleToUser();
2087     }
2088
2089     protected void updateVisibleToUser() {
2090         boolean oldVisibleToUser = mVisibleToUser;
2091         mVisibleToUser = mVisible && mDeviceInteractive;
2092
2093         if (oldVisibleToUser != mVisibleToUser) {
2094             handleVisibleToUserChanged(mVisibleToUser);
2095         }
2096     }
2097
2098     /**
2099      * The LEDs are turned off when the notification panel is shown, even just a little bit.
2100      * See also PhoneStatusBar.setPanelExpanded for another place where we attempt to do this.
2101      */
2102     protected void handleVisibleToUserChanged(boolean visibleToUser) {
2103         try {
2104             if (visibleToUser) {
2105                 boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
2106                 boolean clearNotificationEffects =
2107                         !isPanelFullyCollapsed() &&
2108                         (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED);
2109                 int notificationLoad = mNotificationData.getActiveNotifications().size();
2110                 if (pinnedHeadsUp && isPanelFullyCollapsed())  {
2111                     notificationLoad = 1;
2112                 } else {
2113                     MetricsLogger.histogram(mContext, "note_load", notificationLoad);
2114                 }
2115                 mBarService.onPanelRevealed(clearNotificationEffects, notificationLoad);
2116             } else {
2117                 mBarService.onPanelHidden();
2118             }
2119         } catch (RemoteException ex) {
2120             // Won't fail unless the world has ended.
2121         }
2122     }
2123
2124     /**
2125      * Clear Buzz/Beep/Blink.
2126      */
2127     public void clearNotificationEffects() {
2128         try {
2129             mBarService.clearNotificationEffects();
2130         } catch (RemoteException e) {
2131             // Won't fail unless the world has ended.
2132         }
2133     }
2134
2135     public abstract boolean isPanelFullyCollapsed();
2136
2137     /**
2138      * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
2139      * about the failure.
2140      *
2141      * WARNING: this will call back into us.  Don't hold any locks.
2142      */
2143     void handleNotificationError(StatusBarNotification n, String message) {
2144         removeNotification(n.getKey(), null);
2145         try {
2146             mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
2147                     n.getInitialPid(), message, n.getUserId());
2148         } catch (RemoteException ex) {
2149             // The end is nigh.
2150         }
2151     }
2152
2153     protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) {
2154         NotificationData.Entry entry = mNotificationData.remove(key, ranking);
2155         if (entry == null) {
2156             Log.w(TAG, "removeNotification for unknown key: " + key);
2157             return null;
2158         }
2159         updateNotifications();
2160         return entry.notification;
2161     }
2162
2163     protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) {
2164         if (DEBUG) {
2165             Log.d(TAG, "createNotificationViews(notification=" + sbn);
2166         }
2167         final StatusBarIconView iconView = createIcon(sbn);
2168         if (iconView == null) {
2169             return null;
2170         }
2171
2172         // Construct the expanded view.
2173         NotificationData.Entry entry = new NotificationData.Entry(sbn, iconView);
2174         if (!inflateViews(entry, mStackScroller)) {
2175             handleNotificationError(sbn, "Couldn't expand RemoteViews for: " + sbn);
2176             return null;
2177         }
2178         return entry;
2179     }
2180
2181     public StatusBarIconView createIcon(StatusBarNotification sbn) {
2182         // Construct the icon.
2183         Notification n = sbn.getNotification();
2184         final StatusBarIconView iconView = new StatusBarIconView(mContext,
2185                 sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), n);
2186         iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
2187
2188         final Icon smallIcon = n.getSmallIcon();
2189         if (smallIcon == null) {
2190             handleNotificationError(sbn,
2191                     "No small icon in notification from " + sbn.getPackageName());
2192             return null;
2193         }
2194         final StatusBarIcon ic = new StatusBarIcon(
2195                 sbn.getUser(),
2196                 sbn.getPackageName(),
2197                 smallIcon,
2198                 n.iconLevel,
2199                 n.number,
2200                 StatusBarIconView.contentDescForNotification(mContext, n));
2201         if (!iconView.set(ic)) {
2202             handleNotificationError(sbn, "Couldn't create icon: " + ic);
2203             return null;
2204         }
2205         return iconView;
2206     }
2207
2208     protected void addNotificationViews(Entry entry, RankingMap ranking) {
2209         if (entry == null) {
2210             return;
2211         }
2212         // Add the expanded view and icon.
2213         mNotificationData.add(entry, ranking);
2214         updateNotifications();
2215     }
2216
2217     /**
2218      * @param recompute wheter the number should be recomputed
2219      * @return The number of notifications we show on Keyguard.
2220      */
2221     protected abstract int getMaxKeyguardNotifications(boolean recompute);
2222
2223     /**
2224      * Updates expanded, dimmed and locked states of notification rows.
2225      */
2226     protected void updateRowStates() {
2227         mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
2228
2229         ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
2230         final int N = activeNotifications.size();
2231
2232         int visibleNotifications = 0;
2233         boolean onKeyguard = mState == StatusBarState.KEYGUARD;
2234         int maxNotifications = 0;
2235         if (onKeyguard) {
2236             maxNotifications = getMaxKeyguardNotifications(true /* recompute */);
2237         }
2238         for (int i = 0; i < N; i++) {
2239             NotificationData.Entry entry = activeNotifications.get(i);
2240             boolean childNotification = mGroupManager.isChildInGroupWithSummary(entry.notification);
2241             if (onKeyguard) {
2242                 entry.row.setOnKeyguard(true);
2243             } else {
2244                 entry.row.setOnKeyguard(false);
2245                 entry.row.setSystemExpanded(visibleNotifications == 0 && !childNotification);
2246             }
2247             boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
2248                     entry.notification) && !entry.row.isRemoved();
2249             boolean childWithVisibleSummary = childNotification
2250                     && mGroupManager.getGroupSummary(entry.notification).getVisibility()
2251                     == View.VISIBLE;
2252             boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
2253             if (suppressedSummary || (isLockscreenPublicMode() && !mShowLockscreenNotifications) ||
2254                     (onKeyguard && !childWithVisibleSummary
2255                             && (visibleNotifications >= maxNotifications || !showOnKeyguard))) {
2256                 entry.row.setVisibility(View.GONE);
2257                 if (onKeyguard && showOnKeyguard && !childNotification && !suppressedSummary) {
2258                     mKeyguardIconOverflowContainer.getIconsView().addNotification(entry);
2259                 }
2260             } else {
2261                 boolean wasGone = entry.row.getVisibility() == View.GONE;
2262                 entry.row.setVisibility(View.VISIBLE);
2263                 if (!childNotification && !entry.row.isRemoved()) {
2264                     if (wasGone) {
2265                         // notify the scroller of a child addition
2266                         mStackScroller.generateAddAnimation(entry.row,
2267                                 !showOnKeyguard /* fromMoreCard */);
2268                     }
2269                     visibleNotifications++;
2270                 }
2271             }
2272         }
2273
2274         mStackScroller.updateOverflowContainerVisibility(onKeyguard
2275                 && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0);
2276
2277         mStackScroller.changeViewPosition(mDismissView, mStackScroller.getChildCount() - 1);
2278         mStackScroller.changeViewPosition(mEmptyShadeView, mStackScroller.getChildCount() - 2);
2279         mStackScroller.changeViewPosition(mKeyguardIconOverflowContainer,
2280                 mStackScroller.getChildCount() - 3);
2281     }
2282
2283     public boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
2284         return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey());
2285     }
2286
2287     protected void setZenMode(int mode) {
2288         if (!isDeviceProvisioned()) return;
2289         mZenMode = mode;
2290         updateNotifications();
2291     }
2292
2293     // extended in PhoneStatusBar
2294     protected void setShowLockscreenNotifications(boolean show) {
2295         mShowLockscreenNotifications = show;
2296     }
2297
2298     protected void setLockScreenAllowRemoteInput(boolean allowLockscreenRemoteInput) {
2299         mAllowLockscreenRemoteInput = allowLockscreenRemoteInput;
2300     }
2301
2302     private void updateLockscreenNotificationSetting() {
2303         final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
2304                 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
2305                 1,
2306                 mCurrentUserId) != 0;
2307         final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
2308                 null /* admin */, mCurrentUserId);
2309         final boolean allowedByDpm = (dpmFlags
2310                 & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
2311
2312         setShowLockscreenNotifications(show && allowedByDpm);
2313
2314         if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
2315             final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(),
2316                     Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
2317                     0,
2318                     mCurrentUserId) != 0;
2319             final boolean remoteInputDpm =
2320                     (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT) == 0;
2321
2322             setLockScreenAllowRemoteInput(remoteInput && remoteInputDpm);
2323         } else {
2324             setLockScreenAllowRemoteInput(false);
2325         }
2326     }
2327
2328     protected abstract void setAreThereNotifications();
2329     protected abstract void updateNotifications();
2330     public abstract boolean shouldDisableNavbarGestures();
2331
2332     public abstract void addNotification(StatusBarNotification notification,
2333             RankingMap ranking, Entry oldEntry);
2334     protected abstract void updateNotificationRanking(RankingMap ranking);
2335     public abstract void removeNotification(String key, RankingMap ranking);
2336
2337     public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
2338         if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
2339
2340         final String key = notification.getKey();
2341         Entry entry = mNotificationData.get(key);
2342         if (entry == null) {
2343             return;
2344         } else {
2345             mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
2346             mRemoteInputEntriesToRemoveOnCollapse.remove(entry);
2347         }
2348
2349         Notification n = notification.getNotification();
2350         mNotificationData.updateRanking(ranking);
2351
2352         boolean applyInPlace = entry.cacheContentViews(mContext, notification.getNotification());
2353         boolean shouldPeek = shouldPeek(entry, notification);
2354         boolean alertAgain = alertAgain(entry, n);
2355         if (DEBUG) {
2356             Log.d(TAG, "applyInPlace=" + applyInPlace
2357                     + " shouldPeek=" + shouldPeek
2358                     + " alertAgain=" + alertAgain);
2359         }
2360
2361         final StatusBarNotification oldNotification = entry.notification;
2362         entry.notification = notification;
2363         mGroupManager.onEntryUpdated(entry, oldNotification);
2364
2365         boolean updateSuccessful = false;
2366         if (applyInPlace) {
2367             if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
2368             try {
2369                 if (entry.icon != null) {
2370                     // Update the icon
2371                     final StatusBarIcon ic = new StatusBarIcon(
2372                             notification.getUser(),
2373                             notification.getPackageName(),
2374                             n.getSmallIcon(),
2375                             n.iconLevel,
2376                             n.number,
2377                             StatusBarIconView.contentDescForNotification(mContext, n));
2378                     entry.icon.setNotification(n);
2379                     if (!entry.icon.set(ic)) {
2380                         handleNotificationError(notification, "Couldn't update icon: " + ic);
2381                         return;
2382                     }
2383                 }
2384                 updateNotificationViews(entry, notification);
2385                 updateSuccessful = true;
2386             }
2387             catch (RuntimeException e) {
2388                 // It failed to apply cleanly.
2389                 Log.w(TAG, "Couldn't reapply views for package " +
2390                         notification.getPackageName(), e);
2391             }
2392         }
2393         if (!updateSuccessful) {
2394             if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);
2395             final StatusBarIcon ic = new StatusBarIcon(
2396                     notification.getUser(),
2397                     notification.getPackageName(),
2398                     n.getSmallIcon(),
2399                     n.iconLevel,
2400                     n.number,
2401                     StatusBarIconView.contentDescForNotification(mContext, n));
2402             entry.icon.setNotification(n);
2403             entry.icon.set(ic);
2404             inflateViews(entry, mStackScroller);
2405         }
2406         updateHeadsUp(key, entry, shouldPeek, alertAgain);
2407         updateNotifications();
2408
2409         // Update the veto button accordingly (and as a result, whether this row is
2410         // swipe-dismissable)
2411         bindVetoButtonClickListener(entry.row, notification);
2412
2413         if (!notification.isClearable()) {
2414             // The user may have performed a dismiss action on the notification, since it's
2415             // not clearable we should snap it back.
2416             mStackScroller.snapViewIfNeeded(entry.row);
2417         }
2418
2419         if (DEBUG) {
2420             // Is this for you?
2421             boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);
2422             Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
2423         }
2424
2425         setAreThereNotifications();
2426     }
2427
2428     protected abstract void updateHeadsUp(String key, Entry entry, boolean shouldPeek,
2429             boolean alertAgain);
2430
2431     private void updateNotificationViews(Entry entry, StatusBarNotification sbn) {
2432         final RemoteViews contentView = entry.cachedContentView;
2433         final RemoteViews bigContentView = entry.cachedBigContentView;
2434         final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView;
2435         final RemoteViews publicContentView = entry.cachedPublicContentView;
2436
2437         // Reapply the RemoteViews
2438         contentView.reapply(mContext, entry.getContentView(), mOnClickHandler);
2439         if (bigContentView != null && entry.getExpandedContentView() != null) {
2440             bigContentView.reapply(sbn.getPackageContext(mContext),
2441                     entry.getExpandedContentView(),
2442                     mOnClickHandler);
2443         }
2444         View headsUpChild = entry.getHeadsUpContentView();
2445         if (headsUpContentView != null && headsUpChild != null) {
2446             headsUpContentView.reapply(sbn.getPackageContext(mContext),
2447                     headsUpChild, mOnClickHandler);
2448         }
2449         if (publicContentView != null && entry.getPublicContentView() != null) {
2450             publicContentView.reapply(sbn.getPackageContext(mContext),
2451                     entry.getPublicContentView(), mOnClickHandler);
2452         }
2453         // update the contentIntent
2454         mNotificationClicker.register(entry.row, sbn);
2455
2456         entry.row.onNotificationUpdated(entry);
2457         entry.row.resetHeight();
2458     }
2459
2460     protected void updatePublicContentView(Entry entry,
2461             StatusBarNotification sbn) {
2462         final RemoteViews publicContentView = entry.cachedPublicContentView;
2463         View inflatedView = entry.getPublicContentView();
2464         if (entry.autoRedacted && publicContentView != null && inflatedView != null) {
2465             final boolean disabledByPolicy =
2466                     !adminAllowsUnredactedNotifications(entry.notification.getUserId());
2467             String notificationHiddenText = mContext.getString(disabledByPolicy
2468                     ? com.android.internal.R.string.notification_hidden_by_policy_text
2469                     : com.android.internal.R.string.notification_hidden_text);
2470             TextView titleView = (TextView) inflatedView.findViewById(android.R.id.title);
2471             if (titleView != null
2472                     && !titleView.getText().toString().equals(notificationHiddenText)) {
2473                 publicContentView.setTextViewText(android.R.id.title, notificationHiddenText);
2474                 publicContentView.reapply(sbn.getPackageContext(mContext),
2475                         inflatedView, mOnClickHandler);
2476                 entry.row.onNotificationUpdated(entry);
2477             }
2478         }
2479     }
2480
2481     protected void notifyHeadsUpScreenOff() {
2482         maybeEscalateHeadsUp();
2483     }
2484
2485     private boolean alertAgain(Entry oldEntry, Notification newNotification) {
2486         return oldEntry == null || !oldEntry.hasInterrupted()
2487                 || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
2488     }
2489
2490     protected boolean shouldPeek(Entry entry) {
2491         return shouldPeek(entry, entry.notification);
2492     }
2493
2494     protected boolean shouldPeek(Entry entry, StatusBarNotification sbn) {
2495         if (!mUseHeadsUp || isDeviceInVrMode()) {
2496             return false;
2497         }
2498
2499         if (mNotificationData.shouldFilterOut(sbn)) {
2500             if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey());
2501             return false;
2502         }
2503
2504         boolean inUse = mPowerManager.isScreenOn()
2505                 && (!mStatusBarKeyguardViewManager.isShowing()
2506                 || mStatusBarKeyguardViewManager.isOccluded())
2507                 && !mStatusBarKeyguardViewManager.isInputRestricted();
2508         try {
2509             inUse = inUse && !mDreamManager.isDreaming();
2510         } catch (RemoteException e) {
2511             Log.d(TAG, "failed to query dream manager", e);
2512         }
2513
2514         if (!inUse) {
2515             if (DEBUG) {
2516                 Log.d(TAG, "No peeking: not in use: " + sbn.getKey());
2517             }
2518             return false;
2519         }
2520
2521         if (mNotificationData.shouldSuppressScreenOn(sbn.getKey())) {
2522             if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
2523             return false;
2524         }
2525
2526         if (entry.hasJustLaunchedFullScreenIntent()) {
2527             if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey());
2528             return false;
2529         }
2530
2531         if (isSnoozedPackage(sbn)) {
2532             if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
2533             return false;
2534         }
2535
2536         if (mNotificationData.getImportance(sbn.getKey()) < IMPORTANCE_HIGH) {
2537             if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());
2538             return false;
2539         }
2540
2541         if (sbn.getNotification().fullScreenIntent != null) {
2542             if (mAccessibilityManager.isTouchExplorationEnabled()) {
2543                 if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
2544                 return false;
2545             } else {
2546                 return true;
2547             }
2548         }
2549
2550         return true;
2551     }
2552
2553     protected abstract boolean isSnoozedPackage(StatusBarNotification sbn);
2554
2555     public void setInteracting(int barWindow, boolean interacting) {
2556         // hook for subclasses
2557     }
2558
2559     public void setBouncerShowing(boolean bouncerShowing) {
2560         mBouncerShowing = bouncerShowing;
2561     }
2562
2563     /**
2564      * @return Whether the security bouncer from Keyguard is showing.
2565      */
2566     public boolean isBouncerShowing() {
2567         return mBouncerShowing;
2568     }
2569
2570     public void destroy() {
2571         mContext.unregisterReceiver(mBroadcastReceiver);
2572         try {
2573             mNotificationListener.unregisterAsSystemService();
2574         } catch (RemoteException e) {
2575             // Ignore.
2576         }
2577     }
2578
2579     /**
2580      * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then
2581      *         return PackageManager for mContext
2582      */
2583     public static PackageManager getPackageManagerForUser(Context context, int userId) {
2584         Context contextForUser = context;
2585         // UserHandle defines special userId as negative values, e.g. USER_ALL
2586         if (userId >= 0) {
2587             try {
2588                 // Create a context for the correct user so if a package isn't installed
2589                 // for user 0 we can still load information about the package.
2590                 contextForUser =
2591                         context.createPackageContextAsUser(context.getPackageName(),
2592                         Context.CONTEXT_RESTRICTED,
2593                         new UserHandle(userId));
2594             } catch (NameNotFoundException e) {
2595                 // Shouldn't fail to find the package name for system ui.
2596             }
2597         }
2598         return contextForUser.getPackageManager();
2599     }
2600
2601     @Override
2602     public void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
2603         try {
2604             mBarService.onNotificationExpansionChanged(key, userAction, expanded);
2605         } catch (RemoteException e) {
2606             // Ignore.
2607         }
2608     }
2609
2610     public boolean isKeyguardSecure() {
2611         if (mStatusBarKeyguardViewManager == null) {
2612             // startKeyguard() hasn't been called yet, so we don't know.
2613             // Make sure anything that needs to know isKeyguardSecure() checks and re-checks this
2614             // value onVisibilityChanged().
2615             Slog.w(TAG, "isKeyguardSecure() called before startKeyguard(), returning false",
2616                     new Throwable());
2617             return false;
2618         }
2619         return mStatusBarKeyguardViewManager.isSecure();
2620     }
2621
2622     @Override
2623     public void showAssistDisclosure() {
2624         if (mAssistManager != null) {
2625             mAssistManager.showDisclosure();
2626         }
2627     }
2628
2629     @Override
2630     public void startAssist(Bundle args) {
2631         if (mAssistManager != null) {
2632             mAssistManager.startAssist(args);
2633         }
2634     }
2635 }