OSDN Git Service

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