Home | History | Annotate | Download | only in statusbar
      1 
      2 /*
      3  * Copyright (C) 2010 The Android Open Source Project
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package com.android.systemui.statusbar;
     19 
     20 import com.android.internal.statusbar.IStatusBarService;
     21 import com.android.internal.statusbar.StatusBarIcon;
     22 import com.android.internal.statusbar.StatusBarIconList;
     23 import com.android.internal.statusbar.StatusBarNotification;
     24 import com.android.internal.widget.SizeAdaptiveLayout;
     25 import com.android.systemui.R;
     26 import com.android.systemui.SearchPanelView;
     27 import com.android.systemui.SystemUI;
     28 import com.android.systemui.recent.RecentTasksLoader;
     29 import com.android.systemui.recent.RecentsActivity;
     30 import com.android.systemui.recent.TaskDescription;
     31 import com.android.systemui.statusbar.policy.NotificationRowLayout;
     32 import com.android.systemui.statusbar.tablet.StatusBarPanel;
     33 
     34 import android.app.ActivityManager;
     35 import android.app.ActivityManagerNative;
     36 import android.app.ActivityOptions;
     37 import android.app.KeyguardManager;
     38 import android.app.PendingIntent;
     39 import android.app.TaskStackBuilder;
     40 import android.content.ActivityNotFoundException;
     41 import android.content.BroadcastReceiver;
     42 import android.content.Context;
     43 import android.content.Intent;
     44 import android.content.IntentFilter;
     45 import android.content.pm.ApplicationInfo;
     46 import android.content.pm.PackageManager.NameNotFoundException;
     47 import android.content.res.Configuration;
     48 import android.content.res.Resources;
     49 import android.database.ContentObserver;
     50 import android.graphics.Bitmap;
     51 import android.graphics.Paint;
     52 import android.graphics.Rect;
     53 import android.net.Uri;
     54 import android.os.Build;
     55 import android.os.Handler;
     56 import android.os.IBinder;
     57 import android.os.Message;
     58 import android.os.RemoteException;
     59 import android.os.ServiceManager;
     60 import android.os.UserHandle;
     61 import android.provider.Settings;
     62 import android.text.TextUtils;
     63 import android.util.DisplayMetrics;
     64 import android.util.Log;
     65 import android.util.Slog;
     66 import android.view.Display;
     67 import android.view.IWindowManager;
     68 import android.view.LayoutInflater;
     69 import android.view.MenuItem;
     70 import android.view.MotionEvent;
     71 import android.view.View;
     72 import android.view.ViewGroup;
     73 import android.view.ViewGroup.LayoutParams;
     74 import android.view.WindowManager;
     75 import android.view.WindowManagerGlobal;
     76 import android.widget.ImageView;
     77 import android.widget.LinearLayout;
     78 import android.widget.PopupMenu;
     79 import android.widget.RemoteViews;
     80 import android.widget.TextView;
     81 
     82 import java.util.ArrayList;
     83 
     84 public abstract class BaseStatusBar extends SystemUI implements
     85         CommandQueue.Callbacks {
     86     public static final String TAG = "StatusBar";
     87     public static final boolean DEBUG = false;
     88     public static final boolean MULTIUSER_DEBUG = false;
     89 
     90     protected static final int MSG_TOGGLE_RECENTS_PANEL = 1020;
     91     protected static final int MSG_CLOSE_RECENTS_PANEL = 1021;
     92     protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
     93     protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
     94     protected static final int MSG_OPEN_SEARCH_PANEL = 1024;
     95     protected static final int MSG_CLOSE_SEARCH_PANEL = 1025;
     96     protected static final int MSG_SHOW_INTRUDER = 1026;
     97     protected static final int MSG_HIDE_INTRUDER = 1027;
     98 
     99     protected static final boolean ENABLE_INTRUDERS = false;
    100 
    101     // Should match the value in PhoneWindowManager
    102     public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
    103 
    104     public static final int EXPANDED_LEAVE_ALONE = -10000;
    105     public static final int EXPANDED_FULL_OPEN = -10001;
    106 
    107     protected CommandQueue mCommandQueue;
    108     protected IStatusBarService mBarService;
    109     protected H mHandler = createHandler();
    110 
    111     // all notifications
    112     protected NotificationData mNotificationData = new NotificationData();
    113     protected NotificationRowLayout mPile;
    114 
    115     protected StatusBarNotification mCurrentlyIntrudingNotification;
    116 
    117     // used to notify status bar for suppressing notification LED
    118     protected boolean mPanelSlightlyVisible;
    119 
    120     // Search panel
    121     protected SearchPanelView mSearchPanelView;
    122 
    123     protected PopupMenu mNotificationBlamePopup;
    124 
    125     protected int mCurrentUserId = 0;
    126 
    127     // UI-specific methods
    128 
    129     /**
    130      * Create all windows necessary for the status bar (including navigation, overlay panels, etc)
    131      * and add them to the window manager.
    132      */
    133     protected abstract void createAndAddWindows();
    134 
    135     protected WindowManager mWindowManager;
    136     protected IWindowManager mWindowManagerService;
    137     protected Display mDisplay;
    138 
    139     private boolean mDeviceProvisioned = false;
    140 
    141     public IStatusBarService getStatusBarService() {
    142         return mBarService;
    143     }
    144 
    145     public boolean isDeviceProvisioned() {
    146         return mDeviceProvisioned;
    147     }
    148 
    149     private ContentObserver mProvisioningObserver = new ContentObserver(new Handler()) {
    150         @Override
    151         public void onChange(boolean selfChange) {
    152             final boolean provisioned = 0 != Settings.Global.getInt(
    153                     mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
    154             if (provisioned != mDeviceProvisioned) {
    155                 mDeviceProvisioned = provisioned;
    156                 updateNotificationIcons();
    157             }
    158         }
    159     };
    160 
    161     private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
    162         @Override
    163         public boolean onClickHandler(View view, PendingIntent pendingIntent, Intent fillInIntent) {
    164             if (DEBUG) {
    165                 Slog.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
    166             }
    167             final boolean isActivity = pendingIntent.isActivity();
    168             if (isActivity) {
    169                 try {
    170                     // The intent we are sending is for the application, which
    171                     // won't have permission to immediately start an activity after
    172                     // the user switches to home.  We know it is safe to do at this
    173                     // point, so make sure new activity switches are now allowed.
    174                     ActivityManagerNative.getDefault().resumeAppSwitches();
    175                     // Also, notifications can be launched from the lock screen,
    176                     // so dismiss the lock screen when the activity starts.
    177                     ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
    178                 } catch (RemoteException e) {
    179                 }
    180             }
    181 
    182             boolean handled = super.onClickHandler(view, pendingIntent, fillInIntent);
    183 
    184             if (isActivity && handled) {
    185                 // close the shade if it was open
    186                 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
    187                 visibilityChanged(false);
    188             }
    189             return handled;
    190         }
    191     };
    192 
    193     public void start() {
    194         mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    195         mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
    196         mDisplay = mWindowManager.getDefaultDisplay();
    197 
    198         mProvisioningObserver.onChange(false); // set up
    199         mContext.getContentResolver().registerContentObserver(
    200                 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
    201                 mProvisioningObserver);
    202 
    203         mBarService = IStatusBarService.Stub.asInterface(
    204                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
    205 
    206         // Connect in to the status bar manager service
    207         StatusBarIconList iconList = new StatusBarIconList();
    208         ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();
    209         ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();
    210         mCommandQueue = new CommandQueue(this, iconList);
    211 
    212         int[] switches = new int[7];
    213         ArrayList<IBinder> binders = new ArrayList<IBinder>();
    214         try {
    215             mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,
    216                     switches, binders);
    217         } catch (RemoteException ex) {
    218             // If the system process isn't there we're doomed anyway.
    219         }
    220 
    221         createAndAddWindows();
    222 
    223         disable(switches[0]);
    224         setSystemUiVisibility(switches[1], 0xffffffff);
    225         topAppWindowChanged(switches[2] != 0);
    226         // StatusBarManagerService has a back up of IME token and it's restored here.
    227         setImeWindowStatus(binders.get(0), switches[3], switches[4]);
    228         setHardKeyboardStatus(switches[5] != 0, switches[6] != 0);
    229 
    230         // Set up the initial icon state
    231         int N = iconList.size();
    232         int viewIndex = 0;
    233         for (int i=0; i<N; i++) {
    234             StatusBarIcon icon = iconList.getIcon(i);
    235             if (icon != null) {
    236                 addIcon(iconList.getSlot(i), i, viewIndex, icon);
    237                 viewIndex++;
    238             }
    239         }
    240 
    241         // Set up the initial notification state
    242         N = notificationKeys.size();
    243         if (N == notifications.size()) {
    244             for (int i=0; i<N; i++) {
    245                 addNotification(notificationKeys.get(i), notifications.get(i));
    246             }
    247         } else {
    248             Log.wtf(TAG, "Notification list length mismatch: keys=" + N
    249                     + " notifications=" + notifications.size());
    250         }
    251 
    252         if (DEBUG) {
    253             Slog.d(TAG, String.format(
    254                     "init: icons=%d disabled=0x%08x lights=0x%08x menu=0x%08x imeButton=0x%08x",
    255                    iconList.size(),
    256                    switches[0],
    257                    switches[1],
    258                    switches[2],
    259                    switches[3]
    260                    ));
    261         }
    262 
    263         mCurrentUserId = ActivityManager.getCurrentUser();
    264 
    265         IntentFilter filter = new IntentFilter();
    266         filter.addAction(Intent.ACTION_USER_SWITCHED);
    267         mContext.registerReceiver(new BroadcastReceiver() {
    268             @Override
    269             public void onReceive(Context context, Intent intent) {
    270                 String action = intent.getAction();
    271                 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
    272                     mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
    273                     if (true) Slog.v(TAG, "userId " + mCurrentUserId + " is in the house");
    274                     userSwitched(mCurrentUserId);
    275                 }
    276             }}, filter);
    277     }
    278 
    279     public void userSwitched(int newUserId) {
    280         // should be overridden
    281     }
    282 
    283     public boolean notificationIsForCurrentUser(StatusBarNotification n) {
    284         final int thisUserId = mCurrentUserId;
    285         final int notificationUserId = n.getUserId();
    286         if (DEBUG && MULTIUSER_DEBUG) {
    287             Slog.v(TAG, String.format("%s: current userid: %d, notification userid: %d",
    288                     n, thisUserId, notificationUserId));
    289         }
    290         return notificationUserId == UserHandle.USER_ALL
    291                 || thisUserId == notificationUserId;
    292     }
    293 
    294     protected View updateNotificationVetoButton(View row, StatusBarNotification n) {
    295         View vetoButton = row.findViewById(R.id.veto);
    296         if (n.isClearable()) {
    297             final String _pkg = n.pkg;
    298             final String _tag = n.tag;
    299             final int _id = n.id;
    300             vetoButton.setOnClickListener(new View.OnClickListener() {
    301                     public void onClick(View v) {
    302                         // Accessibility feedback
    303                         v.announceForAccessibility(
    304                                 mContext.getString(R.string.accessibility_notification_dismissed));
    305                         try {
    306                             mBarService.onNotificationClear(_pkg, _tag, _id);
    307 
    308                         } catch (RemoteException ex) {
    309                             // system process is dead if we're here.
    310                         }
    311                     }
    312                 });
    313             vetoButton.setVisibility(View.VISIBLE);
    314         } else {
    315             vetoButton.setVisibility(View.GONE);
    316         }
    317         vetoButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
    318         return vetoButton;
    319     }
    320 
    321 
    322     protected void applyLegacyRowBackground(StatusBarNotification sbn, View content) {
    323         if (sbn.notification.contentView.getLayoutId() !=
    324                 com.android.internal.R.layout.notification_template_base) {
    325             int version = 0;
    326             try {
    327                 ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(sbn.pkg, 0);
    328                 version = info.targetSdkVersion;
    329             } catch (NameNotFoundException ex) {
    330                 Slog.e(TAG, "Failed looking up ApplicationInfo for " + sbn.pkg, ex);
    331             }
    332             if (version > 0 && version < Build.VERSION_CODES.GINGERBREAD) {
    333                 content.setBackgroundResource(R.drawable.notification_row_legacy_bg);
    334             } else {
    335                 content.setBackgroundResource(com.android.internal.R.drawable.notification_bg);
    336             }
    337         }
    338     }
    339 
    340     private void startApplicationDetailsActivity(String packageName) {
    341         Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
    342                 Uri.fromParts("package", packageName, null));
    343         intent.setComponent(intent.resolveActivity(mContext.getPackageManager()));
    344         TaskStackBuilder.create(mContext).addNextIntentWithParentStack(intent).startActivities(
    345                 null, UserHandle.CURRENT);
    346     }
    347 
    348     protected View.OnLongClickListener getNotificationLongClicker() {
    349         return new View.OnLongClickListener() {
    350             @Override
    351             public boolean onLongClick(View v) {
    352                 final String packageNameF = (String) v.getTag();
    353                 if (packageNameF == null) return false;
    354                 if (v.getWindowToken() == null) return false;
    355                 mNotificationBlamePopup = new PopupMenu(mContext, v);
    356                 mNotificationBlamePopup.getMenuInflater().inflate(
    357                         R.menu.notification_popup_menu,
    358                         mNotificationBlamePopup.getMenu());
    359                 mNotificationBlamePopup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
    360                     public boolean onMenuItemClick(MenuItem item) {
    361                         if (item.getItemId() == R.id.notification_inspect_item) {
    362                             startApplicationDetailsActivity(packageNameF);
    363                             animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
    364                         } else {
    365                             return false;
    366                         }
    367                         return true;
    368                     }
    369                 });
    370                 mNotificationBlamePopup.show();
    371 
    372                 return true;
    373             }
    374         };
    375     }
    376 
    377     public void dismissPopups() {
    378         if (mNotificationBlamePopup != null) {
    379             mNotificationBlamePopup.dismiss();
    380             mNotificationBlamePopup = null;
    381         }
    382     }
    383 
    384     public void dismissIntruder() {
    385         // pass
    386     }
    387 
    388     @Override
    389     public void toggleRecentApps() {
    390         int msg = MSG_TOGGLE_RECENTS_PANEL;
    391         mHandler.removeMessages(msg);
    392         mHandler.sendEmptyMessage(msg);
    393     }
    394 
    395     @Override
    396     public void preloadRecentApps() {
    397         int msg = MSG_PRELOAD_RECENT_APPS;
    398         mHandler.removeMessages(msg);
    399         mHandler.sendEmptyMessage(msg);
    400     }
    401 
    402     @Override
    403     public void cancelPreloadRecentApps() {
    404         int msg = MSG_CANCEL_PRELOAD_RECENT_APPS;
    405         mHandler.removeMessages(msg);
    406         mHandler.sendEmptyMessage(msg);
    407     }
    408 
    409     @Override
    410     public void showSearchPanel() {
    411         int msg = MSG_OPEN_SEARCH_PANEL;
    412         mHandler.removeMessages(msg);
    413         mHandler.sendEmptyMessage(msg);
    414     }
    415 
    416     @Override
    417     public void hideSearchPanel() {
    418         int msg = MSG_CLOSE_SEARCH_PANEL;
    419         mHandler.removeMessages(msg);
    420         mHandler.sendEmptyMessage(msg);
    421     }
    422 
    423     protected abstract WindowManager.LayoutParams getRecentsLayoutParams(
    424             LayoutParams layoutParams);
    425 
    426     protected abstract WindowManager.LayoutParams getSearchLayoutParams(
    427             LayoutParams layoutParams);
    428 
    429 
    430     protected void updateSearchPanel() {
    431         // Search Panel
    432         boolean visible = false;
    433         if (mSearchPanelView != null) {
    434             visible = mSearchPanelView.isShowing();
    435             mWindowManager.removeView(mSearchPanelView);
    436         }
    437 
    438         // Provide SearchPanel with a temporary parent to allow layout params to work.
    439         LinearLayout tmpRoot = new LinearLayout(mContext);
    440         mSearchPanelView = (SearchPanelView) LayoutInflater.from(mContext).inflate(
    441                  R.layout.status_bar_search_panel, tmpRoot, false);
    442         mSearchPanelView.setOnTouchListener(
    443                  new TouchOutsideListener(MSG_CLOSE_SEARCH_PANEL, mSearchPanelView));
    444         mSearchPanelView.setVisibility(View.GONE);
    445 
    446         WindowManager.LayoutParams lp = getSearchLayoutParams(mSearchPanelView.getLayoutParams());
    447 
    448         mWindowManager.addView(mSearchPanelView, lp);
    449         mSearchPanelView.setBar(this);
    450         if (visible) {
    451             mSearchPanelView.show(true, false);
    452         }
    453     }
    454 
    455     protected H createHandler() {
    456          return new H();
    457     }
    458 
    459     static void sendCloseSystemWindows(Context context, String reason) {
    460         if (ActivityManagerNative.isSystemReady()) {
    461             try {
    462                 ActivityManagerNative.getDefault().closeSystemDialogs(reason);
    463             } catch (RemoteException e) {
    464             }
    465         }
    466     }
    467 
    468     protected abstract View getStatusBarView();
    469 
    470     protected void toggleRecentsActivity() {
    471         try {
    472 
    473             TaskDescription firstTask = RecentTasksLoader.getInstance(mContext).getFirstTask();
    474 
    475             Intent intent = new Intent(RecentsActivity.TOGGLE_RECENTS_INTENT);
    476             intent.setClassName("com.android.systemui",
    477                     "com.android.systemui.recent.RecentsActivity");
    478             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
    479                     | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
    480 
    481             if (firstTask == null) {
    482                 if (RecentsActivity.forceOpaqueBackground(mContext)) {
    483                     ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
    484                             R.anim.recents_launch_from_launcher_enter,
    485                             R.anim.recents_launch_from_launcher_exit);
    486                     mContext.startActivityAsUser(intent, opts.toBundle(), new UserHandle(
    487                             UserHandle.USER_CURRENT));
    488                 } else {
    489                     // The correct window animation will be applied via the activity's style
    490                     mContext.startActivityAsUser(intent, new UserHandle(
    491                             UserHandle.USER_CURRENT));
    492                 }
    493 
    494             } else {
    495                 Bitmap first = firstTask.getThumbnail();
    496                 final Resources res = mContext.getResources();
    497 
    498                 float thumbWidth = res
    499                         .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_width);
    500                 float thumbHeight = res
    501                         .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_height);
    502                 if (first == null) {
    503                     throw new RuntimeException("Recents thumbnail is null");
    504                 }
    505                 if (first.getWidth() != thumbWidth || first.getHeight() != thumbHeight) {
    506                     first = Bitmap.createScaledBitmap(first, (int) thumbWidth, (int) thumbHeight,
    507                             true);
    508                     if (first == null) {
    509                         throw new RuntimeException("Recents thumbnail is null");
    510                     }
    511                 }
    512 
    513 
    514                 DisplayMetrics dm = new DisplayMetrics();
    515                 mDisplay.getMetrics(dm);
    516                 // calculate it here, but consider moving it elsewhere
    517                 // first, determine which orientation you're in.
    518                 // todo: move the system_bar layouts to sw600dp ?
    519                 final Configuration config = res.getConfiguration();
    520                 int x, y;
    521 
    522                 if (config.orientation == Configuration.ORIENTATION_PORTRAIT) {
    523                     float appLabelLeftMargin = res
    524                             .getDimensionPixelSize(R.dimen.status_bar_recents_app_label_left_margin);
    525                     float appLabelWidth = res
    526                             .getDimensionPixelSize(R.dimen.status_bar_recents_app_label_width);
    527                     float thumbLeftMargin = res
    528                             .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_left_margin);
    529                     float thumbBgPadding = res
    530                             .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_bg_padding);
    531 
    532                     float width = appLabelLeftMargin +
    533                             +appLabelWidth
    534                             + thumbLeftMargin
    535                             + thumbWidth
    536                             + 2 * thumbBgPadding;
    537 
    538                     x = (int) ((dm.widthPixels - width) / 2f + appLabelLeftMargin + appLabelWidth
    539                             + thumbBgPadding + thumbLeftMargin);
    540                     y = (int) (dm.heightPixels
    541                             - res.getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_height) - thumbBgPadding);
    542                 } else { // if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
    543                     float thumbTopMargin = res
    544                             .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_top_margin);
    545                     float thumbBgPadding = res
    546                             .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_bg_padding);
    547                     float textPadding = res
    548                             .getDimensionPixelSize(R.dimen.status_bar_recents_text_description_padding);
    549                     float labelTextSize = res
    550                             .getDimensionPixelSize(R.dimen.status_bar_recents_app_label_text_size);
    551                     Paint p = new Paint();
    552                     p.setTextSize(labelTextSize);
    553                     float labelTextHeight = p.getFontMetricsInt().bottom
    554                             - p.getFontMetricsInt().top;
    555                     float descriptionTextSize = res
    556                             .getDimensionPixelSize(R.dimen.status_bar_recents_app_description_text_size);
    557                     p.setTextSize(descriptionTextSize);
    558                     float descriptionTextHeight = p.getFontMetricsInt().bottom
    559                             - p.getFontMetricsInt().top;
    560 
    561                     float statusBarHeight = res
    562                             .getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
    563                     float recentsItemTopPadding = statusBarHeight;
    564 
    565                     float height = thumbTopMargin
    566                             + thumbHeight
    567                             + 2 * thumbBgPadding + textPadding + labelTextHeight
    568                             + recentsItemTopPadding + textPadding + descriptionTextHeight;
    569                     float recentsItemRightPadding = res
    570                             .getDimensionPixelSize(R.dimen.status_bar_recents_item_padding);
    571                     float recentsScrollViewRightPadding = res
    572                             .getDimensionPixelSize(R.dimen.status_bar_recents_right_glow_margin);
    573                     x = (int) (dm.widthPixels - res
    574                             .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_width)
    575                             - thumbBgPadding - recentsItemRightPadding - recentsScrollViewRightPadding);
    576                     y = (int) ((dm.heightPixels - statusBarHeight - height) / 2f + thumbTopMargin
    577                             + recentsItemTopPadding + thumbBgPadding + statusBarHeight);
    578                 }
    579 
    580                 ActivityOptions opts = ActivityOptions.makeThumbnailScaleDownAnimation(
    581                         getStatusBarView(),
    582                         first, x, y,
    583                         new ActivityOptions.OnAnimationStartedListener() {
    584                             public void onAnimationStarted() {
    585                                 Intent intent = new Intent(RecentsActivity.WINDOW_ANIMATION_START_INTENT);
    586                                 intent.setPackage("com.android.systemui");
    587                                 mContext.sendBroadcastAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
    588                             }
    589                         });
    590                 intent.putExtra(RecentsActivity.WAITING_FOR_WINDOW_ANIMATION_PARAM, true);
    591                 mContext.startActivityAsUser(intent, opts.toBundle(), new UserHandle(
    592                         UserHandle.USER_CURRENT));
    593             }
    594             return;
    595         } catch (ActivityNotFoundException e) {
    596             Log.e(TAG, "Failed to launch RecentAppsIntent", e);
    597         }
    598     }
    599 
    600     protected View.OnTouchListener mRecentsPreloadOnTouchListener = new View.OnTouchListener() {
    601         // additional optimization when we have software system buttons - start loading the recent
    602         // tasks on touch down
    603         @Override
    604         public boolean onTouch(View v, MotionEvent event) {
    605             int action = event.getAction() & MotionEvent.ACTION_MASK;
    606             if (action == MotionEvent.ACTION_DOWN) {
    607                 preloadRecentTasksList();
    608             } else if (action == MotionEvent.ACTION_CANCEL) {
    609                 cancelPreloadingRecentTasksList();
    610             } else if (action == MotionEvent.ACTION_UP) {
    611                 if (!v.isPressed()) {
    612                     cancelPreloadingRecentTasksList();
    613                 }
    614 
    615             }
    616             return false;
    617         }
    618     };
    619 
    620     protected void preloadRecentTasksList() {
    621         if (DEBUG) Slog.d(TAG, "preloading recents");
    622         Intent intent = new Intent(RecentsActivity.PRELOAD_INTENT);
    623         intent.setClassName("com.android.systemui",
    624                 "com.android.systemui.recent.RecentsPreloadReceiver");
    625         mContext.sendBroadcastAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
    626 
    627         RecentTasksLoader.getInstance(mContext).preloadFirstTask();
    628     }
    629 
    630     protected void cancelPreloadingRecentTasksList() {
    631         if (DEBUG) Slog.d(TAG, "cancel preloading recents");
    632         Intent intent = new Intent(RecentsActivity.CANCEL_PRELOAD_INTENT);
    633         intent.setClassName("com.android.systemui",
    634                 "com.android.systemui.recent.RecentsPreloadReceiver");
    635         mContext.sendBroadcastAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
    636 
    637         RecentTasksLoader.getInstance(mContext).cancelPreloadingFirstTask();
    638     }
    639 
    640     protected class H extends Handler {
    641         public void handleMessage(Message m) {
    642             Intent intent;
    643             switch (m.what) {
    644              case MSG_TOGGLE_RECENTS_PANEL:
    645                  if (DEBUG) Slog.d(TAG, "toggle recents panel");
    646                  toggleRecentsActivity();
    647                  break;
    648              case MSG_CLOSE_RECENTS_PANEL:
    649                  if (DEBUG) Slog.d(TAG, "closing recents panel");
    650                  intent = new Intent(RecentsActivity.CLOSE_RECENTS_INTENT);
    651                  intent.setPackage("com.android.systemui");
    652                  mContext.sendBroadcastAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
    653                  break;
    654              case MSG_PRELOAD_RECENT_APPS:
    655                   preloadRecentTasksList();
    656                   break;
    657              case MSG_CANCEL_PRELOAD_RECENT_APPS:
    658                   cancelPreloadingRecentTasksList();
    659                   break;
    660              case MSG_OPEN_SEARCH_PANEL:
    661                  if (DEBUG) Slog.d(TAG, "opening search panel");
    662                  if (mSearchPanelView != null && mSearchPanelView.isAssistantAvailable()) {
    663                      mSearchPanelView.show(true, true);
    664                  }
    665                  break;
    666              case MSG_CLOSE_SEARCH_PANEL:
    667                  if (DEBUG) Slog.d(TAG, "closing search panel");
    668                  if (mSearchPanelView != null && mSearchPanelView.isShowing()) {
    669                      mSearchPanelView.show(false, true);
    670                  }
    671                  break;
    672             }
    673         }
    674     }
    675 
    676     public class TouchOutsideListener implements View.OnTouchListener {
    677         private int mMsg;
    678         private StatusBarPanel mPanel;
    679 
    680         public TouchOutsideListener(int msg, StatusBarPanel panel) {
    681             mMsg = msg;
    682             mPanel = panel;
    683         }
    684 
    685         public boolean onTouch(View v, MotionEvent ev) {
    686             final int action = ev.getAction();
    687             if (action == MotionEvent.ACTION_OUTSIDE
    688                 || (action == MotionEvent.ACTION_DOWN
    689                     && !mPanel.isInContentArea((int)ev.getX(), (int)ev.getY()))) {
    690                 mHandler.removeMessages(mMsg);
    691                 mHandler.sendEmptyMessage(mMsg);
    692                 return true;
    693             }
    694             return false;
    695         }
    696     }
    697 
    698     protected void workAroundBadLayerDrawableOpacity(View v) {
    699     }
    700 
    701     protected  boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) {
    702         int minHeight =
    703                 mContext.getResources().getDimensionPixelSize(R.dimen.notification_min_height);
    704         int maxHeight =
    705                 mContext.getResources().getDimensionPixelSize(R.dimen.notification_max_height);
    706         StatusBarNotification sbn = entry.notification;
    707         RemoteViews oneU = sbn.notification.contentView;
    708         RemoteViews large = sbn.notification.bigContentView;
    709         if (oneU == null) {
    710             return false;
    711         }
    712 
    713         // create the row view
    714         LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
    715                 Context.LAYOUT_INFLATER_SERVICE);
    716         View row = inflater.inflate(R.layout.status_bar_notification_row, parent, false);
    717 
    718         // for blaming (see SwipeHelper.setLongPressListener)
    719         row.setTag(sbn.pkg);
    720 
    721         workAroundBadLayerDrawableOpacity(row);
    722         View vetoButton = updateNotificationVetoButton(row, sbn);
    723         vetoButton.setContentDescription(mContext.getString(
    724                 R.string.accessibility_remove_notification));
    725 
    726         // NB: the large icon is now handled entirely by the template
    727 
    728         // bind the click event to the content area
    729         ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
    730         ViewGroup adaptive = (ViewGroup)row.findViewById(R.id.adaptive);
    731 
    732         content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
    733 
    734         PendingIntent contentIntent = sbn.notification.contentIntent;
    735         if (contentIntent != null) {
    736             final View.OnClickListener listener = new NotificationClicker(contentIntent,
    737                     sbn.pkg, sbn.tag, sbn.id);
    738             content.setOnClickListener(listener);
    739         } else {
    740             content.setOnClickListener(null);
    741         }
    742 
    743         // TODO(cwren) normalize variable names with those in updateNotification
    744         View expandedOneU = null;
    745         View expandedLarge = null;
    746         try {
    747             expandedOneU = oneU.apply(mContext, adaptive, mOnClickHandler);
    748             if (large != null) {
    749                 expandedLarge = large.apply(mContext, adaptive, mOnClickHandler);
    750             }
    751         }
    752         catch (RuntimeException e) {
    753             final String ident = sbn.pkg + "/0x" + Integer.toHexString(sbn.id);
    754             Slog.e(TAG, "couldn't inflate view for notification " + ident, e);
    755             return false;
    756         }
    757 
    758         if (expandedOneU != null) {
    759             SizeAdaptiveLayout.LayoutParams params =
    760                     new SizeAdaptiveLayout.LayoutParams(expandedOneU.getLayoutParams());
    761             params.minHeight = minHeight;
    762             params.maxHeight = minHeight;
    763             adaptive.addView(expandedOneU, params);
    764         }
    765         if (expandedLarge != null) {
    766             SizeAdaptiveLayout.LayoutParams params =
    767                     new SizeAdaptiveLayout.LayoutParams(expandedLarge.getLayoutParams());
    768             params.minHeight = minHeight+1;
    769             params.maxHeight = maxHeight;
    770             adaptive.addView(expandedLarge, params);
    771         }
    772         row.setDrawingCacheEnabled(true);
    773 
    774         applyLegacyRowBackground(sbn, content);
    775 
    776         row.setTag(R.id.expandable_tag, Boolean.valueOf(large != null));
    777 
    778         if (MULTIUSER_DEBUG) {
    779             TextView debug = (TextView) row.findViewById(R.id.debug_info);
    780             if (debug != null) {
    781                 debug.setVisibility(View.VISIBLE);
    782                 debug.setText("U " + entry.notification.getUserId());
    783             }
    784         }
    785         entry.row = row;
    786         entry.content = content;
    787         entry.expanded = expandedOneU;
    788         entry.setLargeView(expandedLarge);
    789 
    790         return true;
    791     }
    792 
    793     public NotificationClicker makeClicker(PendingIntent intent, String pkg, String tag, int id) {
    794         return new NotificationClicker(intent, pkg, tag, id);
    795     }
    796 
    797     private class NotificationClicker implements View.OnClickListener {
    798         private PendingIntent mIntent;
    799         private String mPkg;
    800         private String mTag;
    801         private int mId;
    802 
    803         NotificationClicker(PendingIntent intent, String pkg, String tag, int id) {
    804             mIntent = intent;
    805             mPkg = pkg;
    806             mTag = tag;
    807             mId = id;
    808         }
    809 
    810         public void onClick(View v) {
    811             try {
    812                 // The intent we are sending is for the application, which
    813                 // won't have permission to immediately start an activity after
    814                 // the user switches to home.  We know it is safe to do at this
    815                 // point, so make sure new activity switches are now allowed.
    816                 ActivityManagerNative.getDefault().resumeAppSwitches();
    817                 // Also, notifications can be launched from the lock screen,
    818                 // so dismiss the lock screen when the activity starts.
    819                 ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
    820             } catch (RemoteException e) {
    821             }
    822 
    823             if (mIntent != null) {
    824                 int[] pos = new int[2];
    825                 v.getLocationOnScreen(pos);
    826                 Intent overlay = new Intent();
    827                 overlay.setSourceBounds(
    828                         new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
    829                 try {
    830                     mIntent.send(mContext, 0, overlay);
    831                 } catch (PendingIntent.CanceledException e) {
    832                     // the stack trace isn't very helpful here.  Just log the exception message.
    833                     Slog.w(TAG, "Sending contentIntent failed: " + e);
    834                 }
    835 
    836                 KeyguardManager kgm =
    837                     (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
    838                 if (kgm != null) kgm.exitKeyguardSecurely(null);
    839             }
    840 
    841             try {
    842                 mBarService.onNotificationClick(mPkg, mTag, mId);
    843             } catch (RemoteException ex) {
    844                 // system process is dead if we're here.
    845             }
    846 
    847             // close the shade if it was open
    848             animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
    849             visibilityChanged(false);
    850 
    851             // If this click was on the intruder alert, hide that instead
    852 //            mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
    853         }
    854     }
    855     /**
    856      * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
    857      * This was added last-minute and is inconsistent with the way the rest of the notifications
    858      * are handled, because the notification isn't really cancelled.  The lights are just
    859      * turned off.  If any other notifications happen, the lights will turn back on.  Steve says
    860      * this is what he wants. (see bug 1131461)
    861      */
    862     protected void visibilityChanged(boolean visible) {
    863         if (mPanelSlightlyVisible != visible) {
    864             mPanelSlightlyVisible = visible;
    865             try {
    866                 mBarService.onPanelRevealed();
    867             } catch (RemoteException ex) {
    868                 // Won't fail unless the world has ended.
    869             }
    870         }
    871     }
    872 
    873     /**
    874      * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
    875      * about the failure.
    876      *
    877      * WARNING: this will call back into us.  Don't hold any locks.
    878      */
    879     void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
    880         removeNotification(key);
    881         try {
    882             mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message);
    883         } catch (RemoteException ex) {
    884             // The end is nigh.
    885         }
    886     }
    887 
    888     protected StatusBarNotification removeNotificationViews(IBinder key) {
    889         NotificationData.Entry entry = mNotificationData.remove(key);
    890         if (entry == null) {
    891             Slog.w(TAG, "removeNotification for unknown key: " + key);
    892             return null;
    893         }
    894         // Remove the expanded view.
    895         ViewGroup rowParent = (ViewGroup)entry.row.getParent();
    896         if (rowParent != null) rowParent.removeView(entry.row);
    897         updateExpansionStates();
    898         updateNotificationIcons();
    899 
    900         return entry.notification;
    901     }
    902 
    903     protected StatusBarIconView addNotificationViews(IBinder key,
    904             StatusBarNotification notification) {
    905         if (DEBUG) {
    906             Slog.d(TAG, "addNotificationViews(key=" + key + ", notification=" + notification);
    907         }
    908         // Construct the icon.
    909         final StatusBarIconView iconView = new StatusBarIconView(mContext,
    910                 notification.pkg + "/0x" + Integer.toHexString(notification.id),
    911                 notification.notification);
    912         iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
    913 
    914         final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
    915                     notification.user,
    916                     notification.notification.icon,
    917                     notification.notification.iconLevel,
    918                     notification.notification.number,
    919                     notification.notification.tickerText);
    920         if (!iconView.set(ic)) {
    921             handleNotificationError(key, notification, "Couldn't create icon: " + ic);
    922             return null;
    923         }
    924         // Construct the expanded view.
    925         NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView);
    926         if (!inflateViews(entry, mPile)) {
    927             handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
    928                     + notification);
    929             return null;
    930         }
    931 
    932         // Add the expanded view and icon.
    933         int pos = mNotificationData.add(entry);
    934         if (DEBUG) {
    935             Slog.d(TAG, "addNotificationViews: added at " + pos);
    936         }
    937         updateExpansionStates();
    938         updateNotificationIcons();
    939 
    940         return iconView;
    941     }
    942 
    943     protected boolean expandView(NotificationData.Entry entry, boolean expand) {
    944         int rowHeight =
    945                 mContext.getResources().getDimensionPixelSize(R.dimen.notification_row_min_height);
    946         ViewGroup.LayoutParams lp = entry.row.getLayoutParams();
    947         if (entry.expandable() && expand) {
    948             if (DEBUG) Slog.d(TAG, "setting expanded row height to WRAP_CONTENT");
    949             lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
    950         } else {
    951             if (DEBUG) Slog.d(TAG, "setting collapsed row height to " + rowHeight);
    952             lp.height = rowHeight;
    953         }
    954         entry.row.setLayoutParams(lp);
    955         return expand;
    956     }
    957 
    958     protected void updateExpansionStates() {
    959         int N = mNotificationData.size();
    960         for (int i = 0; i < N; i++) {
    961             NotificationData.Entry entry = mNotificationData.get(i);
    962             if (!entry.userLocked()) {
    963                 if (i == (N-1)) {
    964                     if (DEBUG) Slog.d(TAG, "expanding top notification at " + i);
    965                     expandView(entry, true);
    966                 } else {
    967                     if (!entry.userExpanded()) {
    968                         if (DEBUG) Slog.d(TAG, "collapsing notification at " + i);
    969                         expandView(entry, false);
    970                     } else {
    971                         if (DEBUG) Slog.d(TAG, "ignoring user-modified notification at " + i);
    972                     }
    973                 }
    974             } else {
    975                 if (DEBUG) Slog.d(TAG, "ignoring notification being held by user at " + i);
    976             }
    977         }
    978     }
    979 
    980     protected abstract void haltTicker();
    981     protected abstract void setAreThereNotifications();
    982     protected abstract void updateNotificationIcons();
    983     protected abstract void tick(IBinder key, StatusBarNotification n, boolean firstTime);
    984     protected abstract void updateExpandedViewPos(int expandedPosition);
    985     protected abstract int getExpandedViewMaxHeight();
    986     protected abstract boolean shouldDisableNavbarGestures();
    987 
    988     protected boolean isTopNotification(ViewGroup parent, NotificationData.Entry entry) {
    989         return parent != null && parent.indexOfChild(entry.row) == 0;
    990     }
    991 
    992     public void updateNotification(IBinder key, StatusBarNotification notification) {
    993         if (DEBUG) Slog.d(TAG, "updateNotification(" + key + " -> " + notification + ")");
    994 
    995         final NotificationData.Entry oldEntry = mNotificationData.findByKey(key);
    996         if (oldEntry == null) {
    997             Slog.w(TAG, "updateNotification for unknown key: " + key);
    998             return;
    999         }
   1000 
   1001         final StatusBarNotification oldNotification = oldEntry.notification;
   1002 
   1003         // XXX: modify when we do something more intelligent with the two content views
   1004         final RemoteViews oldContentView = oldNotification.notification.contentView;
   1005         final RemoteViews contentView = notification.notification.contentView;
   1006         final RemoteViews oldBigContentView = oldNotification.notification.bigContentView;
   1007         final RemoteViews bigContentView = notification.notification.bigContentView;
   1008 
   1009         if (DEBUG) {
   1010             Slog.d(TAG, "old notification: when=" + oldNotification.notification.when
   1011                     + " ongoing=" + oldNotification.isOngoing()
   1012                     + " expanded=" + oldEntry.expanded
   1013                     + " contentView=" + oldContentView
   1014                     + " bigContentView=" + oldBigContentView
   1015                     + " rowParent=" + oldEntry.row.getParent());
   1016             Slog.d(TAG, "new notification: when=" + notification.notification.when
   1017                     + " ongoing=" + oldNotification.isOngoing()
   1018                     + " contentView=" + contentView
   1019                     + " bigContentView=" + bigContentView);
   1020         }
   1021 
   1022         // Can we just reapply the RemoteViews in place?  If when didn't change, the order
   1023         // didn't change.
   1024 
   1025         // 1U is never null
   1026         boolean contentsUnchanged = oldEntry.expanded != null
   1027                 && contentView.getPackage() != null
   1028                 && oldContentView.getPackage() != null
   1029                 && oldContentView.getPackage().equals(contentView.getPackage())
   1030                 && oldContentView.getLayoutId() == contentView.getLayoutId();
   1031         // large view may be null
   1032         boolean bigContentsUnchanged =
   1033                 (oldEntry.getLargeView() == null && bigContentView == null)
   1034                 || ((oldEntry.getLargeView() != null && bigContentView != null)
   1035                     && bigContentView.getPackage() != null
   1036                     && oldBigContentView.getPackage() != null
   1037                     && oldBigContentView.getPackage().equals(bigContentView.getPackage())
   1038                     && oldBigContentView.getLayoutId() == bigContentView.getLayoutId());
   1039         ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent();
   1040         boolean orderUnchanged = notification.notification.when==oldNotification.notification.when
   1041                 && notification.score == oldNotification.score;
   1042                 // score now encompasses/supersedes isOngoing()
   1043 
   1044         boolean updateTicker = notification.notification.tickerText != null
   1045                 && !TextUtils.equals(notification.notification.tickerText,
   1046                         oldEntry.notification.notification.tickerText);
   1047         boolean isTopAnyway = isTopNotification(rowParent, oldEntry);
   1048         if (contentsUnchanged && bigContentsUnchanged && (orderUnchanged || isTopAnyway)) {
   1049             if (DEBUG) Slog.d(TAG, "reusing notification for key: " + key);
   1050             oldEntry.notification = notification;
   1051             try {
   1052                 // Reapply the RemoteViews
   1053                 contentView.reapply(mContext, oldEntry.expanded, mOnClickHandler);
   1054                 if (bigContentView != null && oldEntry.getLargeView() != null) {
   1055                     bigContentView.reapply(mContext, oldEntry.getLargeView(), mOnClickHandler);
   1056                 }
   1057                 // update the contentIntent
   1058                 final PendingIntent contentIntent = notification.notification.contentIntent;
   1059                 if (contentIntent != null) {
   1060                     final View.OnClickListener listener = makeClicker(contentIntent,
   1061                             notification.pkg, notification.tag, notification.id);
   1062                     oldEntry.content.setOnClickListener(listener);
   1063                 } else {
   1064                     oldEntry.content.setOnClickListener(null);
   1065                 }
   1066                 // Update the icon.
   1067                 final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
   1068                         notification.user,
   1069                         notification.notification.icon, notification.notification.iconLevel,
   1070                         notification.notification.number,
   1071                         notification.notification.tickerText);
   1072                 if (!oldEntry.icon.set(ic)) {
   1073                     handleNotificationError(key, notification, "Couldn't update icon: " + ic);
   1074                     return;
   1075                 }
   1076                 updateExpansionStates();
   1077             }
   1078             catch (RuntimeException e) {
   1079                 // It failed to add cleanly.  Log, and remove the view from the panel.
   1080                 Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
   1081                 removeNotificationViews(key);
   1082                 addNotificationViews(key, notification);
   1083             }
   1084         } else {
   1085             if (DEBUG) Slog.d(TAG, "not reusing notification for key: " + key);
   1086             if (DEBUG) Slog.d(TAG, "contents was " + (contentsUnchanged ? "unchanged" : "changed"));
   1087             if (DEBUG) Slog.d(TAG, "order was " + (orderUnchanged ? "unchanged" : "changed"));
   1088             if (DEBUG) Slog.d(TAG, "notification is " + (isTopAnyway ? "top" : "not top"));
   1089             final boolean wasExpanded = oldEntry.userExpanded();
   1090             removeNotificationViews(key);
   1091             addNotificationViews(key, notification);
   1092             if (wasExpanded) {
   1093                 final NotificationData.Entry newEntry = mNotificationData.findByKey(key);
   1094                 expandView(newEntry, true);
   1095                 newEntry.setUserExpanded(true);
   1096             }
   1097         }
   1098 
   1099         // Update the veto button accordingly (and as a result, whether this row is
   1100         // swipe-dismissable)
   1101         updateNotificationVetoButton(oldEntry.row, notification);
   1102 
   1103         // Is this for you?
   1104         boolean isForCurrentUser = notificationIsForCurrentUser(notification);
   1105         if (DEBUG) Slog.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
   1106 
   1107         // Restart the ticker if it's still running
   1108         if (updateTicker && isForCurrentUser) {
   1109             haltTicker();
   1110             tick(key, notification, false);
   1111         }
   1112 
   1113         // Recalculate the position of the sliding windows and the titles.
   1114         setAreThereNotifications();
   1115         updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
   1116 
   1117         // See if we need to update the intruder.
   1118         if (ENABLE_INTRUDERS && oldNotification == mCurrentlyIntrudingNotification) {
   1119             if (DEBUG) Slog.d(TAG, "updating the current intruder:" + notification);
   1120             // XXX: this is a hack for Alarms. The real implementation will need to *update*
   1121             // the intruder.
   1122             if (notification.notification.fullScreenIntent == null) { // TODO(dsandler): consistent logic with add()
   1123                 if (DEBUG) Slog.d(TAG, "no longer intrudes!");
   1124                 mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
   1125             }
   1126         }
   1127     }
   1128 
   1129     // Q: What kinds of notifications should show during setup?
   1130     // A: Almost none! Only things coming from the system (package is "android") that also
   1131     // have special "kind" tags marking them as relevant for setup (see below).
   1132     protected boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
   1133         if ("android".equals(sbn.pkg)) {
   1134             if (sbn.notification.kind != null) {
   1135                 for (String aKind : sbn.notification.kind) {
   1136                     // IME switcher, created by InputMethodManagerService
   1137                     if ("android.system.imeswitcher".equals(aKind)) return true;
   1138                     // OTA availability & errors, created by SystemUpdateService
   1139                     if ("android.system.update".equals(aKind)) return true;
   1140                 }
   1141             }
   1142         }
   1143         return false;
   1144     }
   1145 
   1146     public boolean inKeyguardRestrictedInputMode() {
   1147         KeyguardManager km = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
   1148         return km.inKeyguardRestrictedInputMode();
   1149     }
   1150 }
   1151