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