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