OSDN Git Service

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