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.ActivityManagerNative;
23 import android.app.KeyguardManager;
24 import android.app.Notification;
25 import android.app.NotificationManager;
26 import android.app.PendingIntent;
27 import android.app.RemoteInput;
28 import android.app.TaskStackBuilder;
29 import android.app.admin.DevicePolicyManager;
30 import android.content.BroadcastReceiver;
31 import android.content.ComponentName;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.content.IntentFilter;
35 import android.content.IntentSender;
36 import android.content.pm.ApplicationInfo;
37 import android.content.pm.PackageManager;
38 import android.content.pm.PackageManager.NameNotFoundException;
39 import android.content.pm.UserInfo;
40 import android.content.res.Configuration;
41 import android.content.res.Resources;
42 import android.database.ContentObserver;
43 import android.graphics.Rect;
44 import android.graphics.drawable.Drawable;
45 import android.graphics.drawable.Icon;
46 import android.os.AsyncTask;
47 import android.os.Build;
48 import android.os.Bundle;
49 import android.os.Handler;
50 import android.os.IBinder;
51 import android.os.Message;
52 import android.os.PowerManager;
53 import android.os.RemoteException;
54 import android.os.ServiceManager;
55 import android.os.SystemProperties;
56 import android.os.UserHandle;
57 import android.os.UserManager;
58 import android.provider.Settings;
59 import android.service.dreams.DreamService;
60 import android.service.dreams.IDreamManager;
61 import android.service.notification.NotificationListenerService;
62 import android.service.notification.NotificationListenerService.RankingMap;
63 import android.service.notification.StatusBarNotification;
64 import android.text.TextUtils;
65 import android.util.ArraySet;
66 import android.util.Log;
67 import android.util.Slog;
68 import android.util.SparseArray;
69 import android.util.SparseBooleanArray;
70 import android.view.Display;
71 import android.view.IWindowManager;
72 import android.view.LayoutInflater;
73 import android.view.MotionEvent;
74 import android.view.View;
75 import android.view.ViewAnimationUtils;
76 import android.view.ViewGroup;
77 import android.view.ViewParent;
78 import android.view.WindowManager;
79 import android.view.WindowManagerGlobal;
80 import android.view.accessibility.AccessibilityManager;
81 import android.widget.ImageView;
82 import android.widget.RemoteViews;
83 import android.widget.TextView;
84 import android.widget.Toast;
86 import com.android.internal.logging.MetricsLogger;
87 import com.android.internal.logging.MetricsProto.MetricsEvent;
88 import com.android.internal.statusbar.IStatusBarService;
89 import com.android.internal.statusbar.StatusBarIcon;
90 import com.android.internal.widget.LockPatternUtils;
91 import com.android.keyguard.KeyguardUpdateMonitor;
92 import com.android.systemui.DejankUtils;
93 import com.android.systemui.Interpolators;
94 import com.android.systemui.R;
95 import com.android.systemui.RecentsComponent;
96 import com.android.systemui.SwipeHelper;
97 import com.android.systemui.SystemUI;
98 import com.android.systemui.assist.AssistManager;
99 import com.android.systemui.recents.Recents;
100 import com.android.systemui.statusbar.NotificationData.Entry;
101 import com.android.systemui.statusbar.phone.NavigationBarView;
102 import com.android.systemui.statusbar.phone.NotificationGroupManager;
103 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
104 import com.android.systemui.statusbar.policy.HeadsUpManager;
105 import com.android.systemui.statusbar.policy.PreviewInflater;
106 import com.android.systemui.statusbar.policy.RemoteInputView;
107 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
108 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.GearDisplayedListener;
109 import com.android.systemui.statusbar.stack.StackStateAnimator;
111 import java.util.ArrayList;
112 import java.util.List;
113 import java.util.Locale;
115 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
116 import static com.android.keyguard.KeyguardHostView.OnDismissAction;
118 public abstract class BaseStatusBar extends SystemUI implements
119 CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
120 ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
121 ExpandableNotificationRow.OnExpandClickListener, GearDisplayedListener {
122 public static final String TAG = "StatusBar";
123 public static final boolean DEBUG = false;
124 public static final boolean MULTIUSER_DEBUG = false;
126 public static final boolean ENABLE_REMOTE_INPUT =
127 SystemProperties.getBoolean("debug.enable_remote_input", true);
128 public static final boolean ENABLE_CHILD_NOTIFICATIONS
129 = SystemProperties.getBoolean("debug.child_notifs", true);
131 protected static final int MSG_SHOW_RECENT_APPS = 1019;
132 protected static final int MSG_HIDE_RECENT_APPS = 1020;
133 protected static final int MSG_TOGGLE_RECENTS_APPS = 1021;
134 protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
135 protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
136 protected static final int MSG_SHOW_NEXT_AFFILIATED_TASK = 1024;
137 protected static final int MSG_SHOW_PREV_AFFILIATED_TASK = 1025;
138 protected static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU = 1026;
140 protected static final boolean ENABLE_HEADS_UP = true;
141 protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
143 private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
145 // Should match the values in PhoneWindowManager
146 public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
147 public static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
149 private static final String BANNER_ACTION_CANCEL =
150 "com.android.systemui.statusbar.banner_action_cancel";
151 private static final String BANNER_ACTION_SETUP =
152 "com.android.systemui.statusbar.banner_action_setup";
153 private static final String WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION
154 = "com.android.systemui.statusbar.work_challenge_unlocked_notification_action";
156 protected CommandQueue mCommandQueue;
157 protected IStatusBarService mBarService;
158 protected H mHandler = createHandler();
161 protected NotificationData mNotificationData;
162 protected NotificationStackScrollLayout mStackScroller;
164 protected NotificationGroupManager mGroupManager = new NotificationGroupManager();
166 protected RemoteInputController mRemoteInputController;
168 // for heads up notifications
169 protected HeadsUpManager mHeadsUpManager;
171 protected int mCurrentUserId = 0;
172 final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
174 protected int mLayoutDirection = -1; // invalid
175 protected AccessibilityManager mAccessibilityManager;
177 // on-screen navigation buttons
178 protected NavigationBarView mNavigationBarView = null;
180 protected boolean mDeviceInteractive;
182 protected boolean mVisible;
183 protected ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>();
185 // mScreenOnFromKeyguard && mVisible.
186 private boolean mVisibleToUser;
188 private Locale mLocale;
189 private float mFontScale;
191 protected boolean mUseHeadsUp = false;
192 protected boolean mHeadsUpTicker = false;
193 protected boolean mDisableNotificationAlerts = false;
195 protected DevicePolicyManager mDevicePolicyManager;
196 protected IDreamManager mDreamManager;
197 protected PowerManager mPowerManager;
198 protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
200 // public mode, private notifications, etc
201 private boolean mLockscreenPublicMode = false;
202 private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
203 private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray();
205 private UserManager mUserManager;
206 private int mDensity;
208 private KeyguardManager mKeyguardManager;
209 private LockPatternUtils mLockPatternUtils;
211 // UI-specific methods
214 * Create all windows necessary for the status bar (including navigation, overlay panels, etc)
215 * and add them to the window manager.
217 protected abstract void createAndAddWindows();
219 protected WindowManager mWindowManager;
220 protected IWindowManager mWindowManagerService;
222 protected abstract void refreshLayout(int layoutDirection);
224 protected Display mDisplay;
226 private boolean mDeviceProvisioned = false;
228 protected RecentsComponent mRecents;
230 protected int mZenMode;
232 // which notification is currently being longpress-examined by the user
233 private NotificationGuts mNotificationGutsExposed;
234 private ExpandableNotificationRow mNotificationGearDisplayed;
236 private KeyboardShortcuts mKeyboardShortcuts;
239 * The {@link StatusBarState} of the status bar.
241 protected int mState;
242 protected boolean mBouncerShowing;
243 protected boolean mShowLockscreenNotifications;
244 protected boolean mAllowLockscreenRemoteInput;
246 protected NotificationOverflowContainer mKeyguardIconOverflowContainer;
247 protected DismissView mDismissView;
248 protected EmptyShadeView mEmptyShadeView;
250 private NotificationClicker mNotificationClicker = new NotificationClicker();
252 protected AssistManager mAssistManager;
254 @Override // NotificationData.Environment
255 public boolean isDeviceProvisioned() {
256 return mDeviceProvisioned;
259 protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
261 public void onChange(boolean selfChange) {
262 final boolean provisioned = 0 != Settings.Global.getInt(
263 mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
264 if (provisioned != mDeviceProvisioned) {
265 mDeviceProvisioned = provisioned;
266 updateNotifications();
268 final int mode = Settings.Global.getInt(mContext.getContentResolver(),
269 Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
272 updateLockscreenNotificationSetting();
276 private final ContentObserver mLockscreenSettingsObserver = new ContentObserver(mHandler) {
278 public void onChange(boolean selfChange) {
279 // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
280 // so we just dump our cache ...
281 mUsersAllowingPrivateNotifications.clear();
282 // ... and refresh all the notifications
283 updateNotifications();
287 private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
289 public boolean onClickHandler(
290 final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
291 if (handleRemoteInput(view, pendingIntent, fillInIntent)) {
296 Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
298 logActionClick(view);
299 // The intent we are sending is for the application, which
300 // won't have permission to immediately start an activity after
301 // the user switches to home. We know it is safe to do at this
302 // point, so make sure new activity switches are now allowed.
304 ActivityManagerNative.getDefault().resumeAppSwitches();
305 } catch (RemoteException e) {
307 final boolean isActivity = pendingIntent.isActivity();
309 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
310 final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
311 mContext, pendingIntent.getIntent(), mCurrentUserId);
312 dismissKeyguardThenExecute(new OnDismissAction() {
314 public boolean onDismiss() {
315 if (keyguardShowing && !afterKeyguardGone) {
317 ActivityManagerNative.getDefault()
318 .keyguardWaitingForActivityDrawn();
319 ActivityManagerNative.getDefault().resumeAppSwitches();
320 } catch (RemoteException e) {
324 boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
325 overrideActivityPendingAppTransition(keyguardShowing && !afterKeyguardGone);
327 // close the shade if it was open
329 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
331 visibilityChanged(false);
332 mAssistManager.hideAssist();
335 // Wait for activity start.
338 }, afterKeyguardGone);
341 return super.onClickHandler(view, pendingIntent, fillInIntent);
345 private void logActionClick(View view) {
346 ViewParent parent = view.getParent();
347 String key = getNotificationKeyForParent(parent);
349 Log.w(TAG, "Couldn't determine notification for click.");
353 // If this is a default template, determine the index of the button.
354 if (view.getId() == com.android.internal.R.id.action0 &&
355 parent != null && parent instanceof ViewGroup) {
356 ViewGroup actionGroup = (ViewGroup) parent;
357 index = actionGroup.indexOfChild(view);
360 mBarService.onNotificationActionClick(key, index);
361 } catch (RemoteException e) {
366 private String getNotificationKeyForParent(ViewParent parent) {
367 while (parent != null) {
368 if (parent instanceof ExpandableNotificationRow) {
369 return ((ExpandableNotificationRow) parent).getStatusBarNotification().getKey();
371 parent = parent.getParent();
376 private boolean superOnClickHandler(View view, PendingIntent pendingIntent,
377 Intent fillInIntent) {
378 return super.onClickHandler(view, pendingIntent, fillInIntent);
381 private boolean handleRemoteInput(View view, PendingIntent pendingIntent, Intent fillInIntent) {
382 Object tag = view.getTag(com.android.internal.R.id.remote_input_tag);
383 RemoteInput[] inputs = null;
384 if (tag instanceof RemoteInput[]) {
385 inputs = (RemoteInput[]) tag;
388 if (inputs == null) {
392 RemoteInput input = null;
394 for (RemoteInput i : inputs) {
395 if (i.getAllowFreeFormInput()) {
404 ViewParent p = view.getParent();
405 RemoteInputView riv = null;
407 if (p instanceof View) {
409 if (pv.isRootNamespace()) {
410 riv = (RemoteInputView) pv.findViewWithTag(RemoteInputView.VIEW_TAG);
416 ExpandableNotificationRow row = null;
418 if (p instanceof ExpandableNotificationRow) {
419 row = (ExpandableNotificationRow) p;
425 if (riv == null || row == null) {
429 row.setUserExpanded(true);
431 if (isLockscreenPublicMode() && !mAllowLockscreenRemoteInput) {
432 onLockedRemoteInput(row, view);
436 riv.setVisibility(View.VISIBLE);
437 int cx = view.getLeft() + view.getWidth() / 2;
438 int cy = view.getTop() + view.getHeight() / 2;
439 int w = riv.getWidth();
440 int h = riv.getHeight();
442 Math.max(cx + cy, cx + (h - cy)),
443 Math.max((w - cx) + cy, (w - cx) + (h - cy)));
444 ViewAnimationUtils.createCircularReveal(riv, cx, cy, 0, r)
447 riv.setPendingIntent(pendingIntent);
448 riv.setRemoteInput(inputs, input);
456 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
458 public void onReceive(Context context, Intent intent) {
459 String action = intent.getAction();
460 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
461 mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
462 updateCurrentProfilesCache();
463 if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
465 updateLockscreenNotificationSetting();
467 userSwitched(mCurrentUserId);
468 } else if (Intent.ACTION_USER_ADDED.equals(action)) {
469 updateCurrentProfilesCache();
470 } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
471 List<ActivityManager.RecentTaskInfo> recentTask = null;
473 recentTask = ActivityManagerNative.getDefault().getRecentTasks(1,
474 ActivityManager.RECENT_WITH_EXCLUDED
475 | ActivityManager.RECENT_INCLUDE_PROFILES,
477 } catch (RemoteException e) {
478 // Abandon hope activity manager not running.
480 if (recentTask != null && recentTask.size() > 0) {
481 UserInfo user = mUserManager.getUserInfo(recentTask.get(0).userId);
482 if (user != null && user.isManagedProfile()) {
483 Toast toast = Toast.makeText(mContext,
484 R.string.managed_profile_foreground_toast,
486 TextView text = (TextView) toast.getView().findViewById(
487 android.R.id.message);
488 text.setCompoundDrawablesRelativeWithIntrinsicBounds(
489 R.drawable.stat_sys_managed_profile_status, 0, 0, 0);
490 int paddingPx = mContext.getResources().getDimensionPixelSize(
491 R.dimen.managed_profile_toast_padding);
492 text.setCompoundDrawablePadding(paddingPx);
496 } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) {
497 NotificationManager noMan = (NotificationManager)
498 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
499 noMan.cancel(R.id.notification_hidden);
501 Settings.Secure.putInt(mContext.getContentResolver(),
502 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
503 if (BANNER_ACTION_SETUP.equals(action)) {
504 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
506 mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION)
507 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
511 } else if (WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION.equals(action)) {
512 final IntentSender intentSender = (IntentSender) intent
513 .getParcelableExtra(Intent.EXTRA_INTENT);
514 final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
516 mContext.startIntentSender(intentSender, null, 0, 0, 0);
517 } catch (IntentSender.SendIntentException e) {
521 mBarService.onNotificationClick(notificationKey);
522 } catch (RemoteException e) {
529 private final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
531 public void onReceive(Context context, Intent intent) {
532 String action = intent.getAction();
533 if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) &&
534 isCurrentProfile(getSendingUserId())) {
535 mUsersAllowingPrivateNotifications.clear();
536 updateLockscreenNotificationSetting();
537 updateNotifications();
542 private final NotificationListenerService mNotificationListener =
543 new NotificationListenerService() {
545 public void onListenerConnected() {
546 if (DEBUG) Log.d(TAG, "onListenerConnected");
547 final StatusBarNotification[] notifications = getActiveNotifications();
548 final RankingMap currentRanking = getCurrentRanking();
549 mHandler.post(new Runnable() {
552 for (StatusBarNotification sbn : notifications) {
553 addNotification(sbn, currentRanking, null /* oldEntry */);
560 public void onNotificationPosted(final StatusBarNotification sbn,
561 final RankingMap rankingMap) {
562 if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
564 mHandler.post(new Runnable() {
567 processForRemoteInput(sbn.getNotification());
568 String key = sbn.getKey();
569 boolean isUpdate = mNotificationData.get(key) != null;
570 // In case we don't allow child notifications, we ignore children of
571 // notifications that have a summary, since we're not going to show them
572 // anyway. This is true also when the summary is canceled,
573 // because children are automatically canceled by NoMan in that case.
574 if (!ENABLE_CHILD_NOTIFICATIONS
575 && mGroupManager.isChildInGroupWithSummary(sbn)) {
577 Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
580 // Remove existing notification to avoid stale data.
582 removeNotification(key, rankingMap);
584 mNotificationData.updateRanking(rankingMap);
589 updateNotification(sbn, rankingMap);
591 addNotification(sbn, rankingMap, null /* oldEntry */);
599 public void onNotificationRemoved(StatusBarNotification sbn,
600 final RankingMap rankingMap) {
601 if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
603 final String key = sbn.getKey();
604 mHandler.post(new Runnable() {
607 removeNotification(key, rankingMap);
614 public void onNotificationRankingUpdate(final RankingMap rankingMap) {
615 if (DEBUG) Log.d(TAG, "onRankingUpdate");
616 if (rankingMap != null) {
617 mHandler.post(new Runnable() {
620 updateNotificationRanking(rankingMap);
627 private void updateCurrentProfilesCache() {
628 synchronized (mCurrentProfiles) {
629 mCurrentProfiles.clear();
630 if (mUserManager != null) {
631 for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
632 mCurrentProfiles.put(user.id, user);
638 public void start() {
639 mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
640 mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
641 mDisplay = mWindowManager.getDefaultDisplay();
642 mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService(
643 Context.DEVICE_POLICY_SERVICE);
645 mNotificationData = new NotificationData(this);
647 mAccessibilityManager = (AccessibilityManager)
648 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
650 mDreamManager = IDreamManager.Stub.asInterface(
651 ServiceManager.checkService(DreamService.DREAM_SERVICE));
652 mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
654 mContext.getContentResolver().registerContentObserver(
655 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
657 mContext.getContentResolver().registerContentObserver(
658 Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
660 mContext.getContentResolver().registerContentObserver(
661 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
663 UserHandle.USER_ALL);
664 mContext.getContentResolver().registerContentObserver(
665 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT), false,
667 UserHandle.USER_ALL);
669 mContext.getContentResolver().registerContentObserver(
670 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
672 mLockscreenSettingsObserver,
673 UserHandle.USER_ALL);
675 mBarService = IStatusBarService.Stub.asInterface(
676 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
678 mRecents = getComponent(Recents.class);
680 final Configuration currentConfig = mContext.getResources().getConfiguration();
681 mLocale = currentConfig.locale;
682 mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
683 mFontScale = currentConfig.fontScale;
684 mDensity = currentConfig.densityDpi;
686 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
687 mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
688 mLockPatternUtils = new LockPatternUtils(mContext);
690 // Connect in to the status bar manager service
691 mCommandQueue = new CommandQueue(this);
693 int[] switches = new int[9];
694 ArrayList<IBinder> binders = new ArrayList<IBinder>();
695 ArrayList<String> iconSlots = new ArrayList<>();
696 ArrayList<StatusBarIcon> icons = new ArrayList<>();
697 Rect fullscreenStackBounds = new Rect();
698 Rect dockedStackBounds = new Rect();
700 mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
701 fullscreenStackBounds, dockedStackBounds);
702 } catch (RemoteException ex) {
703 // If the system process isn't there we're doomed anyway.
706 createAndAddWindows();
708 mSettingsObserver.onChange(false); // set up
709 disable(switches[0], switches[6], false /* animate */);
710 setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,
711 fullscreenStackBounds, dockedStackBounds);
712 topAppWindowChanged(switches[2] != 0);
713 // StatusBarManagerService has a back up of IME token and it's restored here.
714 setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
716 // Set up the initial icon state
717 int N = iconSlots.size();
719 for (int i=0; i < N; i++) {
720 setIcon(iconSlots.get(i), icons.get(i));
723 // Set up the initial notification state.
725 mNotificationListener.registerAsSystemService(mContext,
726 new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
727 UserHandle.USER_ALL);
728 } catch (RemoteException e) {
729 Log.e(TAG, "Unable to register notification listener", e);
734 Log.d(TAG, String.format(
735 "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
744 mCurrentUserId = ActivityManager.getCurrentUser();
745 setHeadsUpUser(mCurrentUserId);
747 IntentFilter filter = new IntentFilter();
748 filter.addAction(Intent.ACTION_USER_SWITCHED);
749 filter.addAction(Intent.ACTION_USER_ADDED);
750 filter.addAction(Intent.ACTION_USER_PRESENT);
751 mContext.registerReceiver(mBroadcastReceiver, filter);
753 IntentFilter internalFilter = new IntentFilter();
754 internalFilter.addAction(WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION);
755 internalFilter.addAction(BANNER_ACTION_CANCEL);
756 internalFilter.addAction(BANNER_ACTION_SETUP);
757 mContext.registerReceiver(mBroadcastReceiver, internalFilter, PERMISSION_SELF, null);
759 IntentFilter allUsersFilter = new IntentFilter();
760 allUsersFilter.addAction(
761 DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
762 mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter,
764 updateCurrentProfilesCache();
767 protected void notifyUserAboutHiddenNotifications() {
768 if (0 != Settings.Secure.getInt(mContext.getContentResolver(),
769 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) {
770 Log.d(TAG, "user hasn't seen notification about hidden notifications");
771 if (!mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())) {
772 Log.d(TAG, "insecure lockscreen, skipping notification");
773 Settings.Secure.putInt(mContext.getContentResolver(),
774 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
777 Log.d(TAG, "disabling lockecreen notifications and alerting the user");
778 // disable lockscreen notifications until user acts on the banner.
779 Settings.Secure.putInt(mContext.getContentResolver(),
780 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
781 Settings.Secure.putInt(mContext.getContentResolver(),
782 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
784 final String packageName = mContext.getPackageName();
785 PendingIntent cancelIntent = PendingIntent.getBroadcast(mContext, 0,
786 new Intent(BANNER_ACTION_CANCEL).setPackage(packageName),
787 PendingIntent.FLAG_CANCEL_CURRENT);
788 PendingIntent setupIntent = PendingIntent.getBroadcast(mContext, 0,
789 new Intent(BANNER_ACTION_SETUP).setPackage(packageName),
790 PendingIntent.FLAG_CANCEL_CURRENT);
792 final Resources res = mContext.getResources();
793 final int colorRes = com.android.internal.R.color.system_notification_accent_color;
794 Notification.Builder note = new Notification.Builder(mContext)
795 .setSmallIcon(R.drawable.ic_android)
796 .setContentTitle(mContext.getString(R.string.hidden_notifications_title))
797 .setContentText(mContext.getString(R.string.hidden_notifications_text))
798 .setPriority(Notification.PRIORITY_HIGH)
800 .setColor(mContext.getColor(colorRes))
801 .setContentIntent(setupIntent)
802 .addAction(R.drawable.ic_close,
803 mContext.getString(R.string.hidden_notifications_cancel),
805 .addAction(R.drawable.ic_settings,
806 mContext.getString(R.string.hidden_notifications_setup),
809 NotificationManager noMan =
810 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
811 noMan.notify(R.id.notification_hidden, note.build());
815 public void userSwitched(int newUserId) {
816 setHeadsUpUser(newUserId);
819 protected abstract void setHeadsUpUser(int newUserId);
821 @Override // NotificationData.Environment
822 public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
823 final int thisUserId = mCurrentUserId;
824 final int notificationUserId = n.getUserId();
825 if (DEBUG && MULTIUSER_DEBUG) {
826 Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
827 n, thisUserId, notificationUserId));
829 return isCurrentProfile(notificationUserId);
832 protected void setNotificationShown(StatusBarNotification n) {
833 setNotificationsShown(new String[]{n.getKey()});
836 protected void setNotificationsShown(String[] keys) {
838 mNotificationListener.setNotificationsShown(keys);
839 } catch (RuntimeException e) {
840 Log.d(TAG, "failed setNotificationsShown: ", e);
844 protected boolean isCurrentProfile(int userId) {
845 synchronized (mCurrentProfiles) {
846 return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
851 public String getCurrentMediaNotificationKey() {
856 public NotificationGroupManager getGroupManager() {
857 return mGroupManager;
861 * Takes the necessary steps to prepare the status bar for starting an activity, then starts it.
862 * @param action A dismiss action that is called if it's safe to start the activity.
863 * @param afterKeyguardGone Whether the action should be executed after the Keyguard is gone.
865 protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) {
870 protected void onConfigurationChanged(Configuration newConfig) {
871 final Locale locale = mContext.getResources().getConfiguration().locale;
872 final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
873 final float fontScale = newConfig.fontScale;
874 final int density = newConfig.densityDpi;
875 if (density != mDensity || mFontScale != fontScale) {
878 mFontScale = fontScale;
880 if (! locale.equals(mLocale) || ld != mLayoutDirection) {
882 Log.v(TAG, String.format(
883 "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
887 mLayoutDirection = ld;
892 protected void reInflateViews() {
893 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
894 for (int i = 0; i < activeNotifications.size(); i++) {
895 Entry entry = activeNotifications.get(i);
896 boolean exposedGuts = entry.row.getGuts() == mNotificationGutsExposed;
897 entry.row.reInflateViews();
899 mNotificationGutsExposed = entry.row.getGuts();
902 entry.cacheContentViews(mContext, null /* updatedNotification */);
903 inflateViews(entry, mStackScroller);
907 protected View bindVetoButtonClickListener(View row, StatusBarNotification n) {
908 View vetoButton = row.findViewById(R.id.veto);
909 final String _pkg = n.getPackageName();
910 final String _tag = n.getTag();
911 final int _id = n.getId();
912 final int _userId = n.getUserId();
913 vetoButton.setOnClickListener(new View.OnClickListener() {
914 public void onClick(View v) {
915 // Accessibility feedback
916 v.announceForAccessibility(
917 mContext.getString(R.string.accessibility_notification_dismissed));
919 mBarService.onNotificationClear(_pkg, _tag, _id, _userId);
921 } catch (RemoteException ex) {
922 // system process is dead if we're here.
926 vetoButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
931 protected void applyColorsAndBackgrounds(StatusBarNotification sbn,
932 NotificationData.Entry entry) {
934 if (entry.getContentView().getId()
935 != com.android.internal.R.id.status_bar_latest_event_content) {
936 // Using custom RemoteViews
937 if (entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
938 && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP) {
939 entry.row.setShowingLegacyBackground(true);
944 if (entry.icon != null) {
945 entry.icon.setTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
949 public boolean isMediaNotification(NotificationData.Entry entry) {
950 // TODO: confirm that there's a valid media key
951 return entry.getExpandedContentView() != null &&
952 entry.getExpandedContentView()
953 .findViewById(com.android.internal.R.id.media_actions) != null;
956 // The (i) button in the guts that links to the system notification settings for that app
957 private void startAppNotificationSettingsActivity(String packageName, final int appUid) {
958 final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
959 intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
960 intent.putExtra(Settings.EXTRA_APP_UID, appUid);
961 startNotificationGutsIntent(intent, appUid);
964 private void startNotificationGutsIntent(final Intent intent, final int appUid) {
965 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
966 dismissKeyguardThenExecute(new OnDismissAction() {
968 public boolean onDismiss() {
969 AsyncTask.execute(new Runnable() {
972 if (keyguardShowing) {
973 ActivityManagerNative.getDefault()
974 .keyguardWaitingForActivityDrawn();
976 TaskStackBuilder.create(mContext)
977 .addNextIntentWithParentStack(intent)
978 .startActivities(null,
979 new UserHandle(UserHandle.getUserId(appUid)));
980 overrideActivityPendingAppTransition(keyguardShowing);
981 } catch (RemoteException e) {
985 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
988 }, false /* afterKeyguardGone */);
991 private void bindGuts(final ExpandableNotificationRow row) {
993 final StatusBarNotification sbn = row.getStatusBarNotification();
994 PackageManager pmUser = getPackageManagerForUser(mContext, sbn.getUser().getIdentifier());
995 row.setTag(sbn.getPackageName());
996 final NotificationGuts guts = row.getGuts();
997 final String pkg = sbn.getPackageName();
998 String appname = pkg;
999 Drawable pkgicon = null;
1002 final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
1003 PackageManager.GET_UNINSTALLED_PACKAGES
1004 | PackageManager.GET_DISABLED_COMPONENTS);
1006 appname = String.valueOf(pmUser.getApplicationLabel(info));
1007 pkgicon = pmUser.getApplicationIcon(info);
1010 } catch (NameNotFoundException e) {
1011 // app is gone, just show package name and generic icon
1012 pkgicon = pmUser.getDefaultActivityIcon();
1015 ((ImageView) row.findViewById(R.id.app_icon)).setImageDrawable(pkgicon);
1016 ((TextView) row.findViewById(R.id.pkgname)).setText(appname);
1018 final View settingsButton = guts.findViewById(R.id.more_settings);
1020 final int appUidF = appUid;
1021 settingsButton.setOnClickListener(new View.OnClickListener() {
1022 public void onClick(View v) {
1023 MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_INFO);
1024 startAppNotificationSettingsActivity(pkg, appUidF);
1028 settingsButton.setVisibility(View.GONE);
1031 row.findViewById(R.id.done).setOnClickListener(new View.OnClickListener() {
1033 public void onClick(View v) {
1034 guts.saveImportance(sbn);
1036 int[] rowLocation = new int[2];
1037 int[] doneLocation = new int[2];
1038 row.getLocationOnScreen(rowLocation);
1039 v.getLocationOnScreen(doneLocation);
1041 final int centerX = v.getWidth() / 2;
1042 final int centerY = v.getHeight() / 2;
1043 final int x = doneLocation[0] - rowLocation[0] + centerX;
1044 final int y = doneLocation[1] - rowLocation[1] + centerY;
1045 dismissPopups(x, y);
1049 guts.bindImportance(pmUser, sbn, row, mNotificationData.getImportance(sbn.getKey()));
1052 protected GearDisplayedListener getGearDisplayedListener() {
1056 protected SwipeHelper.LongPressListener getNotificationLongClicker() {
1057 return new SwipeHelper.LongPressListener() {
1059 public boolean onLongPress(View v, final int x, final int y) {
1060 if (!(v instanceof ExpandableNotificationRow)) {
1063 if (v.getWindowToken() == null) {
1064 Log.e(TAG, "Trying to show notification guts, but not attached to window");
1068 final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1071 // Assume we are a status_bar_notification_row
1072 final NotificationGuts guts = row.getGuts();
1074 // This view has no guts. Examples are the more card or the dismiss all view
1079 if (guts.getVisibility() == View.VISIBLE) {
1080 dismissPopups(x, y);
1084 MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_CONTROLS);
1086 // ensure that it's laid but not visible until actually laid out
1087 guts.setVisibility(View.INVISIBLE);
1088 // Post to ensure the the guts are properly laid out.
1089 guts.post(new Runnable() {
1091 dismissPopups(-1 /* x */, -1 /* y */, false /* resetGear */);
1092 guts.setVisibility(View.VISIBLE);
1093 final double horz = Math.max(guts.getWidth() - x, x);
1094 final double vert = Math.max(guts.getHeight() - y, y);
1095 final float r = (float) Math.hypot(horz, vert);
1097 = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
1098 a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
1099 a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
1100 a.addListener(new AnimatorListenerAdapter() {
1102 public void onAnimationEnd(Animator animation) {
1103 super.onAnimationEnd(animation);
1104 // Move the notification view back over the gear
1105 row.resetTranslation();
1109 guts.setExposed(true);
1110 row.closeRemoteInput();
1111 mStackScroller.onHeightChanged(null, true /* needsAnimation */);
1112 mNotificationGutsExposed = guts;
1121 public void onGearDisplayed(ExpandableNotificationRow row) {
1122 MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR,
1123 row.getStatusBarNotification().getPackageName());
1124 mNotificationGearDisplayed = row;
1127 public void dismissPopups() {
1128 dismissPopups(-1 /* x */, -1 /* y */, true /* resetGear */);
1131 private void dismissPopups(int x, int y) {
1132 dismissPopups(x, y, true /* resetGear */);
1135 public void dismissPopups(int x, int y, boolean resetGear) {
1136 if (mNotificationGutsExposed != null) {
1137 final NotificationGuts v = mNotificationGutsExposed;
1138 mNotificationGutsExposed = null;
1140 if (v.getWindowToken() == null) return;
1141 if (x == -1 || y == -1) {
1142 x = (v.getLeft() + v.getRight()) / 2;
1143 y = (v.getTop() + v.getHeight() / 2);
1145 final double horz = Math.max(v.getWidth() - x, x);
1146 final double vert = Math.max(v.getHeight() - y, y);
1147 final float r = (float) Math.hypot(horz, vert);
1148 final Animator a = ViewAnimationUtils.createCircularReveal(v,
1150 a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
1151 a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
1152 a.addListener(new AnimatorListenerAdapter() {
1154 public void onAnimationEnd(Animator animation) {
1155 super.onAnimationEnd(animation);
1156 v.setVisibility(View.GONE);
1160 v.setExposed(false);
1161 mStackScroller.onHeightChanged(null, true /* needsAnimation */);
1163 if (resetGear && mNotificationGearDisplayed != null) {
1164 mNotificationGearDisplayed.resetTranslation();
1165 mNotificationGearDisplayed = null;
1170 public void showRecentApps(boolean triggeredFromAltTab) {
1171 int msg = MSG_SHOW_RECENT_APPS;
1172 mHandler.removeMessages(msg);
1173 mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0, 0).sendToTarget();
1177 public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
1178 int msg = MSG_HIDE_RECENT_APPS;
1179 mHandler.removeMessages(msg);
1180 mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0,
1181 triggeredFromHomeKey ? 1 : 0).sendToTarget();
1185 public void toggleRecentApps() {
1190 public void toggleSplitScreen() {
1191 toggleSplitScreenMode();
1195 public void preloadRecentApps() {
1196 int msg = MSG_PRELOAD_RECENT_APPS;
1197 mHandler.removeMessages(msg);
1198 mHandler.sendEmptyMessage(msg);
1202 public void cancelPreloadRecentApps() {
1203 int msg = MSG_CANCEL_PRELOAD_RECENT_APPS;
1204 mHandler.removeMessages(msg);
1205 mHandler.sendEmptyMessage(msg);
1209 public void toggleKeyboardShortcutsMenu(int deviceId) {
1210 int msg = MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU;
1211 mHandler.removeMessages(msg);
1212 mHandler.obtainMessage(msg, deviceId, 0).sendToTarget();
1215 /** Jumps to the next affiliated task in the group. */
1216 public void showNextAffiliatedTask() {
1217 int msg = MSG_SHOW_NEXT_AFFILIATED_TASK;
1218 mHandler.removeMessages(msg);
1219 mHandler.sendEmptyMessage(msg);
1222 /** Jumps to the previous affiliated task in the group. */
1223 public void showPreviousAffiliatedTask() {
1224 int msg = MSG_SHOW_PREV_AFFILIATED_TASK;
1225 mHandler.removeMessages(msg);
1226 mHandler.sendEmptyMessage(msg);
1229 protected H createHandler() {
1233 protected void sendCloseSystemWindows(String reason) {
1234 if (ActivityManagerNative.isSystemReady()) {
1236 ActivityManagerNative.getDefault().closeSystemDialogs(reason);
1237 } catch (RemoteException e) {
1242 protected abstract View getStatusBarView();
1244 protected View.OnTouchListener mRecentsPreloadOnTouchListener = new View.OnTouchListener() {
1245 // additional optimization when we have software system buttons - start loading the recent
1246 // tasks on touch down
1248 public boolean onTouch(View v, MotionEvent event) {
1249 int action = event.getAction() & MotionEvent.ACTION_MASK;
1250 if (action == MotionEvent.ACTION_DOWN) {
1252 } else if (action == MotionEvent.ACTION_CANCEL) {
1253 cancelPreloadingRecents();
1254 } else if (action == MotionEvent.ACTION_UP) {
1255 if (!v.isPressed()) {
1256 cancelPreloadingRecents();
1265 * Toggle docking the app window
1267 * @return {@code true} if the app window is docked after the toggle, {@code false} otherwise.
1269 protected abstract boolean toggleSplitScreenMode();
1271 /** Proxy for RecentsComponent */
1273 protected void showRecents(boolean triggeredFromAltTab) {
1274 if (mRecents != null) {
1275 sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
1276 mRecents.showRecents(triggeredFromAltTab, getStatusBarView());
1280 protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
1281 if (mRecents != null) {
1282 mRecents.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
1286 protected void toggleRecents() {
1287 if (mRecents != null) {
1288 mRecents.toggleRecents(mDisplay, mLayoutDirection, getStatusBarView());
1292 protected void preloadRecents() {
1293 if (mRecents != null) {
1294 mRecents.preloadRecents();
1298 protected void toggleKeyboardShortcuts(int deviceId) {
1299 getKeyboardShortcuts().toggleKeyboardShortcuts(deviceId);
1302 protected void cancelPreloadingRecents() {
1303 if (mRecents != null) {
1304 mRecents.cancelPreloadingRecents();
1308 protected void showRecentsNextAffiliatedTask() {
1309 if (mRecents != null) {
1310 mRecents.showNextAffiliatedTask();
1314 protected void showRecentsPreviousAffiliatedTask() {
1315 if (mRecents != null) {
1316 mRecents.showPrevAffiliatedTask();
1321 * If there is an active heads-up notification and it has a fullscreen intent, fire it now.
1323 public abstract void maybeEscalateHeadsUp();
1326 * Save the current "public" (locked and secure) state of the lockscreen.
1328 public void setLockscreenPublicMode(boolean publicMode) {
1329 mLockscreenPublicMode = publicMode;
1332 public boolean isLockscreenPublicMode() {
1333 return mLockscreenPublicMode;
1337 * Has the given user chosen to allow notifications to be shown even when the lockscreen is in
1338 * "public" (secure & locked) mode?
1340 public boolean userAllowsNotificationsInPublic(int userHandle) {
1341 if (userHandle == UserHandle.USER_ALL) {
1345 if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
1346 final boolean allowed = 0 != Settings.Secure.getIntForUser(
1347 mContext.getContentResolver(),
1348 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle);
1349 mUsersAllowingNotifications.append(userHandle, allowed);
1353 return mUsersAllowingNotifications.get(userHandle);
1357 * Has the given user chosen to allow their private (full) notifications to be shown even
1358 * when the lockscreen is in "public" (secure & locked) mode?
1360 public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
1361 if (userHandle == UserHandle.USER_ALL) {
1365 if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
1366 final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
1367 mContext.getContentResolver(),
1368 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
1369 final boolean allowedByDpm = adminAllowsUnredactedNotifications(userHandle);
1370 final boolean allowed = allowedByUser && allowedByDpm;
1371 mUsersAllowingPrivateNotifications.append(userHandle, allowed);
1375 return mUsersAllowingPrivateNotifications.get(userHandle);
1378 private boolean adminAllowsUnredactedNotifications(int userHandle) {
1379 if (userHandle == UserHandle.USER_ALL) {
1382 final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */,
1384 return (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0;
1388 * Returns true if we're on a secure lockscreen and the user wants to hide notification data.
1389 * If so, notifications should be hidden.
1391 @Override // NotificationData.Environment
1392 public boolean shouldHideNotifications(int userid) {
1393 return isLockscreenPublicMode() && !userAllowsNotificationsInPublic(userid);
1397 * Returns true if we're on a secure lockscreen and the user wants to hide notifications via
1398 * package-specific override.
1400 @Override // NotificationDate.Environment
1401 public boolean shouldHideNotifications(String key) {
1402 return isLockscreenPublicMode()
1403 && mNotificationData.getVisibilityOverride(key) == Notification.VISIBILITY_SECRET;
1407 * Returns true if we're on a secure lockscreen.
1409 @Override // NotificationData.Environment
1410 public boolean onSecureLockScreen() {
1411 return isLockscreenPublicMode();
1414 public void onNotificationClear(StatusBarNotification notification) {
1416 mBarService.onNotificationClear(
1417 notification.getPackageName(),
1418 notification.getTag(),
1419 notification.getId(),
1420 notification.getUserId());
1421 } catch (android.os.RemoteException ex) {
1427 * Called when the notification panel layouts
1429 public void onPanelLaidOut() {
1430 if (mState == StatusBarState.KEYGUARD) {
1431 // Since the number of notifications is determined based on the height of the view, we
1432 // need to update them.
1433 int maxBefore = getMaxKeyguardNotifications(false /* recompute */);
1434 int maxNotifications = getMaxKeyguardNotifications(true /* recompute */);
1435 if (maxBefore != maxNotifications) {
1441 protected void onLockedRemoteInput(ExpandableNotificationRow row, View clickedView) {}
1444 public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
1447 protected class H extends Handler {
1448 public void handleMessage(Message m) {
1450 case MSG_SHOW_RECENT_APPS:
1451 showRecents(m.arg1 > 0);
1453 case MSG_HIDE_RECENT_APPS:
1454 hideRecents(m.arg1 > 0, m.arg2 > 0);
1456 case MSG_TOGGLE_RECENTS_APPS:
1459 case MSG_PRELOAD_RECENT_APPS:
1462 case MSG_CANCEL_PRELOAD_RECENT_APPS:
1463 cancelPreloadingRecents();
1465 case MSG_SHOW_NEXT_AFFILIATED_TASK:
1466 showRecentsNextAffiliatedTask();
1468 case MSG_SHOW_PREV_AFFILIATED_TASK:
1469 showRecentsPreviousAffiliatedTask();
1471 case MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU:
1472 toggleKeyboardShortcuts(m.arg1);
1478 protected void workAroundBadLayerDrawableOpacity(View v) {
1481 protected boolean inflateViews(Entry entry, ViewGroup parent) {
1482 PackageManager pmUser = getPackageManagerForUser(mContext,
1483 entry.notification.getUser().getIdentifier());
1485 final StatusBarNotification sbn = entry.notification;
1486 entry.cacheContentViews(mContext, null);
1488 final RemoteViews contentView = entry.cachedContentView;
1489 final RemoteViews bigContentView = entry.cachedBigContentView;
1490 final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView;
1491 final RemoteViews publicContentView = entry.cachedPublicContentView;
1493 if (contentView == null) {
1494 Log.v(TAG, "no contentView for: " + sbn.getNotification());
1499 Log.v(TAG, "publicContentView: " + publicContentView);
1502 ExpandableNotificationRow row;
1504 // Stash away previous user expansion state so we can restore it at
1506 boolean hasUserChangedExpansion = false;
1507 boolean userExpanded = false;
1508 boolean userLocked = false;
1510 if (entry.row != null) {
1512 hasUserChangedExpansion = row.hasUserChangedExpansion();
1513 userExpanded = row.isUserExpanded();
1514 userLocked = row.isUserLocked();
1516 if (hasUserChangedExpansion) {
1517 row.setUserExpanded(userExpanded);
1520 // create the row view
1521 LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
1522 Context.LAYOUT_INFLATER_SERVICE);
1523 row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row,
1525 row.setExpansionLogger(this, entry.notification.getKey());
1526 row.setGroupManager(mGroupManager);
1527 row.setHeadsUpManager(mHeadsUpManager);
1528 row.setRemoteInputController(mRemoteInputController);
1529 row.setOnExpandClickListener(this);
1532 final String pkg = sbn.getPackageName();
1533 String appname = pkg;
1535 final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
1536 PackageManager.GET_UNINSTALLED_PACKAGES
1537 | PackageManager.GET_DISABLED_COMPONENTS);
1539 appname = String.valueOf(pmUser.getApplicationLabel(info));
1541 } catch (NameNotFoundException e) {
1544 row.setAppName(appname);
1547 workAroundBadLayerDrawableOpacity(row);
1548 View vetoButton = bindVetoButtonClickListener(row, sbn);
1549 vetoButton.setContentDescription(mContext.getString(
1550 R.string.accessibility_remove_notification));
1552 // NB: the large icon is now handled entirely by the template
1554 // bind the click event to the content area
1555 NotificationContentView contentContainer = row.getPrivateLayout();
1556 NotificationContentView contentContainerPublic = row.getPublicLayout();
1558 row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
1559 if (ENABLE_REMOTE_INPUT) {
1560 row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
1563 mNotificationClicker.register(row, sbn);
1565 // set up the adaptive layout
1566 View contentViewLocal = null;
1567 View bigContentViewLocal = null;
1568 View headsUpContentViewLocal = null;
1569 View publicViewLocal = null;
1571 contentViewLocal = contentView.apply(
1572 sbn.getPackageContext(mContext),
1575 if (bigContentView != null) {
1576 bigContentViewLocal = bigContentView.apply(
1577 sbn.getPackageContext(mContext),
1581 if (headsUpContentView != null) {
1582 headsUpContentViewLocal = headsUpContentView.apply(
1583 sbn.getPackageContext(mContext),
1587 if (publicContentView != null) {
1588 publicViewLocal = publicContentView.apply(
1589 sbn.getPackageContext(mContext),
1590 contentContainerPublic, mOnClickHandler);
1593 catch (RuntimeException e) {
1594 final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
1595 Log.e(TAG, "couldn't inflate view for notification " + ident, e);
1599 if (contentViewLocal != null) {
1600 contentViewLocal.setIsRootNamespace(true);
1601 contentContainer.setContractedChild(contentViewLocal);
1603 if (bigContentViewLocal != null) {
1604 bigContentViewLocal.setIsRootNamespace(true);
1605 contentContainer.setExpandedChild(bigContentViewLocal);
1607 if (headsUpContentViewLocal != null) {
1608 headsUpContentViewLocal.setIsRootNamespace(true);
1609 contentContainer.setHeadsUpChild(headsUpContentViewLocal);
1611 if (publicViewLocal != null) {
1612 publicViewLocal.setIsRootNamespace(true);
1613 contentContainerPublic.setContractedChild(publicViewLocal);
1616 // Extract target SDK version.
1618 ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
1619 entry.targetSdk = info.targetSdkVersion;
1620 } catch (NameNotFoundException ex) {
1621 Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
1623 entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
1625 if (MULTIUSER_DEBUG) {
1626 TextView debug = (TextView) row.findViewById(R.id.debug_info);
1627 if (debug != null) {
1628 debug.setVisibility(View.VISIBLE);
1629 debug.setText("CU " + mCurrentUserId +" NU " + entry.notification.getUserId());
1633 entry.row.setOnActivatedListener(this);
1634 entry.row.setExpandable(bigContentViewLocal != null);
1636 applyColorsAndBackgrounds(sbn, entry);
1638 // Restore previous flags.
1639 if (hasUserChangedExpansion) {
1640 // Note: setUserExpanded() conveniently ignores calls with
1641 // userExpanded=true if !isExpandable().
1642 row.setUserExpanded(userExpanded);
1644 row.setUserLocked(userLocked);
1645 row.onNotificationUpdated(entry);
1650 * Adds RemoteInput actions from the WearableExtender; to be removed once more apps support this
1651 * via first-class API.
1653 * TODO: Remove once enough apps specify remote inputs on their own.
1655 private void processForRemoteInput(Notification n) {
1656 if (!ENABLE_REMOTE_INPUT) return;
1658 if (n.extras != null && n.extras.containsKey("android.wearable.EXTENSIONS") &&
1659 (n.actions == null || n.actions.length == 0)) {
1660 Notification.Action viableAction = null;
1661 Notification.WearableExtender we = new Notification.WearableExtender(n);
1663 List<Notification.Action> actions = we.getActions();
1664 final int numActions = actions.size();
1666 for (int i = 0; i < numActions; i++) {
1667 Notification.Action action = actions.get(i);
1668 if (action == null) {
1671 RemoteInput[] remoteInputs = action.getRemoteInputs();
1672 if (remoteInputs == null) {
1675 for (RemoteInput ri : remoteInputs) {
1676 if (ri.getAllowFreeFormInput()) {
1677 viableAction = action;
1681 if (viableAction != null) {
1686 if (viableAction != null) {
1687 Notification.Builder rebuilder = Notification.Builder.recoverBuilder(mContext, n);
1688 rebuilder.setActions(viableAction);
1689 rebuilder.build(); // will rewrite n
1694 protected KeyboardShortcuts getKeyboardShortcuts() {
1695 if (mKeyboardShortcuts == null) {
1696 mKeyboardShortcuts = new KeyboardShortcuts(mContext);
1699 return mKeyboardShortcuts;
1702 public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
1703 if (!isDeviceProvisioned()) return;
1705 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
1706 final boolean afterKeyguardGone = intent.isActivity()
1707 && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
1709 dismissKeyguardThenExecute(new OnDismissAction() {
1710 public boolean onDismiss() {
1715 if (keyguardShowing && !afterKeyguardGone) {
1716 ActivityManagerNative.getDefault()
1717 .keyguardWaitingForActivityDrawn();
1720 // The intent we are sending is for the application, which
1721 // won't have permission to immediately start an activity after
1722 // the user switches to home. We know it is safe to do at this
1723 // point, so make sure new activity switches are now allowed.
1724 ActivityManagerNative.getDefault().resumeAppSwitches();
1725 } catch (RemoteException e) {
1729 } catch (PendingIntent.CanceledException e) {
1730 // the stack trace isn't very helpful here.
1731 // Just log the exception message.
1732 Log.w(TAG, "Sending intent failed: " + e);
1734 // TODO: Dismiss Keyguard.
1736 if (intent.isActivity()) {
1737 mAssistManager.hideAssist();
1738 overrideActivityPendingAppTransition(keyguardShowing
1739 && !afterKeyguardGone);
1744 // close the shade if it was open
1745 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
1746 true /* force */, true /* delayed */);
1747 visibilityChanged(false);
1751 }, afterKeyguardGone);
1754 private final class NotificationClicker implements View.OnClickListener {
1755 public void onClick(final View v) {
1756 if (!(v instanceof ExpandableNotificationRow)) {
1757 Log.e(TAG, "NotificationClicker called on a view that is not a notification row.");
1761 final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
1762 final StatusBarNotification sbn = row.getStatusBarNotification();
1764 Log.e(TAG, "NotificationClicker called on an unclickable notification,");
1768 // Check if the notification is displaying the gear, if so slide notification back
1769 if (row.getSettingsRow() != null && row.getSettingsRow().isVisible()) {
1770 row.animateTranslateNotification(0);
1774 Notification notification = sbn.getNotification();
1775 final PendingIntent intent = notification.contentIntent != null
1776 ? notification.contentIntent
1777 : notification.fullScreenIntent;
1778 final String notificationKey = sbn.getKey();
1780 // Mark notification for one frame.
1781 row.setJustClicked(true);
1782 DejankUtils.postAfterTraversal(new Runnable() {
1785 row.setJustClicked(false);
1789 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
1790 final boolean afterKeyguardGone = intent.isActivity()
1791 && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
1793 dismissKeyguardThenExecute(new OnDismissAction() {
1794 public boolean onDismiss() {
1795 if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) {
1796 // Release the HUN notification to the shade.
1798 // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
1799 // become canceled shortly by NoMan, but we can't assume that.
1800 HeadsUpManager.setIsClickedNotification(row, true);
1801 mHeadsUpManager.releaseImmediately(notificationKey);
1807 if (keyguardShowing && !afterKeyguardGone) {
1808 ActivityManagerNative.getDefault()
1809 .keyguardWaitingForActivityDrawn();
1812 // The intent we are sending is for the application, which
1813 // won't have permission to immediately start an activity after
1814 // the user switches to home. We know it is safe to do at this
1815 // point, so make sure new activity switches are now allowed.
1816 ActivityManagerNative.getDefault().resumeAppSwitches();
1817 } catch (RemoteException e) {
1819 if (intent != null) {
1820 // If we are launching a work activity and require to launch
1821 // separate work challenge, we defer the activity action and cancel
1822 // notification until work challenge is unlocked.
1823 if (intent.isActivity()) {
1824 final int userId = intent.getCreatorUserHandle()
1826 if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
1827 && mKeyguardManager.isDeviceLocked(userId)) {
1828 // Show work challenge, do not run pendingintent and
1829 // remove notification
1830 startWorkChallenge(userId, intent.getIntentSender(),
1837 } catch (PendingIntent.CanceledException e) {
1838 // the stack trace isn't very helpful here.
1839 // Just log the exception message.
1840 Log.w(TAG, "Sending contentIntent failed: " + e);
1842 // TODO: Dismiss Keyguard.
1844 if (intent.isActivity()) {
1845 mAssistManager.hideAssist();
1846 overrideActivityPendingAppTransition(keyguardShowing
1847 && !afterKeyguardGone);
1852 mBarService.onNotificationClick(notificationKey);
1853 } catch (RemoteException ex) {
1854 // system process is dead if we're here.
1859 // close the shade if it was open
1860 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
1861 true /* force */, true /* delayed */);
1862 visibilityChanged(false);
1866 }, afterKeyguardGone);
1869 public void startWorkChallenge(int userId, IntentSender intendSender,
1870 String notificationKey) {
1871 final Intent callBackIntent = new Intent(
1872 WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION);
1873 callBackIntent.putExtra(Intent.EXTRA_INTENT, intendSender);
1874 callBackIntent.putExtra(Intent.EXTRA_INDEX, notificationKey);
1875 callBackIntent.setPackage(mContext.getPackageName());
1877 final Intent newIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null,
1879 newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
1880 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | Intent.FLAG_ACTIVITY_CLEAR_TASK);
1881 newIntent.putExtra(Intent.EXTRA_INTENT, PendingIntent
1882 .getBroadcast(mContext, 0, callBackIntent, 0).getIntentSender());
1883 mContext.startActivity(newIntent);
1886 public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
1887 Notification notification = sbn.getNotification();
1888 if (notification.contentIntent != null || notification.fullScreenIntent != null) {
1889 row.setOnClickListener(this);
1891 row.setOnClickListener(null);
1896 public void animateCollapsePanels(int flags, boolean force) {
1899 public void animateCollapsePanels(int flags, boolean force, boolean delayed) {
1902 public void overrideActivityPendingAppTransition(boolean keyguardShowing) {
1903 if (keyguardShowing) {
1905 mWindowManagerService.overridePendingAppTransition(null, 0, 0, null);
1906 } catch (RemoteException e) {
1907 Log.w(TAG, "Error overriding app transition: " + e);
1912 protected void visibilityChanged(boolean visible) {
1913 if (mVisible != visible) {
1919 updateVisibleToUser();
1922 protected void updateVisibleToUser() {
1923 boolean oldVisibleToUser = mVisibleToUser;
1924 mVisibleToUser = mVisible && mDeviceInteractive;
1926 if (oldVisibleToUser != mVisibleToUser) {
1927 handleVisibleToUserChanged(mVisibleToUser);
1932 * The LEDs are turned off when the notification panel is shown, even just a little bit.
1933 * See also NotificationStackScrollLayout.setIsExpanded() for another place where we
1934 * attempt to do this.
1936 protected void handleVisibleToUserChanged(boolean visibleToUser) {
1938 if (visibleToUser) {
1939 boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
1940 boolean clearNotificationEffects =
1941 !isPanelFullyCollapsed() &&
1942 (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED);
1943 int notificationLoad = mNotificationData.getActiveNotifications().size();
1944 if (pinnedHeadsUp && isPanelFullyCollapsed()) {
1945 notificationLoad = 1;
1947 MetricsLogger.histogram(mContext, "note_load", notificationLoad);
1949 mBarService.onPanelRevealed(clearNotificationEffects, notificationLoad);
1951 mBarService.onPanelHidden();
1953 } catch (RemoteException ex) {
1954 // Won't fail unless the world has ended.
1959 * Clear Buzz/Beep/Blink.
1961 public void clearNotificationEffects() {
1963 mBarService.clearNotificationEffects();
1964 } catch (RemoteException e) {
1965 // Won't fail unless the world has ended.
1969 public abstract boolean isPanelFullyCollapsed();
1972 * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
1973 * about the failure.
1975 * WARNING: this will call back into us. Don't hold any locks.
1977 void handleNotificationError(StatusBarNotification n, String message) {
1978 removeNotification(n.getKey(), null);
1980 mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
1981 n.getInitialPid(), message, n.getUserId());
1982 } catch (RemoteException ex) {
1987 protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) {
1988 NotificationData.Entry entry = mNotificationData.remove(key, ranking);
1989 if (entry == null) {
1990 Log.w(TAG, "removeNotification for unknown key: " + key);
1993 updateNotifications();
1994 return entry.notification;
1997 protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) {
1999 Log.d(TAG, "createNotificationViews(notification=" + sbn);
2001 final StatusBarIconView iconView = createIcon(sbn);
2002 if (iconView == null) {
2006 // Construct the expanded view.
2007 NotificationData.Entry entry = new NotificationData.Entry(sbn, iconView);
2008 if (!inflateViews(entry, mStackScroller)) {
2009 handleNotificationError(sbn, "Couldn't expand RemoteViews for: " + sbn);
2015 public StatusBarIconView createIcon(StatusBarNotification sbn) {
2016 // Construct the icon.
2017 Notification n = sbn.getNotification();
2018 final StatusBarIconView iconView = new StatusBarIconView(mContext,
2019 sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), n);
2020 iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
2022 final Icon smallIcon = n.getSmallIcon();
2023 if (smallIcon == null) {
2024 handleNotificationError(sbn,
2025 "No small icon in notification from " + sbn.getPackageName());
2028 final StatusBarIcon ic = new StatusBarIcon(
2030 sbn.getPackageName(),
2035 if (!iconView.set(ic)) {
2036 handleNotificationError(sbn, "Couldn't create icon: " + ic);
2042 protected void addNotificationViews(Entry entry, RankingMap ranking) {
2043 if (entry == null) {
2046 // Add the expanded view and icon.
2047 mNotificationData.add(entry, ranking);
2048 updateNotifications();
2052 * @param recompute wheter the number should be recomputed
2053 * @return The number of notifications we show on Keyguard.
2055 protected abstract int getMaxKeyguardNotifications(boolean recompute);
2058 * Updates expanded, dimmed and locked states of notification rows.
2060 protected void updateRowStates() {
2061 mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
2063 ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
2064 final int N = activeNotifications.size();
2066 int visibleNotifications = 0;
2067 boolean onKeyguard = mState == StatusBarState.KEYGUARD;
2068 int maxNotifications = 0;
2070 maxNotifications = getMaxKeyguardNotifications(true /* recompute */);
2072 for (int i = 0; i < N; i++) {
2073 NotificationData.Entry entry = activeNotifications.get(i);
2074 boolean childNotification = mGroupManager.isChildInGroupWithSummary(entry.notification);
2076 entry.row.setOnKeyguard(true);
2078 entry.row.setOnKeyguard(false);
2079 entry.row.setSystemExpanded(visibleNotifications == 0 && !childNotification);
2081 boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(entry.notification);
2082 boolean childWithVisibleSummary = childNotification
2083 && mGroupManager.getGroupSummary(entry.notification).getVisibility()
2085 boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
2086 if (suppressedSummary || (isLockscreenPublicMode() && !mShowLockscreenNotifications) ||
2087 (onKeyguard && (visibleNotifications >= maxNotifications
2088 && !childWithVisibleSummary
2089 || !showOnKeyguard))) {
2090 entry.row.setVisibility(View.GONE);
2091 if (onKeyguard && showOnKeyguard && !childNotification && !suppressedSummary) {
2092 mKeyguardIconOverflowContainer.getIconsView().addNotification(entry);
2095 boolean wasGone = entry.row.getVisibility() == View.GONE;
2096 entry.row.setVisibility(View.VISIBLE);
2097 if (!childNotification) {
2099 // notify the scroller of a child addition
2100 mStackScroller.generateAddAnimation(entry.row,
2101 !showOnKeyguard /* fromMoreCard */);
2103 visibleNotifications++;
2108 mStackScroller.updateOverflowContainerVisibility(onKeyguard
2109 && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0);
2111 mStackScroller.changeViewPosition(mDismissView, mStackScroller.getChildCount() - 1);
2112 mStackScroller.changeViewPosition(mEmptyShadeView, mStackScroller.getChildCount() - 2);
2113 mStackScroller.changeViewPosition(mKeyguardIconOverflowContainer,
2114 mStackScroller.getChildCount() - 3);
2117 private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
2118 return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey());
2121 protected void setZenMode(int mode) {
2122 if (!isDeviceProvisioned()) return;
2124 updateNotifications();
2127 // extended in PhoneStatusBar
2128 protected void setShowLockscreenNotifications(boolean show) {
2129 mShowLockscreenNotifications = show;
2132 protected void setLockScreenAllowRemoteInput(boolean allowLockscreenRemoteInput) {
2133 mAllowLockscreenRemoteInput = allowLockscreenRemoteInput;
2136 private void updateLockscreenNotificationSetting() {
2137 final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
2138 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
2140 mCurrentUserId) != 0;
2141 final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
2142 null /* admin */, mCurrentUserId);
2143 final boolean allowedByDpm = (dpmFlags
2144 & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
2146 final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(),
2147 Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
2149 mCurrentUserId) != 0;
2151 setShowLockscreenNotifications(show && allowedByDpm);
2152 setLockScreenAllowRemoteInput(remoteInput);
2155 protected abstract void setAreThereNotifications();
2156 protected abstract void updateNotifications();
2157 public abstract boolean shouldDisableNavbarGestures();
2159 public abstract void addNotification(StatusBarNotification notification,
2160 RankingMap ranking, Entry oldEntry);
2161 protected abstract void updateNotificationRanking(RankingMap ranking);
2162 public abstract void removeNotification(String key, RankingMap ranking);
2164 public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
2165 if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
2167 final String key = notification.getKey();
2168 Entry entry = mNotificationData.get(key);
2169 if (entry == null) {
2171 } else if (mHeadsUpEntriesToRemoveOnSwitch.contains(entry)) {
2172 mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
2175 Notification n = notification.getNotification();
2176 mNotificationData.updateRanking(ranking);
2178 boolean applyInPlace = entry.cacheContentViews(mContext, notification.getNotification());
2179 boolean shouldPeek = shouldPeek(entry, notification);
2180 boolean alertAgain = alertAgain(entry, n);
2182 Log.d(TAG, "applyInPlace=" + applyInPlace
2183 + " shouldPeek=" + shouldPeek
2184 + " alertAgain=" + alertAgain);
2187 final StatusBarNotification oldNotification = entry.notification;
2188 entry.notification = notification;
2189 mGroupManager.onEntryUpdated(entry, oldNotification);
2191 boolean updateSuccessful = false;
2193 if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
2195 if (entry.icon != null) {
2197 final StatusBarIcon ic = new StatusBarIcon(
2198 notification.getUser(),
2199 notification.getPackageName(),
2204 entry.icon.setNotification(n);
2205 if (!entry.icon.set(ic)) {
2206 handleNotificationError(notification, "Couldn't update icon: " + ic);
2210 updateNotificationViews(entry, notification);
2211 updateSuccessful = true;
2213 catch (RuntimeException e) {
2214 // It failed to apply cleanly.
2215 Log.w(TAG, "Couldn't reapply views for package " +
2216 notification.getPackageName(), e);
2219 if (!updateSuccessful) {
2220 if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);
2221 final StatusBarIcon ic = new StatusBarIcon(
2222 notification.getUser(),
2223 notification.getPackageName(),
2228 entry.icon.setNotification(n);
2230 inflateViews(entry, mStackScroller);
2232 updateHeadsUp(key, entry, shouldPeek, alertAgain);
2233 updateNotifications();
2235 // Update the veto button accordingly (and as a result, whether this row is
2236 // swipe-dismissable)
2237 bindVetoButtonClickListener(entry.row, notification);
2239 if (!notification.isClearable()) {
2240 // The user may have performed a dismiss action on the notification, since it's
2241 // not clearable we should snap it back.
2242 mStackScroller.snapViewIfNeeded(entry.row);
2247 boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);
2248 Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
2251 setAreThereNotifications();
2254 protected abstract void updateHeadsUp(String key, Entry entry, boolean shouldPeek,
2255 boolean alertAgain);
2257 private void updateNotificationViews(Entry entry, StatusBarNotification sbn) {
2258 final RemoteViews contentView = entry.cachedContentView;
2259 final RemoteViews bigContentView = entry.cachedBigContentView;
2260 final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView;
2261 final RemoteViews publicContentView = entry.cachedPublicContentView;
2263 // Reapply the RemoteViews
2264 contentView.reapply(mContext, entry.getContentView(), mOnClickHandler);
2265 if (bigContentView != null && entry.getExpandedContentView() != null) {
2266 bigContentView.reapply(sbn.getPackageContext(mContext),
2267 entry.getExpandedContentView(),
2270 View headsUpChild = entry.getHeadsUpContentView();
2271 if (headsUpContentView != null && headsUpChild != null) {
2272 headsUpContentView.reapply(sbn.getPackageContext(mContext),
2273 headsUpChild, mOnClickHandler);
2275 if (publicContentView != null && entry.getPublicContentView() != null) {
2276 publicContentView.reapply(sbn.getPackageContext(mContext),
2277 entry.getPublicContentView(), mOnClickHandler);
2279 // update the contentIntent
2280 mNotificationClicker.register(entry.row, sbn);
2282 entry.row.onNotificationUpdated(entry);
2283 entry.row.resetHeight();
2286 protected void updatePublicContentView(Entry entry,
2287 StatusBarNotification sbn) {
2288 final RemoteViews publicContentView = entry.cachedPublicContentView;
2289 View inflatedView = entry.getPublicContentView();
2290 if (entry.autoRedacted && publicContentView != null && inflatedView != null) {
2291 final boolean disabledByPolicy =
2292 !adminAllowsUnredactedNotifications(entry.notification.getUserId());
2293 String notificationHiddenText = mContext.getString(disabledByPolicy
2294 ? com.android.internal.R.string.notification_hidden_by_policy_text
2295 : com.android.internal.R.string.notification_hidden_text);
2296 TextView titleView = (TextView) inflatedView.findViewById(android.R.id.title);
2297 if (titleView != null
2298 && !titleView.getText().toString().equals(notificationHiddenText)) {
2299 publicContentView.setTextViewText(android.R.id.title, notificationHiddenText);
2300 publicContentView.reapply(sbn.getPackageContext(mContext),
2301 inflatedView, mOnClickHandler);
2302 entry.row.onNotificationUpdated(entry);
2307 protected void notifyHeadsUpScreenOff() {
2308 maybeEscalateHeadsUp();
2311 private boolean alertAgain(Entry oldEntry, Notification newNotification) {
2312 return oldEntry == null || !oldEntry.hasInterrupted()
2313 || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
2316 protected boolean shouldPeek(Entry entry) {
2317 return shouldPeek(entry, entry.notification);
2320 protected boolean shouldPeek(Entry entry, StatusBarNotification sbn) {
2321 if (mNotificationData.shouldFilterOut(sbn)) {
2322 if (DEBUG) Log.d(TAG, "No peeking: filtered notification: " + sbn.getKey());
2326 boolean inUse = mPowerManager.isScreenOn()
2327 && (!mStatusBarKeyguardViewManager.isShowing()
2328 || mStatusBarKeyguardViewManager.isOccluded())
2329 && !mStatusBarKeyguardViewManager.isInputRestricted();
2331 inUse = inUse && !mDreamManager.isDreaming();
2332 } catch (RemoteException e) {
2333 Log.d(TAG, "failed to query dream manager", e);
2338 Log.d(TAG, "No peeking: not in use: " + sbn.getKey());
2343 if (mNotificationData.shouldSuppressScreenOn(sbn.getKey())) {
2344 if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
2348 if (entry.hasJustLaunchedFullScreenIntent()) {
2349 if (DEBUG) Log.d(TAG, "No peeking: recent fullscreen: " + sbn.getKey());
2353 if (isSnoozedPackage(sbn)) {
2354 if (DEBUG) Log.d(TAG, "No peeking: snoozed package: " + sbn.getKey());
2358 if (mNotificationData.getImportance(sbn.getKey()) < IMPORTANCE_HIGH) {
2359 if (DEBUG) Log.d(TAG, "No peeking: unimportant notification: " + sbn.getKey());
2363 if (sbn.getNotification().fullScreenIntent != null) {
2364 if (mAccessibilityManager.isTouchExplorationEnabled()) {
2365 if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
2375 protected abstract boolean isSnoozedPackage(StatusBarNotification sbn);
2377 public void setInteracting(int barWindow, boolean interacting) {
2378 // hook for subclasses
2381 public void setBouncerShowing(boolean bouncerShowing) {
2382 mBouncerShowing = bouncerShowing;
2386 * @return Whether the security bouncer from Keyguard is showing.
2388 public boolean isBouncerShowing() {
2389 return mBouncerShowing;
2392 public void destroy() {
2393 mContext.unregisterReceiver(mBroadcastReceiver);
2395 mNotificationListener.unregisterAsSystemService();
2396 } catch (RemoteException e) {
2402 * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then
2403 * return PackageManager for mContext
2405 public static PackageManager getPackageManagerForUser(Context context, int userId) {
2406 Context contextForUser = context;
2407 // UserHandle defines special userId as negative values, e.g. USER_ALL
2410 // Create a context for the correct user so if a package isn't installed
2411 // for user 0 we can still load information about the package.
2413 context.createPackageContextAsUser(context.getPackageName(),
2414 Context.CONTEXT_RESTRICTED,
2415 new UserHandle(userId));
2416 } catch (NameNotFoundException e) {
2417 // Shouldn't fail to find the package name for system ui.
2420 return contextForUser.getPackageManager();
2424 public void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
2426 mBarService.onNotificationExpansionChanged(key, userAction, expanded);
2427 } catch (RemoteException e) {
2432 public boolean isKeyguardSecure() {
2433 if (mStatusBarKeyguardViewManager == null) {
2434 // startKeyguard() hasn't been called yet, so we don't know.
2435 // Make sure anything that needs to know isKeyguardSecure() checks and re-checks this
2436 // value onVisibilityChanged().
2437 Slog.w(TAG, "isKeyguardSecure() called before startKeyguard(), returning false",
2441 return mStatusBarKeyguardViewManager.isSecure();
2445 public void showAssistDisclosure() {
2446 if (mAssistManager != null) {
2447 mAssistManager.showDisclosure();
2452 public void startAssist(Bundle args) {
2453 if (mAssistManager != null) {
2454 mAssistManager.startAssist(args);