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.animation.TimeInterpolator;
     22 import android.app.ActivityManager;
     23 import android.app.ActivityManagerNative;
     24 import android.app.Notification;
     25 import android.app.NotificationManager;
     26 import android.app.PendingIntent;
     27 import android.app.TaskStackBuilder;
     28 import android.app.admin.DevicePolicyManager;
     29 import android.content.BroadcastReceiver;
     30 import android.content.ComponentName;
     31 import android.content.Context;
     32 import android.content.Intent;
     33 import android.content.IntentFilter;
     34 import android.content.pm.ApplicationInfo;
     35 import android.content.pm.PackageManager;
     36 import android.content.pm.PackageManager.NameNotFoundException;
     37 import android.content.pm.ResolveInfo;
     38 import android.content.pm.UserInfo;
     39 import android.content.res.Configuration;
     40 import android.content.res.Resources;
     41 import android.database.ContentObserver;
     42 import android.graphics.PorterDuff;
     43 import android.graphics.drawable.Drawable;
     44 import android.os.AsyncTask;
     45 import android.os.Build;
     46 import android.os.Handler;
     47 import android.os.IBinder;
     48 import android.os.Message;
     49 import android.os.PowerManager;
     50 import android.os.RemoteException;
     51 import android.os.ServiceManager;
     52 import android.os.UserHandle;
     53 import android.os.UserManager;
     54 import android.provider.Settings;
     55 import android.service.dreams.DreamService;
     56 import android.service.dreams.IDreamManager;
     57 import android.service.notification.NotificationListenerService;
     58 import android.service.notification.NotificationListenerService.RankingMap;
     59 import android.service.notification.StatusBarNotification;
     60 import android.text.TextUtils;
     61 import android.util.Log;
     62 import android.util.Slog;
     63 import android.util.SparseArray;
     64 import android.util.SparseBooleanArray;
     65 import android.view.Display;
     66 import android.view.IWindowManager;
     67 import android.view.LayoutInflater;
     68 import android.view.MotionEvent;
     69 import android.view.View;
     70 import android.view.ViewAnimationUtils;
     71 import android.view.ViewGroup;
     72 import android.view.ViewGroup.LayoutParams;
     73 import android.view.ViewParent;
     74 import android.view.ViewStub;
     75 import android.view.WindowManager;
     76 import android.view.WindowManagerGlobal;
     77 import android.view.accessibility.AccessibilityManager;
     78 import android.view.animation.AnimationUtils;
     79 import android.widget.DateTimeView;
     80 import android.widget.ImageView;
     81 import android.widget.LinearLayout;
     82 import android.widget.RemoteViews;
     83 import android.widget.TextView;
     84 
     85 import com.android.internal.statusbar.IStatusBarService;
     86 import com.android.internal.statusbar.StatusBarIcon;
     87 import com.android.internal.statusbar.StatusBarIconList;
     88 import com.android.internal.util.NotificationColorUtil;
     89 import com.android.internal.widget.LockPatternUtils;
     90 import com.android.systemui.R;
     91 import com.android.systemui.RecentsComponent;
     92 import com.android.systemui.SearchPanelView;
     93 import com.android.systemui.SwipeHelper;
     94 import com.android.systemui.SystemUI;
     95 import com.android.systemui.statusbar.NotificationData.Entry;
     96 import com.android.systemui.statusbar.phone.NavigationBarView;
     97 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
     98 import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
     99 import com.android.systemui.statusbar.policy.PreviewInflater;
    100 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
    101 
    102 import java.util.ArrayList;
    103 import java.util.List;
    104 import java.util.Locale;
    105 
    106 import static com.android.keyguard.KeyguardHostView.OnDismissAction;
    107 
    108 public abstract class BaseStatusBar extends SystemUI implements
    109         CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
    110         RecentsComponent.Callbacks, ExpandableNotificationRow.ExpansionLogger,
    111         NotificationData.Environment {
    112     public static final String TAG = "StatusBar";
    113     public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    114     public static final boolean MULTIUSER_DEBUG = false;
    115 
    116     // STOPSHIP disable once we resolve b/18102199
    117     private static final boolean NOTIFICATION_CLICK_DEBUG = true;
    118 
    119     protected static final int MSG_SHOW_RECENT_APPS = 1019;
    120     protected static final int MSG_HIDE_RECENT_APPS = 1020;
    121     protected static final int MSG_TOGGLE_RECENTS_APPS = 1021;
    122     protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
    123     protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
    124     protected static final int MSG_SHOW_NEXT_AFFILIATED_TASK = 1024;
    125     protected static final int MSG_SHOW_PREV_AFFILIATED_TASK = 1025;
    126     protected static final int MSG_CLOSE_SEARCH_PANEL = 1027;
    127     protected static final int MSG_SHOW_HEADS_UP = 1028;
    128     protected static final int MSG_HIDE_HEADS_UP = 1029;
    129     protected static final int MSG_ESCALATE_HEADS_UP = 1030;
    130     protected static final int MSG_DECAY_HEADS_UP = 1031;
    131 
    132     protected static final boolean ENABLE_HEADS_UP = true;
    133     // scores above this threshold should be displayed in heads up mode.
    134     protected static final int INTERRUPTION_THRESHOLD = 10;
    135     protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
    136 
    137     // Should match the value in PhoneWindowManager
    138     public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
    139 
    140     public static final int EXPANDED_LEAVE_ALONE = -10000;
    141     public static final int EXPANDED_FULL_OPEN = -10001;
    142 
    143     private static final int HIDDEN_NOTIFICATION_ID = 10000;
    144     private static final String BANNER_ACTION_CANCEL =
    145             "com.android.systemui.statusbar.banner_action_cancel";
    146     private static final String BANNER_ACTION_SETUP =
    147             "com.android.systemui.statusbar.banner_action_setup";
    148 
    149     protected CommandQueue mCommandQueue;
    150     protected IStatusBarService mBarService;
    151     protected H mHandler = createHandler();
    152 
    153     // all notifications
    154     protected NotificationData mNotificationData;
    155     protected NotificationStackScrollLayout mStackScroller;
    156 
    157     // for heads up notifications
    158     protected HeadsUpNotificationView mHeadsUpNotificationView;
    159     protected int mHeadsUpNotificationDecay;
    160 
    161     // Search panel
    162     protected SearchPanelView mSearchPanelView;
    163 
    164     protected int mCurrentUserId = 0;
    165     final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
    166 
    167     protected int mLayoutDirection = -1; // invalid
    168     protected AccessibilityManager mAccessibilityManager;
    169 
    170     // on-screen navigation buttons
    171     protected NavigationBarView mNavigationBarView = null;
    172 
    173     protected Boolean mScreenOn;
    174 
    175     // The second field is a bit different from the first one because it only listens to screen on/
    176     // screen of events from Keyguard. We need this so we don't have a race condition with the
    177     // broadcast. In the future, we should remove the first field altogether and rename the second
    178     // field.
    179     protected boolean mScreenOnFromKeyguard;
    180 
    181     protected boolean mVisible;
    182 
    183     // mScreenOnFromKeyguard && mVisible.
    184     private boolean mVisibleToUser;
    185 
    186     private Locale mLocale;
    187     private float mFontScale;
    188 
    189     protected boolean mUseHeadsUp = false;
    190     protected boolean mHeadsUpTicker = false;
    191     protected boolean mDisableNotificationAlerts = false;
    192 
    193     protected DevicePolicyManager mDevicePolicyManager;
    194     protected IDreamManager mDreamManager;
    195     PowerManager mPowerManager;
    196     protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
    197     protected int mRowMinHeight;
    198     protected int mRowMaxHeight;
    199 
    200     // public mode, private notifications, etc
    201     private boolean mLockscreenPublicMode = false;
    202     private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
    203     private NotificationColorUtil mNotificationColorUtil;
    204 
    205     private UserManager mUserManager;
    206 
    207     // UI-specific methods
    208 
    209     /**
    210      * Create all windows necessary for the status bar (including navigation, overlay panels, etc)
    211      * and add them to the window manager.
    212      */
    213     protected abstract void createAndAddWindows();
    214 
    215     protected WindowManager mWindowManager;
    216     protected IWindowManager mWindowManagerService;
    217 
    218     protected abstract void refreshLayout(int layoutDirection);
    219 
    220     protected Display mDisplay;
    221 
    222     private boolean mDeviceProvisioned = false;
    223 
    224     private RecentsComponent mRecents;
    225 
    226     protected int mZenMode;
    227 
    228     // which notification is currently being longpress-examined by the user
    229     private NotificationGuts mNotificationGutsExposed;
    230 
    231     private TimeInterpolator mLinearOutSlowIn, mFastOutLinearIn;
    232 
    233     /**
    234      * The {@link StatusBarState} of the status bar.
    235      */
    236     protected int mState;
    237     protected boolean mBouncerShowing;
    238     protected boolean mShowLockscreenNotifications;
    239 
    240     protected NotificationOverflowContainer mKeyguardIconOverflowContainer;
    241     protected DismissView mDismissView;
    242     protected EmptyShadeView mEmptyShadeView;
    243 
    244     @Override  // NotificationData.Environment
    245     public boolean isDeviceProvisioned() {
    246         return mDeviceProvisioned;
    247     }
    248 
    249     protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
    250         @Override
    251         public void onChange(boolean selfChange) {
    252             final boolean provisioned = 0 != Settings.Global.getInt(
    253                     mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
    254             if (provisioned != mDeviceProvisioned) {
    255                 mDeviceProvisioned = provisioned;
    256                 updateNotifications();
    257             }
    258             final int mode = Settings.Global.getInt(mContext.getContentResolver(),
    259                     Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
    260             setZenMode(mode);
    261 
    262             updateLockscreenNotificationSetting();
    263         }
    264     };
    265 
    266     private final ContentObserver mLockscreenSettingsObserver = new ContentObserver(mHandler) {
    267         @Override
    268         public void onChange(boolean selfChange) {
    269             // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
    270             // so we just dump our cache ...
    271             mUsersAllowingPrivateNotifications.clear();
    272             // ... and refresh all the notifications
    273             updateNotifications();
    274         }
    275     };
    276 
    277     private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
    278         @Override
    279         public boolean onClickHandler(
    280                 final View view, final PendingIntent pendingIntent, final Intent fillInIntent) {
    281             if (DEBUG) {
    282                 Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
    283             }
    284             logActionClick(view);
    285             // The intent we are sending is for the application, which
    286             // won't have permission to immediately start an activity after
    287             // the user switches to home.  We know it is safe to do at this
    288             // point, so make sure new activity switches are now allowed.
    289             try {
    290                 ActivityManagerNative.getDefault().resumeAppSwitches();
    291             } catch (RemoteException e) {
    292             }
    293             final boolean isActivity = pendingIntent.isActivity();
    294             if (isActivity) {
    295                 final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
    296                 final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
    297                         mContext, pendingIntent.getIntent(), mCurrentUserId);
    298                 dismissKeyguardThenExecute(new OnDismissAction() {
    299                     @Override
    300                     public boolean onDismiss() {
    301                         if (keyguardShowing && !afterKeyguardGone) {
    302                             try {
    303                                 ActivityManagerNative.getDefault()
    304                                         .keyguardWaitingForActivityDrawn();
    305                             } catch (RemoteException e) {
    306                             }
    307                         }
    308 
    309                         boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
    310                         overrideActivityPendingAppTransition(keyguardShowing && !afterKeyguardGone);
    311 
    312                         // close the shade if it was open
    313                         if (handled) {
    314                             animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
    315                                     true /* force */);
    316                             visibilityChanged(false);
    317                         }
    318                         // Wait for activity start.
    319                         return handled;
    320                     }
    321                 }, afterKeyguardGone);
    322                 return true;
    323             } else {
    324                 return super.onClickHandler(view, pendingIntent, fillInIntent);
    325             }
    326         }
    327 
    328         private void logActionClick(View view) {
    329             ViewParent parent = view.getParent();
    330             String key = getNotificationKeyForParent(parent);
    331             if (key == null) {
    332                 Log.w(TAG, "Couldn't determine notification for click.");
    333                 return;
    334             }
    335             int index = -1;
    336             // If this is a default template, determine the index of the button.
    337             if (view.getId() == com.android.internal.R.id.action0 &&
    338                     parent != null && parent instanceof ViewGroup) {
    339                 ViewGroup actionGroup = (ViewGroup) parent;
    340                 index = actionGroup.indexOfChild(view);
    341             }
    342             if (NOTIFICATION_CLICK_DEBUG) {
    343                 Log.d(TAG, "Clicked on button " + index + " for " + key);
    344             }
    345             try {
    346                 mBarService.onNotificationActionClick(key, index);
    347             } catch (RemoteException e) {
    348                 // Ignore
    349             }
    350         }
    351 
    352         private String getNotificationKeyForParent(ViewParent parent) {
    353             while (parent != null) {
    354                 if (parent instanceof ExpandableNotificationRow) {
    355                     return ((ExpandableNotificationRow) parent).getStatusBarNotification().getKey();
    356                 }
    357                 parent = parent.getParent();
    358             }
    359             return null;
    360         }
    361 
    362         private boolean superOnClickHandler(View view, PendingIntent pendingIntent,
    363                 Intent fillInIntent) {
    364             return super.onClickHandler(view, pendingIntent, fillInIntent);
    365         }
    366     };
    367 
    368     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
    369         @Override
    370         public void onReceive(Context context, Intent intent) {
    371             String action = intent.getAction();
    372             if (Intent.ACTION_USER_SWITCHED.equals(action)) {
    373                 mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
    374                 updateCurrentProfilesCache();
    375                 if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
    376 
    377                 updateLockscreenNotificationSetting();
    378 
    379                 userSwitched(mCurrentUserId);
    380             } else if (Intent.ACTION_USER_ADDED.equals(action)) {
    381                 updateCurrentProfilesCache();
    382             } else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(
    383                     action)) {
    384                 mUsersAllowingPrivateNotifications.clear();
    385                 updateLockscreenNotificationSetting();
    386                 updateNotifications();
    387             } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) {
    388                 NotificationManager noMan = (NotificationManager)
    389                         mContext.getSystemService(Context.NOTIFICATION_SERVICE);
    390                 noMan.cancel(HIDDEN_NOTIFICATION_ID);
    391 
    392                 Settings.Secure.putInt(mContext.getContentResolver(),
    393                         Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
    394                 if (BANNER_ACTION_SETUP.equals(action)) {
    395                     animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
    396                             true /* force */);
    397                     mContext.startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_REDACTION)
    398                             .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    399 
    400                     );
    401                 }
    402             }
    403         }
    404     };
    405 
    406     private final NotificationListenerService mNotificationListener =
    407             new NotificationListenerService() {
    408         @Override
    409         public void onListenerConnected() {
    410             if (DEBUG) Log.d(TAG, "onListenerConnected");
    411             final StatusBarNotification[] notifications = getActiveNotifications();
    412             final RankingMap currentRanking = getCurrentRanking();
    413             mHandler.post(new Runnable() {
    414                 @Override
    415                 public void run() {
    416                     for (StatusBarNotification sbn : notifications) {
    417                         addNotification(sbn, currentRanking);
    418                     }
    419                 }
    420             });
    421         }
    422 
    423         @Override
    424         public void onNotificationPosted(final StatusBarNotification sbn,
    425                 final RankingMap rankingMap) {
    426             if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
    427             mHandler.post(new Runnable() {
    428                 @Override
    429                 public void run() {
    430                     Notification n = sbn.getNotification();
    431                     boolean isUpdate = mNotificationData.get(sbn.getKey()) != null
    432                             || isHeadsUp(sbn.getKey());
    433 
    434                     // Ignore children of notifications that have a summary, since we're not
    435                     // going to show them anyway. This is true also when the summary is canceled,
    436                     // because children are automatically canceled by NoMan in that case.
    437                     if (n.isGroupChild() &&
    438                             mNotificationData.isGroupWithSummary(sbn.getGroupKey())) {
    439                         if (DEBUG) {
    440                             Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
    441                         }
    442 
    443                         // Remove existing notification to avoid stale data.
    444                         if (isUpdate) {
    445                             removeNotification(sbn.getKey(), rankingMap);
    446                         } else {
    447                             mNotificationData.updateRanking(rankingMap);
    448                         }
    449                         return;
    450                     }
    451                     if (isUpdate) {
    452                         updateNotification(sbn, rankingMap);
    453                     } else {
    454                         addNotification(sbn, rankingMap);
    455                     }
    456                 }
    457             });
    458         }
    459 
    460         @Override
    461         public void onNotificationRemoved(final StatusBarNotification sbn,
    462                 final RankingMap rankingMap) {
    463             if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
    464             mHandler.post(new Runnable() {
    465                 @Override
    466                 public void run() {
    467                     removeNotification(sbn.getKey(), rankingMap);
    468                 }
    469             });
    470         }
    471 
    472         @Override
    473         public void onNotificationRankingUpdate(final RankingMap rankingMap) {
    474             if (DEBUG) Log.d(TAG, "onRankingUpdate");
    475             mHandler.post(new Runnable() {
    476                 @Override
    477                 public void run() {
    478                     updateNotificationRanking(rankingMap);
    479                 }
    480             });
    481         }
    482 
    483     };
    484 
    485     private void updateCurrentProfilesCache() {
    486         synchronized (mCurrentProfiles) {
    487             mCurrentProfiles.clear();
    488             if (mUserManager != null) {
    489                 for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
    490                     mCurrentProfiles.put(user.id, user);
    491                 }
    492             }
    493         }
    494     }
    495 
    496     public void start() {
    497         mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    498         mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
    499         mDisplay = mWindowManager.getDefaultDisplay();
    500         mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService(
    501                 Context.DEVICE_POLICY_SERVICE);
    502 
    503         mNotificationColorUtil = NotificationColorUtil.getInstance(mContext);
    504 
    505         mNotificationData = new NotificationData(this);
    506 
    507         mAccessibilityManager = (AccessibilityManager)
    508                 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
    509 
    510         mDreamManager = IDreamManager.Stub.asInterface(
    511                 ServiceManager.checkService(DreamService.DREAM_SERVICE));
    512         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
    513 
    514         mSettingsObserver.onChange(false); // set up
    515         mContext.getContentResolver().registerContentObserver(
    516                 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
    517                 mSettingsObserver);
    518         mContext.getContentResolver().registerContentObserver(
    519                 Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
    520                 mSettingsObserver);
    521         mContext.getContentResolver().registerContentObserver(
    522                 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
    523                 mSettingsObserver,
    524                 UserHandle.USER_ALL);
    525 
    526         mContext.getContentResolver().registerContentObserver(
    527                 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
    528                 true,
    529                 mLockscreenSettingsObserver,
    530                 UserHandle.USER_ALL);
    531 
    532         mBarService = IStatusBarService.Stub.asInterface(
    533                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
    534 
    535         mRecents = getComponent(RecentsComponent.class);
    536         mRecents.setCallback(this);
    537 
    538         final Configuration currentConfig = mContext.getResources().getConfiguration();
    539         mLocale = currentConfig.locale;
    540         mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
    541         mFontScale = currentConfig.fontScale;
    542 
    543         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
    544 
    545         mLinearOutSlowIn = AnimationUtils.loadInterpolator(mContext,
    546                 android.R.interpolator.linear_out_slow_in);
    547         mFastOutLinearIn = AnimationUtils.loadInterpolator(mContext,
    548                 android.R.interpolator.fast_out_linear_in);
    549 
    550         // Connect in to the status bar manager service
    551         StatusBarIconList iconList = new StatusBarIconList();
    552         mCommandQueue = new CommandQueue(this, iconList);
    553 
    554         int[] switches = new int[8];
    555         ArrayList<IBinder> binders = new ArrayList<IBinder>();
    556         try {
    557             mBarService.registerStatusBar(mCommandQueue, iconList, switches, binders);
    558         } catch (RemoteException ex) {
    559             // If the system process isn't there we're doomed anyway.
    560         }
    561 
    562         createAndAddWindows();
    563 
    564         disable(switches[0], false /* animate */);
    565         setSystemUiVisibility(switches[1], 0xffffffff);
    566         topAppWindowChanged(switches[2] != 0);
    567         // StatusBarManagerService has a back up of IME token and it's restored here.
    568         setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[5] != 0);
    569 
    570         // Set up the initial icon state
    571         int N = iconList.size();
    572         int viewIndex = 0;
    573         for (int i=0; i<N; i++) {
    574             StatusBarIcon icon = iconList.getIcon(i);
    575             if (icon != null) {
    576                 addIcon(iconList.getSlot(i), i, viewIndex, icon);
    577                 viewIndex++;
    578             }
    579         }
    580 
    581         // Set up the initial notification state.
    582         try {
    583             mNotificationListener.registerAsSystemService(mContext,
    584                     new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
    585                     UserHandle.USER_ALL);
    586         } catch (RemoteException e) {
    587             Log.e(TAG, "Unable to register notification listener", e);
    588         }
    589 
    590 
    591         if (DEBUG) {
    592             Log.d(TAG, String.format(
    593                     "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
    594                    iconList.size(),
    595                    switches[0],
    596                    switches[1],
    597                    switches[2],
    598                    switches[3]
    599                    ));
    600         }
    601 
    602         mCurrentUserId = ActivityManager.getCurrentUser();
    603         setHeadsUpUser(mCurrentUserId);
    604 
    605         IntentFilter filter = new IntentFilter();
    606         filter.addAction(Intent.ACTION_USER_SWITCHED);
    607         filter.addAction(Intent.ACTION_USER_ADDED);
    608         filter.addAction(BANNER_ACTION_CANCEL);
    609         filter.addAction(BANNER_ACTION_SETUP);
    610         filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
    611         mContext.registerReceiver(mBroadcastReceiver, filter);
    612 
    613         updateCurrentProfilesCache();
    614     }
    615 
    616     protected void notifyUserAboutHiddenNotifications() {
    617         if (0 != Settings.Secure.getInt(mContext.getContentResolver(),
    618                 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) {
    619             Log.d(TAG, "user hasn't seen notification about hidden notifications");
    620             final LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
    621             if (!lockPatternUtils.isSecure()) {
    622                 Log.d(TAG, "insecure lockscreen, skipping notification");
    623                 Settings.Secure.putInt(mContext.getContentResolver(),
    624                         Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
    625                 return;
    626             }
    627             Log.d(TAG, "disabling lockecreen notifications and alerting the user");
    628             // disable lockscreen notifications until user acts on the banner.
    629             Settings.Secure.putInt(mContext.getContentResolver(),
    630                     Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
    631             Settings.Secure.putInt(mContext.getContentResolver(),
    632                     Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
    633 
    634             final String packageName = mContext.getPackageName();
    635             PendingIntent cancelIntent = PendingIntent.getBroadcast(mContext, 0,
    636                     new Intent(BANNER_ACTION_CANCEL).setPackage(packageName),
    637                     PendingIntent.FLAG_CANCEL_CURRENT);
    638             PendingIntent setupIntent = PendingIntent.getBroadcast(mContext, 0,
    639                     new Intent(BANNER_ACTION_SETUP).setPackage(packageName),
    640                     PendingIntent.FLAG_CANCEL_CURRENT);
    641 
    642             final Resources res = mContext.getResources();
    643             final int colorRes = com.android.internal.R.color.system_notification_accent_color;
    644             Notification.Builder note = new Notification.Builder(mContext)
    645                     .setSmallIcon(R.drawable.ic_android)
    646                     .setContentTitle(mContext.getString(R.string.hidden_notifications_title))
    647                     .setContentText(mContext.getString(R.string.hidden_notifications_text))
    648                     .setPriority(Notification.PRIORITY_HIGH)
    649                     .setOngoing(true)
    650                     .setColor(res.getColor(colorRes))
    651                     .setContentIntent(setupIntent)
    652                     .addAction(R.drawable.ic_close,
    653                             mContext.getString(R.string.hidden_notifications_cancel),
    654                             cancelIntent)
    655                     .addAction(R.drawable.ic_settings,
    656                             mContext.getString(R.string.hidden_notifications_setup),
    657                             setupIntent);
    658 
    659             NotificationManager noMan =
    660                     (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
    661             noMan.notify(HIDDEN_NOTIFICATION_ID, note.build());
    662         }
    663     }
    664 
    665     public void userSwitched(int newUserId) {
    666         setHeadsUpUser(newUserId);
    667     }
    668 
    669     private void setHeadsUpUser(int newUserId) {
    670         if (mHeadsUpNotificationView != null) {
    671             mHeadsUpNotificationView.setUser(newUserId);
    672         }
    673     }
    674 
    675     public boolean isHeadsUp(String key) {
    676       return mHeadsUpNotificationView != null && mHeadsUpNotificationView.isShowing(key);
    677     }
    678 
    679     @Override  // NotificationData.Environment
    680     public boolean isNotificationForCurrentProfiles(StatusBarNotification n) {
    681         final int thisUserId = mCurrentUserId;
    682         final int notificationUserId = n.getUserId();
    683         if (DEBUG && MULTIUSER_DEBUG) {
    684             Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
    685                     n, thisUserId, notificationUserId));
    686         }
    687         return isCurrentProfile(notificationUserId);
    688     }
    689 
    690     protected boolean isCurrentProfile(int userId) {
    691         synchronized (mCurrentProfiles) {
    692             return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
    693         }
    694     }
    695 
    696     @Override
    697     public String getCurrentMediaNotificationKey() {
    698         return null;
    699     }
    700 
    701     /**
    702      * Takes the necessary steps to prepare the status bar for starting an activity, then starts it.
    703      * @param action A dismiss action that is called if it's safe to start the activity.
    704      * @param afterKeyguardGone Whether the action should be executed after the Keyguard is gone.
    705      */
    706     protected void dismissKeyguardThenExecute(OnDismissAction action, boolean afterKeyguardGone) {
    707         action.onDismiss();
    708     }
    709 
    710     @Override
    711     protected void onConfigurationChanged(Configuration newConfig) {
    712         final Locale locale = mContext.getResources().getConfiguration().locale;
    713         final int ld = TextUtils.getLayoutDirectionFromLocale(locale);
    714         final float fontScale = newConfig.fontScale;
    715 
    716         if (! locale.equals(mLocale) || ld != mLayoutDirection || fontScale != mFontScale) {
    717             if (DEBUG) {
    718                 Log.v(TAG, String.format(
    719                         "config changed locale/LD: %s (%d) -> %s (%d)", mLocale, mLayoutDirection,
    720                         locale, ld));
    721             }
    722             mLocale = locale;
    723             mLayoutDirection = ld;
    724             refreshLayout(ld);
    725         }
    726     }
    727 
    728     protected View updateNotificationVetoButton(View row, StatusBarNotification n) {
    729         View vetoButton = row.findViewById(R.id.veto);
    730         if (n.isClearable() || (mHeadsUpNotificationView.getEntry() != null
    731                 && mHeadsUpNotificationView.getEntry().row == row)) {
    732             final String _pkg = n.getPackageName();
    733             final String _tag = n.getTag();
    734             final int _id = n.getId();
    735             final int _userId = n.getUserId();
    736             vetoButton.setOnClickListener(new View.OnClickListener() {
    737                     public void onClick(View v) {
    738                         // Accessibility feedback
    739                         v.announceForAccessibility(
    740                                 mContext.getString(R.string.accessibility_notification_dismissed));
    741                         try {
    742                             mBarService.onNotificationClear(_pkg, _tag, _id, _userId);
    743 
    744                         } catch (RemoteException ex) {
    745                             // system process is dead if we're here.
    746                         }
    747                     }
    748                 });
    749             vetoButton.setVisibility(View.VISIBLE);
    750         } else {
    751             vetoButton.setVisibility(View.GONE);
    752         }
    753         vetoButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
    754         return vetoButton;
    755     }
    756 
    757 
    758     protected void applyColorsAndBackgrounds(StatusBarNotification sbn,
    759             NotificationData.Entry entry) {
    760 
    761         if (entry.expanded.getId() != com.android.internal.R.id.status_bar_latest_event_content) {
    762             // Using custom RemoteViews
    763             if (entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
    764                     && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP) {
    765                 entry.row.setShowingLegacyBackground(true);
    766                 entry.legacy = true;
    767             }
    768         } else {
    769             // Using platform templates
    770             final int color = sbn.getNotification().color;
    771             if (isMediaNotification(entry)) {
    772                 entry.row.setTintColor(color == Notification.COLOR_DEFAULT
    773                         ? mContext.getResources().getColor(
    774                                 R.color.notification_material_background_media_default_color)
    775                         : color);
    776             }
    777         }
    778 
    779         if (entry.icon != null) {
    780             if (entry.targetSdk >= Build.VERSION_CODES.LOLLIPOP) {
    781                 entry.icon.setColorFilter(mContext.getResources().getColor(android.R.color.white));
    782             } else {
    783                 entry.icon.setColorFilter(null);
    784             }
    785         }
    786     }
    787 
    788     public boolean isMediaNotification(NotificationData.Entry entry) {
    789         // TODO: confirm that there's a valid media key
    790         return entry.expandedBig != null &&
    791                entry.expandedBig.findViewById(com.android.internal.R.id.media_actions) != null;
    792     }
    793 
    794     // The gear button in the guts that links to the app's own notification settings
    795     private void startAppOwnNotificationSettingsActivity(Intent intent,
    796             final int notificationId, final String notificationTag, final int appUid) {
    797         intent.putExtra("notification_id", notificationId);
    798         intent.putExtra("notification_tag", notificationTag);
    799         startNotificationGutsIntent(intent, appUid);
    800     }
    801 
    802     // The (i) button in the guts that links to the system notification settings for that app
    803     private void startAppNotificationSettingsActivity(String packageName, final int appUid) {
    804         final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
    805         intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
    806         intent.putExtra(Settings.EXTRA_APP_UID, appUid);
    807         startNotificationGutsIntent(intent, appUid);
    808     }
    809 
    810     private void startNotificationGutsIntent(final Intent intent, final int appUid) {
    811         final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
    812         dismissKeyguardThenExecute(new OnDismissAction() {
    813             @Override
    814             public boolean onDismiss() {
    815                 AsyncTask.execute(new Runnable() {
    816                     public void run() {
    817                         try {
    818                             if (keyguardShowing) {
    819                                 ActivityManagerNative.getDefault()
    820                                         .keyguardWaitingForActivityDrawn();
    821                             }
    822                             TaskStackBuilder.create(mContext)
    823                                     .addNextIntentWithParentStack(intent)
    824                                     .startActivities(null,
    825                                             new UserHandle(UserHandle.getUserId(appUid)));
    826                             overrideActivityPendingAppTransition(keyguardShowing);
    827                         } catch (RemoteException e) {
    828                         }
    829                     }
    830                 });
    831                 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
    832                 return true;
    833             }
    834         }, false /* afterKeyguardGone */);
    835     }
    836 
    837     private void inflateGuts(ExpandableNotificationRow row) {
    838         ViewStub stub = (ViewStub) row.findViewById(R.id.notification_guts_stub);
    839         if (stub != null) {
    840             stub.inflate();
    841         }
    842         final StatusBarNotification sbn = row.getStatusBarNotification();
    843         PackageManager pmUser = getPackageManagerForUser(
    844                 sbn.getUser().getIdentifier());
    845         row.setTag(sbn.getPackageName());
    846         final View guts = row.findViewById(R.id.notification_guts);
    847         final String pkg = sbn.getPackageName();
    848         String appname = pkg;
    849         Drawable pkgicon = null;
    850         int appUid = -1;
    851         try {
    852             final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
    853                     PackageManager.GET_UNINSTALLED_PACKAGES
    854                             | PackageManager.GET_DISABLED_COMPONENTS);
    855             if (info != null) {
    856                 appname = String.valueOf(pmUser.getApplicationLabel(info));
    857                 pkgicon = pmUser.getApplicationIcon(info);
    858                 appUid = info.uid;
    859             }
    860         } catch (NameNotFoundException e) {
    861             // app is gone, just show package name and generic icon
    862             pkgicon = pmUser.getDefaultActivityIcon();
    863         }
    864         ((ImageView) row.findViewById(android.R.id.icon)).setImageDrawable(pkgicon);
    865         ((DateTimeView) row.findViewById(R.id.timestamp)).setTime(sbn.getPostTime());
    866         ((TextView) row.findViewById(R.id.pkgname)).setText(appname);
    867         final View settingsButton = guts.findViewById(R.id.notification_inspect_item);
    868         final View appSettingsButton
    869                 = guts.findViewById(R.id.notification_inspect_app_provided_settings);
    870         if (appUid >= 0) {
    871             final int appUidF = appUid;
    872             settingsButton.setOnClickListener(new View.OnClickListener() {
    873                 public void onClick(View v) {
    874                     startAppNotificationSettingsActivity(pkg, appUidF);
    875                 }
    876             });
    877 
    878             final Intent appSettingsQueryIntent
    879                     = new Intent(Intent.ACTION_MAIN)
    880                     .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
    881                     .setPackage(pkg);
    882             List<ResolveInfo> infos = pmUser.queryIntentActivities(appSettingsQueryIntent, 0);
    883             if (infos.size() > 0) {
    884                 appSettingsButton.setVisibility(View.VISIBLE);
    885                 appSettingsButton.setContentDescription(
    886                         mContext.getResources().getString(
    887                                 R.string.status_bar_notification_app_settings_title,
    888                                 appname
    889                         ));
    890                 final Intent appSettingsLaunchIntent = new Intent(appSettingsQueryIntent)
    891                         .setClassName(pkg, infos.get(0).activityInfo.name);
    892                 appSettingsButton.setOnClickListener(new View.OnClickListener() {
    893                     public void onClick(View v) {
    894                         startAppOwnNotificationSettingsActivity(appSettingsLaunchIntent,
    895                                 sbn.getId(),
    896                                 sbn.getTag(),
    897                                 appUidF);
    898                     }
    899                 });
    900             } else {
    901                 appSettingsButton.setVisibility(View.GONE);
    902             }
    903         } else {
    904             settingsButton.setVisibility(View.GONE);
    905             appSettingsButton.setVisibility(View.GONE);
    906         }
    907 
    908     }
    909 
    910     protected SwipeHelper.LongPressListener getNotificationLongClicker() {
    911         return new SwipeHelper.LongPressListener() {
    912             @Override
    913             public boolean onLongPress(View v, int x, int y) {
    914                 dismissPopups();
    915 
    916                 if (!(v instanceof ExpandableNotificationRow)) {
    917                     return false;
    918                 }
    919                 if (v.getWindowToken() == null) {
    920                     Log.e(TAG, "Trying to show notification guts, but not attached to window");
    921                     return false;
    922                 }
    923 
    924                 inflateGuts((ExpandableNotificationRow) v);
    925 
    926                 // Assume we are a status_bar_notification_row
    927                 final NotificationGuts guts = (NotificationGuts) v.findViewById(
    928                         R.id.notification_guts);
    929                 if (guts == null) {
    930                     // This view has no guts. Examples are the more card or the dismiss all view
    931                     return false;
    932                 }
    933 
    934                 // Already showing?
    935                 if (guts.getVisibility() == View.VISIBLE) {
    936                     Log.e(TAG, "Trying to show notification guts, but already visible");
    937                     return false;
    938                 }
    939 
    940                 guts.setVisibility(View.VISIBLE);
    941                 final double horz = Math.max(guts.getWidth() - x, x);
    942                 final double vert = Math.max(guts.getActualHeight() - y, y);
    943                 final float r = (float) Math.hypot(horz, vert);
    944                 final Animator a
    945                         = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
    946                 a.setDuration(400);
    947                 a.setInterpolator(mLinearOutSlowIn);
    948                 a.start();
    949 
    950                 mNotificationGutsExposed = guts;
    951 
    952                 return true;
    953             }
    954         };
    955     }
    956 
    957     public void dismissPopups() {
    958         if (mNotificationGutsExposed != null) {
    959             final NotificationGuts v = mNotificationGutsExposed;
    960             mNotificationGutsExposed = null;
    961 
    962             if (v.getWindowToken() == null) return;
    963 
    964             final int x = (v.getLeft() + v.getRight()) / 2;
    965             final int y = (v.getTop() + v.getActualHeight() / 2);
    966             final Animator a = ViewAnimationUtils.createCircularReveal(v,
    967                     x, y, x, 0);
    968             a.setDuration(200);
    969             a.setInterpolator(mFastOutLinearIn);
    970             a.addListener(new AnimatorListenerAdapter() {
    971                 @Override
    972                 public void onAnimationEnd(Animator animation) {
    973                     super.onAnimationEnd(animation);
    974                     v.setVisibility(View.GONE);
    975                 }
    976             });
    977             a.start();
    978         }
    979     }
    980 
    981     public void onHeadsUpDismissed() {
    982     }
    983 
    984     @Override
    985     public void showRecentApps(boolean triggeredFromAltTab) {
    986         int msg = MSG_SHOW_RECENT_APPS;
    987         mHandler.removeMessages(msg);
    988         mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0, 0).sendToTarget();
    989     }
    990 
    991     @Override
    992     public void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
    993         int msg = MSG_HIDE_RECENT_APPS;
    994         mHandler.removeMessages(msg);
    995         mHandler.obtainMessage(msg, triggeredFromAltTab ? 1 : 0,
    996                 triggeredFromHomeKey ? 1 : 0).sendToTarget();
    997     }
    998 
    999     @Override
   1000     public void toggleRecentApps() {
   1001         int msg = MSG_TOGGLE_RECENTS_APPS;
   1002         mHandler.removeMessages(msg);
   1003         mHandler.sendEmptyMessage(msg);
   1004     }
   1005 
   1006     @Override
   1007     public void preloadRecentApps() {
   1008         int msg = MSG_PRELOAD_RECENT_APPS;
   1009         mHandler.removeMessages(msg);
   1010         mHandler.sendEmptyMessage(msg);
   1011     }
   1012 
   1013     @Override
   1014     public void cancelPreloadRecentApps() {
   1015         int msg = MSG_CANCEL_PRELOAD_RECENT_APPS;
   1016         mHandler.removeMessages(msg);
   1017         mHandler.sendEmptyMessage(msg);
   1018     }
   1019 
   1020     /** Jumps to the next affiliated task in the group. */
   1021     public void showNextAffiliatedTask() {
   1022         int msg = MSG_SHOW_NEXT_AFFILIATED_TASK;
   1023         mHandler.removeMessages(msg);
   1024         mHandler.sendEmptyMessage(msg);
   1025     }
   1026 
   1027     /** Jumps to the previous affiliated task in the group. */
   1028     public void showPreviousAffiliatedTask() {
   1029         int msg = MSG_SHOW_PREV_AFFILIATED_TASK;
   1030         mHandler.removeMessages(msg);
   1031         mHandler.sendEmptyMessage(msg);
   1032     }
   1033 
   1034     @Override
   1035     public void showSearchPanel() {
   1036         if (mSearchPanelView != null && mSearchPanelView.isAssistantAvailable()) {
   1037             mSearchPanelView.show(true, true);
   1038         }
   1039     }
   1040 
   1041     @Override
   1042     public void hideSearchPanel() {
   1043         int msg = MSG_CLOSE_SEARCH_PANEL;
   1044         mHandler.removeMessages(msg);
   1045         mHandler.sendEmptyMessage(msg);
   1046     }
   1047 
   1048     protected abstract WindowManager.LayoutParams getSearchLayoutParams(
   1049             LayoutParams layoutParams);
   1050 
   1051     protected void updateSearchPanel() {
   1052         // Search Panel
   1053         boolean visible = false;
   1054         if (mSearchPanelView != null) {
   1055             visible = mSearchPanelView.isShowing();
   1056             mWindowManager.removeView(mSearchPanelView);
   1057         }
   1058 
   1059         // Provide SearchPanel with a temporary parent to allow layout params to work.
   1060         LinearLayout tmpRoot = new LinearLayout(mContext);
   1061         mSearchPanelView = (SearchPanelView) LayoutInflater.from(mContext).inflate(
   1062                  R.layout.status_bar_search_panel, tmpRoot, false);
   1063         mSearchPanelView.setOnTouchListener(
   1064                  new TouchOutsideListener(MSG_CLOSE_SEARCH_PANEL, mSearchPanelView));
   1065         mSearchPanelView.setVisibility(View.GONE);
   1066         boolean vertical = mNavigationBarView != null && mNavigationBarView.isVertical();
   1067         mSearchPanelView.setHorizontal(vertical);
   1068 
   1069         WindowManager.LayoutParams lp = getSearchLayoutParams(mSearchPanelView.getLayoutParams());
   1070 
   1071         mWindowManager.addView(mSearchPanelView, lp);
   1072         mSearchPanelView.setBar(this);
   1073         if (visible) {
   1074             mSearchPanelView.show(true, false);
   1075         }
   1076     }
   1077 
   1078     protected H createHandler() {
   1079          return new H();
   1080     }
   1081 
   1082     static void sendCloseSystemWindows(Context context, String reason) {
   1083         if (ActivityManagerNative.isSystemReady()) {
   1084             try {
   1085                 ActivityManagerNative.getDefault().closeSystemDialogs(reason);
   1086             } catch (RemoteException e) {
   1087             }
   1088         }
   1089     }
   1090 
   1091     protected abstract View getStatusBarView();
   1092 
   1093     protected View.OnTouchListener mRecentsPreloadOnTouchListener = new View.OnTouchListener() {
   1094         // additional optimization when we have software system buttons - start loading the recent
   1095         // tasks on touch down
   1096         @Override
   1097         public boolean onTouch(View v, MotionEvent event) {
   1098             int action = event.getAction() & MotionEvent.ACTION_MASK;
   1099             if (action == MotionEvent.ACTION_DOWN) {
   1100                 preloadRecents();
   1101             } else if (action == MotionEvent.ACTION_CANCEL) {
   1102                 cancelPreloadingRecents();
   1103             } else if (action == MotionEvent.ACTION_UP) {
   1104                 if (!v.isPressed()) {
   1105                     cancelPreloadingRecents();
   1106                 }
   1107 
   1108             }
   1109             return false;
   1110         }
   1111     };
   1112 
   1113     /** Proxy for RecentsComponent */
   1114 
   1115     protected void showRecents(boolean triggeredFromAltTab) {
   1116         if (mRecents != null) {
   1117             sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
   1118             mRecents.showRecents(triggeredFromAltTab, getStatusBarView());
   1119         }
   1120     }
   1121 
   1122     protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
   1123         if (mRecents != null) {
   1124             mRecents.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
   1125         }
   1126     }
   1127 
   1128     protected void toggleRecents() {
   1129         if (mRecents != null) {
   1130             sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
   1131             mRecents.toggleRecents(mDisplay, mLayoutDirection, getStatusBarView());
   1132         }
   1133     }
   1134 
   1135     protected void preloadRecents() {
   1136         if (mRecents != null) {
   1137             mRecents.preloadRecents();
   1138         }
   1139     }
   1140 
   1141     protected void cancelPreloadingRecents() {
   1142         if (mRecents != null) {
   1143             mRecents.cancelPreloadingRecents();
   1144         }
   1145     }
   1146 
   1147     protected void showRecentsNextAffiliatedTask() {
   1148         if (mRecents != null) {
   1149             mRecents.showNextAffiliatedTask();
   1150         }
   1151     }
   1152 
   1153     protected void showRecentsPreviousAffiliatedTask() {
   1154         if (mRecents != null) {
   1155             mRecents.showPrevAffiliatedTask();
   1156         }
   1157     }
   1158 
   1159     @Override
   1160     public void onVisibilityChanged(boolean visible) {
   1161         // Do nothing
   1162     }
   1163 
   1164     public abstract void resetHeadsUpDecayTimer();
   1165 
   1166     public abstract void scheduleHeadsUpOpen();
   1167 
   1168     public abstract void scheduleHeadsUpClose();
   1169 
   1170     public abstract void scheduleHeadsUpEscalation();
   1171 
   1172     /**
   1173      * Save the current "public" (locked and secure) state of the lockscreen.
   1174      */
   1175     public void setLockscreenPublicMode(boolean publicMode) {
   1176         mLockscreenPublicMode = publicMode;
   1177     }
   1178 
   1179     public boolean isLockscreenPublicMode() {
   1180         return mLockscreenPublicMode;
   1181     }
   1182 
   1183     /**
   1184      * Has the given user chosen to allow their private (full) notifications to be shown even
   1185      * when the lockscreen is in "public" (secure & locked) mode?
   1186      */
   1187     public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
   1188         if (userHandle == UserHandle.USER_ALL) {
   1189             return true;
   1190         }
   1191 
   1192         if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
   1193             final boolean allowed = 0 != Settings.Secure.getIntForUser(
   1194                     mContext.getContentResolver(),
   1195                     Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
   1196             final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */,
   1197                     userHandle);
   1198             final boolean allowedByDpm = (dpmFlags
   1199                     & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0;
   1200             mUsersAllowingPrivateNotifications.append(userHandle, allowed && allowedByDpm);
   1201             return allowed;
   1202         }
   1203 
   1204         return mUsersAllowingPrivateNotifications.get(userHandle);
   1205     }
   1206 
   1207     /**
   1208      * Returns true if we're on a secure lockscreen and the user wants to hide "sensitive"
   1209      * notification data. If so, private notifications should show their (possibly
   1210      * auto-generated) publicVersion, and secret notifications should be totally invisible.
   1211      */
   1212     @Override  // NotificationData.Environment
   1213     public boolean shouldHideSensitiveContents(int userid) {
   1214         return isLockscreenPublicMode() && !userAllowsPrivateNotificationsInPublic(userid);
   1215     }
   1216 
   1217     public void onNotificationClear(StatusBarNotification notification) {
   1218         try {
   1219             mBarService.onNotificationClear(
   1220                     notification.getPackageName(),
   1221                     notification.getTag(),
   1222                     notification.getId(),
   1223                     notification.getUserId());
   1224         } catch (android.os.RemoteException ex) {
   1225             // oh well
   1226         }
   1227     }
   1228 
   1229     protected class H extends Handler {
   1230         public void handleMessage(Message m) {
   1231             switch (m.what) {
   1232              case MSG_SHOW_RECENT_APPS:
   1233                  showRecents(m.arg1 > 0);
   1234                  break;
   1235              case MSG_HIDE_RECENT_APPS:
   1236                  hideRecents(m.arg1 > 0, m.arg2 > 0);
   1237                  break;
   1238              case MSG_TOGGLE_RECENTS_APPS:
   1239                  toggleRecents();
   1240                  break;
   1241              case MSG_PRELOAD_RECENT_APPS:
   1242                   preloadRecents();
   1243                   break;
   1244              case MSG_CANCEL_PRELOAD_RECENT_APPS:
   1245                   cancelPreloadingRecents();
   1246                   break;
   1247              case MSG_SHOW_NEXT_AFFILIATED_TASK:
   1248                   showRecentsNextAffiliatedTask();
   1249                   break;
   1250              case MSG_SHOW_PREV_AFFILIATED_TASK:
   1251                   showRecentsPreviousAffiliatedTask();
   1252                   break;
   1253              case MSG_CLOSE_SEARCH_PANEL:
   1254                  if (DEBUG) Log.d(TAG, "closing search panel");
   1255                  if (mSearchPanelView != null && mSearchPanelView.isShowing()) {
   1256                      mSearchPanelView.show(false, true);
   1257                  }
   1258                  break;
   1259             }
   1260         }
   1261     }
   1262 
   1263     public class TouchOutsideListener implements View.OnTouchListener {
   1264         private int mMsg;
   1265         private StatusBarPanel mPanel;
   1266 
   1267         public TouchOutsideListener(int msg, StatusBarPanel panel) {
   1268             mMsg = msg;
   1269             mPanel = panel;
   1270         }
   1271 
   1272         public boolean onTouch(View v, MotionEvent ev) {
   1273             final int action = ev.getAction();
   1274             if (action == MotionEvent.ACTION_OUTSIDE
   1275                 || (action == MotionEvent.ACTION_DOWN
   1276                     && !mPanel.isInContentArea((int)ev.getX(), (int)ev.getY()))) {
   1277                 mHandler.removeMessages(mMsg);
   1278                 mHandler.sendEmptyMessage(mMsg);
   1279                 return true;
   1280             }
   1281             return false;
   1282         }
   1283     }
   1284 
   1285     protected void workAroundBadLayerDrawableOpacity(View v) {
   1286     }
   1287 
   1288     private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
   1289             return inflateViews(entry, parent, false);
   1290     }
   1291 
   1292     protected boolean inflateViewsForHeadsUp(NotificationData.Entry entry, ViewGroup parent) {
   1293             return inflateViews(entry, parent, true);
   1294     }
   1295 
   1296     private boolean inflateViews(NotificationData.Entry entry, ViewGroup parent, boolean isHeadsUp) {
   1297         PackageManager pmUser = getPackageManagerForUser(
   1298                 entry.notification.getUser().getIdentifier());
   1299 
   1300         int maxHeight = mRowMaxHeight;
   1301         final StatusBarNotification sbn = entry.notification;
   1302         RemoteViews contentView = sbn.getNotification().contentView;
   1303         RemoteViews bigContentView = sbn.getNotification().bigContentView;
   1304 
   1305         if (isHeadsUp) {
   1306             maxHeight =
   1307                     mContext.getResources().getDimensionPixelSize(R.dimen.notification_mid_height);
   1308             bigContentView = sbn.getNotification().headsUpContentView;
   1309         }
   1310 
   1311         if (contentView == null) {
   1312             return false;
   1313         }
   1314 
   1315         if (DEBUG) {
   1316             Log.v(TAG, "publicNotification: " + sbn.getNotification().publicVersion);
   1317         }
   1318 
   1319         Notification publicNotification = sbn.getNotification().publicVersion;
   1320 
   1321         ExpandableNotificationRow row;
   1322 
   1323         // Stash away previous user expansion state so we can restore it at
   1324         // the end.
   1325         boolean hasUserChangedExpansion = false;
   1326         boolean userExpanded = false;
   1327         boolean userLocked = false;
   1328 
   1329         if (entry.row != null) {
   1330             row = entry.row;
   1331             hasUserChangedExpansion = row.hasUserChangedExpansion();
   1332             userExpanded = row.isUserExpanded();
   1333             userLocked = row.isUserLocked();
   1334             entry.reset();
   1335             if (hasUserChangedExpansion) {
   1336                 row.setUserExpanded(userExpanded);
   1337             }
   1338         } else {
   1339             // create the row view
   1340             LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
   1341                     Context.LAYOUT_INFLATER_SERVICE);
   1342             row = (ExpandableNotificationRow) inflater.inflate(R.layout.status_bar_notification_row,
   1343                     parent, false);
   1344             row.setExpansionLogger(this, entry.notification.getKey());
   1345         }
   1346 
   1347         workAroundBadLayerDrawableOpacity(row);
   1348         View vetoButton = updateNotificationVetoButton(row, sbn);
   1349         vetoButton.setContentDescription(mContext.getString(
   1350                 R.string.accessibility_remove_notification));
   1351 
   1352         // NB: the large icon is now handled entirely by the template
   1353 
   1354         // bind the click event to the content area
   1355         NotificationContentView expanded =
   1356                 (NotificationContentView) row.findViewById(R.id.expanded);
   1357         NotificationContentView expandedPublic =
   1358                 (NotificationContentView) row.findViewById(R.id.expandedPublic);
   1359 
   1360         row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
   1361 
   1362         PendingIntent contentIntent = sbn.getNotification().contentIntent;
   1363         if (contentIntent != null) {
   1364             final View.OnClickListener listener = makeClicker(contentIntent, sbn.getKey(),
   1365                     isHeadsUp);
   1366             row.setOnClickListener(listener);
   1367         } else {
   1368             row.setOnClickListener(null);
   1369         }
   1370 
   1371         // set up the adaptive layout
   1372         View contentViewLocal = null;
   1373         View bigContentViewLocal = null;
   1374         try {
   1375             contentViewLocal = contentView.apply(mContext, expanded,
   1376                     mOnClickHandler);
   1377             if (bigContentView != null) {
   1378                 bigContentViewLocal = bigContentView.apply(mContext, expanded,
   1379                         mOnClickHandler);
   1380             }
   1381         }
   1382         catch (RuntimeException e) {
   1383             final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
   1384             Log.e(TAG, "couldn't inflate view for notification " + ident, e);
   1385             return false;
   1386         }
   1387 
   1388         if (contentViewLocal != null) {
   1389             contentViewLocal.setIsRootNamespace(true);
   1390             expanded.setContractedChild(contentViewLocal);
   1391         }
   1392         if (bigContentViewLocal != null) {
   1393             bigContentViewLocal.setIsRootNamespace(true);
   1394             expanded.setExpandedChild(bigContentViewLocal);
   1395         }
   1396 
   1397         // now the public version
   1398         View publicViewLocal = null;
   1399         if (publicNotification != null) {
   1400             try {
   1401                 publicViewLocal = publicNotification.contentView.apply(mContext, expandedPublic,
   1402                         mOnClickHandler);
   1403 
   1404                 if (publicViewLocal != null) {
   1405                     publicViewLocal.setIsRootNamespace(true);
   1406                     expandedPublic.setContractedChild(publicViewLocal);
   1407                 }
   1408             }
   1409             catch (RuntimeException e) {
   1410                 final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
   1411                 Log.e(TAG, "couldn't inflate public view for notification " + ident, e);
   1412                 publicViewLocal = null;
   1413             }
   1414         }
   1415 
   1416         // Extract target SDK version.
   1417         try {
   1418             ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
   1419             entry.targetSdk = info.targetSdkVersion;
   1420         } catch (NameNotFoundException ex) {
   1421             Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
   1422         }
   1423 
   1424         if (publicViewLocal == null) {
   1425             // Add a basic notification template
   1426             publicViewLocal = LayoutInflater.from(mContext).inflate(
   1427                     R.layout.notification_public_default,
   1428                     expandedPublic, false);
   1429             publicViewLocal.setIsRootNamespace(true);
   1430             expandedPublic.setContractedChild(publicViewLocal);
   1431 
   1432             final TextView title = (TextView) publicViewLocal.findViewById(R.id.title);
   1433             try {
   1434                 title.setText(pmUser.getApplicationLabel(
   1435                         pmUser.getApplicationInfo(entry.notification.getPackageName(), 0)));
   1436             } catch (NameNotFoundException e) {
   1437                 title.setText(entry.notification.getPackageName());
   1438             }
   1439 
   1440             final ImageView icon = (ImageView) publicViewLocal.findViewById(R.id.icon);
   1441             final ImageView profileBadge = (ImageView) publicViewLocal.findViewById(
   1442                     R.id.profile_badge_line3);
   1443 
   1444             final StatusBarIcon ic = new StatusBarIcon(entry.notification.getPackageName(),
   1445                     entry.notification.getUser(),
   1446                     entry.notification.getNotification().icon,
   1447                     entry.notification.getNotification().iconLevel,
   1448                     entry.notification.getNotification().number,
   1449                     entry.notification.getNotification().tickerText);
   1450 
   1451             Drawable iconDrawable = StatusBarIconView.getIcon(mContext, ic);
   1452             icon.setImageDrawable(iconDrawable);
   1453             if (entry.targetSdk >= Build.VERSION_CODES.LOLLIPOP
   1454                     || mNotificationColorUtil.isGrayscaleIcon(iconDrawable)) {
   1455                 icon.setBackgroundResource(
   1456                         com.android.internal.R.drawable.notification_icon_legacy_bg);
   1457                 int padding = mContext.getResources().getDimensionPixelSize(
   1458                         com.android.internal.R.dimen.notification_large_icon_circle_padding);
   1459                 icon.setPadding(padding, padding, padding, padding);
   1460                 if (sbn.getNotification().color != Notification.COLOR_DEFAULT) {
   1461                     icon.getBackground().setColorFilter(
   1462                             sbn.getNotification().color, PorterDuff.Mode.SRC_ATOP);
   1463                 }
   1464             }
   1465 
   1466             if (profileBadge != null) {
   1467                 Drawable profileDrawable = mContext.getPackageManager().getUserBadgeForDensity(
   1468                         entry.notification.getUser(), 0);
   1469                 if (profileDrawable != null) {
   1470                     profileBadge.setImageDrawable(profileDrawable);
   1471                     profileBadge.setVisibility(View.VISIBLE);
   1472                 } else {
   1473                     profileBadge.setVisibility(View.GONE);
   1474                 }
   1475             }
   1476 
   1477             final View privateTime = contentViewLocal.findViewById(com.android.internal.R.id.time);
   1478             final DateTimeView time = (DateTimeView) publicViewLocal.findViewById(R.id.time);
   1479             if (privateTime != null && privateTime.getVisibility() == View.VISIBLE) {
   1480                 time.setVisibility(View.VISIBLE);
   1481                 time.setTime(entry.notification.getNotification().when);
   1482             }
   1483 
   1484             final TextView text = (TextView) publicViewLocal.findViewById(R.id.text);
   1485             if (text != null) {
   1486                 text.setText(R.string.notification_hidden_text);
   1487                 text.setTextAppearance(mContext,
   1488                         R.style.TextAppearance_Material_Notification_Parenthetical);
   1489             }
   1490 
   1491             int topPadding = Notification.Builder.calculateTopPadding(mContext,
   1492                     false /* hasThreeLines */,
   1493                     mContext.getResources().getConfiguration().fontScale);
   1494             title.setPadding(0, topPadding, 0, 0);
   1495 
   1496             entry.autoRedacted = true;
   1497         }
   1498 
   1499         if (MULTIUSER_DEBUG) {
   1500             TextView debug = (TextView) row.findViewById(R.id.debug_info);
   1501             if (debug != null) {
   1502                 debug.setVisibility(View.VISIBLE);
   1503                 debug.setText("CU " + mCurrentUserId +" NU " + entry.notification.getUserId());
   1504             }
   1505         }
   1506         entry.row = row;
   1507         entry.row.setHeightRange(mRowMinHeight, maxHeight);
   1508         entry.row.setOnActivatedListener(this);
   1509         entry.expanded = contentViewLocal;
   1510         entry.expandedPublic = publicViewLocal;
   1511         entry.setBigContentView(bigContentViewLocal);
   1512 
   1513         applyColorsAndBackgrounds(sbn, entry);
   1514 
   1515         // Restore previous flags.
   1516         if (hasUserChangedExpansion) {
   1517             // Note: setUserExpanded() conveniently ignores calls with
   1518             //       userExpanded=true if !isExpandable().
   1519             row.setUserExpanded(userExpanded);
   1520         }
   1521         row.setUserLocked(userLocked);
   1522         row.setStatusBarNotification(entry.notification);
   1523         return true;
   1524     }
   1525 
   1526     public NotificationClicker makeClicker(PendingIntent intent, String notificationKey,
   1527             boolean forHun) {
   1528         return new NotificationClicker(intent, notificationKey, forHun);
   1529     }
   1530 
   1531     protected class NotificationClicker implements View.OnClickListener {
   1532         private PendingIntent mIntent;
   1533         private final String mNotificationKey;
   1534         private boolean mIsHeadsUp;
   1535 
   1536         public NotificationClicker(PendingIntent intent, String notificationKey, boolean forHun) {
   1537             mIntent = intent;
   1538             mNotificationKey = notificationKey;
   1539             mIsHeadsUp = forHun;
   1540         }
   1541 
   1542         public void onClick(final View v) {
   1543             if (NOTIFICATION_CLICK_DEBUG) {
   1544                 Log.d(TAG, "Clicked on content of " + mNotificationKey);
   1545             }
   1546             final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
   1547             final boolean afterKeyguardGone = mIntent.isActivity()
   1548                     && PreviewInflater.wouldLaunchResolverActivity(mContext, mIntent.getIntent(),
   1549                             mCurrentUserId);
   1550             dismissKeyguardThenExecute(new OnDismissAction() {
   1551                 public boolean onDismiss() {
   1552                     if (mIsHeadsUp) {
   1553                         // Release the HUN notification to the shade.
   1554                         //
   1555                         // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
   1556                         // become canceled shortly by NoMan, but we can't assume that.
   1557                         mHeadsUpNotificationView.releaseAndClose();
   1558                     }
   1559                     new Thread() {
   1560                         @Override
   1561                         public void run() {
   1562                             try {
   1563                                 if (keyguardShowing && !afterKeyguardGone) {
   1564                                     ActivityManagerNative.getDefault()
   1565                                             .keyguardWaitingForActivityDrawn();
   1566                                 }
   1567 
   1568                                 // The intent we are sending is for the application, which
   1569                                 // won't have permission to immediately start an activity after
   1570                                 // the user switches to home.  We know it is safe to do at this
   1571                                 // point, so make sure new activity switches are now allowed.
   1572                                 ActivityManagerNative.getDefault().resumeAppSwitches();
   1573                             } catch (RemoteException e) {
   1574                             }
   1575 
   1576                             if (mIntent != null) {
   1577                                 try {
   1578                                     mIntent.send();
   1579                                 } catch (PendingIntent.CanceledException e) {
   1580                                     // the stack trace isn't very helpful here.
   1581                                     // Just log the exception message.
   1582                                     Log.w(TAG, "Sending contentIntent failed: " + e);
   1583 
   1584                                     // TODO: Dismiss Keyguard.
   1585                                 }
   1586                                 if (mIntent.isActivity()) {
   1587                                     overrideActivityPendingAppTransition(keyguardShowing
   1588                                             && !afterKeyguardGone);
   1589                                 }
   1590                             }
   1591 
   1592                             try {
   1593                                 mBarService.onNotificationClick(mNotificationKey);
   1594                             } catch (RemoteException ex) {
   1595                                 // system process is dead if we're here.
   1596                             }
   1597                         }
   1598                     }.start();
   1599 
   1600                     // close the shade if it was open
   1601                     animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
   1602                             true /* force */);
   1603                     visibilityChanged(false);
   1604 
   1605                     return mIntent != null && mIntent.isActivity();
   1606                 }
   1607             }, afterKeyguardGone);
   1608         }
   1609     }
   1610 
   1611     public void animateCollapsePanels(int flags, boolean force) {
   1612     }
   1613 
   1614     public void overrideActivityPendingAppTransition(boolean keyguardShowing) {
   1615         if (keyguardShowing) {
   1616             try {
   1617                 mWindowManagerService.overridePendingAppTransition(null, 0, 0, null);
   1618             } catch (RemoteException e) {
   1619                 Log.w(TAG, "Error overriding app transition: " + e);
   1620             }
   1621         }
   1622     }
   1623 
   1624     protected void visibilityChanged(boolean visible) {
   1625         if (mVisible != visible) {
   1626             mVisible = visible;
   1627             if (!visible) {
   1628                 dismissPopups();
   1629             }
   1630         }
   1631         updateVisibleToUser();
   1632     }
   1633 
   1634     protected void updateVisibleToUser() {
   1635         boolean oldVisibleToUser = mVisibleToUser;
   1636         mVisibleToUser = mVisible && mScreenOnFromKeyguard;
   1637 
   1638         if (oldVisibleToUser != mVisibleToUser) {
   1639             handleVisibleToUserChanged(mVisibleToUser);
   1640         }
   1641     }
   1642 
   1643     /**
   1644      * The LEDs are turned off when the notification panel is shown, even just a little bit.
   1645      * This was added last-minute and is inconsistent with the way the rest of the notifications
   1646      * are handled, because the notification isn't really cancelled.  The lights are just
   1647      * turned off.  If any other notifications happen, the lights will turn back on.  Steve says
   1648      * this is what he wants. (see bug 1131461)
   1649      */
   1650     protected void handleVisibleToUserChanged(boolean visibleToUser) {
   1651         try {
   1652             if (visibleToUser) {
   1653                 // Only stop blinking, vibrating, ringing when the user went into the shade
   1654                 // manually (SHADE or SHADE_LOCKED).
   1655                 boolean clearNotificationEffects =
   1656                         (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED);
   1657                 mBarService.onPanelRevealed(clearNotificationEffects);
   1658             } else {
   1659                 mBarService.onPanelHidden();
   1660             }
   1661         } catch (RemoteException ex) {
   1662             // Won't fail unless the world has ended.
   1663         }
   1664     }
   1665 
   1666     /**
   1667      * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
   1668      * about the failure.
   1669      *
   1670      * WARNING: this will call back into us.  Don't hold any locks.
   1671      */
   1672     void handleNotificationError(StatusBarNotification n, String message) {
   1673         removeNotification(n.getKey(), null);
   1674         try {
   1675             mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
   1676                     n.getInitialPid(), message, n.getUserId());
   1677         } catch (RemoteException ex) {
   1678             // The end is nigh.
   1679         }
   1680     }
   1681 
   1682     protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) {
   1683         NotificationData.Entry entry = mNotificationData.remove(key, ranking);
   1684         if (entry == null) {
   1685             Log.w(TAG, "removeNotification for unknown key: " + key);
   1686             return null;
   1687         }
   1688         updateNotifications();
   1689         return entry.notification;
   1690     }
   1691 
   1692     protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn) {
   1693         if (DEBUG) {
   1694             Log.d(TAG, "createNotificationViews(notification=" + sbn);
   1695         }
   1696         // Construct the icon.
   1697         Notification n = sbn.getNotification();
   1698         final StatusBarIconView iconView = new StatusBarIconView(mContext,
   1699                 sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), n);
   1700         iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
   1701 
   1702         final StatusBarIcon ic = new StatusBarIcon(sbn.getPackageName(),
   1703                 sbn.getUser(),
   1704                     n.icon,
   1705                     n.iconLevel,
   1706                     n.number,
   1707                     n.tickerText);
   1708         if (!iconView.set(ic)) {
   1709             handleNotificationError(sbn, "Couldn't create icon: " + ic);
   1710             return null;
   1711         }
   1712         // Construct the expanded view.
   1713         NotificationData.Entry entry = new NotificationData.Entry(sbn, iconView);
   1714         if (!inflateViews(entry, mStackScroller)) {
   1715             handleNotificationError(sbn, "Couldn't expand RemoteViews for: " + sbn);
   1716             return null;
   1717         }
   1718         return entry;
   1719     }
   1720 
   1721     protected void addNotificationViews(Entry entry, RankingMap ranking) {
   1722         if (entry == null) {
   1723             return;
   1724         }
   1725         // Add the expanded view and icon.
   1726         mNotificationData.add(entry, ranking);
   1727         updateNotifications();
   1728     }
   1729 
   1730     /**
   1731      * @return The number of notifications we show on Keyguard.
   1732      */
   1733     protected abstract int getMaxKeyguardNotifications();
   1734 
   1735     /**
   1736      * Updates expanded, dimmed and locked states of notification rows.
   1737      */
   1738     protected void updateRowStates() {
   1739         int maxKeyguardNotifications = getMaxKeyguardNotifications();
   1740         mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
   1741 
   1742         ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
   1743         final int N = activeNotifications.size();
   1744 
   1745         int visibleNotifications = 0;
   1746         boolean onKeyguard = mState == StatusBarState.KEYGUARD;
   1747         for (int i = 0; i < N; i++) {
   1748             NotificationData.Entry entry = activeNotifications.get(i);
   1749             if (onKeyguard) {
   1750                 entry.row.setExpansionDisabled(true);
   1751             } else {
   1752                 entry.row.setExpansionDisabled(false);
   1753                 if (!entry.row.isUserLocked()) {
   1754                     boolean top = (i == 0);
   1755                     entry.row.setSystemExpanded(top);
   1756                 }
   1757             }
   1758             boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
   1759             if ((isLockscreenPublicMode() && !mShowLockscreenNotifications) ||
   1760                     (onKeyguard && (visibleNotifications >= maxKeyguardNotifications
   1761                             || !showOnKeyguard))) {
   1762                 entry.row.setVisibility(View.GONE);
   1763                 if (onKeyguard && showOnKeyguard) {
   1764                     mKeyguardIconOverflowContainer.getIconsView().addNotification(entry);
   1765                 }
   1766             } else {
   1767                 boolean wasGone = entry.row.getVisibility() == View.GONE;
   1768                 entry.row.setVisibility(View.VISIBLE);
   1769                 if (wasGone) {
   1770                     // notify the scroller of a child addition
   1771                     mStackScroller.generateAddAnimation(entry.row, true /* fromMoreCard */);
   1772                 }
   1773                 visibleNotifications++;
   1774             }
   1775         }
   1776 
   1777         if (onKeyguard && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0) {
   1778             mKeyguardIconOverflowContainer.setVisibility(View.VISIBLE);
   1779         } else {
   1780             mKeyguardIconOverflowContainer.setVisibility(View.GONE);
   1781         }
   1782 
   1783         mStackScroller.changeViewPosition(mKeyguardIconOverflowContainer,
   1784                 mStackScroller.getChildCount() - 3);
   1785         mStackScroller.changeViewPosition(mEmptyShadeView, mStackScroller.getChildCount() - 2);
   1786         mStackScroller.changeViewPosition(mDismissView, mStackScroller.getChildCount() - 1);
   1787     }
   1788 
   1789     private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
   1790         return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey());
   1791     }
   1792 
   1793     protected void setZenMode(int mode) {
   1794         if (!isDeviceProvisioned()) return;
   1795         mZenMode = mode;
   1796         updateNotifications();
   1797     }
   1798 
   1799     // extended in PhoneStatusBar
   1800     protected void setShowLockscreenNotifications(boolean show) {
   1801         mShowLockscreenNotifications = show;
   1802     }
   1803 
   1804     private void updateLockscreenNotificationSetting() {
   1805         final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
   1806                 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
   1807                 1,
   1808                 mCurrentUserId) != 0;
   1809         final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
   1810                 null /* admin */, mCurrentUserId);
   1811         final boolean allowedByDpm = (dpmFlags
   1812                 & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
   1813         setShowLockscreenNotifications(show && allowedByDpm);
   1814     }
   1815 
   1816     protected abstract void haltTicker();
   1817     protected abstract void setAreThereNotifications();
   1818     protected abstract void updateNotifications();
   1819     protected abstract void tick(StatusBarNotification n, boolean firstTime);
   1820     protected abstract void updateExpandedViewPos(int expandedPosition);
   1821     protected abstract boolean shouldDisableNavbarGestures();
   1822 
   1823     public abstract void addNotification(StatusBarNotification notification,
   1824             RankingMap ranking);
   1825     protected abstract void updateNotificationRanking(RankingMap ranking);
   1826     public abstract void removeNotification(String key, RankingMap ranking);
   1827 
   1828     public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
   1829         if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
   1830 
   1831         final String key = notification.getKey();
   1832         boolean wasHeadsUp = isHeadsUp(key);
   1833         Entry oldEntry;
   1834         if (wasHeadsUp) {
   1835             oldEntry = mHeadsUpNotificationView.getEntry();
   1836         } else {
   1837             oldEntry = mNotificationData.get(key);
   1838         }
   1839         if (oldEntry == null) {
   1840             return;
   1841         }
   1842 
   1843         final StatusBarNotification oldNotification = oldEntry.notification;
   1844 
   1845         // XXX: modify when we do something more intelligent with the two content views
   1846         final RemoteViews oldContentView = oldNotification.getNotification().contentView;
   1847         Notification n = notification.getNotification();
   1848         final RemoteViews contentView = n.contentView;
   1849         final RemoteViews oldBigContentView = oldNotification.getNotification().bigContentView;
   1850         final RemoteViews bigContentView = n.bigContentView;
   1851         final RemoteViews oldHeadsUpContentView = oldNotification.getNotification().headsUpContentView;
   1852         final RemoteViews headsUpContentView = n.headsUpContentView;
   1853         final Notification oldPublicNotification = oldNotification.getNotification().publicVersion;
   1854         final RemoteViews oldPublicContentView = oldPublicNotification != null
   1855                 ? oldPublicNotification.contentView : null;
   1856         final Notification publicNotification = n.publicVersion;
   1857         final RemoteViews publicContentView = publicNotification != null
   1858                 ? publicNotification.contentView : null;
   1859 
   1860         if (DEBUG) {
   1861             Log.d(TAG, "old notification: when=" + oldNotification.getNotification().when
   1862                     + " ongoing=" + oldNotification.isOngoing()
   1863                     + " expanded=" + oldEntry.expanded
   1864                     + " contentView=" + oldContentView
   1865                     + " bigContentView=" + oldBigContentView
   1866                     + " publicView=" + oldPublicContentView
   1867                     + " rowParent=" + oldEntry.row.getParent());
   1868             Log.d(TAG, "new notification: when=" + n.when
   1869                     + " ongoing=" + oldNotification.isOngoing()
   1870                     + " contentView=" + contentView
   1871                     + " bigContentView=" + bigContentView
   1872                     + " publicView=" + publicContentView);
   1873         }
   1874 
   1875         // Can we just reapply the RemoteViews in place?
   1876 
   1877         // 1U is never null
   1878         boolean contentsUnchanged = oldEntry.expanded != null
   1879                 && contentView.getPackage() != null
   1880                 && oldContentView.getPackage() != null
   1881                 && oldContentView.getPackage().equals(contentView.getPackage())
   1882                 && oldContentView.getLayoutId() == contentView.getLayoutId();
   1883         // large view may be null
   1884         boolean bigContentsUnchanged =
   1885                 (oldEntry.getBigContentView() == null && bigContentView == null)
   1886                 || ((oldEntry.getBigContentView() != null && bigContentView != null)
   1887                     && bigContentView.getPackage() != null
   1888                     && oldBigContentView.getPackage() != null
   1889                     && oldBigContentView.getPackage().equals(bigContentView.getPackage())
   1890                     && oldBigContentView.getLayoutId() == bigContentView.getLayoutId());
   1891         boolean headsUpContentsUnchanged =
   1892                 (oldHeadsUpContentView == null && headsUpContentView == null)
   1893                 || ((oldHeadsUpContentView != null && headsUpContentView != null)
   1894                     && headsUpContentView.getPackage() != null
   1895                     && oldHeadsUpContentView.getPackage() != null
   1896                     && oldHeadsUpContentView.getPackage().equals(headsUpContentView.getPackage())
   1897                     && oldHeadsUpContentView.getLayoutId() == headsUpContentView.getLayoutId());
   1898         boolean publicUnchanged  =
   1899                 (oldPublicContentView == null && publicContentView == null)
   1900                 || ((oldPublicContentView != null && publicContentView != null)
   1901                         && publicContentView.getPackage() != null
   1902                         && oldPublicContentView.getPackage() != null
   1903                         && oldPublicContentView.getPackage().equals(publicContentView.getPackage())
   1904                         && oldPublicContentView.getLayoutId() == publicContentView.getLayoutId());
   1905         boolean updateTicker = n.tickerText != null
   1906                 && !TextUtils.equals(n.tickerText,
   1907                 oldEntry.notification.getNotification().tickerText);
   1908 
   1909         final boolean shouldInterrupt = shouldInterrupt(notification);
   1910         final boolean alertAgain = alertAgain(oldEntry, n);
   1911         boolean updateSuccessful = false;
   1912         if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged
   1913                 && publicUnchanged) {
   1914             if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
   1915             oldEntry.notification = notification;
   1916             try {
   1917                 if (oldEntry.icon != null) {
   1918                     // Update the icon
   1919                     final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
   1920                             notification.getUser(),
   1921                             n.icon,
   1922                             n.iconLevel,
   1923                             n.number,
   1924                             n.tickerText);
   1925                     oldEntry.icon.setNotification(n);
   1926                     if (!oldEntry.icon.set(ic)) {
   1927                         handleNotificationError(notification, "Couldn't update icon: " + ic);
   1928                         return;
   1929                     }
   1930                 }
   1931 
   1932                 if (wasHeadsUp) {
   1933                     if (shouldInterrupt) {
   1934                         updateHeadsUpViews(oldEntry, notification);
   1935                         if (alertAgain) {
   1936                             resetHeadsUpDecayTimer();
   1937                         }
   1938                     } else {
   1939                         // we updated the notification above, so release to build a new shade entry
   1940                         mHeadsUpNotificationView.releaseAndClose();
   1941                         return;
   1942                     }
   1943                 } else {
   1944                     if (shouldInterrupt && alertAgain) {
   1945                         removeNotificationViews(key, ranking);
   1946                         addNotification(notification, ranking);  //this will pop the headsup
   1947                     } else {
   1948                         updateNotificationViews(oldEntry, notification);
   1949                     }
   1950                 }
   1951                 mNotificationData.updateRanking(ranking);
   1952                 updateNotifications();
   1953                 updateSuccessful = true;
   1954             }
   1955             catch (RuntimeException e) {
   1956                 // It failed to add cleanly.  Log, and remove the view from the panel.
   1957                 Log.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
   1958             }
   1959         }
   1960         if (!updateSuccessful) {
   1961             if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);
   1962             if (wasHeadsUp) {
   1963                 if (shouldInterrupt) {
   1964                     if (DEBUG) Log.d(TAG, "rebuilding heads up for key: " + key);
   1965                     Entry newEntry = new Entry(notification, null);
   1966                     ViewGroup holder = mHeadsUpNotificationView.getHolder();
   1967                     if (inflateViewsForHeadsUp(newEntry, holder)) {
   1968                         mHeadsUpNotificationView.showNotification(newEntry);
   1969                         if (alertAgain) {
   1970                             resetHeadsUpDecayTimer();
   1971                         }
   1972                     } else {
   1973                         Log.w(TAG, "Couldn't create new updated headsup for package "
   1974                                 + contentView.getPackage());
   1975                     }
   1976                 } else {
   1977                     if (DEBUG) Log.d(TAG, "releasing heads up for key: " + key);
   1978                     oldEntry.notification = notification;
   1979                     mHeadsUpNotificationView.releaseAndClose();
   1980                     return;
   1981                 }
   1982             } else {
   1983                 if (shouldInterrupt && alertAgain) {
   1984                     if (DEBUG) Log.d(TAG, "reposting to invoke heads up for key: " + key);
   1985                     removeNotificationViews(key, ranking);
   1986                     addNotification(notification, ranking);  //this will pop the headsup
   1987                 } else {
   1988                     if (DEBUG) Log.d(TAG, "rebuilding update in place for key: " + key);
   1989                     oldEntry.notification = notification;
   1990                     final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
   1991                             notification.getUser(),
   1992                             n.icon,
   1993                             n.iconLevel,
   1994                             n.number,
   1995                             n.tickerText);
   1996                     oldEntry.icon.setNotification(n);
   1997                     oldEntry.icon.set(ic);
   1998                     inflateViews(oldEntry, mStackScroller, wasHeadsUp);
   1999                     mNotificationData.updateRanking(ranking);
   2000                     updateNotifications();
   2001                 }
   2002             }
   2003         }
   2004 
   2005         // Update the veto button accordingly (and as a result, whether this row is
   2006         // swipe-dismissable)
   2007         updateNotificationVetoButton(oldEntry.row, notification);
   2008 
   2009         // Is this for you?
   2010         boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);
   2011         if (DEBUG) Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
   2012 
   2013         // Restart the ticker if it's still running
   2014         if (updateTicker && isForCurrentUser) {
   2015             haltTicker();
   2016             tick(notification, false);
   2017         }
   2018 
   2019         // Recalculate the position of the sliding windows and the titles.
   2020         setAreThereNotifications();
   2021         updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
   2022     }
   2023 
   2024     private void updateNotificationViews(NotificationData.Entry entry,
   2025             StatusBarNotification notification) {
   2026         updateNotificationViews(entry, notification, false);
   2027     }
   2028 
   2029     private void updateHeadsUpViews(NotificationData.Entry entry,
   2030             StatusBarNotification notification) {
   2031         updateNotificationViews(entry, notification, true);
   2032     }
   2033 
   2034     private void updateNotificationViews(NotificationData.Entry entry,
   2035             StatusBarNotification notification, boolean isHeadsUp) {
   2036         final RemoteViews contentView = notification.getNotification().contentView;
   2037         final RemoteViews bigContentView = isHeadsUp
   2038                 ? notification.getNotification().headsUpContentView
   2039                 : notification.getNotification().bigContentView;
   2040         final Notification publicVersion = notification.getNotification().publicVersion;
   2041         final RemoteViews publicContentView = publicVersion != null ? publicVersion.contentView
   2042                 : null;
   2043 
   2044         // Reapply the RemoteViews
   2045         contentView.reapply(mContext, entry.expanded, mOnClickHandler);
   2046         if (bigContentView != null && entry.getBigContentView() != null) {
   2047             bigContentView.reapply(mContext, entry.getBigContentView(),
   2048                     mOnClickHandler);
   2049         }
   2050         if (publicContentView != null && entry.getPublicContentView() != null) {
   2051             publicContentView.reapply(mContext, entry.getPublicContentView(), mOnClickHandler);
   2052         }
   2053         // update the contentIntent
   2054         final PendingIntent contentIntent = notification.getNotification().contentIntent;
   2055         if (contentIntent != null) {
   2056             final View.OnClickListener listener = makeClicker(contentIntent, notification.getKey(),
   2057                     isHeadsUp);
   2058             entry.row.setOnClickListener(listener);
   2059         } else {
   2060             entry.row.setOnClickListener(null);
   2061         }
   2062         entry.row.setStatusBarNotification(notification);
   2063         entry.row.notifyContentUpdated();
   2064         entry.row.resetHeight();
   2065     }
   2066 
   2067     protected void notifyHeadsUpScreenOn(boolean screenOn) {
   2068         if (!screenOn) {
   2069             scheduleHeadsUpEscalation();
   2070         }
   2071     }
   2072 
   2073     private boolean alertAgain(Entry oldEntry, Notification newNotification) {
   2074         return oldEntry == null || !oldEntry.hasInterrupted()
   2075                 || (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
   2076     }
   2077 
   2078     protected boolean shouldInterrupt(StatusBarNotification sbn) {
   2079         if (mNotificationData.shouldFilterOut(sbn)) {
   2080             if (DEBUG) {
   2081                 Log.d(TAG, "Skipping HUN check for " + sbn.getKey() + " since it's filtered out.");
   2082             }
   2083             return false;
   2084         }
   2085 
   2086         if (mHeadsUpNotificationView.isSnoozed(sbn.getPackageName())) {
   2087             return false;
   2088         }
   2089 
   2090         Notification notification = sbn.getNotification();
   2091         // some predicates to make the boolean logic legible
   2092         boolean isNoisy = (notification.defaults & Notification.DEFAULT_SOUND) != 0
   2093                 || (notification.defaults & Notification.DEFAULT_VIBRATE) != 0
   2094                 || notification.sound != null
   2095                 || notification.vibrate != null;
   2096         boolean isHighPriority = sbn.getScore() >= INTERRUPTION_THRESHOLD;
   2097         boolean isFullscreen = notification.fullScreenIntent != null;
   2098         boolean hasTicker = mHeadsUpTicker && !TextUtils.isEmpty(notification.tickerText);
   2099         boolean isAllowed = notification.extras.getInt(Notification.EXTRA_AS_HEADS_UP,
   2100                 Notification.HEADS_UP_ALLOWED) != Notification.HEADS_UP_NEVER;
   2101         boolean accessibilityForcesLaunch = isFullscreen
   2102                 && mAccessibilityManager.isTouchExplorationEnabled();
   2103 
   2104         boolean interrupt = (isFullscreen || (isHighPriority && (isNoisy || hasTicker)))
   2105                 && isAllowed
   2106                 && !accessibilityForcesLaunch
   2107                 && mPowerManager.isScreenOn()
   2108                 && (!mStatusBarKeyguardViewManager.isShowing()
   2109                         || mStatusBarKeyguardViewManager.isOccluded())
   2110                 && !mStatusBarKeyguardViewManager.isInputRestricted();
   2111         try {
   2112             interrupt = interrupt && !mDreamManager.isDreaming();
   2113         } catch (RemoteException e) {
   2114             Log.d(TAG, "failed to query dream manager", e);
   2115         }
   2116         if (DEBUG) Log.d(TAG, "interrupt: " + interrupt);
   2117         return interrupt;
   2118     }
   2119 
   2120     public void setInteracting(int barWindow, boolean interacting) {
   2121         // hook for subclasses
   2122     }
   2123 
   2124     public void setBouncerShowing(boolean bouncerShowing) {
   2125         mBouncerShowing = bouncerShowing;
   2126     }
   2127 
   2128     /**
   2129      * @return Whether the security bouncer from Keyguard is showing.
   2130      */
   2131     public boolean isBouncerShowing() {
   2132         return mBouncerShowing;
   2133     }
   2134 
   2135     public void destroy() {
   2136         if (mSearchPanelView != null) {
   2137             mWindowManager.removeViewImmediate(mSearchPanelView);
   2138         }
   2139         mContext.unregisterReceiver(mBroadcastReceiver);
   2140         try {
   2141             mNotificationListener.unregisterAsSystemService();
   2142         } catch (RemoteException e) {
   2143             // Ignore.
   2144         }
   2145     }
   2146 
   2147     /**
   2148      * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then
   2149      *         return PackageManager for mContext
   2150      */
   2151     protected PackageManager getPackageManagerForUser(int userId) {
   2152         Context contextForUser = mContext;
   2153         // UserHandle defines special userId as negative values, e.g. USER_ALL
   2154         if (userId >= 0) {
   2155             try {
   2156                 // Create a context for the correct user so if a package isn't installed
   2157                 // for user 0 we can still load information about the package.
   2158                 contextForUser =
   2159                         mContext.createPackageContextAsUser(mContext.getPackageName(),
   2160                         Context.CONTEXT_RESTRICTED,
   2161                         new UserHandle(userId));
   2162             } catch (NameNotFoundException e) {
   2163                 // Shouldn't fail to find the package name for system ui.
   2164             }
   2165         }
   2166         return contextForUser.getPackageManager();
   2167     }
   2168 
   2169     @Override
   2170     public void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
   2171         try {
   2172             mBarService.onNotificationExpansionChanged(key, userAction, expanded);
   2173         } catch (RemoteException e) {
   2174             // Ignore.
   2175         }
   2176     }
   2177 
   2178     public boolean isKeyguardSecure() {
   2179         if (mStatusBarKeyguardViewManager == null) {
   2180             // startKeyguard() hasn't been called yet, so we don't know.
   2181             // Make sure anything that needs to know isKeyguardSecure() checks and re-checks this
   2182             // value onVisibilityChanged().
   2183             Slog.w(TAG, "isKeyguardSecure() called before startKeyguard(), returning false",
   2184                     new Throwable());
   2185             return false;
   2186         }
   2187         return mStatusBarKeyguardViewManager.isSecure();
   2188     }
   2189 }
   2190