2 * Copyright (C) 2010 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.systemui.statusbar;
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;
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;
115 import java.util.ArrayList;
116 import java.util.List;
117 import java.util.Locale;
119 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
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;
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;
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;
148 protected static final boolean ENABLE_HEADS_UP = true;
149 protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
151 private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
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";
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";
164 protected CommandQueue mCommandQueue;
165 protected IStatusBarService mBarService;
166 protected H mHandler = createHandler();
169 protected NotificationData mNotificationData;
170 protected NotificationStackScrollLayout mStackScroller;
172 protected NotificationGroupManager mGroupManager = new NotificationGroupManager();
174 protected RemoteInputController mRemoteInputController;
176 // for heads up notifications
177 protected HeadsUpManager mHeadsUpManager;
179 protected int mCurrentUserId = 0;
180 final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
182 protected int mLayoutDirection = -1; // invalid
183 protected AccessibilityManager mAccessibilityManager;
185 // on-screen navigation buttons
186 protected NavigationBarView mNavigationBarView = null;
188 protected boolean mDeviceInteractive;
190 protected boolean mVisible;
191 protected ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>();
192 protected ArraySet<Entry> mRemoteInputEntriesToRemoveOnCollapse = new ArraySet<>();
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.
199 protected ArraySet<String> mKeysKeptForRemoteInput = new ArraySet<>();
201 // mScreenOnFromKeyguard && mVisible.
202 private boolean mVisibleToUser;
204 private Locale mLocale;
205 private float mFontScale;
207 protected boolean mUseHeadsUp = false;
208 protected boolean mHeadsUpTicker = false;
209 protected boolean mDisableNotificationAlerts = false;
211 protected DevicePolicyManager mDevicePolicyManager;
212 protected IDreamManager mDreamManager;
213 protected PowerManager mPowerManager;
214 protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
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();
221 private UserManager mUserManager;
222 private int mDensity;
224 private KeyguardManager mKeyguardManager;
225 private LockPatternUtils mLockPatternUtils;
227 // UI-specific methods
230 * Create all windows necessary for the status bar (including navigation, overlay panels, etc)
231 * and add them to the window manager.
233 protected abstract void createAndAddWindows();
235 protected WindowManager mWindowManager;
236 protected IWindowManager mWindowManagerService;
238 protected abstract void refreshLayout(int layoutDirection);
240 protected Display mDisplay;
242 private boolean mDeviceProvisioned = false;
244 protected RecentsComponent mRecents;
246 protected int mZenMode;
248 // which notification is currently being longpress-examined by the user
249 private NotificationGuts mNotificationGutsExposed;
251 private KeyboardShortcuts mKeyboardShortcuts;
254 * The {@link StatusBarState} of the status bar.
256 protected int mState;
257 protected boolean mBouncerShowing;
258 protected boolean mShowLockscreenNotifications;
259 protected boolean mAllowLockscreenRemoteInput;
261 protected NotificationOverflowContainer mKeyguardIconOverflowContainer;
262 protected DismissView mDismissView;
263 protected EmptyShadeView mEmptyShadeView;
265 private NotificationClicker mNotificationClicker = new NotificationClicker();
267 protected AssistManager mAssistManager;
269 protected boolean mVrMode;
271 @Override // NotificationData.Environment
272 public boolean isDeviceProvisioned() {
273 return mDeviceProvisioned;
276 private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
278 public void onVrStateChanged(boolean enabled) {
283 public boolean isDeviceInVrMode() {
287 protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
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();
296 final int mode = Settings.Global.getInt(mContext.getContentResolver(),
297 Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
300 updateLockscreenNotificationSetting();
304 private final ContentObserver mLockscreenSettingsObserver = new ContentObserver(mHandler) {
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();
316 private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
318 public boolean onClickHandler(
319 final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
320 if (handleRemoteInput(view, pendingIntent, fillInIntent)) {
325 Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
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.
333 ActivityManagerNative.getDefault().resumeAppSwitches();
334 } catch (RemoteException e) {
336 final boolean isActivity = pendingIntent.isActivity();
338 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
339 final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
340 mContext, pendingIntent.getIntent(), mCurrentUserId);
341 dismissKeyguardThenExecute(new OnDismissAction() {
343 public boolean onDismiss() {
344 if (keyguardShowing && !afterKeyguardGone) {
346 ActivityManagerNative.getDefault()
347 .keyguardWaitingForActivityDrawn();
348 ActivityManagerNative.getDefault().resumeAppSwitches();
349 } catch (RemoteException e) {
353 boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
354 overrideActivityPendingAppTransition(keyguardShowing && !afterKeyguardGone);
356 // close the shade if it was open
358 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
360 visibilityChanged(false);
361 mAssistManager.hideAssist();
364 // Wait for activity start.
367 }, afterKeyguardGone);
370 return superOnClickHandler(view, pendingIntent, fillInIntent);
374 private void logActionClick(View view) {
375 ViewParent parent = view.getParent();
376 String key = getNotificationKeyForParent(parent);
378 Log.w(TAG, "Couldn't determine notification for click.");
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);
389 mBarService.onNotificationActionClick(key, index);
390 } catch (RemoteException e) {
395 private String getNotificationKeyForParent(ViewParent parent) {
396 while (parent != null) {
397 if (parent instanceof ExpandableNotificationRow) {
398 return ((ExpandableNotificationRow) parent).getStatusBarNotification().getKey();
400 parent = parent.getParent();
405 private boolean superOnClickHandler(View view, PendingIntent pendingIntent,
406 Intent fillInIntent) {
407 return super.onClickHandler(view, pendingIntent, fillInIntent,
408 StackId.FULLSCREEN_WORKSPACE_STACK_ID);
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;
418 if (inputs == null) {
422 RemoteInput input = null;
424 for (RemoteInput i : inputs) {
425 if (i.getAllowFreeFormInput()) {
434 ViewParent p = view.getParent();
435 RemoteInputView riv = null;
437 if (p instanceof View) {
439 if (pv.isRootNamespace()) {
440 riv = (RemoteInputView) pv.findViewWithTag(RemoteInputView.VIEW_TAG);
446 ExpandableNotificationRow row = null;
448 if (p instanceof ExpandableNotificationRow) {
449 row = (ExpandableNotificationRow) p;
455 if (riv == null || row == null) {
459 row.setUserExpanded(true);
461 if (!mAllowLockscreenRemoteInput) {
462 if (isLockscreenPublicMode()) {
463 onLockedRemoteInput(row, view);
466 final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
467 if (mUserManager.getUserInfo(userId).isManagedProfile()
468 && mKeyguardManager.isDeviceLocked(userId)) {
469 onLockedWorkRemoteInput(userId, row, view);
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();
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)
485 riv.setPendingIntent(pendingIntent);
486 riv.setRemoteInput(inputs, input);
494 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
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");
503 updateLockscreenNotificationSetting();
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;
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.
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,
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);
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);
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,
544 mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION)
545 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
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) {
554 mContext.startIntentSender(intentSender, null, 0, 0, 0);
555 } catch (IntentSender.SendIntentException e) {
559 if (notificationKey != null) {
561 mBarService.onNotificationClick(notificationKey);
562 } catch (RemoteException e) {
566 onWorkChallengeUnlocked();
571 private final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
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();
584 private final NotificationListenerService mNotificationListener =
585 new NotificationListenerService() {
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() {
594 for (StatusBarNotification sbn : notifications) {
595 addNotification(sbn, currentRanking, null /* oldEntry */);
602 public void onNotificationPosted(final StatusBarNotification sbn,
603 final RankingMap rankingMap) {
604 if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
606 mHandler.post(new Runnable() {
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)) {
620 Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
623 // Remove existing notification to avoid stale data.
625 removeNotification(key, rankingMap);
627 mNotificationData.updateRanking(rankingMap);
632 updateNotification(sbn, rankingMap);
634 addNotification(sbn, rankingMap, null /* oldEntry */);
642 public void onNotificationRemoved(StatusBarNotification sbn,
643 final RankingMap rankingMap) {
644 if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
646 final String key = sbn.getKey();
647 mHandler.post(new Runnable() {
650 removeNotification(key, rankingMap);
657 public void onNotificationRankingUpdate(final RankingMap rankingMap) {
658 if (DEBUG) Log.d(TAG, "onRankingUpdate");
659 if (rankingMap != null) {
660 mHandler.post(new Runnable() {
663 updateNotificationRanking(rankingMap);
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);
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);
688 mNotificationData = new NotificationData(this);
690 mAccessibilityManager = (AccessibilityManager)
691 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
693 mDreamManager = IDreamManager.Stub.asInterface(
694 ServiceManager.checkService(DreamService.DREAM_SERVICE));
695 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
697 mContext.getContentResolver().registerContentObserver(
698 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
700 mContext.getContentResolver().registerContentObserver(
701 Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
703 mContext.getContentResolver().registerContentObserver(
704 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
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),
712 UserHandle.USER_ALL);
715 mContext.getContentResolver().registerContentObserver(
716 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
718 mLockscreenSettingsObserver,
719 UserHandle.USER_ALL);
721 mBarService = IStatusBarService.Stub.asInterface(
722 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
724 mRecents = getComponent(Recents.class);
726 final Configuration currentConfig = mContext.getResources().getConfiguration();
727 mLocale = currentConfig.locale;
728 mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
729 mFontScale = currentConfig.fontScale;
730 mDensity = currentConfig.densityDpi;
732 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
733 mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
734 mLockPatternUtils = new LockPatternUtils(mContext);
736 // Connect in to the status bar manager service
737 mCommandQueue = new CommandQueue(this);
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();
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.
752 createAndAddWindows();
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);
762 // Set up the initial icon state
763 int N = iconSlots.size();
765 for (int i=0; i < N; i++) {
766 setIcon(iconSlots.get(i), icons.get(i));
769 // Set up the initial notification state.
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);
780 Log.d(TAG, String.format(
781 "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
790 mCurrentUserId = ActivityManager.getCurrentUser();
791 setHeadsUpUser(mCurrentUserId);
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);
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);
805 IntentFilter allUsersFilter = new IntentFilter();
806 allUsersFilter.addAction(
807 DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
808 mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter,
810 updateCurrentProfilesCache();
812 IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService("vrmanager"));
814 vrManager.registerListener(mVrStateCallbacks);
815 } catch (RemoteException e) {
816 Slog.e(TAG, "Failed to register VR mode state listener: " + e);
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);
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);
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);
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)
853 .setColor(mContext.getColor(colorRes))
854 .setContentIntent(setupIntent)
855 .addAction(R.drawable.ic_close,
856 mContext.getString(R.string.hidden_notifications_cancel),
858 .addAction(R.drawable.ic_settings,
859 mContext.getString(R.string.hidden_notifications_setup),
861 overrideNotificationAppName(mContext, note);
863 NotificationManager noMan =
864 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
865 noMan.notify(R.id.notification_hidden, note.build());
869 public void userSwitched(int newUserId) {
870 setHeadsUpUser(newUserId);
873 protected abstract void setHeadsUpUser(int newUserId);
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));
883 return isCurrentProfile(notificationUserId);
886 protected void setNotificationShown(StatusBarNotification n) {
887 setNotificationsShown(new String[]{n.getKey()});
890 protected void setNotificationsShown(String[] keys) {
892 mNotificationListener.setNotificationsShown(keys);
893 } catch (RuntimeException e) {
894 Log.d(TAG, "failed setNotificationsShown: ", e);
898 protected boolean isCurrentProfile(int userId) {
899 synchronized (mCurrentProfiles) {
900 return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
905 public String getCurrentMediaNotificationKey() {
910 public NotificationGroupManager getGroupManager() {
911 return mGroupManager;
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.
919 protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) {
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();
932 mFontScale = fontScale;
934 if (! locale.equals(mLocale) || ld != mLayoutDirection) {
936 Log.v(TAG, String.format(
937 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
941 mLayoutDirection = ld;
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();
953 mNotificationGutsExposed = entry.row.getGuts();
956 entry.cacheContentViews(mContext, null /* updatedNotification */);
957 inflateViews(entry, mStackScroller);
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 */);
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();
978 mBarService.onNotificationClear(pkg, tag, id, userId);
979 if (FORCE_REMOTE_INPUT_HISTORY
980 && mKeysKeptForRemoteInput.contains(n.getKey())) {
981 mKeysKeptForRemoteInput.remove(n.getKey());
984 if (mRemoteInputEntriesToRemoveOnCollapse.remove(mNotificationData.get(n.getKey()))) {
988 removeNotification(n.getKey(), null);
991 } catch (RemoteException ex) {
992 // system process is dead if we're here.
997 protected void applyColorsAndBackgrounds(StatusBarNotification sbn,
998 NotificationData.Entry entry) {
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;
1010 if (entry.icon != null) {
1011 entry.icon.setTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
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;
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);
1030 private void startNotificationGutsIntent(final Intent intent, final int appUid) {
1031 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
1032 dismissKeyguardThenExecute(new OnDismissAction() {
1034 public boolean onDismiss() {
1035 AsyncTask.execute(new Runnable() {
1038 if (keyguardShowing) {
1039 ActivityManagerNative.getDefault()
1040 .keyguardWaitingForActivityDrawn();
1042 TaskStackBuilder.create(mContext)
1043 .addNextIntentWithParentStack(intent)
1044 .startActivities(getActivityOptions(),
1045 new UserHandle(UserHandle.getUserId(appUid)));
1046 overrideActivityPendingAppTransition(keyguardShowing);
1047 } catch (RemoteException e) {
1051 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
1054 }, false /* afterKeyguardGone */);
1057 private void bindGuts(final ExpandableNotificationRow row) {
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;
1069 final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
1070 PackageManager.GET_UNINSTALLED_PACKAGES
1071 | PackageManager.GET_DISABLED_COMPONENTS);
1073 appname = String.valueOf(pmUser.getApplicationLabel(info));
1074 pkgicon = pmUser.getApplicationIcon(info);
1077 } catch (NameNotFoundException e) {
1078 // app is gone, just show package name and generic icon
1079 pkgicon = pmUser.getDefaultActivityIcon();
1082 ((ImageView) guts.findViewById(R.id.app_icon)).setImageDrawable(pkgicon);
1083 ((TextView) guts.findViewById(R.id.pkgname)).setText(appname);
1085 final TextView settingsButton = (TextView) guts.findViewById(R.id.more_settings);
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);
1095 settingsButton.setText(R.string.notification_more_settings);
1097 settingsButton.setVisibility(View.GONE);
1100 guts.bindImportance(pmUser, sbn, mNotificationData.getImportance(sbn.getKey()));
1102 final TextView doneButton = (TextView) guts.findViewById(R.id.done);
1103 doneButton.setText(R.string.notification_done);
1104 doneButton.setOnClickListener(new View.OnClickListener() {
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() {
1113 public boolean onDismiss() {
1114 saveImportanceCloseControls(sbn, row, guts, v);
1118 onLockedNotificationImportanceChange(dismissAction);
1120 saveImportanceCloseControls(sbn, row, guts, v);
1126 private void saveImportanceCloseControls(StatusBarNotification sbn,
1127 ExpandableNotificationRow row, NotificationGuts guts, View done) {
1128 guts.resetFalsingCheck();
1129 guts.saveImportance(sbn);
1131 int[] rowLocation = new int[2];
1132 int[] doneLocation = new int[2];
1133 row.getLocationOnScreen(rowLocation);
1134 done.getLocationOnScreen(doneLocation);
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);
1143 protected SwipeHelper.LongPressListener getNotificationLongClicker() {
1144 return new SwipeHelper.LongPressListener() {
1146 public boolean onLongPress(View v, final int x, final int y) {
1147 if (!(v instanceof ExpandableNotificationRow)) {
1150 if (v.getWindowToken() == null) {
1151 Log.e(TAG, "Trying to show notification guts, but not attached to window");
1155 final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1158 // Assume we are a status_bar_notification_row
1159 final NotificationGuts guts = row.getGuts();
1161 // This view has no guts. Examples are the more card or the dismiss all view
1166 if (guts.getVisibility() == View.VISIBLE) {
1167 dismissPopups(x, y);
1171 MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_CONTROLS);
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() {
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);
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() {
1190 public void onAnimationEnd(Animator animation) {
1191 super.onAnimationEnd(animation);
1192 // Move the notification view back over the gear
1193 row.resetTranslation();
1197 guts.setExposed(true /* exposed */,
1198 mState == StatusBarState.KEYGUARD /* needsFalsingProtection */);
1199 row.closeRemoteInput();
1200 mStackScroller.onHeightChanged(null, true /* needsAnimation */);
1201 mNotificationGutsExposed = guts;
1210 * Returns the exposed NotificationGuts or null if none are exposed.
1212 public NotificationGuts getExposedGuts() {
1213 return mNotificationGutsExposed;
1216 public void dismissPopups() {
1217 dismissPopups(-1 /* x */, -1 /* y */, true /* resetGear */, false /* animate */);
1220 private void dismissPopups(int x, int y) {
1221 dismissPopups(x, y, true /* resetGear */, false /* animate */);
1224 public void dismissPopups(int x, int y, boolean resetGear, boolean animate) {
1225 if (mNotificationGutsExposed != null) {
1226 mNotificationGutsExposed.closeControls(x, y, true /* notify */);
1229 mStackScroller.resetExposedGearView(animate, true /* force */);
1234 public void onGutsClosed(NotificationGuts guts) {
1235 mStackScroller.onHeightChanged(null, true /* needsAnimation */);
1236 mNotificationGutsExposed = null;
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();
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();
1255 public void toggleRecentApps() {
1260 public void toggleSplitScreen() {
1261 toggleSplitScreenMode(-1 /* metricsDockAction */, -1 /* metricsUndockAction */);
1265 public void preloadRecentApps() {
1266 int msg = MSG_PRELOAD_RECENT_APPS;
1267 mHandler.removeMessages(msg);
1268 mHandler.sendEmptyMessage(msg);
1272 public void cancelPreloadRecentApps() {
1273 int msg = MSG_CANCEL_PRELOAD_RECENT_APPS;
1274 mHandler.removeMessages(msg);
1275 mHandler.sendEmptyMessage(msg);
1279 public void dismissKeyboardShortcutsMenu() {
1280 int msg = MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU;
1281 mHandler.removeMessages(msg);
1282 mHandler.sendEmptyMessage(msg);
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();
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);
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);
1306 protected H createHandler() {
1310 protected void sendCloseSystemWindows(String reason) {
1311 if (ActivityManagerNative.isSystemReady()) {
1313 ActivityManagerNative.getDefault().closeSystemDialogs(reason);
1314 } catch (RemoteException e) {
1319 protected abstract View getStatusBarView();
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
1325 public boolean onTouch(View v, MotionEvent event) {
1326 int action = event.getAction() & MotionEvent.ACTION_MASK;
1327 if (action == MotionEvent.ACTION_DOWN) {
1329 } else if (action == MotionEvent.ACTION_CANCEL) {
1330 cancelPreloadingRecents();
1331 } else if (action == MotionEvent.ACTION_UP) {
1332 if (!v.isPressed()) {
1333 cancelPreloadingRecents();
1342 * Toggle docking the app window
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
1349 protected abstract void toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction);
1351 /** Proxy for RecentsComponent */
1353 protected void showRecents(boolean triggeredFromAltTab, boolean fromHome) {
1354 if (mRecents != null) {
1355 sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
1356 mRecents.showRecents(triggeredFromAltTab, fromHome);
1360 protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
1361 if (mRecents != null) {
1362 mRecents.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
1366 protected void toggleRecents() {
1367 if (mRecents != null) {
1368 mRecents.toggleRecents(mDisplay);
1372 protected void preloadRecents() {
1373 if (mRecents != null) {
1374 mRecents.preloadRecents();
1378 protected void toggleKeyboardShortcuts(int deviceId) {
1379 KeyboardShortcuts.toggle(mContext, deviceId);
1382 protected void dismissKeyboardShortcuts() {
1383 KeyboardShortcuts.dismiss();
1386 protected void cancelPreloadingRecents() {
1387 if (mRecents != null) {
1388 mRecents.cancelPreloadingRecents();
1392 protected void showRecentsNextAffiliatedTask() {
1393 if (mRecents != null) {
1394 mRecents.showNextAffiliatedTask();
1398 protected void showRecentsPreviousAffiliatedTask() {
1399 if (mRecents != null) {
1400 mRecents.showPrevAffiliatedTask();
1405 * If there is an active heads-up notification and it has a fullscreen intent, fire it now.
1407 public abstract void maybeEscalateHeadsUp();
1410 * Save the current "public" (locked and secure) state of the lockscreen.
1412 public void setLockscreenPublicMode(boolean publicMode) {
1413 mLockscreenPublicMode = publicMode;
1416 public boolean isLockscreenPublicMode() {
1417 return mLockscreenPublicMode;
1420 protected void onWorkChallengeUnlocked() {}
1423 * Has the given user chosen to allow notifications to be shown even when the lockscreen is in
1424 * "public" (secure & locked) mode?
1426 public boolean userAllowsNotificationsInPublic(int userHandle) {
1427 if (userHandle == UserHandle.USER_ALL) {
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);
1439 return mUsersAllowingNotifications.get(userHandle);
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?
1446 public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
1447 if (userHandle == UserHandle.USER_ALL) {
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);
1461 return mUsersAllowingPrivateNotifications.get(userHandle);
1464 private boolean adminAllowsUnredactedNotifications(int userHandle) {
1465 if (userHandle == UserHandle.USER_ALL) {
1468 final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */,
1470 return (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0;
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.
1477 @Override // NotificationData.Environment
1478 public boolean shouldHideNotifications(int userid) {
1479 return isLockscreenPublicMode() && !userAllowsNotificationsInPublic(userid);
1483 * Returns true if we're on a secure lockscreen and the user wants to hide notifications via
1484 * package-specific override.
1486 @Override // NotificationDate.Environment
1487 public boolean shouldHideNotifications(String key) {
1488 return isLockscreenPublicMode()
1489 && mNotificationData.getVisibilityOverride(key) == Notification.VISIBILITY_SECRET;
1493 * Returns true if we're on a secure lockscreen.
1495 @Override // NotificationData.Environment
1496 public boolean onSecureLockScreen() {
1497 return isLockscreenPublicMode();
1500 public void onNotificationClear(StatusBarNotification notification) {
1502 mBarService.onNotificationClear(
1503 notification.getPackageName(),
1504 notification.getTag(),
1505 notification.getId(),
1506 notification.getUserId());
1507 } catch (android.os.RemoteException ex) {
1513 * Called when the notification panel layouts
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) {
1527 protected void onLockedNotificationImportanceChange(OnDismissAction dismissAction) {}
1529 protected void onLockedRemoteInput(ExpandableNotificationRow row, View clickedView) {}
1531 protected void onLockedWorkRemoteInput(int userId, ExpandableNotificationRow row,
1535 public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
1538 protected class H extends Handler {
1539 public void handleMessage(Message m) {
1541 case MSG_SHOW_RECENT_APPS:
1542 showRecents(m.arg1 > 0, m.arg2 != 0);
1544 case MSG_HIDE_RECENT_APPS:
1545 hideRecents(m.arg1 > 0, m.arg2 > 0);
1547 case MSG_TOGGLE_RECENTS_APPS:
1550 case MSG_PRELOAD_RECENT_APPS:
1553 case MSG_CANCEL_PRELOAD_RECENT_APPS:
1554 cancelPreloadingRecents();
1556 case MSG_SHOW_NEXT_AFFILIATED_TASK:
1557 showRecentsNextAffiliatedTask();
1559 case MSG_SHOW_PREV_AFFILIATED_TASK:
1560 showRecentsPreviousAffiliatedTask();
1562 case MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU:
1563 toggleKeyboardShortcuts(m.arg1);
1565 case MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU:
1566 dismissKeyboardShortcuts();
1572 protected void workAroundBadLayerDrawableOpacity(View v) {
1575 protected boolean inflateViews(Entry entry, ViewGroup parent) {
1576 PackageManager pmUser = getPackageManagerForUser(mContext,
1577 entry.notification.getUser().getIdentifier());
1579 final StatusBarNotification sbn = entry.notification;
1580 entry.cacheContentViews(mContext, null);
1582 final RemoteViews contentView = entry.cachedContentView;
1583 final RemoteViews bigContentView = entry.cachedBigContentView;
1584 final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView;
1585 final RemoteViews publicContentView = entry.cachedPublicContentView;
1587 if (contentView == null) {
1588 Log.v(TAG, "no contentView for: " + sbn.getNotification());
1593 Log.v(TAG, "publicContentView: " + publicContentView);
1596 ExpandableNotificationRow row;
1598 // Stash away previous user expansion state so we can restore it at
1600 boolean hasUserChangedExpansion = false;
1601 boolean userExpanded = false;
1602 boolean userLocked = false;
1604 if (entry.row != null) {
1606 hasUserChangedExpansion = row.hasUserChangedExpansion();
1607 userExpanded = row.isUserExpanded();
1608 userLocked = row.isUserLocked();
1610 if (hasUserChangedExpansion) {
1611 row.setUserExpanded(userExpanded);
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,
1619 row.setExpansionLogger(this, entry.notification.getKey());
1620 row.setGroupManager(mGroupManager);
1621 row.setHeadsUpManager(mHeadsUpManager);
1622 row.setRemoteInputController(mRemoteInputController);
1623 row.setOnExpandClickListener(this);
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;
1633 final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
1634 PackageManager.GET_UNINSTALLED_PACKAGES
1635 | PackageManager.GET_DISABLED_COMPONENTS);
1637 appname = String.valueOf(pmUser.getApplicationLabel(info));
1639 } catch (NameNotFoundException e) {
1642 row.setAppName(appname);
1645 workAroundBadLayerDrawableOpacity(row);
1646 bindDismissListener(row);
1648 // NB: the large icon is now handled entirely by the template
1650 // bind the click event to the content area
1651 NotificationContentView contentContainer = row.getPrivateLayout();
1652 NotificationContentView contentContainerPublic = row.getPublicLayout();
1654 row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
1655 if (ENABLE_REMOTE_INPUT) {
1656 row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
1659 mNotificationClicker.register(row, sbn);
1661 // set up the adaptive layout
1662 View contentViewLocal = null;
1663 View bigContentViewLocal = null;
1664 View headsUpContentViewLocal = null;
1665 View publicViewLocal = null;
1667 contentViewLocal = contentView.apply(
1668 sbn.getPackageContext(mContext),
1671 if (bigContentView != null) {
1672 bigContentViewLocal = bigContentView.apply(
1673 sbn.getPackageContext(mContext),
1677 if (headsUpContentView != null) {
1678 headsUpContentViewLocal = headsUpContentView.apply(
1679 sbn.getPackageContext(mContext),
1683 if (publicContentView != null) {
1684 publicViewLocal = publicContentView.apply(
1685 sbn.getPackageContext(mContext),
1686 contentContainerPublic, mOnClickHandler);
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);
1695 if (contentViewLocal != null) {
1696 contentViewLocal.setIsRootNamespace(true);
1697 contentContainer.setContractedChild(contentViewLocal);
1699 if (bigContentViewLocal != null) {
1700 bigContentViewLocal.setIsRootNamespace(true);
1701 contentContainer.setExpandedChild(bigContentViewLocal);
1703 if (headsUpContentViewLocal != null) {
1704 headsUpContentViewLocal.setIsRootNamespace(true);
1705 contentContainer.setHeadsUpChild(headsUpContentViewLocal);
1707 if (publicViewLocal != null) {
1708 publicViewLocal.setIsRootNamespace(true);
1709 contentContainerPublic.setContractedChild(publicViewLocal);
1712 // Extract target SDK version.
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);
1719 entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
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());
1729 entry.row.setOnActivatedListener(this);
1730 entry.row.setExpandable(bigContentViewLocal != null);
1732 applyColorsAndBackgrounds(sbn, entry);
1734 // Restore previous flags.
1735 if (hasUserChangedExpansion) {
1736 // Note: setUserExpanded() conveniently ignores calls with
1737 // userExpanded=true if !isExpandable().
1738 row.setUserExpanded(userExpanded);
1740 row.setUserLocked(userLocked);
1741 row.onNotificationUpdated(entry);
1746 * Adds RemoteInput actions from the WearableExtender; to be removed once more apps support this
1747 * via first-class API.
1749 * TODO: Remove once enough apps specify remote inputs on their own.
1751 private void processForRemoteInput(Notification n) {
1752 if (!ENABLE_REMOTE_INPUT) return;
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);
1759 List<Notification.Action> actions = we.getActions();
1760 final int numActions = actions.size();
1762 for (int i = 0; i < numActions; i++) {
1763 Notification.Action action = actions.get(i);
1764 if (action == null) {
1767 RemoteInput[] remoteInputs = action.getRemoteInputs();
1768 if (remoteInputs == null) {
1771 for (RemoteInput ri : remoteInputs) {
1772 if (ri.getAllowFreeFormInput()) {
1773 viableAction = action;
1777 if (viableAction != null) {
1782 if (viableAction != null) {
1783 Notification.Builder rebuilder = Notification.Builder.recoverBuilder(mContext, n);
1784 rebuilder.setActions(viableAction);
1785 rebuilder.build(); // will rewrite n
1790 public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
1791 if (!isDeviceProvisioned()) return;
1793 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
1794 final boolean afterKeyguardGone = intent.isActivity()
1795 && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
1797 dismissKeyguardThenExecute(new OnDismissAction() {
1798 public boolean onDismiss() {
1803 if (keyguardShowing && !afterKeyguardGone) {
1804 ActivityManagerNative.getDefault()
1805 .keyguardWaitingForActivityDrawn();
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) {
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);
1822 // TODO: Dismiss Keyguard.
1824 if (intent.isActivity()) {
1825 mAssistManager.hideAssist();
1826 overrideActivityPendingAppTransition(keyguardShowing
1827 && !afterKeyguardGone);
1832 // close the shade if it was open
1833 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
1834 true /* force */, true /* delayed */);
1835 visibilityChanged(false);
1839 }, afterKeyguardGone);
1842 public void addPostCollapseAction(Runnable r) {
1845 public boolean isCollapsing() {
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.");
1856 final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1857 final StatusBarNotification sbn = row.getStatusBarNotification();
1859 Log.e(TAG, "NotificationClicker called on an unclickable notification,");
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);
1869 Notification notification = sbn.getNotification();
1870 final PendingIntent intent = notification.contentIntent != null
1871 ? notification.contentIntent
1872 : notification.fullScreenIntent;
1873 final String notificationKey = sbn.getKey();
1875 // Mark notification for one frame.
1876 row.setJustClicked(true);
1877 DejankUtils.postAfterTraversal(new Runnable() {
1880 row.setJustClicked(false);
1884 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
1885 final boolean afterKeyguardGone = intent.isActivity()
1886 && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
1888 dismissKeyguardThenExecute(new OnDismissAction() {
1889 public boolean onDismiss() {
1890 if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) {
1891 // Release the HUN notification to the shade.
1893 if (isPanelFullyCollapsed()) {
1894 HeadsUpManager.setIsClickedNotification(row, true);
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);
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;
1909 final StatusBarNotification parentToCancelFinal = parentToCancel;
1914 if (keyguardShowing && !afterKeyguardGone) {
1915 ActivityManagerNative.getDefault()
1916 .keyguardWaitingForActivityDrawn();
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) {
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()
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
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);
1951 // TODO: Dismiss Keyguard.
1953 if (intent.isActivity()) {
1954 mAssistManager.hideAssist();
1955 overrideActivityPendingAppTransition(keyguardShowing
1956 && !afterKeyguardGone);
1961 mBarService.onNotificationClick(notificationKey);
1962 } catch (RemoteException ex) {
1963 // system process is dead if we're here.
1965 if (parentToCancelFinal != null) {
1966 // We have to post it to the UI thread for synchronization
1967 mHandler.post(new Runnable() {
1970 Runnable removeRunnable = new Runnable() {
1973 performRemoveNotification(parentToCancelFinal,
1977 if (isCollapsing()) {
1978 // To avoid lags we're only performing the remove
1979 // after the shade was collapsed
1980 addPostCollapseAction(removeRunnable);
1982 removeRunnable.run();
1990 // close the shade if it was open
1991 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
1992 true /* force */, true /* delayed */);
1993 visibilityChanged(false);
1997 }, afterKeyguardGone);
2000 private boolean shouldAutoCancel(StatusBarNotification sbn) {
2001 int flags = sbn.getNotification().flags;
2002 if ((flags & Notification.FLAG_AUTO_CANCEL) != Notification.FLAG_AUTO_CANCEL) {
2005 if ((flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
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);
2016 row.setOnClickListener(null);
2021 public void animateCollapsePanels(int flags, boolean force) {
2024 public void animateCollapsePanels(int flags, boolean force, boolean delayed) {
2027 public void overrideActivityPendingAppTransition(boolean keyguardShowing) {
2028 if (keyguardShowing) {
2030 mWindowManagerService.overridePendingAppTransition(null, 0, 0, null);
2031 } catch (RemoteException e) {
2032 Log.w(TAG, "Error overriding app transition: " + e);
2037 protected boolean startWorkChallengeIfNecessary(int userId, IntentSender intendSender,
2038 String notificationKey) {
2039 final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null,
2041 if (newIntent == null) {
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());
2050 PendingIntent callBackPendingIntent = PendingIntent.getBroadcast(
2054 PendingIntent.FLAG_CANCEL_CURRENT |
2055 PendingIntent.FLAG_ONE_SHOT |
2056 PendingIntent.FLAG_IMMUTABLE);
2058 Intent.EXTRA_INTENT,
2059 callBackPendingIntent.getIntentSender());
2061 ActivityManagerNative.getDefault().startConfirmDeviceCredentialIntent(newIntent);
2062 } catch (RemoteException ex) {
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();
2076 protected void visibilityChanged(boolean visible) {
2077 if (mVisible != visible) {
2083 updateVisibleToUser();
2086 protected void updateVisibleToUser() {
2087 boolean oldVisibleToUser = mVisibleToUser;
2088 mVisibleToUser = mVisible && mDeviceInteractive;
2090 if (oldVisibleToUser != mVisibleToUser) {
2091 handleVisibleToUserChanged(mVisibleToUser);
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.
2099 protected void handleVisibleToUserChanged(boolean visibleToUser) {
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;
2110 MetricsLogger.histogram(mContext, "note_load", notificationLoad);
2112 mBarService.onPanelRevealed(clearNotificationEffects, notificationLoad);
2114 mBarService.onPanelHidden();
2116 } catch (RemoteException ex) {
2117 // Won't fail unless the world has ended.
2122 * Clear Buzz/Beep/Blink.
2124 public void clearNotificationEffects() {
2126 mBarService.clearNotificationEffects();
2127 } catch (RemoteException e) {
2128 // Won't fail unless the world has ended.
2132 public abstract boolean isPanelFullyCollapsed();
2135 * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
2136 * about the failure.
2138 * WARNING: this will call back into us. Don't hold any locks.
2140 void handleNotificationError(StatusBarNotification n, String message) {
2141 removeNotification(n.getKey(), null);
2143 mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
2144 n.getInitialPid(), message, n.getUserId());
2145 } catch (RemoteException ex) {
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);
2156 updateNotifications();
2157 return entry.notification;
2160 protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) {
2162 Log.d(TAG, "createNotificationViews(notification=" + sbn);
2164 final StatusBarIconView iconView = createIcon(sbn);
2165 if (iconView == null) {
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);
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);
2185 final Icon smallIcon = n.getSmallIcon();
2186 if (smallIcon == null) {
2187 handleNotificationError(sbn,
2188 "No small icon in notification from " + sbn.getPackageName());
2191 final StatusBarIcon ic = new StatusBarIcon(
2193 sbn.getPackageName(),
2197 StatusBarIconView.contentDescForNotification(mContext, n));
2198 if (!iconView.set(ic)) {
2199 handleNotificationError(sbn, "Couldn't create icon: " + ic);
2205 protected void addNotificationViews(Entry entry, RankingMap ranking) {
2206 if (entry == null) {
2209 // Add the expanded view and icon.
2210 mNotificationData.add(entry, ranking);
2211 updateNotifications();
2215 * @param recompute wheter the number should be recomputed
2216 * @return The number of notifications we show on Keyguard.
2218 protected abstract int getMaxKeyguardNotifications(boolean recompute);
2221 * Updates expanded, dimmed and locked states of notification rows.
2223 protected void updateRowStates() {
2224 mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
2226 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
2227 final int N = activeNotifications.size();
2229 int visibleNotifications = 0;
2230 boolean onKeyguard = mState == StatusBarState.KEYGUARD;
2231 int maxNotifications = 0;
2233 maxNotifications = getMaxKeyguardNotifications(true /* recompute */);
2235 for (int i = 0; i < N; i++) {
2236 NotificationData.Entry entry = activeNotifications.get(i);
2237 boolean childNotification = mGroupManager.isChildInGroupWithSummary(entry.notification);
2239 entry.row.setOnKeyguard(true);
2241 entry.row.setOnKeyguard(false);
2242 entry.row.setSystemExpanded(visibleNotifications == 0 && !childNotification);
2244 boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
2245 entry.notification) && !entry.row.isRemoved();
2246 boolean childWithVisibleSummary = childNotification
2247 && mGroupManager.getGroupSummary(entry.notification).getVisibility()
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);
2258 boolean wasGone = entry.row.getVisibility() == View.GONE;
2259 entry.row.setVisibility(View.VISIBLE);
2260 if (!childNotification && !entry.row.isRemoved()) {
2262 // notify the scroller of a child addition
2263 mStackScroller.generateAddAnimation(entry.row,
2264 !showOnKeyguard /* fromMoreCard */);
2266 visibleNotifications++;
2271 mStackScroller.updateOverflowContainerVisibility(onKeyguard
2272 && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0);
2274 mStackScroller.changeViewPosition(mDismissView, mStackScroller.getChildCount() - 1);
2275 mStackScroller.changeViewPosition(mEmptyShadeView, mStackScroller.getChildCount() - 2);
2276 mStackScroller.changeViewPosition(mKeyguardIconOverflowContainer,
2277 mStackScroller.getChildCount() - 3);
2280 public boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
2281 return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey());
2284 protected void setZenMode(int mode) {
2285 if (!isDeviceProvisioned()) return;
2287 updateNotifications();
2290 // extended in PhoneStatusBar
2291 protected void setShowLockscreenNotifications(boolean show) {
2292 mShowLockscreenNotifications = show;
2295 protected void setLockScreenAllowRemoteInput(boolean allowLockscreenRemoteInput) {
2296 mAllowLockscreenRemoteInput = allowLockscreenRemoteInput;
2299 private void updateLockscreenNotificationSetting() {
2300 final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
2301 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
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;
2309 setShowLockscreenNotifications(show && allowedByDpm);
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,
2315 mCurrentUserId) != 0;
2316 final boolean remoteInputDpm =
2317 (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT) == 0;
2319 setLockScreenAllowRemoteInput(remoteInput && remoteInputDpm);
2321 setLockScreenAllowRemoteInput(false);
2325 protected abstract void setAreThereNotifications();
2326 protected abstract void updateNotifications();
2327 public abstract boolean shouldDisableNavbarGestures();
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);
2334 public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
2335 if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
2337 final String key = notification.getKey();
2338 Entry entry = mNotificationData.get(key);
2339 if (entry == null) {
2342 mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
2343 mRemoteInputEntriesToRemoveOnCollapse.remove(entry);
2346 Notification n = notification.getNotification();
2347 mNotificationData.updateRanking(ranking);
2349 boolean applyInPlace = entry.cacheContentViews(mContext, notification.getNotification());
2350 boolean shouldPeek = shouldPeek(entry, notification);
2351 boolean alertAgain = alertAgain(entry, n);
2353 Log.d(TAG, "applyInPlace=" + applyInPlace
2354 + " shouldPeek=" + shouldPeek
2355 + " alertAgain=" + alertAgain);
2358 final StatusBarNotification oldNotification = entry.notification;
2359 entry.notification = notification;
2360 mGroupManager.onEntryUpdated(entry, oldNotification);
2362 boolean updateSuccessful = false;
2364 if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
2366 if (entry.icon != null) {
2368 final StatusBarIcon ic = new StatusBarIcon(
2369 notification.getUser(),
2370 notification.getPackageName(),
2374 StatusBarIconView.contentDescForNotification(mContext, n));
2375 entry.icon.setNotification(n);
2376 if (!entry.icon.set(ic)) {
2377 handleNotificationError(notification, "Couldn't update icon: " + ic);
2381 updateNotificationViews(entry, notification);
2382 updateSuccessful = true;
2384 catch (RuntimeException e) {
2385 // It failed to apply cleanly.
2386 Log.w(TAG, "Couldn't reapply views for package " +
2387 notification.getPackageName(), e);
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(),
2398 StatusBarIconView.contentDescForNotification(mContext, n));
2399 entry.icon.setNotification(n);
2401 inflateViews(entry, mStackScroller);
2403 updateHeadsUp(key, entry, shouldPeek, alertAgain);
2404 updateNotifications();
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);
2414 boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);
2415 Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
2418 setAreThereNotifications();
2421 protected abstract void updateHeadsUp(String key, Entry entry, boolean shouldPeek,
2422 boolean alertAgain);
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;
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(),
2437 View headsUpChild = entry.getHeadsUpContentView();
2438 if (headsUpContentView != null && headsUpChild != null) {
2439 headsUpContentView.reapply(sbn.getPackageContext(mContext),
2440 headsUpChild, mOnClickHandler);
2442 if (publicContentView != null && entry.getPublicContentView() != null) {
2443 publicContentView.reapply(sbn.getPackageContext(mContext),
2444 entry.getPublicContentView(), mOnClickHandler);
2446 // update the contentIntent
2447 mNotificationClicker.register(entry.row, sbn);
2449 entry.row.onNotificationUpdated(entry);
2450 entry.row.resetHeight();
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);
2474 protected void notifyHeadsUpScreenOff() {
2475 maybeEscalateHeadsUp();
2478 private boolean alertAgain(Entry oldEntry, Notification newNotification) {
2479 return oldEntry == null || !oldEntry.hasInterrupted()
2480 || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
2483 protected boolean shouldPeek(Entry entry) {
2484 return shouldPeek(entry, entry.notification);
2487 protected boolean shouldPeek(Entry entry, StatusBarNotification sbn) {
2488 if (!mUseHeadsUp || isDeviceInVrMode()) {
2492 if (mNotificationData.shouldFilterOut(sbn)) {
2493 if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey());
2497 boolean inUse = mPowerManager.isScreenOn()
2498 && (!mStatusBarKeyguardViewManager.isShowing()
2499 || mStatusBarKeyguardViewManager.isOccluded())
2500 && !mStatusBarKeyguardViewManager.isInputRestricted();
2502 inUse = inUse && !mDreamManager.isDreaming();
2503 } catch (RemoteException e) {
2504 Log.d(TAG, "failed to query dream manager", e);
2509 Log.d(TAG, "No peeking: not in use: " + sbn.getKey());
2514 if (mNotificationData.shouldSuppressScreenOn(sbn.getKey())) {
2515 if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
2519 if (entry.hasJustLaunchedFullScreenIntent()) {
2520 if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey());
2524 if (isSnoozedPackage(sbn)) {
2525 if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
2529 if (mNotificationData.getImportance(sbn.getKey()) < IMPORTANCE_HIGH) {
2530 if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());
2534 if (sbn.getNotification().fullScreenIntent != null) {
2535 if (mAccessibilityManager.isTouchExplorationEnabled()) {
2536 if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
2546 protected abstract boolean isSnoozedPackage(StatusBarNotification sbn);
2548 public void setInteracting(int barWindow, boolean interacting) {
2549 // hook for subclasses
2552 public void setBouncerShowing(boolean bouncerShowing) {
2553 mBouncerShowing = bouncerShowing;
2557 * @return Whether the security bouncer from Keyguard is showing.
2559 public boolean isBouncerShowing() {
2560 return mBouncerShowing;
2563 public void destroy() {
2564 mContext.unregisterReceiver(mBroadcastReceiver);
2566 mNotificationListener.unregisterAsSystemService();
2567 } catch (RemoteException e) {
2573 * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then
2574 * return PackageManager for mContext
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
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.
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.
2591 return contextForUser.getPackageManager();
2595 public void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
2597 mBarService.onNotificationExpansionChanged(key, userAction, expanded);
2598 } catch (RemoteException e) {
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",
2612 return mStatusBarKeyguardViewManager.isSecure();
2616 public void showAssistDisclosure() {
2617 if (mAssistManager != null) {
2618 mAssistManager.showDisclosure();
2623 public void startAssist(Bundle args) {
2624 if (mAssistManager != null) {
2625 mAssistManager.startAssist(args);