Home | History | Annotate | Download | only in phone
      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.phone;
     18 
     19 
     20 import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
     21 import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
     22 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
     23 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
     24 import static android.app.StatusBarManager.windowStateToString;
     25 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
     26 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
     27 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
     28 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
     29 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
     30 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
     31 
     32 import android.animation.Animator;
     33 import android.animation.AnimatorListenerAdapter;
     34 import android.animation.TimeInterpolator;
     35 import android.annotation.NonNull;
     36 import android.app.ActivityManager;
     37 import android.app.ActivityManagerNative;
     38 import android.app.IActivityManager;
     39 import android.app.Notification;
     40 import android.app.PendingIntent;
     41 import android.app.StatusBarManager;
     42 import android.content.BroadcastReceiver;
     43 import android.content.Context;
     44 import android.content.Intent;
     45 import android.content.IntentFilter;
     46 import android.content.res.Configuration;
     47 import android.content.res.Resources;
     48 import android.database.ContentObserver;
     49 import android.graphics.Bitmap;
     50 import android.graphics.Canvas;
     51 import android.graphics.ColorFilter;
     52 import android.graphics.PixelFormat;
     53 import android.graphics.Point;
     54 import android.graphics.PorterDuff;
     55 import android.graphics.PorterDuffXfermode;
     56 import android.graphics.Rect;
     57 import android.graphics.Xfermode;
     58 import android.graphics.drawable.ColorDrawable;
     59 import android.graphics.drawable.Drawable;
     60 import android.inputmethodservice.InputMethodService;
     61 import android.media.AudioAttributes;
     62 import android.media.MediaMetadata;
     63 import android.media.session.MediaController;
     64 import android.media.session.MediaSession;
     65 import android.media.session.MediaSessionManager;
     66 import android.media.session.PlaybackState;
     67 import android.os.AsyncTask;
     68 import android.os.Bundle;
     69 import android.os.Handler;
     70 import android.os.IBinder;
     71 import android.os.Message;
     72 import android.os.PowerManager;
     73 import android.os.RemoteException;
     74 import android.os.SystemClock;
     75 import android.os.UserHandle;
     76 import android.provider.Settings;
     77 import android.service.notification.NotificationListenerService;
     78 import android.service.notification.NotificationListenerService.RankingMap;
     79 import android.service.notification.StatusBarNotification;
     80 import android.util.ArraySet;
     81 import android.util.DisplayMetrics;
     82 import android.util.EventLog;
     83 import android.util.Log;
     84 import android.view.Display;
     85 import android.view.Gravity;
     86 import android.view.HardwareCanvas;
     87 import android.view.KeyEvent;
     88 import android.view.LayoutInflater;
     89 import android.view.MotionEvent;
     90 import android.view.VelocityTracker;
     91 import android.view.View;
     92 import android.view.ViewGroup;
     93 import android.view.ViewGroup.LayoutParams;
     94 import android.view.ViewPropertyAnimator;
     95 import android.view.ViewStub;
     96 import android.view.ViewTreeObserver;
     97 import android.view.WindowManager;
     98 import android.view.accessibility.AccessibilityEvent;
     99 import android.view.accessibility.AccessibilityManager;
    100 import android.view.animation.AccelerateDecelerateInterpolator;
    101 import android.view.animation.AccelerateInterpolator;
    102 import android.view.animation.Animation;
    103 import android.view.animation.AnimationUtils;
    104 import android.view.animation.DecelerateInterpolator;
    105 import android.view.animation.Interpolator;
    106 import android.view.animation.LinearInterpolator;
    107 import android.view.animation.PathInterpolator;
    108 import android.widget.FrameLayout;
    109 import android.widget.ImageView;
    110 import android.widget.LinearLayout;
    111 import android.widget.TextView;
    112 
    113 import com.android.internal.statusbar.StatusBarIcon;
    114 import com.android.keyguard.KeyguardHostView.OnDismissAction;
    115 import com.android.keyguard.ViewMediatorCallback;
    116 import com.android.systemui.BatteryMeterView;
    117 import com.android.systemui.DemoMode;
    118 import com.android.systemui.EventLogTags;
    119 import com.android.systemui.FontSizeUtils;
    120 import com.android.systemui.R;
    121 import com.android.systemui.doze.DozeHost;
    122 import com.android.systemui.doze.DozeLog;
    123 import com.android.systemui.keyguard.KeyguardViewMediator;
    124 import com.android.systemui.qs.QSPanel;
    125 import com.android.systemui.statusbar.ActivatableNotificationView;
    126 import com.android.systemui.statusbar.BackDropView;
    127 import com.android.systemui.statusbar.BaseStatusBar;
    128 import com.android.systemui.statusbar.CommandQueue;
    129 import com.android.systemui.statusbar.DismissView;
    130 import com.android.systemui.statusbar.DragDownHelper;
    131 import com.android.systemui.statusbar.EmptyShadeView;
    132 import com.android.systemui.statusbar.ExpandableNotificationRow;
    133 import com.android.systemui.statusbar.GestureRecorder;
    134 import com.android.systemui.statusbar.KeyguardIndicationController;
    135 import com.android.systemui.statusbar.NotificationData;
    136 import com.android.systemui.statusbar.NotificationData.Entry;
    137 import com.android.systemui.statusbar.NotificationOverflowContainer;
    138 import com.android.systemui.statusbar.ScrimView;
    139 import com.android.systemui.statusbar.SignalClusterView;
    140 import com.android.systemui.statusbar.SpeedBumpView;
    141 import com.android.systemui.statusbar.StatusBarIconView;
    142 import com.android.systemui.statusbar.StatusBarState;
    143 import com.android.systemui.statusbar.policy.AccessibilityController;
    144 import com.android.systemui.statusbar.policy.BatteryController;
    145 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
    146 import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
    147 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
    148 import com.android.systemui.statusbar.policy.CastControllerImpl;
    149 import com.android.systemui.statusbar.policy.FlashlightController;
    150 import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
    151 import com.android.systemui.statusbar.policy.HotspotControllerImpl;
    152 import com.android.systemui.statusbar.policy.KeyButtonView;
    153 import com.android.systemui.statusbar.policy.KeyguardMonitor;
    154 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
    155 import com.android.systemui.statusbar.policy.LocationControllerImpl;
    156 import com.android.systemui.statusbar.policy.NetworkControllerImpl;
    157 import com.android.systemui.statusbar.policy.NextAlarmController;
    158 import com.android.systemui.statusbar.policy.PreviewInflater;
    159 import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
    160 import com.android.systemui.statusbar.policy.SecurityControllerImpl;
    161 import com.android.systemui.statusbar.policy.UserInfoController;
    162 import com.android.systemui.statusbar.policy.UserSwitcherController;
    163 import com.android.systemui.statusbar.policy.ZenModeController;
    164 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
    165 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
    166 import com.android.systemui.statusbar.stack.StackScrollAlgorithm;
    167 import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
    168 import com.android.systemui.volume.VolumeComponent;
    169 
    170 import java.io.FileDescriptor;
    171 import java.io.PrintWriter;
    172 import java.util.ArrayList;
    173 import java.util.Collection;
    174 import java.util.Collections;
    175 import java.util.List;
    176 
    177 public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
    178         DragDownHelper.DragDownCallback, ActivityStarter {
    179     static final String TAG = "PhoneStatusBar";
    180     public static final boolean DEBUG = BaseStatusBar.DEBUG;
    181     public static final boolean SPEW = false;
    182     public static final boolean DUMPTRUCK = true; // extra dumpsys info
    183     public static final boolean DEBUG_GESTURES = false;
    184     public static final boolean DEBUG_MEDIA = false;
    185     public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
    186 
    187     public static final boolean DEBUG_WINDOW_STATE = false;
    188 
    189     // additional instrumentation for testing purposes; intended to be left on during development
    190     public static final boolean CHATTY = DEBUG;
    191 
    192     public static final String ACTION_STATUSBAR_START
    193             = "com.android.internal.policy.statusbar.START";
    194 
    195     public static final boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = true;
    196 
    197     private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
    198     private static final int MSG_CLOSE_PANELS = 1001;
    199     private static final int MSG_OPEN_SETTINGS_PANEL = 1002;
    200     // 1020-1040 reserved for BaseStatusBar
    201 
    202     private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
    203 
    204     private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; // see NotificationManagerService
    205     private static final int HIDE_ICONS_BELOW_SCORE = Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
    206 
    207     private static final int STATUS_OR_NAV_TRANSIENT =
    208             View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
    209     private static final long AUTOHIDE_TIMEOUT_MS = 3000;
    210 
    211     /** The minimum delay in ms between reports of notification visibility. */
    212     private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500;
    213 
    214     /**
    215      * The delay to reset the hint text when the hint animation is finished running.
    216      */
    217     private static final int HINT_RESET_DELAY_MS = 1200;
    218 
    219     private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
    220             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
    221             .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
    222             .build();
    223 
    224     public static final int FADE_KEYGUARD_START_DELAY = 100;
    225     public static final int FADE_KEYGUARD_DURATION = 300;
    226 
    227     /** Allow some time inbetween the long press for back and recents. */
    228     private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
    229 
    230     PhoneStatusBarPolicy mIconPolicy;
    231 
    232     // These are no longer handled by the policy, because we need custom strategies for them
    233     BluetoothControllerImpl mBluetoothController;
    234     SecurityControllerImpl mSecurityController;
    235     BatteryController mBatteryController;
    236     LocationControllerImpl mLocationController;
    237     NetworkControllerImpl mNetworkController;
    238     HotspotControllerImpl mHotspotController;
    239     RotationLockControllerImpl mRotationLockController;
    240     UserInfoController mUserInfoController;
    241     ZenModeController mZenModeController;
    242     CastControllerImpl mCastController;
    243     VolumeComponent mVolumeComponent;
    244     KeyguardUserSwitcher mKeyguardUserSwitcher;
    245     FlashlightController mFlashlightController;
    246     UserSwitcherController mUserSwitcherController;
    247     NextAlarmController mNextAlarmController;
    248     KeyguardMonitor mKeyguardMonitor;
    249     BrightnessMirrorController mBrightnessMirrorController;
    250     AccessibilityController mAccessibilityController;
    251 
    252     int mNaturalBarHeight = -1;
    253     int mIconSize = -1;
    254     int mIconHPadding = -1;
    255     Display mDisplay;
    256     Point mCurrentDisplaySize = new Point();
    257 
    258     StatusBarWindowView mStatusBarWindow;
    259     PhoneStatusBarView mStatusBarView;
    260     private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
    261     private StatusBarWindowManager mStatusBarWindowManager;
    262     private UnlockMethodCache mUnlockMethodCache;
    263     private DozeServiceHost mDozeServiceHost;
    264     private boolean mScreenOnComingFromTouch;
    265 
    266     int mPixelFormat;
    267     Object mQueueLock = new Object();
    268 
    269     // viewgroup containing the normal contents of the statusbar
    270     LinearLayout mStatusBarContents;
    271 
    272     // right-hand icons
    273     LinearLayout mSystemIconArea;
    274     LinearLayout mSystemIcons;
    275 
    276     // left-hand icons
    277     LinearLayout mStatusIcons;
    278     LinearLayout mStatusIconsKeyguard;
    279 
    280     // the icons themselves
    281     IconMerger mNotificationIcons;
    282     View mNotificationIconArea;
    283 
    284     // [+>
    285     View mMoreIcon;
    286 
    287     // expanded notifications
    288     NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
    289     View mExpandedContents;
    290     int mNotificationPanelGravity;
    291     int mNotificationPanelMarginBottomPx;
    292     float mNotificationPanelMinHeightFrac;
    293     TextView mNotificationPanelDebugText;
    294 
    295     // settings
    296     View mFlipSettingsView;
    297     private QSPanel mQSPanel;
    298 
    299     // top bar
    300     StatusBarHeaderView mHeader;
    301     KeyguardStatusBarView mKeyguardStatusBar;
    302     View mKeyguardStatusView;
    303     KeyguardBottomAreaView mKeyguardBottomArea;
    304     boolean mLeaveOpenOnKeyguardHide;
    305     KeyguardIndicationController mKeyguardIndicationController;
    306 
    307     private boolean mKeyguardFadingAway;
    308     private long mKeyguardFadingAwayDelay;
    309     private long mKeyguardFadingAwayDuration;
    310 
    311     int mKeyguardMaxNotificationCount;
    312 
    313     // carrier/wifi label
    314     private TextView mCarrierLabel;
    315     private boolean mCarrierLabelVisible = false;
    316     private int mCarrierLabelHeight;
    317     private int mStatusBarHeaderHeight;
    318 
    319     private boolean mShowCarrierInPanel = false;
    320 
    321     // position
    322     int[] mPositionTmp = new int[2];
    323     boolean mExpandedVisible;
    324 
    325     private int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
    326 
    327     // the tracker view
    328     int mTrackingPosition; // the position of the top of the tracking view.
    329 
    330     // ticker
    331     private boolean mTickerEnabled;
    332     private Ticker mTicker;
    333     private View mTickerView;
    334     private boolean mTicking;
    335 
    336     // Tracking finger for opening/closing.
    337     int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
    338     boolean mTracking;
    339     VelocityTracker mVelocityTracker;
    340 
    341     int[] mAbsPos = new int[2];
    342     ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
    343 
    344     // for disabling the status bar
    345     int mDisabled = 0;
    346 
    347     // tracking calls to View.setSystemUiVisibility()
    348     int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
    349 
    350     DisplayMetrics mDisplayMetrics = new DisplayMetrics();
    351 
    352     // XXX: gesture research
    353     private final GestureRecorder mGestureRec = DEBUG_GESTURES
    354         ? new GestureRecorder("/sdcard/statusbar_gestures.dat")
    355         : null;
    356 
    357     private int mNavigationIconHints = 0;
    358 
    359     // ensure quick settings is disabled until the current user makes it through the setup wizard
    360     private boolean mUserSetup = false;
    361     private ContentObserver mUserSetupObserver = new ContentObserver(new Handler()) {
    362         @Override
    363         public void onChange(boolean selfChange) {
    364             final boolean userSetup = 0 != Settings.Secure.getIntForUser(
    365                     mContext.getContentResolver(),
    366                     Settings.Secure.USER_SETUP_COMPLETE,
    367                     0 /*default */,
    368                     mCurrentUserId);
    369             if (MULTIUSER_DEBUG) Log.d(TAG, String.format("User setup changed: " +
    370                     "selfChange=%s userSetup=%s mUserSetup=%s",
    371                     selfChange, userSetup, mUserSetup));
    372 
    373             if (userSetup != mUserSetup) {
    374                 mUserSetup = userSetup;
    375                 if (!mUserSetup && mStatusBarView != null)
    376                     animateCollapseQuickSettings();
    377             }
    378         }
    379     };
    380 
    381     final private ContentObserver mHeadsUpObserver = new ContentObserver(mHandler) {
    382         @Override
    383         public void onChange(boolean selfChange) {
    384             boolean wasUsing = mUseHeadsUp;
    385             mUseHeadsUp = ENABLE_HEADS_UP && !mDisableNotificationAlerts
    386                     && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt(
    387                     mContext.getContentResolver(), Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
    388                     Settings.Global.HEADS_UP_OFF);
    389             mHeadsUpTicker = mUseHeadsUp && 0 != Settings.Global.getInt(
    390                     mContext.getContentResolver(), SETTING_HEADS_UP_TICKER, 0);
    391             Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled"));
    392             if (wasUsing != mUseHeadsUp) {
    393                 if (!mUseHeadsUp) {
    394                     Log.d(TAG, "dismissing any existing heads up notification on disable event");
    395                     setHeadsUpVisibility(false);
    396                     mHeadsUpNotificationView.release();
    397                     removeHeadsUpView();
    398                 } else {
    399                     addHeadsUpView();
    400                 }
    401             }
    402         }
    403     };
    404 
    405     private int mInteractingWindows;
    406     private boolean mAutohideSuspended;
    407     private int mStatusBarMode;
    408     private int mNavigationBarMode;
    409     private Boolean mScreenOn;
    410 
    411     // The second field is a bit different from the first one because it only listens to screen on/
    412     // screen of events from Keyguard. We need this so we don't have a race condition with the
    413     // broadcast. In the future, we should remove the first field altogether and rename the second
    414     // field.
    415     private boolean mScreenOnFromKeyguard;
    416 
    417     private ViewMediatorCallback mKeyguardViewMediatorCallback;
    418     private ScrimController mScrimController;
    419 
    420     private final Runnable mAutohide = new Runnable() {
    421         @Override
    422         public void run() {
    423             int requested = mSystemUiVisibility & ~STATUS_OR_NAV_TRANSIENT;
    424             if (mSystemUiVisibility != requested) {
    425                 notifyUiVisibilityChanged(requested);
    426             }
    427         }};
    428 
    429     private boolean mVisible;
    430     private boolean mWaitingForKeyguardExit;
    431     private boolean mDozing;
    432     private boolean mScrimSrcModeEnabled;
    433 
    434     private Interpolator mLinearOutSlowIn;
    435     private Interpolator mLinearInterpolator = new LinearInterpolator();
    436     private Interpolator mBackdropInterpolator = new AccelerateDecelerateInterpolator();
    437     public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
    438     public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
    439 
    440     private BackDropView mBackdrop;
    441     private ImageView mBackdropFront, mBackdropBack;
    442     private PorterDuffXfermode mSrcXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
    443     private PorterDuffXfermode mSrcOverXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER);
    444 
    445     private MediaSessionManager mMediaSessionManager;
    446     private MediaController mMediaController;
    447     private String mMediaNotificationKey;
    448     private MediaMetadata mMediaMetadata;
    449     private MediaController.Callback mMediaListener
    450             = new MediaController.Callback() {
    451         @Override
    452         public void onPlaybackStateChanged(PlaybackState state) {
    453             super.onPlaybackStateChanged(state);
    454             if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onPlaybackStateChanged: " + state);
    455         }
    456 
    457         @Override
    458         public void onMetadataChanged(MediaMetadata metadata) {
    459             super.onMetadataChanged(metadata);
    460             if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
    461             mMediaMetadata = metadata;
    462             updateMediaMetaData(true);
    463         }
    464     };
    465 
    466     private final OnChildLocationsChangedListener mOnChildLocationsChangedListener =
    467             new OnChildLocationsChangedListener() {
    468         @Override
    469         public void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout) {
    470             userActivity();
    471         }
    472     };
    473 
    474     private int mDisabledUnmodified;
    475 
    476     /** Keys of notifications currently visible to the user. */
    477     private final ArraySet<String> mCurrentlyVisibleNotifications = new ArraySet<String>();
    478     private long mLastVisibilityReportUptimeMs;
    479 
    480     private final ShadeUpdates mShadeUpdates = new ShadeUpdates();
    481 
    482     private int mDrawCount;
    483     private Runnable mLaunchTransitionEndRunnable;
    484     private boolean mLaunchTransitionFadingAway;
    485     private ExpandableNotificationRow mDraggedDownRow;
    486 
    487     private static final int VISIBLE_LOCATIONS = ViewState.LOCATION_FIRST_CARD
    488             | ViewState.LOCATION_TOP_STACK_PEEKING
    489             | ViewState.LOCATION_MAIN_AREA
    490             | ViewState.LOCATION_BOTTOM_STACK_PEEKING;
    491 
    492     private final OnChildLocationsChangedListener mNotificationLocationsChangedListener =
    493             new OnChildLocationsChangedListener() {
    494                 @Override
    495                 public void onChildLocationsChanged(
    496                         NotificationStackScrollLayout stackScrollLayout) {
    497                     if (mHandler.hasCallbacks(mVisibilityReporter)) {
    498                         // Visibilities will be reported when the existing
    499                         // callback is executed.
    500                         return;
    501                     }
    502                     // Calculate when we're allowed to run the visibility
    503                     // reporter. Note that this timestamp might already have
    504                     // passed. That's OK, the callback will just be executed
    505                     // ASAP.
    506                     long nextReportUptimeMs =
    507                             mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS;
    508                     mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs);
    509                 }
    510             };
    511 
    512     // Tracks notifications currently visible in mNotificationStackScroller and
    513     // emits visibility events via NoMan on changes.
    514     private final Runnable mVisibilityReporter = new Runnable() {
    515         private final ArrayList<String> mTmpNewlyVisibleNotifications = new ArrayList<String>();
    516         private final ArrayList<String> mTmpCurrentlyVisibleNotifications = new ArrayList<String>();
    517 
    518         @Override
    519         public void run() {
    520             mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis();
    521 
    522             // 1. Loop over mNotificationData entries:
    523             //   A. Keep list of visible notifications.
    524             //   B. Keep list of previously hidden, now visible notifications.
    525             // 2. Compute no-longer visible notifications by removing currently
    526             //    visible notifications from the set of previously visible
    527             //    notifications.
    528             // 3. Report newly visible and no-longer visible notifications.
    529             // 4. Keep currently visible notifications for next report.
    530             ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
    531             int N = activeNotifications.size();
    532             for (int i = 0; i < N; i++) {
    533                 Entry entry = activeNotifications.get(i);
    534                 String key = entry.notification.getKey();
    535                 boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(key);
    536                 boolean currentlyVisible =
    537                         (mStackScroller.getChildLocation(entry.row) & VISIBLE_LOCATIONS) != 0;
    538                 if (currentlyVisible) {
    539                     // Build new set of visible notifications.
    540                     mTmpCurrentlyVisibleNotifications.add(key);
    541                 }
    542                 if (!previouslyVisible && currentlyVisible) {
    543                     mTmpNewlyVisibleNotifications.add(key);
    544                 }
    545             }
    546             ArraySet<String> noLongerVisibleNotifications = mCurrentlyVisibleNotifications;
    547             noLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications);
    548 
    549             logNotificationVisibilityChanges(
    550                     mTmpNewlyVisibleNotifications, noLongerVisibleNotifications);
    551 
    552             mCurrentlyVisibleNotifications.clear();
    553             mCurrentlyVisibleNotifications.addAll(mTmpCurrentlyVisibleNotifications);
    554 
    555             mTmpNewlyVisibleNotifications.clear();
    556             mTmpCurrentlyVisibleNotifications.clear();
    557         }
    558     };
    559 
    560     private final View.OnClickListener mOverflowClickListener = new View.OnClickListener() {
    561         @Override
    562         public void onClick(View v) {
    563             goToLockedShade(null);
    564         }
    565     };
    566 
    567     @Override
    568     public void start() {
    569         mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
    570                 .getDefaultDisplay();
    571         updateDisplaySize();
    572         mScrimSrcModeEnabled = mContext.getResources().getBoolean(
    573                 R.bool.config_status_bar_scrim_behind_use_src);
    574         super.start(); // calls createAndAddWindows()
    575 
    576         mMediaSessionManager
    577                 = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
    578         // TODO: use MediaSessionManager.SessionListener to hook us up to future updates
    579         // in session state
    580 
    581         addNavigationBar();
    582 
    583         // Lastly, call to the icon policy to install/update all the icons.
    584         mIconPolicy = new PhoneStatusBarPolicy(mContext, mCastController);
    585         mSettingsObserver.onChange(false); // set up
    586 
    587         mHeadsUpObserver.onChange(true); // set up
    588         if (ENABLE_HEADS_UP) {
    589             mContext.getContentResolver().registerContentObserver(
    590                     Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED), true,
    591                     mHeadsUpObserver);
    592             mContext.getContentResolver().registerContentObserver(
    593                     Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
    594                     mHeadsUpObserver);
    595         }
    596         mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
    597         startKeyguard();
    598 
    599         mDozeServiceHost = new DozeServiceHost();
    600         putComponent(DozeHost.class, mDozeServiceHost);
    601         putComponent(PhoneStatusBar.class, this);
    602 
    603         setControllerUsers();
    604 
    605         notifyUserAboutHiddenNotifications();
    606     }
    607 
    608     // ================================================================================
    609     // Constructing the view
    610     // ================================================================================
    611     protected PhoneStatusBarView makeStatusBarView() {
    612         final Context context = mContext;
    613 
    614         Resources res = context.getResources();
    615 
    616         updateDisplaySize(); // populates mDisplayMetrics
    617         updateResources();
    618 
    619         mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
    620 
    621         mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
    622                 R.layout.super_status_bar, null);
    623         mStatusBarWindow.mService = this;
    624         mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() {
    625             @Override
    626             public boolean onTouch(View v, MotionEvent event) {
    627                 checkUserAutohide(v, event);
    628                 if (event.getAction() == MotionEvent.ACTION_DOWN) {
    629                     if (mExpandedVisible) {
    630                         animateCollapsePanels();
    631                     }
    632                 }
    633                 return mStatusBarWindow.onTouchEvent(event);
    634             }});
    635 
    636         mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
    637         mStatusBarView.setBar(this);
    638 
    639         PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
    640         mStatusBarView.setPanelHolder(holder);
    641 
    642         mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
    643                 R.id.notification_panel);
    644         mNotificationPanel.setStatusBar(this);
    645 
    646         if (!ActivityManager.isHighEndGfx()) {
    647             mStatusBarWindow.setBackground(null);
    648             mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
    649                     R.color.notification_panel_solid_background)));
    650         }
    651         if (ENABLE_HEADS_UP) {
    652             mHeadsUpNotificationView =
    653                     (HeadsUpNotificationView) View.inflate(context, R.layout.heads_up, null);
    654             mHeadsUpNotificationView.setVisibility(View.GONE);
    655             mHeadsUpNotificationView.setBar(this);
    656         }
    657         if (MULTIUSER_DEBUG) {
    658             mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(
    659                     R.id.header_debug_info);
    660             mNotificationPanelDebugText.setVisibility(View.VISIBLE);
    661         }
    662 
    663         updateShowSearchHoldoff();
    664 
    665         try {
    666             boolean showNav = mWindowManagerService.hasNavigationBar();
    667             if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
    668             if (showNav) {
    669                 mNavigationBarView =
    670                     (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
    671 
    672                 mNavigationBarView.setDisabledFlags(mDisabled);
    673                 mNavigationBarView.setBar(this);
    674                 mNavigationBarView.setOnVerticalChangedListener(
    675                         new NavigationBarView.OnVerticalChangedListener() {
    676                     @Override
    677                     public void onVerticalChanged(boolean isVertical) {
    678                         if (mSearchPanelView != null) {
    679                             mSearchPanelView.setHorizontal(isVertical);
    680                         }
    681                         mNotificationPanel.setQsScrimEnabled(!isVertical);
    682                     }
    683                 });
    684                 mNavigationBarView.setOnTouchListener(new View.OnTouchListener() {
    685                     @Override
    686                     public boolean onTouch(View v, MotionEvent event) {
    687                         checkUserAutohide(v, event);
    688                         return false;
    689                     }});
    690             }
    691         } catch (RemoteException ex) {
    692             // no window manager? good luck with that
    693         }
    694 
    695         // figure out which pixel-format to use for the status bar.
    696         mPixelFormat = PixelFormat.OPAQUE;
    697 
    698         mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area);
    699         mSystemIcons = (LinearLayout) mStatusBarView.findViewById(R.id.system_icons);
    700         mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons);
    701         mNotificationIconArea = mStatusBarView.findViewById(R.id.notification_icon_area_inner);
    702         mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);
    703         mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon);
    704         mNotificationIcons.setOverflowIndicator(mMoreIcon);
    705         mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents);
    706 
    707         mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById(
    708                 R.id.notification_stack_scroller);
    709         mStackScroller.setLongPressListener(getNotificationLongClicker());
    710         mStackScroller.setPhoneStatusBar(this);
    711 
    712         mKeyguardIconOverflowContainer =
    713                 (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate(
    714                         R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);
    715         mKeyguardIconOverflowContainer.setOnActivatedListener(this);
    716         mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);
    717         mStackScroller.addView(mKeyguardIconOverflowContainer);
    718 
    719         SpeedBumpView speedBump = (SpeedBumpView) LayoutInflater.from(mContext).inflate(
    720                         R.layout.status_bar_notification_speed_bump, mStackScroller, false);
    721         mStackScroller.setSpeedBumpView(speedBump);
    722         mEmptyShadeView = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
    723                 R.layout.status_bar_no_notifications, mStackScroller, false);
    724         mStackScroller.setEmptyShadeView(mEmptyShadeView);
    725         mDismissView = (DismissView) LayoutInflater.from(mContext).inflate(
    726                 R.layout.status_bar_notification_dismiss_all, mStackScroller, false);
    727         mDismissView.setOnButtonClickListener(new View.OnClickListener() {
    728             @Override
    729             public void onClick(View v) {
    730                 clearAllNotifications();
    731             }
    732         });
    733         mStackScroller.setDismissView(mDismissView);
    734         mExpandedContents = mStackScroller;
    735 
    736         mBackdrop = (BackDropView) mStatusBarWindow.findViewById(R.id.backdrop);
    737         mBackdropFront = (ImageView) mBackdrop.findViewById(R.id.backdrop_front);
    738         mBackdropBack = (ImageView) mBackdrop.findViewById(R.id.backdrop_back);
    739 
    740         ScrimView scrimBehind = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_behind);
    741         ScrimView scrimInFront = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_in_front);
    742         mScrimController = new ScrimController(scrimBehind, scrimInFront, mScrimSrcModeEnabled);
    743         mScrimController.setBackDropView(mBackdrop);
    744         mStatusBarView.setScrimController(mScrimController);
    745 
    746         mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header);
    747         mHeader.setActivityStarter(this);
    748         mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header);
    749         mStatusIconsKeyguard = (LinearLayout) mKeyguardStatusBar.findViewById(R.id.statusIcons);
    750         mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
    751         mKeyguardBottomArea =
    752                 (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area);
    753         mKeyguardBottomArea.setActivityStarter(this);
    754         mKeyguardIndicationController = new KeyguardIndicationController(mContext,
    755                 (KeyguardIndicationTextView) mStatusBarWindow.findViewById(
    756                         R.id.keyguard_indication_text));
    757         mKeyguardBottomArea.setKeyguardIndicationController(mKeyguardIndicationController);
    758 
    759         mTickerEnabled = res.getBoolean(R.bool.enable_ticker);
    760         if (mTickerEnabled) {
    761             final ViewStub tickerStub = (ViewStub) mStatusBarView.findViewById(R.id.ticker_stub);
    762             if (tickerStub != null) {
    763                 mTickerView = tickerStub.inflate();
    764                 mTicker = new MyTicker(context, mStatusBarView);
    765 
    766                 TickerView tickerView = (TickerView) mStatusBarView.findViewById(R.id.tickerText);
    767                 tickerView.mTicker = mTicker;
    768             }
    769         }
    770 
    771         mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
    772 
    773         // set the inital view visibility
    774         setAreThereNotifications();
    775 
    776         // Other icons
    777         mLocationController = new LocationControllerImpl(mContext); // will post a notification
    778         mBatteryController = new BatteryController(mContext);
    779         mBatteryController.addStateChangedCallback(new BatteryStateChangeCallback() {
    780             @Override
    781             public void onPowerSaveChanged() {
    782                 mHandler.post(mCheckBarModes);
    783                 if (mDozeServiceHost != null) {
    784                     mDozeServiceHost.firePowerSaveChanged(mBatteryController.isPowerSave());
    785                 }
    786             }
    787             @Override
    788             public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
    789                 // noop
    790             }
    791         });
    792         mNetworkController = new NetworkControllerImpl(mContext);
    793         mHotspotController = new HotspotControllerImpl(mContext);
    794         mBluetoothController = new BluetoothControllerImpl(mContext);
    795         mSecurityController = new SecurityControllerImpl(mContext);
    796         if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)) {
    797             mRotationLockController = new RotationLockControllerImpl(mContext);
    798         }
    799         mUserInfoController = new UserInfoController(mContext);
    800         mVolumeComponent = getComponent(VolumeComponent.class);
    801         mZenModeController = mVolumeComponent.getZenController();
    802         mCastController = new CastControllerImpl(mContext);
    803         final SignalClusterView signalCluster =
    804                 (SignalClusterView) mStatusBarView.findViewById(R.id.signal_cluster);
    805         final SignalClusterView signalClusterKeyguard =
    806                 (SignalClusterView) mKeyguardStatusBar.findViewById(R.id.signal_cluster);
    807         final SignalClusterView signalClusterQs =
    808                 (SignalClusterView) mHeader.findViewById(R.id.signal_cluster);
    809         mNetworkController.addSignalCluster(signalCluster);
    810         mNetworkController.addSignalCluster(signalClusterKeyguard);
    811         mNetworkController.addSignalCluster(signalClusterQs);
    812         signalCluster.setSecurityController(mSecurityController);
    813         signalCluster.setNetworkController(mNetworkController);
    814         signalClusterKeyguard.setSecurityController(mSecurityController);
    815         signalClusterKeyguard.setNetworkController(mNetworkController);
    816         signalClusterQs.setSecurityController(mSecurityController);
    817         signalClusterQs.setNetworkController(mNetworkController);
    818         final boolean isAPhone = mNetworkController.hasVoiceCallingFeature();
    819         if (isAPhone) {
    820             mNetworkController.addEmergencyLabelView(mHeader);
    821         }
    822 
    823         mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
    824         mShowCarrierInPanel = (mCarrierLabel != null);
    825         if (DEBUG) Log.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" + mShowCarrierInPanel);
    826         if (mShowCarrierInPanel) {
    827             mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE);
    828 
    829             // for mobile devices, we always show mobile connection info here (SPN/PLMN)
    830             // for other devices, we show whatever network is connected
    831             if (mNetworkController.hasMobileDataFeature()) {
    832                 mNetworkController.addMobileLabelView(mCarrierLabel);
    833             } else {
    834                 mNetworkController.addCombinedLabelView(mCarrierLabel);
    835             }
    836 
    837             // set up the dynamic hide/show of the label
    838             // TODO: uncomment, handle this for the Stack scroller aswell
    839 //                ((NotificationRowLayout) mStackScroller)
    840 // .setOnSizeChangedListener(new OnSizeChangedListener() {
    841 //                @Override
    842 //                public void onSizeChanged(View view, int w, int h, int oldw, int oldh) {
    843 //                    updateCarrierLabelVisibility(false);
    844         }
    845 
    846         mFlashlightController = new FlashlightController(mContext);
    847         mKeyguardBottomArea.setFlashlightController(mFlashlightController);
    848         mKeyguardBottomArea.setPhoneStatusBar(this);
    849         mAccessibilityController = new AccessibilityController(mContext);
    850         mKeyguardBottomArea.setAccessibilityController(mAccessibilityController);
    851         mNextAlarmController = new NextAlarmController(mContext);
    852         mKeyguardMonitor = new KeyguardMonitor();
    853         mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor);
    854 
    855         mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
    856                 (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher),
    857                 mKeyguardStatusBar, mNotificationPanel, mUserSwitcherController);
    858 
    859 
    860         // Set up the quick settings tile panel
    861         mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel);
    862         if (mQSPanel != null) {
    863             final QSTileHost qsh = new QSTileHost(mContext, this,
    864                     mBluetoothController, mLocationController, mRotationLockController,
    865                     mNetworkController, mZenModeController, mHotspotController,
    866                     mCastController, mFlashlightController,
    867                     mUserSwitcherController, mKeyguardMonitor,
    868                     mSecurityController);
    869             mQSPanel.setHost(qsh);
    870             mQSPanel.setTiles(qsh.getTiles());
    871             mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow);
    872             mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
    873             mHeader.setQSPanel(mQSPanel);
    874             qsh.setCallback(new QSTileHost.Callback() {
    875                 @Override
    876                 public void onTilesChanged() {
    877                     mQSPanel.setTiles(qsh.getTiles());
    878                 }
    879             });
    880         }
    881 
    882         // User info. Trigger first load.
    883         mHeader.setUserInfoController(mUserInfoController);
    884         mKeyguardStatusBar.setUserInfoController(mUserInfoController);
    885         mUserInfoController.reloadUserInfo();
    886 
    887         mHeader.setBatteryController(mBatteryController);
    888         ((BatteryMeterView) mStatusBarView.findViewById(R.id.battery)).setBatteryController(
    889                 mBatteryController);
    890         mKeyguardStatusBar.setBatteryController(mBatteryController);
    891         mHeader.setNextAlarmController(mNextAlarmController);
    892 
    893         PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
    894         mBroadcastReceiver.onReceive(mContext,
    895                 new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
    896 
    897         // receive broadcasts
    898         IntentFilter filter = new IntentFilter();
    899         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
    900         filter.addAction(Intent.ACTION_SCREEN_OFF);
    901         filter.addAction(Intent.ACTION_SCREEN_ON);
    902         if (DEBUG_MEDIA_FAKE_ARTWORK) {
    903             filter.addAction("fake_artwork");
    904         }
    905         filter.addAction(ACTION_DEMO);
    906         context.registerReceiver(mBroadcastReceiver, filter);
    907 
    908         // listen for USER_SETUP_COMPLETE setting (per-user)
    909         resetUserSetupObserver();
    910 
    911         startGlyphRasterizeHack();
    912         return mStatusBarView;
    913     }
    914 
    915     private void clearAllNotifications() {
    916 
    917         // animate-swipe all dismissable notifications, then animate the shade closed
    918         int numChildren = mStackScroller.getChildCount();
    919 
    920         final ArrayList<View> viewsToHide = new ArrayList<View>(numChildren);
    921         for (int i = 0; i < numChildren; i++) {
    922             final View child = mStackScroller.getChildAt(i);
    923             if (mStackScroller.canChildBeDismissed(child)) {
    924                 if (child.getVisibility() == View.VISIBLE) {
    925                     viewsToHide.add(child);
    926                 }
    927             }
    928         }
    929         if (viewsToHide.isEmpty()) {
    930             animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
    931             return;
    932         }
    933 
    934         addPostCollapseAction(new Runnable() {
    935             @Override
    936             public void run() {
    937                 try {
    938                     mBarService.onClearAllNotifications(mCurrentUserId);
    939                 } catch (Exception ex) { }
    940             }
    941         });
    942 
    943         performDismissAllAnimations(viewsToHide);
    944 
    945     }
    946 
    947     private void performDismissAllAnimations(ArrayList<View> hideAnimatedList) {
    948         Runnable animationFinishAction = new Runnable() {
    949             @Override
    950             public void run() {
    951                 mStackScroller.post(new Runnable() {
    952                     @Override
    953                     public void run() {
    954                         mStackScroller.setDismissAllInProgress(false);
    955                     }
    956                 });
    957                 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
    958             }
    959         };
    960 
    961         // let's disable our normal animations
    962         mStackScroller.setDismissAllInProgress(true);
    963 
    964         // Decrease the delay for every row we animate to give the sense of
    965         // accelerating the swipes
    966         int rowDelayDecrement = 10;
    967         int currentDelay = 140;
    968         int totalDelay = 0;
    969         int numItems = hideAnimatedList.size();
    970         for (int i = 0; i < numItems; i++) {
    971             View view = hideAnimatedList.get(i);
    972             Runnable endRunnable = null;
    973             if (i == numItems - 1) {
    974                 endRunnable = animationFinishAction;
    975             }
    976             mStackScroller.dismissViewAnimated(view, endRunnable, totalDelay, 260);
    977             currentDelay = Math.max(50, currentDelay - rowDelayDecrement);
    978             totalDelay += currentDelay;
    979         }
    980     }
    981 
    982     /**
    983      * Hack to improve glyph rasterization for scaled text views.
    984      */
    985     private void startGlyphRasterizeHack() {
    986         mStatusBarView.getViewTreeObserver().addOnPreDrawListener(
    987                 new ViewTreeObserver.OnPreDrawListener() {
    988             @Override
    989             public boolean onPreDraw() {
    990                 if (mDrawCount == 1) {
    991                     mStatusBarView.getViewTreeObserver().removeOnPreDrawListener(this);
    992                     HardwareCanvas.setProperty("extraRasterBucket",
    993                             Float.toString(StackScrollAlgorithm.DIMMED_SCALE));
    994                     HardwareCanvas.setProperty("extraRasterBucket", Float.toString(
    995                             mContext.getResources().getDimensionPixelSize(
    996                                     R.dimen.qs_time_collapsed_size)
    997                             / mContext.getResources().getDimensionPixelSize(
    998                                     R.dimen.qs_time_expanded_size)));
    999                 }
   1000                 mDrawCount++;
   1001                 return true;
   1002             }
   1003         });
   1004     }
   1005 
   1006     @Override
   1007     protected void setZenMode(int mode) {
   1008         super.setZenMode(mode);
   1009         if (mIconPolicy != null) {
   1010             mIconPolicy.setZenMode(mode);
   1011         }
   1012     }
   1013 
   1014     private void startKeyguard() {
   1015         KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
   1016         mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
   1017                 mStatusBarWindow, mStatusBarWindowManager, mScrimController);
   1018         mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
   1019     }
   1020 
   1021     @Override
   1022     protected View getStatusBarView() {
   1023         return mStatusBarView;
   1024     }
   1025 
   1026     public StatusBarWindowView getStatusBarWindow() {
   1027         return mStatusBarWindow;
   1028     }
   1029 
   1030     @Override
   1031     protected WindowManager.LayoutParams getSearchLayoutParams(LayoutParams layoutParams) {
   1032         boolean opaque = false;
   1033         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
   1034                 LayoutParams.MATCH_PARENT,
   1035                 LayoutParams.MATCH_PARENT,
   1036                 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
   1037                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
   1038                 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
   1039                 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
   1040                 (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
   1041         if (ActivityManager.isHighEndGfx()) {
   1042             lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
   1043         }
   1044         lp.gravity = Gravity.BOTTOM | Gravity.START;
   1045         lp.setTitle("SearchPanel");
   1046         lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
   1047         | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
   1048         return lp;
   1049     }
   1050 
   1051     @Override
   1052     protected void updateSearchPanel() {
   1053         super.updateSearchPanel();
   1054         if (mNavigationBarView != null) {
   1055             mNavigationBarView.setDelegateView(mSearchPanelView);
   1056         }
   1057     }
   1058 
   1059     @Override
   1060     public void showSearchPanel() {
   1061         super.showSearchPanel();
   1062         mHandler.removeCallbacks(mShowSearchPanel);
   1063 
   1064         // we want to freeze the sysui state wherever it is
   1065         mSearchPanelView.setSystemUiVisibility(mSystemUiVisibility);
   1066 
   1067         if (mNavigationBarView != null) {
   1068             WindowManager.LayoutParams lp =
   1069                 (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
   1070             lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
   1071             mWindowManager.updateViewLayout(mNavigationBarView, lp);
   1072         }
   1073     }
   1074 
   1075     @Override
   1076     public void hideSearchPanel() {
   1077         super.hideSearchPanel();
   1078         if (mNavigationBarView != null) {
   1079             WindowManager.LayoutParams lp =
   1080                 (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
   1081             lp.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
   1082             mWindowManager.updateViewLayout(mNavigationBarView, lp);
   1083         }
   1084     }
   1085 
   1086     public int getStatusBarHeight() {
   1087         if (mNaturalBarHeight < 0) {
   1088             final Resources res = mContext.getResources();
   1089             mNaturalBarHeight =
   1090                     res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
   1091         }
   1092         return mNaturalBarHeight;
   1093     }
   1094 
   1095     private View.OnClickListener mRecentsClickListener = new View.OnClickListener() {
   1096         public void onClick(View v) {
   1097             awakenDreams();
   1098             toggleRecentApps();
   1099         }
   1100     };
   1101 
   1102     private long mLastLockToAppLongPress;
   1103     private View.OnLongClickListener mLongPressBackRecentsListener =
   1104             new View.OnLongClickListener() {
   1105         @Override
   1106         public boolean onLongClick(View v) {
   1107             handleLongPressBackRecents(v);
   1108             return true;
   1109         }
   1110     };
   1111 
   1112     private int mShowSearchHoldoff = 0;
   1113     private Runnable mShowSearchPanel = new Runnable() {
   1114         public void run() {
   1115             showSearchPanel();
   1116             awakenDreams();
   1117         }
   1118     };
   1119 
   1120     View.OnTouchListener mHomeActionListener = new View.OnTouchListener() {
   1121         public boolean onTouch(View v, MotionEvent event) {
   1122             switch(event.getAction()) {
   1123                 case MotionEvent.ACTION_DOWN:
   1124                 if (!shouldDisableNavbarGestures()) {
   1125                     mHandler.removeCallbacks(mShowSearchPanel);
   1126                     mHandler.postDelayed(mShowSearchPanel, mShowSearchHoldoff);
   1127                 }
   1128             break;
   1129 
   1130             case MotionEvent.ACTION_UP:
   1131             case MotionEvent.ACTION_CANCEL:
   1132                 mHandler.removeCallbacks(mShowSearchPanel);
   1133                 awakenDreams();
   1134             break;
   1135         }
   1136         return false;
   1137         }
   1138     };
   1139 
   1140     private void awakenDreams() {
   1141         if (mDreamManager != null) {
   1142             try {
   1143                 mDreamManager.awaken();
   1144             } catch (RemoteException e) {
   1145                 // fine, stay asleep then
   1146             }
   1147         }
   1148     }
   1149 
   1150     private void prepareNavigationBarView() {
   1151         mNavigationBarView.reorient();
   1152 
   1153         mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
   1154         mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
   1155         mNavigationBarView.getRecentsButton().setLongClickable(true);
   1156         mNavigationBarView.getRecentsButton().setOnLongClickListener(mLongPressBackRecentsListener);
   1157         mNavigationBarView.getBackButton().setLongClickable(true);
   1158         mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener);
   1159         mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);
   1160         updateSearchPanel();
   1161     }
   1162 
   1163     // For small-screen devices (read: phones) that lack hardware navigation buttons
   1164     private void addNavigationBar() {
   1165         if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
   1166         if (mNavigationBarView == null) return;
   1167 
   1168         prepareNavigationBarView();
   1169 
   1170         mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams());
   1171     }
   1172 
   1173     private void repositionNavigationBar() {
   1174         if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;
   1175 
   1176         prepareNavigationBarView();
   1177 
   1178         mWindowManager.updateViewLayout(mNavigationBarView, getNavigationBarLayoutParams());
   1179     }
   1180 
   1181     private void notifyNavigationBarScreenOn(boolean screenOn) {
   1182         if (mNavigationBarView == null) return;
   1183         mNavigationBarView.notifyScreenOn(screenOn);
   1184     }
   1185 
   1186     private WindowManager.LayoutParams getNavigationBarLayoutParams() {
   1187         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
   1188                 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
   1189                 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
   1190                     0
   1191                     | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
   1192                     | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
   1193                     | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
   1194                     | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
   1195                     | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
   1196                 PixelFormat.TRANSLUCENT);
   1197         // this will allow the navbar to run in an overlay on devices that support this
   1198         if (ActivityManager.isHighEndGfx()) {
   1199             lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
   1200         }
   1201 
   1202         lp.setTitle("NavigationBar");
   1203         lp.windowAnimations = 0;
   1204         return lp;
   1205     }
   1206 
   1207     private void addHeadsUpView() {
   1208         int headsUpHeight = mContext.getResources()
   1209                 .getDimensionPixelSize(R.dimen.heads_up_window_height);
   1210         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
   1211                 LayoutParams.MATCH_PARENT, headsUpHeight,
   1212                 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar!
   1213                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
   1214                     | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
   1215                     | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
   1216                     | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
   1217                     | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
   1218                     | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
   1219                 PixelFormat.TRANSLUCENT);
   1220         lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
   1221         lp.gravity = Gravity.TOP;
   1222         lp.setTitle("Heads Up");
   1223         lp.packageName = mContext.getPackageName();
   1224         lp.windowAnimations = R.style.Animation_StatusBar_HeadsUp;
   1225 
   1226         mWindowManager.addView(mHeadsUpNotificationView, lp);
   1227     }
   1228 
   1229     private void removeHeadsUpView() {
   1230         mWindowManager.removeView(mHeadsUpNotificationView);
   1231     }
   1232 
   1233     public void refreshAllStatusBarIcons() {
   1234         refreshAllIconsForLayout(mStatusIcons);
   1235         refreshAllIconsForLayout(mStatusIconsKeyguard);
   1236         refreshAllIconsForLayout(mNotificationIcons);
   1237     }
   1238 
   1239     private void refreshAllIconsForLayout(LinearLayout ll) {
   1240         final int count = ll.getChildCount();
   1241         for (int n = 0; n < count; n++) {
   1242             View child = ll.getChildAt(n);
   1243             if (child instanceof StatusBarIconView) {
   1244                 ((StatusBarIconView) child).updateDrawable();
   1245             }
   1246         }
   1247     }
   1248 
   1249     public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
   1250         if (SPEW) Log.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
   1251                 + " icon=" + icon);
   1252         StatusBarIconView view = new StatusBarIconView(mContext, slot, null);
   1253         view.set(icon);
   1254         mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(
   1255                 LayoutParams.WRAP_CONTENT, mIconSize));
   1256         view = new StatusBarIconView(mContext, slot, null);
   1257         view.set(icon);
   1258         mStatusIconsKeyguard.addView(view, viewIndex, new LinearLayout.LayoutParams(
   1259                 LayoutParams.WRAP_CONTENT, mIconSize));
   1260     }
   1261 
   1262     public void updateIcon(String slot, int index, int viewIndex,
   1263             StatusBarIcon old, StatusBarIcon icon) {
   1264         if (SPEW) Log.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
   1265                 + " old=" + old + " icon=" + icon);
   1266         StatusBarIconView view = (StatusBarIconView) mStatusIcons.getChildAt(viewIndex);
   1267         view.set(icon);
   1268         view = (StatusBarIconView) mStatusIconsKeyguard.getChildAt(viewIndex);
   1269         view.set(icon);
   1270     }
   1271 
   1272     public void removeIcon(String slot, int index, int viewIndex) {
   1273         if (SPEW) Log.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
   1274         mStatusIcons.removeViewAt(viewIndex);
   1275         mStatusIconsKeyguard.removeViewAt(viewIndex);
   1276     }
   1277 
   1278     public UserHandle getCurrentUserHandle() {
   1279         return new UserHandle(mCurrentUserId);
   1280     }
   1281 
   1282     @Override
   1283     public void addNotification(StatusBarNotification notification, RankingMap ranking) {
   1284         if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey());
   1285         if (mUseHeadsUp && shouldInterrupt(notification)) {
   1286             if (DEBUG) Log.d(TAG, "launching notification in heads up mode");
   1287             Entry interruptionCandidate = new Entry(notification, null);
   1288             ViewGroup holder = mHeadsUpNotificationView.getHolder();
   1289             if (inflateViewsForHeadsUp(interruptionCandidate, holder)) {
   1290                 // 1. Populate mHeadsUpNotificationView
   1291                 mHeadsUpNotificationView.showNotification(interruptionCandidate);
   1292 
   1293                 // do not show the notification in the shade, yet.
   1294                 return;
   1295             }
   1296         }
   1297 
   1298         Entry shadeEntry = createNotificationViews(notification);
   1299         if (shadeEntry == null) {
   1300             return;
   1301         }
   1302 
   1303         if (notification.getNotification().fullScreenIntent != null) {
   1304             // Stop screensaver if the notification has a full-screen intent.
   1305             // (like an incoming phone call)
   1306             awakenDreams();
   1307 
   1308             // not immersive & a full-screen alert should be shown
   1309             if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
   1310             try {
   1311                 notification.getNotification().fullScreenIntent.send();
   1312             } catch (PendingIntent.CanceledException e) {
   1313             }
   1314         } else {
   1315             // usual case: status bar visible & not immersive
   1316 
   1317             // show the ticker if there isn't already a heads up
   1318             if (mHeadsUpNotificationView.getEntry() == null) {
   1319                 tick(notification, true);
   1320             }
   1321         }
   1322         addNotificationViews(shadeEntry, ranking);
   1323         // Recalculate the position of the sliding windows and the titles.
   1324         setAreThereNotifications();
   1325         updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
   1326     }
   1327 
   1328     public void displayNotificationFromHeadsUp(StatusBarNotification notification) {
   1329         NotificationData.Entry shadeEntry = createNotificationViews(notification);
   1330         if (shadeEntry == null) {
   1331             return;
   1332         }
   1333         shadeEntry.setInterruption();
   1334 
   1335         addNotificationViews(shadeEntry, null);
   1336         // Recalculate the position of the sliding windows and the titles.
   1337         setAreThereNotifications();
   1338         updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
   1339     }
   1340 
   1341     @Override
   1342     public void resetHeadsUpDecayTimer() {
   1343         mHandler.removeMessages(MSG_DECAY_HEADS_UP);
   1344         if (mUseHeadsUp && mHeadsUpNotificationDecay > 0
   1345                 && mHeadsUpNotificationView.isClearable()) {
   1346             mHandler.sendEmptyMessageDelayed(MSG_DECAY_HEADS_UP, mHeadsUpNotificationDecay);
   1347         }
   1348     }
   1349 
   1350     @Override
   1351     public void scheduleHeadsUpOpen() {
   1352         mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP);
   1353     }
   1354 
   1355     @Override
   1356     public void scheduleHeadsUpClose() {
   1357         mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
   1358     }
   1359 
   1360     @Override
   1361     public void scheduleHeadsUpEscalation() {
   1362         mHandler.sendEmptyMessage(MSG_ESCALATE_HEADS_UP);
   1363     }
   1364 
   1365     @Override
   1366     protected void updateNotificationRanking(RankingMap ranking) {
   1367         mNotificationData.updateRanking(ranking);
   1368         updateNotifications();
   1369     }
   1370 
   1371     @Override
   1372     public void removeNotification(String key, RankingMap ranking) {
   1373         if (ENABLE_HEADS_UP && mHeadsUpNotificationView.getEntry() != null
   1374                 && key.equals(mHeadsUpNotificationView.getEntry().notification.getKey())) {
   1375             mHeadsUpNotificationView.clear();
   1376         }
   1377 
   1378         StatusBarNotification old = removeNotificationViews(key, ranking);
   1379         if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
   1380 
   1381         if (old != null) {
   1382             // Cancel the ticker if it's still running
   1383             if (mTickerEnabled) {
   1384                 mTicker.removeEntry(old);
   1385             }
   1386 
   1387             // Recalculate the position of the sliding windows and the titles.
   1388             updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
   1389 
   1390             if (CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications()
   1391                     && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()) {
   1392                 if (mState == StatusBarState.SHADE) {
   1393                     animateCollapsePanels();
   1394                 } else if (mState == StatusBarState.SHADE_LOCKED) {
   1395                     goToKeyguard();
   1396                 }
   1397             }
   1398         }
   1399         setAreThereNotifications();
   1400     }
   1401 
   1402     @Override
   1403     protected void refreshLayout(int layoutDirection) {
   1404         if (mNavigationBarView != null) {
   1405             mNavigationBarView.setLayoutDirection(layoutDirection);
   1406         }
   1407         refreshAllStatusBarIcons();
   1408     }
   1409 
   1410     private void updateShowSearchHoldoff() {
   1411         mShowSearchHoldoff = mContext.getResources().getInteger(
   1412             R.integer.config_show_search_delay);
   1413     }
   1414 
   1415     private void updateNotificationShade() {
   1416         if (mStackScroller == null) return;
   1417 
   1418         // Do not modify the notifications during collapse.
   1419         if (isCollapsing()) {
   1420             addPostCollapseAction(new Runnable() {
   1421                 @Override
   1422                 public void run() {
   1423                     updateNotificationShade();
   1424                 }
   1425             });
   1426             return;
   1427         }
   1428 
   1429         ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
   1430         ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
   1431         final int N = activeNotifications.size();
   1432         for (int i=0; i<N; i++) {
   1433             Entry ent = activeNotifications.get(i);
   1434             int vis = ent.notification.getNotification().visibility;
   1435 
   1436             // Display public version of the notification if we need to redact.
   1437             final boolean hideSensitive =
   1438                     !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId());
   1439             boolean sensitiveNote = vis == Notification.VISIBILITY_PRIVATE;
   1440             boolean sensitivePackage = packageHasVisibilityOverride(ent.notification.getKey());
   1441             boolean sensitive = (sensitiveNote && hideSensitive) || sensitivePackage;
   1442             boolean showingPublic = sensitive && isLockscreenPublicMode();
   1443             ent.row.setSensitive(sensitive);
   1444             if (ent.autoRedacted && ent.legacy) {
   1445                 // TODO: Also fade this? Or, maybe easier (and better), provide a dark redacted form
   1446                 // for legacy auto redacted notifications.
   1447                 if (showingPublic) {
   1448                     ent.row.setShowingLegacyBackground(false);
   1449                 } else {
   1450                     ent.row.setShowingLegacyBackground(true);
   1451                 }
   1452             }
   1453             toShow.add(ent.row);
   1454         }
   1455 
   1456         ArrayList<View> toRemove = new ArrayList<View>();
   1457         for (int i=0; i< mStackScroller.getChildCount(); i++) {
   1458             View child = mStackScroller.getChildAt(i);
   1459             if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {
   1460                 toRemove.add(child);
   1461             }
   1462         }
   1463 
   1464         for (View remove : toRemove) {
   1465             mStackScroller.removeView(remove);
   1466         }
   1467         for (int i=0; i<toShow.size(); i++) {
   1468             View v = toShow.get(i);
   1469             if (v.getParent() == null) {
   1470                 mStackScroller.addView(v);
   1471             }
   1472         }
   1473 
   1474         // So after all this work notifications still aren't sorted correctly.
   1475         // Let's do that now by advancing through toShow and mStackScroller in
   1476         // lock-step, making sure mStackScroller matches what we see in toShow.
   1477         int j = 0;
   1478         for (int i = 0; i < mStackScroller.getChildCount(); i++) {
   1479             View child = mStackScroller.getChildAt(i);
   1480             if (!(child instanceof ExpandableNotificationRow)) {
   1481                 // We don't care about non-notification views.
   1482                 continue;
   1483             }
   1484 
   1485             if (child == toShow.get(j)) {
   1486                 // Everything is well, advance both lists.
   1487                 j++;
   1488                 continue;
   1489             }
   1490 
   1491             // Oops, wrong notification at this position. Put the right one
   1492             // here and advance both lists.
   1493             mStackScroller.changeViewPosition(toShow.get(j), i);
   1494             j++;
   1495         }
   1496         updateRowStates();
   1497         updateSpeedbump();
   1498         updateClearAll();
   1499         updateEmptyShadeView();
   1500 
   1501         // Disable QS if device not provisioned.
   1502         // If the user switcher is simple then disable QS during setup because
   1503         // the user intends to use the lock screen user switcher, QS in not needed.
   1504         mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned()
   1505                 && (!mUserSwitcherController.isSimpleUserSwitcher() || mUserSetup));
   1506         mShadeUpdates.check();
   1507     }
   1508 
   1509     private boolean packageHasVisibilityOverride(String key) {
   1510         return mNotificationData.getVisibilityOverride(key)
   1511                 != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE;
   1512     }
   1513 
   1514     private void updateClearAll() {
   1515         boolean showDismissView =
   1516                 mState != StatusBarState.KEYGUARD &&
   1517                 mNotificationData.hasActiveClearableNotifications();
   1518         mStackScroller.updateDismissView(showDismissView);
   1519     }
   1520 
   1521     private void updateEmptyShadeView() {
   1522         boolean showEmptyShade =
   1523                 mState != StatusBarState.KEYGUARD &&
   1524                         mNotificationData.getActiveNotifications().size() == 0;
   1525         mNotificationPanel.setShadeEmpty(showEmptyShade);
   1526     }
   1527 
   1528     private void updateSpeedbump() {
   1529         int speedbumpIndex = -1;
   1530         int currentIndex = 0;
   1531         ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
   1532         final int N = activeNotifications.size();
   1533         for (int i = 0; i < N; i++) {
   1534             Entry entry = activeNotifications.get(i);
   1535             if (entry.row.getVisibility() != View.GONE &&
   1536                     mNotificationData.isAmbient(entry.key)) {
   1537                 speedbumpIndex = currentIndex;
   1538                 break;
   1539             }
   1540             currentIndex++;
   1541         }
   1542         mStackScroller.updateSpeedBumpIndex(speedbumpIndex);
   1543     }
   1544 
   1545     @Override
   1546     protected void updateNotifications() {
   1547         // TODO: Move this into updateNotificationIcons()?
   1548         if (mNotificationIcons == null) return;
   1549 
   1550         mNotificationData.filterAndSort();
   1551 
   1552         updateNotificationShade();
   1553         updateNotificationIcons();
   1554     }
   1555 
   1556     private void updateNotificationIcons() {
   1557         final LinearLayout.LayoutParams params
   1558             = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
   1559 
   1560         ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
   1561         final int N = activeNotifications.size();
   1562         ArrayList<StatusBarIconView> toShow = new ArrayList<>(N);
   1563 
   1564         // Filter out notifications with low scores.
   1565         for (int i = 0; i < N; i++) {
   1566             Entry ent = activeNotifications.get(i);
   1567             if (ent.notification.getScore() < HIDE_ICONS_BELOW_SCORE &&
   1568                     !NotificationData.showNotificationEvenIfUnprovisioned(ent.notification)) {
   1569                 continue;
   1570             }
   1571             toShow.add(ent.icon);
   1572         }
   1573 
   1574         if (DEBUG) {
   1575             Log.d(TAG, "refreshing icons: " + toShow.size() +
   1576                     " notifications, mNotificationIcons=" + mNotificationIcons);
   1577         }
   1578 
   1579         ArrayList<View> toRemove = new ArrayList<View>();
   1580         for (int i=0; i<mNotificationIcons.getChildCount(); i++) {
   1581             View child = mNotificationIcons.getChildAt(i);
   1582             if (!toShow.contains(child)) {
   1583                 toRemove.add(child);
   1584             }
   1585         }
   1586 
   1587         final int toRemoveCount = toRemove.size();
   1588         for (int i = 0; i < toRemoveCount; i++) {
   1589             mNotificationIcons.removeView(toRemove.get(i));
   1590         }
   1591 
   1592         for (int i=0; i<toShow.size(); i++) {
   1593             View v = toShow.get(i);
   1594             if (v.getParent() == null) {
   1595                 mNotificationIcons.addView(v, i, params);
   1596             }
   1597         }
   1598 
   1599         // Resort notification icons
   1600         final int childCount = mNotificationIcons.getChildCount();
   1601         for (int i = 0; i < childCount; i++) {
   1602             View actual = mNotificationIcons.getChildAt(i);
   1603             StatusBarIconView expected = toShow.get(i);
   1604             if (actual == expected) {
   1605                 continue;
   1606             }
   1607             mNotificationIcons.removeView(expected);
   1608             mNotificationIcons.addView(expected, i);
   1609         }
   1610     }
   1611 
   1612     @Override
   1613     protected void updateRowStates() {
   1614         super.updateRowStates();
   1615         mNotificationPanel.notifyVisibleChildrenChanged();
   1616     }
   1617 
   1618     protected void updateCarrierLabelVisibility(boolean force) {
   1619         // TODO: Handle this for the notification stack scroller as well
   1620         if (!mShowCarrierInPanel) return;
   1621         // The idea here is to only show the carrier label when there is enough room to see it,
   1622         // i.e. when there aren't enough notifications to fill the panel.
   1623         if (SPEW) {
   1624             Log.d(TAG, String.format("stackScrollerh=%d scrollh=%d carrierh=%d",
   1625                     mStackScroller.getHeight(), mStackScroller.getHeight(),
   1626                     mCarrierLabelHeight));
   1627         }
   1628 
   1629         // Emergency calls only is shown in the expanded header now.
   1630         final boolean emergencyCallsShownElsewhere = true;
   1631         final boolean makeVisible =
   1632             !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly())
   1633             && mStackScroller.getHeight() < (mNotificationPanel.getHeight()
   1634                     - mCarrierLabelHeight - mStatusBarHeaderHeight)
   1635             && mStackScroller.getVisibility() == View.VISIBLE
   1636             && mState != StatusBarState.KEYGUARD;
   1637 
   1638         if (force || mCarrierLabelVisible != makeVisible) {
   1639             mCarrierLabelVisible = makeVisible;
   1640             if (DEBUG) {
   1641                 Log.d(TAG, "making carrier label " + (makeVisible?"visible":"invisible"));
   1642             }
   1643             mCarrierLabel.animate().cancel();
   1644             if (makeVisible) {
   1645                 mCarrierLabel.setVisibility(View.VISIBLE);
   1646             }
   1647             mCarrierLabel.animate()
   1648                 .alpha(makeVisible ? 1f : 0f)
   1649                 //.setStartDelay(makeVisible ? 500 : 0)
   1650                 //.setDuration(makeVisible ? 750 : 100)
   1651                 .setDuration(150)
   1652                 .setListener(makeVisible ? null : new AnimatorListenerAdapter() {
   1653                     @Override
   1654                     public void onAnimationEnd(Animator animation) {
   1655                         if (!mCarrierLabelVisible) { // race
   1656                             mCarrierLabel.setVisibility(View.INVISIBLE);
   1657                             mCarrierLabel.setAlpha(0f);
   1658                         }
   1659                     }
   1660                 })
   1661                 .start();
   1662         }
   1663     }
   1664 
   1665     @Override
   1666     protected void setAreThereNotifications() {
   1667 
   1668         if (SPEW) {
   1669             final boolean clearable = hasActiveNotifications() &&
   1670                     mNotificationData.hasActiveClearableNotifications();
   1671             Log.d(TAG, "setAreThereNotifications: N=" +
   1672                     mNotificationData.getActiveNotifications().size() + " any=" +
   1673                     hasActiveNotifications() + " clearable=" + clearable);
   1674         }
   1675 
   1676         final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out);
   1677         final boolean showDot = hasActiveNotifications() && !areLightsOn();
   1678         if (showDot != (nlo.getAlpha() == 1.0f)) {
   1679             if (showDot) {
   1680                 nlo.setAlpha(0f);
   1681                 nlo.setVisibility(View.VISIBLE);
   1682             }
   1683             nlo.animate()
   1684                 .alpha(showDot?1:0)
   1685                 .setDuration(showDot?750:250)
   1686                 .setInterpolator(new AccelerateInterpolator(2.0f))
   1687                 .setListener(showDot ? null : new AnimatorListenerAdapter() {
   1688                     @Override
   1689                     public void onAnimationEnd(Animator _a) {
   1690                         nlo.setVisibility(View.GONE);
   1691                     }
   1692                 })
   1693                 .start();
   1694         }
   1695 
   1696         findAndUpdateMediaNotifications();
   1697 
   1698         updateCarrierLabelVisibility(false);
   1699     }
   1700 
   1701     public void findAndUpdateMediaNotifications() {
   1702         boolean metaDataChanged = false;
   1703 
   1704         synchronized (mNotificationData) {
   1705             ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
   1706             final int N = activeNotifications.size();
   1707             Entry mediaNotification = null;
   1708             MediaController controller = null;
   1709             for (int i = 0; i < N; i++) {
   1710                 final Entry entry = activeNotifications.get(i);
   1711                 if (isMediaNotification(entry)) {
   1712                     final MediaSession.Token token = entry.notification.getNotification().extras
   1713                             .getParcelable(Notification.EXTRA_MEDIA_SESSION);
   1714                     if (token != null) {
   1715                         controller = new MediaController(mContext, token);
   1716                         if (controller != null) {
   1717                             // we've got a live one, here
   1718                             mediaNotification = entry;
   1719                         }
   1720                     }
   1721                 }
   1722             }
   1723 
   1724             if (mediaNotification == null) {
   1725                 // Still nothing? OK, let's just look for live media sessions and see if they match
   1726                 // one of our notifications. This will catch apps that aren't (yet!) using media
   1727                 // notifications.
   1728 
   1729                 if (mMediaSessionManager != null) {
   1730                     final List<MediaController> sessions
   1731                             = mMediaSessionManager.getActiveSessionsForUser(
   1732                                     null,
   1733                                     UserHandle.USER_ALL);
   1734 
   1735                     for (MediaController aController : sessions) {
   1736                         if (aController == null) continue;
   1737                         final PlaybackState state = aController.getPlaybackState();
   1738                         if (state == null) continue;
   1739                         switch (state.getState()) {
   1740                             case PlaybackState.STATE_STOPPED:
   1741                             case PlaybackState.STATE_ERROR:
   1742                                 continue;
   1743                             default:
   1744                                 // now to see if we have one like this
   1745                                 final String pkg = aController.getPackageName();
   1746 
   1747                                 for (int i = 0; i < N; i++) {
   1748                                     final Entry entry = activeNotifications.get(i);
   1749                                     if (entry.notification.getPackageName().equals(pkg)) {
   1750                                         if (DEBUG_MEDIA) {
   1751                                             Log.v(TAG, "DEBUG_MEDIA: found controller matching "
   1752                                                 + entry.notification.getKey());
   1753                                         }
   1754                                         controller = aController;
   1755                                         mediaNotification = entry;
   1756                                         break;
   1757                                     }
   1758                                 }
   1759                         }
   1760                     }
   1761                 }
   1762             }
   1763 
   1764             if (!sameSessions(mMediaController, controller)) {
   1765                 // We have a new media session
   1766 
   1767                 if (mMediaController != null) {
   1768                     // something old was playing
   1769                     Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: "
   1770                             + mMediaController);
   1771                     mMediaController.unregisterCallback(mMediaListener);
   1772                 }
   1773                 mMediaController = controller;
   1774 
   1775                 if (mMediaController != null) {
   1776                     mMediaController.registerCallback(mMediaListener);
   1777                     mMediaMetadata = mMediaController.getMetadata();
   1778                     if (DEBUG_MEDIA) {
   1779                         Log.v(TAG, "DEBUG_MEDIA: insert listener, receive metadata: "
   1780                                 + mMediaMetadata);
   1781                     }
   1782 
   1783                     final String notificationKey = mediaNotification == null
   1784                             ? null
   1785                             : mediaNotification.notification.getKey();
   1786 
   1787                     if (notificationKey == null || !notificationKey.equals(mMediaNotificationKey)) {
   1788                         // we have a new notification!
   1789                         if (DEBUG_MEDIA) {
   1790                             Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key="
   1791                                     + notificationKey + " controller=" + controller);
   1792                         }
   1793                         mMediaNotificationKey = notificationKey;
   1794                     }
   1795                 } else {
   1796                     mMediaMetadata = null;
   1797                     mMediaNotificationKey = null;
   1798                 }
   1799 
   1800                 metaDataChanged = true;
   1801             } else {
   1802                 // Media session unchanged
   1803 
   1804                 if (DEBUG_MEDIA) {
   1805                     Log.v(TAG, "DEBUG_MEDIA: Continuing media notification: key=" + mMediaNotificationKey);
   1806                 }
   1807             }
   1808         }
   1809 
   1810         updateMediaMetaData(metaDataChanged);
   1811     }
   1812 
   1813     private boolean sameSessions(MediaController a, MediaController b) {
   1814         if (a == b) return true;
   1815         if (a == null) return false;
   1816         return a.controlsSameSession(b);
   1817     }
   1818 
   1819     /**
   1820      * Hide the album artwork that is fading out and release its bitmap.
   1821      */
   1822     private Runnable mHideBackdropFront = new Runnable() {
   1823         @Override
   1824         public void run() {
   1825             if (DEBUG_MEDIA) {
   1826                 Log.v(TAG, "DEBUG_MEDIA: removing fade layer");
   1827             }
   1828             mBackdropFront.setVisibility(View.INVISIBLE);
   1829             mBackdropFront.animate().cancel();
   1830             mBackdropFront.setImageDrawable(null);
   1831         }
   1832     };
   1833 
   1834     /**
   1835      * Refresh or remove lockscreen artwork from media metadata.
   1836      */
   1837     public void updateMediaMetaData(boolean metaDataChanged) {
   1838         if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) return;
   1839 
   1840         if (mBackdrop == null) return; // called too early
   1841 
   1842         if (DEBUG_MEDIA) {
   1843             Log.v(TAG, "DEBUG_MEDIA: updating album art for notification " + mMediaNotificationKey
   1844                 + " metadata=" + mMediaMetadata
   1845                 + " metaDataChanged=" + metaDataChanged
   1846                 + " state=" + mState);
   1847         }
   1848 
   1849         Bitmap artworkBitmap = null;
   1850         if (mMediaMetadata != null) {
   1851             artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
   1852             if (artworkBitmap == null) {
   1853                 artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
   1854                 // might still be null
   1855             }
   1856         }
   1857 
   1858         final boolean hasArtwork = artworkBitmap != null;
   1859 
   1860         if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
   1861                 && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
   1862             // time to show some art!
   1863             if (mBackdrop.getVisibility() != View.VISIBLE) {
   1864                 mBackdrop.setVisibility(View.VISIBLE);
   1865                 mBackdrop.animate().alpha(1f);
   1866                 metaDataChanged = true;
   1867                 if (DEBUG_MEDIA) {
   1868                     Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork");
   1869                 }
   1870             }
   1871             if (metaDataChanged) {
   1872                 if (mBackdropBack.getDrawable() != null) {
   1873                     Drawable drawable = mBackdropBack.getDrawable();
   1874                     mBackdropFront.setImageDrawable(drawable);
   1875                     if (mScrimSrcModeEnabled) {
   1876                         mBackdropFront.getDrawable().mutate().setXfermode(mSrcOverXferMode);
   1877                     }
   1878                     mBackdropFront.setAlpha(1f);
   1879                     mBackdropFront.setVisibility(View.VISIBLE);
   1880                 } else {
   1881                     mBackdropFront.setVisibility(View.INVISIBLE);
   1882                 }
   1883 
   1884                 if (DEBUG_MEDIA_FAKE_ARTWORK) {
   1885                     final int c = 0xFF000000 | (int)(Math.random() * 0xFFFFFF);
   1886                     Log.v(TAG, String.format("DEBUG_MEDIA: setting new color: 0x%08x", c));
   1887                     mBackdropBack.setBackgroundColor(0xFFFFFFFF);
   1888                     mBackdropBack.setImageDrawable(new ColorDrawable(c));
   1889                 } else {
   1890                     mBackdropBack.setImageBitmap(artworkBitmap);
   1891                 }
   1892                 if (mScrimSrcModeEnabled) {
   1893                     mBackdropBack.getDrawable().mutate().setXfermode(mSrcXferMode);
   1894                 }
   1895 
   1896                 if (mBackdropFront.getVisibility() == View.VISIBLE) {
   1897                     if (DEBUG_MEDIA) {
   1898                         Log.v(TAG, "DEBUG_MEDIA: Crossfading album artwork from "
   1899                                 + mBackdropFront.getDrawable()
   1900                                 + " to "
   1901                                 + mBackdropBack.getDrawable());
   1902                     }
   1903                     mBackdropFront.animate()
   1904                             .setDuration(250)
   1905                             .alpha(0f).withEndAction(mHideBackdropFront);
   1906                 }
   1907             }
   1908         } else {
   1909             // need to hide the album art, either because we are unlocked or because
   1910             // the metadata isn't there to support it
   1911             if (mBackdrop.getVisibility() != View.GONE) {
   1912                 if (DEBUG_MEDIA) {
   1913                     Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
   1914                 }
   1915                 mBackdrop.animate()
   1916                         .alpha(0f)
   1917                         .setInterpolator(mBackdropInterpolator)
   1918                         .setDuration(300)
   1919                         .setStartDelay(0)
   1920                         .withEndAction(new Runnable() {
   1921                             @Override
   1922                             public void run() {
   1923                                 mBackdrop.setVisibility(View.GONE);
   1924                                 mBackdropFront.animate().cancel();
   1925                                 mBackdropBack.animate().cancel();
   1926                                 mHandler.post(mHideBackdropFront);
   1927                             }
   1928                         });
   1929                 if (mKeyguardFadingAway) {
   1930                     mBackdrop.animate()
   1931 
   1932                             // Make it disappear faster, as the focus should be on the activity behind.
   1933                             .setDuration(mKeyguardFadingAwayDuration / 2)
   1934                             .setStartDelay(mKeyguardFadingAwayDelay)
   1935                             .setInterpolator(mLinearInterpolator)
   1936                             .start();
   1937                 }
   1938             }
   1939         }
   1940     }
   1941 
   1942     public void showClock(boolean show) {
   1943         if (mStatusBarView == null) return;
   1944         View clock = mStatusBarView.findViewById(R.id.clock);
   1945         if (clock != null) {
   1946             clock.setVisibility(show ? View.VISIBLE : View.GONE);
   1947         }
   1948     }
   1949 
   1950     private int adjustDisableFlags(int state) {
   1951         if (!mLaunchTransitionFadingAway
   1952                 && (mExpandedVisible || mBouncerShowing || mWaitingForKeyguardExit)) {
   1953             state |= StatusBarManager.DISABLE_NOTIFICATION_ICONS;
   1954             state |= StatusBarManager.DISABLE_SYSTEM_INFO;
   1955         }
   1956         return state;
   1957     }
   1958 
   1959     /**
   1960      * State is one or more of the DISABLE constants from StatusBarManager.
   1961      */
   1962     public void disable(int state, boolean animate) {
   1963         mDisabledUnmodified = state;
   1964         state = adjustDisableFlags(state);
   1965         final int old = mDisabled;
   1966         final int diff = state ^ old;
   1967         mDisabled = state;
   1968 
   1969         if (DEBUG) {
   1970             Log.d(TAG, String.format("disable: 0x%08x -> 0x%08x (diff: 0x%08x)",
   1971                 old, state, diff));
   1972         }
   1973 
   1974         StringBuilder flagdbg = new StringBuilder();
   1975         flagdbg.append("disable: < ");
   1976         flagdbg.append(((state & StatusBarManager.DISABLE_EXPAND) != 0) ? "EXPAND" : "expand");
   1977         flagdbg.append(((diff  & StatusBarManager.DISABLE_EXPAND) != 0) ? "* " : " ");
   1978         flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "ICONS" : "icons");
   1979         flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "* " : " ");
   1980         flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "ALERTS" : "alerts");
   1981         flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "* " : " ");
   1982         flagdbg.append(((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info");
   1983         flagdbg.append(((diff  & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " ");
   1984         flagdbg.append(((state & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back");
   1985         flagdbg.append(((diff  & StatusBarManager.DISABLE_BACK) != 0) ? "* " : " ");
   1986         flagdbg.append(((state & StatusBarManager.DISABLE_HOME) != 0) ? "HOME" : "home");
   1987         flagdbg.append(((diff  & StatusBarManager.DISABLE_HOME) != 0) ? "* " : " ");
   1988         flagdbg.append(((state & StatusBarManager.DISABLE_RECENT) != 0) ? "RECENT" : "recent");
   1989         flagdbg.append(((diff  & StatusBarManager.DISABLE_RECENT) != 0) ? "* " : " ");
   1990         flagdbg.append(((state & StatusBarManager.DISABLE_CLOCK) != 0) ? "CLOCK" : "clock");
   1991         flagdbg.append(((diff  & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " ");
   1992         flagdbg.append(((state & StatusBarManager.DISABLE_SEARCH) != 0) ? "SEARCH" : "search");
   1993         flagdbg.append(((diff  & StatusBarManager.DISABLE_SEARCH) != 0) ? "* " : " ");
   1994         flagdbg.append(">");
   1995         Log.d(TAG, flagdbg.toString());
   1996 
   1997         if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
   1998             mSystemIconArea.animate().cancel();
   1999             if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
   2000                 animateStatusBarHide(mSystemIconArea, animate);
   2001             } else {
   2002                 animateStatusBarShow(mSystemIconArea, animate);
   2003             }
   2004         }
   2005 
   2006         if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) {
   2007             boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0;
   2008             showClock(show);
   2009         }
   2010         if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
   2011             if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
   2012                 animateCollapsePanels();
   2013             }
   2014         }
   2015 
   2016         if ((diff & (StatusBarManager.DISABLE_HOME
   2017                         | StatusBarManager.DISABLE_RECENT
   2018                         | StatusBarManager.DISABLE_BACK
   2019                         | StatusBarManager.DISABLE_SEARCH)) != 0) {
   2020             // the nav bar will take care of these
   2021             if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state);
   2022 
   2023             if ((state & StatusBarManager.DISABLE_RECENT) != 0) {
   2024                 // close recents if it's visible
   2025                 mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
   2026                 mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
   2027             }
   2028         }
   2029 
   2030         if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
   2031             if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
   2032                 if (mTicking) {
   2033                     haltTicker();
   2034                 }
   2035                 animateStatusBarHide(mNotificationIconArea, animate);
   2036             } else {
   2037                 animateStatusBarShow(mNotificationIconArea, animate);
   2038             }
   2039         }
   2040 
   2041         if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
   2042             mDisableNotificationAlerts =
   2043                     (state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
   2044             mHeadsUpObserver.onChange(true);
   2045         }
   2046     }
   2047 
   2048     /**
   2049      * Animates {@code v}, a view that is part of the status bar, out.
   2050      */
   2051     private void animateStatusBarHide(final View v, boolean animate) {
   2052         v.animate().cancel();
   2053         if (!animate) {
   2054             v.setAlpha(0f);
   2055             v.setVisibility(View.INVISIBLE);
   2056             return;
   2057         }
   2058         v.animate()
   2059                 .alpha(0f)
   2060                 .setDuration(160)
   2061                 .setStartDelay(0)
   2062                 .setInterpolator(ALPHA_OUT)
   2063                 .withEndAction(new Runnable() {
   2064                     @Override
   2065                     public void run() {
   2066                         v.setVisibility(View.INVISIBLE);
   2067                     }
   2068                 });
   2069     }
   2070 
   2071     /**
   2072      * Animates {@code v}, a view that is part of the status bar, in.
   2073      */
   2074     private void animateStatusBarShow(View v, boolean animate) {
   2075         v.animate().cancel();
   2076         v.setVisibility(View.VISIBLE);
   2077         if (!animate) {
   2078             v.setAlpha(1f);
   2079             return;
   2080         }
   2081         v.animate()
   2082                 .alpha(1f)
   2083                 .setDuration(320)
   2084                 .setInterpolator(ALPHA_IN)
   2085                 .setStartDelay(50)
   2086 
   2087                 // We need to clean up any pending end action from animateStatusBarHide if we call
   2088                 // both hide and show in the same frame before the animation actually gets started.
   2089                 // cancel() doesn't really remove the end action.
   2090                 .withEndAction(null);
   2091 
   2092         // Synchronize the motion with the Keyguard fading if necessary.
   2093         if (mKeyguardFadingAway) {
   2094             v.animate()
   2095                     .setDuration(mKeyguardFadingAwayDuration)
   2096                     .setInterpolator(mLinearOutSlowIn)
   2097                     .setStartDelay(mKeyguardFadingAwayDelay)
   2098                     .start();
   2099         }
   2100     }
   2101 
   2102     @Override
   2103     protected BaseStatusBar.H createHandler() {
   2104         return new PhoneStatusBar.H();
   2105     }
   2106 
   2107     @Override
   2108     public void startActivity(Intent intent, boolean dismissShade) {
   2109         startActivityDismissingKeyguard(intent, false, dismissShade);
   2110     }
   2111 
   2112     public ScrimController getScrimController() {
   2113         return mScrimController;
   2114     }
   2115 
   2116     public void setQsExpanded(boolean expanded) {
   2117         mStatusBarWindowManager.setQsExpanded(expanded);
   2118     }
   2119 
   2120     public boolean isGoingToNotificationShade() {
   2121         return mLeaveOpenOnKeyguardHide;
   2122     }
   2123 
   2124     public boolean isQsExpanded() {
   2125         return mNotificationPanel.isQsExpanded();
   2126     }
   2127 
   2128     public boolean isScreenOnComingFromTouch() {
   2129         return mScreenOnComingFromTouch;
   2130     }
   2131 
   2132     public boolean isFalsingThresholdNeeded() {
   2133         boolean onKeyguard = getBarState() == StatusBarState.KEYGUARD;
   2134         boolean isMethodInsecure = mUnlockMethodCache.isMethodInsecure();
   2135         return onKeyguard && (isMethodInsecure || mDozing || mScreenOnComingFromTouch);
   2136     }
   2137 
   2138     public boolean isDozing() {
   2139         return mDozing;
   2140     }
   2141 
   2142     @Override  // NotificationData.Environment
   2143     public String getCurrentMediaNotificationKey() {
   2144         return mMediaNotificationKey;
   2145     }
   2146 
   2147     public boolean isScrimSrcModeEnabled() {
   2148         return mScrimSrcModeEnabled;
   2149     }
   2150 
   2151     /**
   2152      * All changes to the status bar and notifications funnel through here and are batched.
   2153      */
   2154     private class H extends BaseStatusBar.H {
   2155         public void handleMessage(Message m) {
   2156             super.handleMessage(m);
   2157             switch (m.what) {
   2158                 case MSG_OPEN_NOTIFICATION_PANEL:
   2159                     animateExpandNotificationsPanel();
   2160                     break;
   2161                 case MSG_OPEN_SETTINGS_PANEL:
   2162                     animateExpandSettingsPanel();
   2163                     break;
   2164                 case MSG_CLOSE_PANELS:
   2165                     animateCollapsePanels();
   2166                     break;
   2167                 case MSG_SHOW_HEADS_UP:
   2168                     setHeadsUpVisibility(true);
   2169                     break;
   2170                 case MSG_DECAY_HEADS_UP:
   2171                     mHeadsUpNotificationView.release();
   2172                     setHeadsUpVisibility(false);
   2173                     break;
   2174                 case MSG_HIDE_HEADS_UP:
   2175                     mHeadsUpNotificationView.release();
   2176                     setHeadsUpVisibility(false);
   2177                     break;
   2178                 case MSG_ESCALATE_HEADS_UP:
   2179                     escalateHeadsUp();
   2180                     setHeadsUpVisibility(false);
   2181                     break;
   2182             }
   2183         }
   2184     }
   2185 
   2186     /**  if the interrupting notification had a fullscreen intent, fire it now.  */
   2187     private void escalateHeadsUp() {
   2188         if (mHeadsUpNotificationView.getEntry() != null) {
   2189             final StatusBarNotification sbn = mHeadsUpNotificationView.getEntry().notification;
   2190             mHeadsUpNotificationView.release();
   2191             final Notification notification = sbn.getNotification();
   2192             if (notification.fullScreenIntent != null) {
   2193                 if (DEBUG)
   2194                     Log.d(TAG, "converting a heads up to fullScreen");
   2195                 try {
   2196                     notification.fullScreenIntent.send();
   2197                 } catch (PendingIntent.CanceledException e) {
   2198                 }
   2199             }
   2200         }
   2201     }
   2202 
   2203     View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
   2204         public void onFocusChange(View v, boolean hasFocus) {
   2205             // Because 'v' is a ViewGroup, all its children will be (un)selected
   2206             // too, which allows marqueeing to work.
   2207             v.setSelected(hasFocus);
   2208         }
   2209     };
   2210 
   2211     boolean panelsEnabled() {
   2212         return (mDisabled & StatusBarManager.DISABLE_EXPAND) == 0;
   2213     }
   2214 
   2215     void makeExpandedVisible(boolean force) {
   2216         if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
   2217         if (!force && (mExpandedVisible || !panelsEnabled())) {
   2218             return;
   2219         }
   2220 
   2221         mExpandedVisible = true;
   2222         if (mNavigationBarView != null)
   2223             mNavigationBarView.setSlippery(true);
   2224 
   2225         updateCarrierLabelVisibility(true);
   2226 
   2227         updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
   2228 
   2229         // Expand the window to encompass the full screen in anticipation of the drag.
   2230         // This is only possible to do atomically because the status bar is at the top of the screen!
   2231         mStatusBarWindowManager.setStatusBarExpanded(true);
   2232         mStatusBarView.setFocusable(false);
   2233 
   2234         visibilityChanged(true);
   2235         mWaitingForKeyguardExit = false;
   2236         disable(mDisabledUnmodified, !force /* animate */);
   2237         setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
   2238     }
   2239 
   2240     public void animateCollapsePanels() {
   2241         animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
   2242     }
   2243 
   2244     private final Runnable mAnimateCollapsePanels = new Runnable() {
   2245         @Override
   2246         public void run() {
   2247             animateCollapsePanels();
   2248         }
   2249     };
   2250 
   2251     public void postAnimateCollapsePanels() {
   2252         mHandler.post(mAnimateCollapsePanels);
   2253     }
   2254 
   2255     public void animateCollapsePanels(int flags) {
   2256         animateCollapsePanels(flags, false /* force */);
   2257     }
   2258 
   2259     public void animateCollapsePanels(int flags, boolean force) {
   2260         if (!force &&
   2261                 (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
   2262             runPostCollapseRunnables();
   2263             return;
   2264         }
   2265         if (SPEW) {
   2266             Log.d(TAG, "animateCollapse():"
   2267                     + " mExpandedVisible=" + mExpandedVisible
   2268                     + " flags=" + flags);
   2269         }
   2270 
   2271         if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
   2272             if (!mHandler.hasMessages(MSG_HIDE_RECENT_APPS)) {
   2273                 mHandler.removeMessages(MSG_HIDE_RECENT_APPS);
   2274                 mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS);
   2275             }
   2276         }
   2277 
   2278         if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) {
   2279             mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL);
   2280             mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL);
   2281         }
   2282 
   2283         if (mStatusBarWindow != null) {
   2284             // release focus immediately to kick off focus change transition
   2285             mStatusBarWindowManager.setStatusBarFocusable(false);
   2286 
   2287             mStatusBarWindow.cancelExpandHelper();
   2288             mStatusBarView.collapseAllPanels(true);
   2289         }
   2290     }
   2291 
   2292     private void runPostCollapseRunnables() {
   2293         int size = mPostCollapseRunnables.size();
   2294         for (int i = 0; i < size; i++) {
   2295             mPostCollapseRunnables.get(i).run();
   2296         }
   2297         mPostCollapseRunnables.clear();
   2298     }
   2299 
   2300     public ViewPropertyAnimator setVisibilityWhenDone(
   2301             final ViewPropertyAnimator a, final View v, final int vis) {
   2302         a.setListener(new AnimatorListenerAdapter() {
   2303             @Override
   2304             public void onAnimationEnd(Animator animation) {
   2305                 v.setVisibility(vis);
   2306                 a.setListener(null); // oneshot
   2307             }
   2308         });
   2309         return a;
   2310     }
   2311 
   2312     public Animator setVisibilityWhenDone(
   2313             final Animator a, final View v, final int vis) {
   2314         a.addListener(new AnimatorListenerAdapter() {
   2315             @Override
   2316             public void onAnimationEnd(Animator animation) {
   2317                 v.setVisibility(vis);
   2318             }
   2319         });
   2320         return a;
   2321     }
   2322 
   2323     public Animator interpolator(TimeInterpolator ti, Animator a) {
   2324         a.setInterpolator(ti);
   2325         return a;
   2326     }
   2327 
   2328     public Animator startDelay(int d, Animator a) {
   2329         a.setStartDelay(d);
   2330         return a;
   2331     }
   2332 
   2333     public Animator start(Animator a) {
   2334         a.start();
   2335         return a;
   2336     }
   2337 
   2338     final TimeInterpolator mAccelerateInterpolator = new AccelerateInterpolator();
   2339     final TimeInterpolator mDecelerateInterpolator = new DecelerateInterpolator();
   2340     final int FLIP_DURATION_OUT = 125;
   2341     final int FLIP_DURATION_IN = 225;
   2342     final int FLIP_DURATION = (FLIP_DURATION_IN + FLIP_DURATION_OUT);
   2343 
   2344     Animator mScrollViewAnim, mClearButtonAnim;
   2345 
   2346     @Override
   2347     public void animateExpandNotificationsPanel() {
   2348         if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
   2349         if (!panelsEnabled()) {
   2350             return ;
   2351         }
   2352 
   2353         mNotificationPanel.expand();
   2354 
   2355         if (false) postStartTracing();
   2356     }
   2357 
   2358     @Override
   2359     public void animateExpandSettingsPanel() {
   2360         if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
   2361         if (!panelsEnabled()) {
   2362             return;
   2363         }
   2364 
   2365         // Settings are not available in setup
   2366         if (!mUserSetup) return;
   2367 
   2368         mNotificationPanel.expand();
   2369         mNotificationPanel.openQs();
   2370 
   2371         if (false) postStartTracing();
   2372     }
   2373 
   2374     public void animateCollapseQuickSettings() {
   2375         if (mState == StatusBarState.SHADE) {
   2376             mStatusBarView.collapseAllPanels(true);
   2377         }
   2378     }
   2379 
   2380     void makeExpandedInvisible() {
   2381         if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
   2382                 + " mExpandedVisible=" + mExpandedVisible);
   2383 
   2384         if (!mExpandedVisible || mStatusBarWindow == null) {
   2385             return;
   2386         }
   2387 
   2388         // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
   2389         mStatusBarView.collapseAllPanels(/*animate=*/ false);
   2390 
   2391         // reset things to their proper state
   2392         if (mScrollViewAnim != null) mScrollViewAnim.cancel();
   2393         if (mClearButtonAnim != null) mClearButtonAnim.cancel();
   2394 
   2395         mStackScroller.setVisibility(View.VISIBLE);
   2396         mNotificationPanel.setVisibility(View.GONE);
   2397 
   2398         mNotificationPanel.closeQs();
   2399 
   2400         mExpandedVisible = false;
   2401         if (mNavigationBarView != null)
   2402             mNavigationBarView.setSlippery(false);
   2403         visibilityChanged(false);
   2404 
   2405         // Shrink the window to the size of the status bar only
   2406         mStatusBarWindowManager.setStatusBarExpanded(false);
   2407         mStatusBarView.setFocusable(true);
   2408 
   2409         // Close any "App info" popups that might have snuck on-screen
   2410         dismissPopups();
   2411 
   2412         runPostCollapseRunnables();
   2413         setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
   2414         showBouncer();
   2415         disable(mDisabledUnmodified, true /* animate */);
   2416     }
   2417 
   2418     public boolean interceptTouchEvent(MotionEvent event) {
   2419         if (DEBUG_GESTURES) {
   2420             if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
   2421                 EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH,
   2422                         event.getActionMasked(), (int) event.getX(), (int) event.getY(), mDisabled);
   2423             }
   2424 
   2425         }
   2426 
   2427         if (SPEW) {
   2428             Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
   2429                 + mDisabled + " mTracking=" + mTracking);
   2430         } else if (CHATTY) {
   2431             if (event.getAction() != MotionEvent.ACTION_MOVE) {
   2432                 Log.d(TAG, String.format(
   2433                             "panel: %s at (%f, %f) mDisabled=0x%08x",
   2434                             MotionEvent.actionToString(event.getAction()),
   2435                             event.getRawX(), event.getRawY(), mDisabled));
   2436             }
   2437         }
   2438 
   2439         if (DEBUG_GESTURES) {
   2440             mGestureRec.add(event);
   2441         }
   2442 
   2443         if (mStatusBarWindowState == WINDOW_STATE_SHOWING) {
   2444             final boolean upOrCancel =
   2445                     event.getAction() == MotionEvent.ACTION_UP ||
   2446                     event.getAction() == MotionEvent.ACTION_CANCEL;
   2447             if (upOrCancel && !mExpandedVisible) {
   2448                 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
   2449             } else {
   2450                 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
   2451             }
   2452         }
   2453         return false;
   2454     }
   2455 
   2456     public GestureRecorder getGestureRecorder() {
   2457         return mGestureRec;
   2458     }
   2459 
   2460     private void setNavigationIconHints(int hints) {
   2461         if (hints == mNavigationIconHints) return;
   2462 
   2463         mNavigationIconHints = hints;
   2464 
   2465         if (mNavigationBarView != null) {
   2466             mNavigationBarView.setNavigationIconHints(hints);
   2467         }
   2468         checkBarModes();
   2469     }
   2470 
   2471     @Override // CommandQueue
   2472     public void setWindowState(int window, int state) {
   2473         boolean showing = state == WINDOW_STATE_SHOWING;
   2474         if (mStatusBarWindow != null
   2475                 && window == StatusBarManager.WINDOW_STATUS_BAR
   2476                 && mStatusBarWindowState != state) {
   2477             mStatusBarWindowState = state;
   2478             if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state));
   2479             if (!showing && mState == StatusBarState.SHADE) {
   2480                 mStatusBarView.collapseAllPanels(false);
   2481             }
   2482         }
   2483         if (mNavigationBarView != null
   2484                 && window == StatusBarManager.WINDOW_NAVIGATION_BAR
   2485                 && mNavigationBarWindowState != state) {
   2486             mNavigationBarWindowState = state;
   2487             if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
   2488         }
   2489     }
   2490 
   2491     @Override // CommandQueue
   2492     public void buzzBeepBlinked() {
   2493         if (mDozeServiceHost != null) {
   2494             mDozeServiceHost.fireBuzzBeepBlinked();
   2495         }
   2496     }
   2497 
   2498     @Override
   2499     public void notificationLightOff() {
   2500         if (mDozeServiceHost != null) {
   2501             mDozeServiceHost.fireNotificationLight(false);
   2502         }
   2503     }
   2504 
   2505     @Override
   2506     public void notificationLightPulse(int argb, int onMillis, int offMillis) {
   2507         if (mDozeServiceHost != null) {
   2508             mDozeServiceHost.fireNotificationLight(true);
   2509         }
   2510     }
   2511 
   2512     @Override // CommandQueue
   2513     public void setSystemUiVisibility(int vis, int mask) {
   2514         final int oldVal = mSystemUiVisibility;
   2515         final int newVal = (oldVal&~mask) | (vis&mask);
   2516         final int diff = newVal ^ oldVal;
   2517         if (DEBUG) Log.d(TAG, String.format(
   2518                 "setSystemUiVisibility vis=%s mask=%s oldVal=%s newVal=%s diff=%s",
   2519                 Integer.toHexString(vis), Integer.toHexString(mask),
   2520                 Integer.toHexString(oldVal), Integer.toHexString(newVal),
   2521                 Integer.toHexString(diff)));
   2522         if (diff != 0) {
   2523             // we never set the recents bit via this method, so save the prior state to prevent
   2524             // clobbering the bit below
   2525             final boolean wasRecentsVisible = (mSystemUiVisibility & View.RECENT_APPS_VISIBLE) > 0;
   2526 
   2527             mSystemUiVisibility = newVal;
   2528 
   2529             // update low profile
   2530             if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
   2531                 final boolean lightsOut = (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0;
   2532                 if (lightsOut) {
   2533                     animateCollapsePanels();
   2534                     if (mTicking) {
   2535                         haltTicker();
   2536                     }
   2537                 }
   2538 
   2539                 setAreThereNotifications();
   2540             }
   2541 
   2542             // update status bar mode
   2543             final int sbMode = computeBarMode(oldVal, newVal, mStatusBarView.getBarTransitions(),
   2544                     View.STATUS_BAR_TRANSIENT, View.STATUS_BAR_TRANSLUCENT);
   2545 
   2546             // update navigation bar mode
   2547             final int nbMode = mNavigationBarView == null ? -1 : computeBarMode(
   2548                     oldVal, newVal, mNavigationBarView.getBarTransitions(),
   2549                     View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT);
   2550             final boolean sbModeChanged = sbMode != -1;
   2551             final boolean nbModeChanged = nbMode != -1;
   2552             boolean checkBarModes = false;
   2553             if (sbModeChanged && sbMode != mStatusBarMode) {
   2554                 mStatusBarMode = sbMode;
   2555                 checkBarModes = true;
   2556             }
   2557             if (nbModeChanged && nbMode != mNavigationBarMode) {
   2558                 mNavigationBarMode = nbMode;
   2559                 checkBarModes = true;
   2560             }
   2561             if (checkBarModes) {
   2562                 checkBarModes();
   2563             }
   2564             if (sbModeChanged || nbModeChanged) {
   2565                 // update transient bar autohide
   2566                 if (mStatusBarMode == MODE_SEMI_TRANSPARENT || mNavigationBarMode == MODE_SEMI_TRANSPARENT) {
   2567                     scheduleAutohide();
   2568                 } else {
   2569                     cancelAutohide();
   2570                 }
   2571             }
   2572 
   2573             // ready to unhide
   2574             if ((vis & View.STATUS_BAR_UNHIDE) != 0) {
   2575                 mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
   2576             }
   2577             if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) {
   2578                 mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
   2579             }
   2580 
   2581             // restore the recents bit
   2582             if (wasRecentsVisible) {
   2583                 mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
   2584             }
   2585 
   2586             // send updated sysui visibility to window manager
   2587             notifyUiVisibilityChanged(mSystemUiVisibility);
   2588         }
   2589     }
   2590 
   2591     private int computeBarMode(int oldVis, int newVis, BarTransitions transitions,
   2592             int transientFlag, int translucentFlag) {
   2593         final int oldMode = barMode(oldVis, transientFlag, translucentFlag);
   2594         final int newMode = barMode(newVis, transientFlag, translucentFlag);
   2595         if (oldMode == newMode) {
   2596             return -1; // no mode change
   2597         }
   2598         return newMode;
   2599     }
   2600 
   2601     private int barMode(int vis, int transientFlag, int translucentFlag) {
   2602         return (vis & transientFlag) != 0 ? MODE_SEMI_TRANSPARENT
   2603                 : (vis & translucentFlag) != 0 ? MODE_TRANSLUCENT
   2604                 : (vis & View.SYSTEM_UI_TRANSPARENT) != 0 ? MODE_TRANSPARENT
   2605                 : (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0 ? MODE_LIGHTS_OUT
   2606                 : MODE_OPAQUE;
   2607     }
   2608 
   2609     private void checkBarModes() {
   2610         if (mDemoMode) return;
   2611         checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarView.getBarTransitions());
   2612         if (mNavigationBarView != null) {
   2613             checkBarMode(mNavigationBarMode,
   2614                     mNavigationBarWindowState, mNavigationBarView.getBarTransitions());
   2615         }
   2616     }
   2617 
   2618     private void checkBarMode(int mode, int windowState, BarTransitions transitions) {
   2619         final boolean powerSave = mBatteryController.isPowerSave();
   2620         final boolean anim = (mScreenOn == null || mScreenOn) && windowState != WINDOW_STATE_HIDDEN
   2621                 && !powerSave;
   2622         if (powerSave && getBarState() == StatusBarState.SHADE) {
   2623             mode = MODE_WARNING;
   2624         }
   2625         transitions.transitionTo(mode, anim);
   2626     }
   2627 
   2628     private void finishBarAnimations() {
   2629         mStatusBarView.getBarTransitions().finishAnimations();
   2630         if (mNavigationBarView != null) {
   2631             mNavigationBarView.getBarTransitions().finishAnimations();
   2632         }
   2633     }
   2634 
   2635     private final Runnable mCheckBarModes = new Runnable() {
   2636         @Override
   2637         public void run() {
   2638             checkBarModes();
   2639         }
   2640     };
   2641 
   2642     @Override
   2643     public void setInteracting(int barWindow, boolean interacting) {
   2644         mInteractingWindows = interacting
   2645                 ? (mInteractingWindows | barWindow)
   2646                 : (mInteractingWindows & ~barWindow);
   2647         if (mInteractingWindows != 0) {
   2648             suspendAutohide();
   2649         } else {
   2650             resumeSuspendedAutohide();
   2651         }
   2652         checkBarModes();
   2653     }
   2654 
   2655     private void resumeSuspendedAutohide() {
   2656         if (mAutohideSuspended) {
   2657             scheduleAutohide();
   2658             mHandler.postDelayed(mCheckBarModes, 500); // longer than home -> launcher
   2659         }
   2660     }
   2661 
   2662     private void suspendAutohide() {
   2663         mHandler.removeCallbacks(mAutohide);
   2664         mHandler.removeCallbacks(mCheckBarModes);
   2665         mAutohideSuspended = (mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0;
   2666     }
   2667 
   2668     private void cancelAutohide() {
   2669         mAutohideSuspended = false;
   2670         mHandler.removeCallbacks(mAutohide);
   2671     }
   2672 
   2673     private void scheduleAutohide() {
   2674         cancelAutohide();
   2675         mHandler.postDelayed(mAutohide, AUTOHIDE_TIMEOUT_MS);
   2676     }
   2677 
   2678     private void checkUserAutohide(View v, MotionEvent event) {
   2679         if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0  // a transient bar is revealed
   2680                 && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar
   2681                 && event.getX() == 0 && event.getY() == 0  // a touch outside both bars
   2682                 ) {
   2683             userAutohide();
   2684         }
   2685     }
   2686 
   2687     private void userAutohide() {
   2688         cancelAutohide();
   2689         mHandler.postDelayed(mAutohide, 350); // longer than app gesture -> flag clear
   2690     }
   2691 
   2692     private boolean areLightsOn() {
   2693         return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
   2694     }
   2695 
   2696     public void setLightsOn(boolean on) {
   2697         Log.v(TAG, "setLightsOn(" + on + ")");
   2698         if (on) {
   2699             setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
   2700         } else {
   2701             setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE);
   2702         }
   2703     }
   2704 
   2705     private void notifyUiVisibilityChanged(int vis) {
   2706         try {
   2707             mWindowManagerService.statusBarVisibilityChanged(vis);
   2708         } catch (RemoteException ex) {
   2709         }
   2710     }
   2711 
   2712     public void topAppWindowChanged(boolean showMenu) {
   2713         if (DEBUG) {
   2714             Log.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
   2715         }
   2716         if (mNavigationBarView != null) {
   2717             mNavigationBarView.setMenuVisibility(showMenu);
   2718         }
   2719 
   2720         // See above re: lights-out policy for legacy apps.
   2721         if (showMenu) setLightsOn(true);
   2722     }
   2723 
   2724     @Override
   2725     public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
   2726             boolean showImeSwitcher) {
   2727         boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
   2728         int flags = mNavigationIconHints;
   2729         if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) {
   2730             flags |= NAVIGATION_HINT_BACK_ALT;
   2731         } else {
   2732             flags &= ~NAVIGATION_HINT_BACK_ALT;
   2733         }
   2734         if (showImeSwitcher) {
   2735             flags |= NAVIGATION_HINT_IME_SHOWN;
   2736         } else {
   2737             flags &= ~NAVIGATION_HINT_IME_SHOWN;
   2738         }
   2739 
   2740         setNavigationIconHints(flags);
   2741     }
   2742 
   2743     @Override
   2744     protected void tick(StatusBarNotification n, boolean firstTime) {
   2745         if (!mTickerEnabled) return;
   2746 
   2747         // no ticking in lights-out mode
   2748         if (!areLightsOn()) return;
   2749 
   2750         // no ticking in Setup
   2751         if (!isDeviceProvisioned()) return;
   2752 
   2753         // not for you
   2754         if (!isNotificationForCurrentProfiles(n)) return;
   2755 
   2756         // Show the ticker if one is requested. Also don't do this
   2757         // until status bar window is attached to the window manager,
   2758         // because...  well, what's the point otherwise?  And trying to
   2759         // run a ticker without being attached will crash!
   2760         if (n.getNotification().tickerText != null && mStatusBarWindow != null
   2761                 && mStatusBarWindow.getWindowToken() != null) {
   2762             if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
   2763                     | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
   2764                 mTicker.addEntry(n);
   2765             }
   2766         }
   2767     }
   2768 
   2769     private class MyTicker extends Ticker {
   2770         MyTicker(Context context, View sb) {
   2771             super(context, sb);
   2772             if (!mTickerEnabled) {
   2773                 Log.w(TAG, "MyTicker instantiated with mTickerEnabled=false", new Throwable());
   2774             }
   2775         }
   2776 
   2777         @Override
   2778         public void tickerStarting() {
   2779             if (!mTickerEnabled) return;
   2780             mTicking = true;
   2781             mStatusBarContents.setVisibility(View.GONE);
   2782             mTickerView.setVisibility(View.VISIBLE);
   2783             mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
   2784             mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
   2785         }
   2786 
   2787         @Override
   2788         public void tickerDone() {
   2789             if (!mTickerEnabled) return;
   2790             mStatusBarContents.setVisibility(View.VISIBLE);
   2791             mTickerView.setVisibility(View.GONE);
   2792             mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
   2793             mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
   2794                         mTickingDoneListener));
   2795         }
   2796 
   2797         public void tickerHalting() {
   2798             if (!mTickerEnabled) return;
   2799             if (mStatusBarContents.getVisibility() != View.VISIBLE) {
   2800                 mStatusBarContents.setVisibility(View.VISIBLE);
   2801                 mStatusBarContents
   2802                         .startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
   2803             }
   2804             mTickerView.setVisibility(View.GONE);
   2805             // we do not animate the ticker away at this point, just get rid of it (b/6992707)
   2806         }
   2807     }
   2808 
   2809     Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
   2810         public void onAnimationEnd(Animation animation) {
   2811             mTicking = false;
   2812         }
   2813         public void onAnimationRepeat(Animation animation) {
   2814         }
   2815         public void onAnimationStart(Animation animation) {
   2816         }
   2817     };
   2818 
   2819     private Animation loadAnim(int id, Animation.AnimationListener listener) {
   2820         Animation anim = AnimationUtils.loadAnimation(mContext, id);
   2821         if (listener != null) {
   2822             anim.setAnimationListener(listener);
   2823         }
   2824         return anim;
   2825     }
   2826 
   2827     public static String viewInfo(View v) {
   2828         return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
   2829                 + ") " + v.getWidth() + "x" + v.getHeight() + "]";
   2830     }
   2831 
   2832     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   2833         synchronized (mQueueLock) {
   2834             pw.println("Current Status Bar state:");
   2835             pw.println("  mExpandedVisible=" + mExpandedVisible
   2836                     + ", mTrackingPosition=" + mTrackingPosition);
   2837             pw.println("  mTickerEnabled=" + mTickerEnabled);
   2838             if (mTickerEnabled) {
   2839                 pw.println("  mTicking=" + mTicking);
   2840                 pw.println("  mTickerView: " + viewInfo(mTickerView));
   2841             }
   2842             pw.println("  mTracking=" + mTracking);
   2843             pw.println("  mDisplayMetrics=" + mDisplayMetrics);
   2844             pw.println("  mStackScroller: " + viewInfo(mStackScroller));
   2845             pw.println("  mStackScroller: " + viewInfo(mStackScroller)
   2846                     + " scroll " + mStackScroller.getScrollX()
   2847                     + "," + mStackScroller.getScrollY());
   2848         }
   2849 
   2850         pw.print("  mInteractingWindows="); pw.println(mInteractingWindows);
   2851         pw.print("  mStatusBarWindowState=");
   2852         pw.println(windowStateToString(mStatusBarWindowState));
   2853         pw.print("  mStatusBarMode=");
   2854         pw.println(BarTransitions.modeToString(mStatusBarMode));
   2855         pw.print("  mDozing="); pw.println(mDozing);
   2856         pw.print("  mZenMode=");
   2857         pw.println(Settings.Global.zenModeToString(mZenMode));
   2858         pw.print("  mUseHeadsUp=");
   2859         pw.println(mUseHeadsUp);
   2860         pw.print("  interrupting package: ");
   2861         pw.println(hunStateToString(mHeadsUpNotificationView.getEntry()));
   2862         dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
   2863         if (mNavigationBarView != null) {
   2864             pw.print("  mNavigationBarWindowState=");
   2865             pw.println(windowStateToString(mNavigationBarWindowState));
   2866             pw.print("  mNavigationBarMode=");
   2867             pw.println(BarTransitions.modeToString(mNavigationBarMode));
   2868             dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
   2869         }
   2870 
   2871         pw.print("  mNavigationBarView=");
   2872         if (mNavigationBarView == null) {
   2873             pw.println("null");
   2874         } else {
   2875             mNavigationBarView.dump(fd, pw, args);
   2876         }
   2877 
   2878         pw.print("  mMediaSessionManager=");
   2879         pw.println(mMediaSessionManager);
   2880         pw.print("  mMediaNotificationKey=");
   2881         pw.println(mMediaNotificationKey);
   2882         pw.print("  mMediaController=");
   2883         pw.print(mMediaController);
   2884         if (mMediaController != null) {
   2885             pw.print(" state=" + mMediaController.getPlaybackState());
   2886         }
   2887         pw.println();
   2888         pw.print("  mMediaMetadata=");
   2889         pw.print(mMediaMetadata);
   2890         if (mMediaMetadata != null) {
   2891             pw.print(" title=" + mMediaMetadata.getText(MediaMetadata.METADATA_KEY_TITLE));
   2892         }
   2893         pw.println();
   2894 
   2895         pw.println("  Panels: ");
   2896         if (mNotificationPanel != null) {
   2897             pw.println("    mNotificationPanel=" +
   2898                 mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug(""));
   2899             pw.print  ("      ");
   2900             mNotificationPanel.dump(fd, pw, args);
   2901         }
   2902 
   2903         DozeLog.dump(pw);
   2904 
   2905         if (DUMPTRUCK) {
   2906             synchronized (mNotificationData) {
   2907                 mNotificationData.dump(pw, "  ");
   2908             }
   2909 
   2910             int N = mStatusIcons.getChildCount();
   2911             pw.println("  system icons: " + N);
   2912             for (int i=0; i<N; i++) {
   2913                 StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i);
   2914                 pw.println("    [" + i + "] icon=" + ic);
   2915             }
   2916 
   2917             if (false) {
   2918                 pw.println("see the logcat for a dump of the views we have created.");
   2919                 // must happen on ui thread
   2920                 mHandler.post(new Runnable() {
   2921                         public void run() {
   2922                             mStatusBarView.getLocationOnScreen(mAbsPos);
   2923                             Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
   2924                                     + ") " + mStatusBarView.getWidth() + "x"
   2925                                     + getStatusBarHeight());
   2926                             mStatusBarView.debug();
   2927                         }
   2928                     });
   2929             }
   2930         }
   2931 
   2932         if (DEBUG_GESTURES) {
   2933             pw.print("  status bar gestures: ");
   2934             mGestureRec.dump(fd, pw, args);
   2935         }
   2936 
   2937         if (mNetworkController != null) {
   2938             mNetworkController.dump(fd, pw, args);
   2939         }
   2940         if (mBluetoothController != null) {
   2941             mBluetoothController.dump(fd, pw, args);
   2942         }
   2943         if (mCastController != null) {
   2944             mCastController.dump(fd, pw, args);
   2945         }
   2946         if (mUserSwitcherController != null) {
   2947             mUserSwitcherController.dump(fd, pw, args);
   2948         }
   2949         if (mBatteryController != null) {
   2950             mBatteryController.dump(fd, pw, args);
   2951         }
   2952         if (mNextAlarmController != null) {
   2953             mNextAlarmController.dump(fd, pw, args);
   2954         }
   2955         if (mSecurityController != null) {
   2956             mSecurityController.dump(fd, pw, args);
   2957         }
   2958     }
   2959 
   2960     private String hunStateToString(Entry entry) {
   2961         if (entry == null) return "null";
   2962         if (entry.notification == null) return "corrupt";
   2963         return entry.notification.getPackageName();
   2964     }
   2965 
   2966     private static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) {
   2967         pw.print("  "); pw.print(var); pw.print(".BarTransitions.mMode=");
   2968         pw.println(BarTransitions.modeToString(transitions.getMode()));
   2969     }
   2970 
   2971     @Override
   2972     public void createAndAddWindows() {
   2973         addStatusBarWindow();
   2974     }
   2975 
   2976     private void addStatusBarWindow() {
   2977         makeStatusBarView();
   2978         mStatusBarWindowManager = new StatusBarWindowManager(mContext);
   2979         mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
   2980     }
   2981 
   2982     static final float saturate(float a) {
   2983         return a < 0f ? 0f : (a > 1f ? 1f : a);
   2984     }
   2985 
   2986     @Override
   2987     public void updateExpandedViewPos(int thingy) {
   2988         if (SPEW) Log.v(TAG, "updateExpandedViewPos");
   2989 
   2990         // on larger devices, the notification panel is propped open a bit
   2991         mNotificationPanel.setMinimumHeight(
   2992                 (int)(mNotificationPanelMinHeightFrac * mCurrentDisplaySize.y));
   2993 
   2994         FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mNotificationPanel.getLayoutParams();
   2995         lp.gravity = mNotificationPanelGravity;
   2996         mNotificationPanel.setLayoutParams(lp);
   2997 
   2998         updateCarrierLabelVisibility(false);
   2999     }
   3000 
   3001     // called by makeStatusbar and also by PhoneStatusBarView
   3002     void updateDisplaySize() {
   3003         mDisplay.getMetrics(mDisplayMetrics);
   3004         mDisplay.getSize(mCurrentDisplaySize);
   3005         if (DEBUG_GESTURES) {
   3006             mGestureRec.tag("display",
   3007                     String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
   3008         }
   3009     }
   3010 
   3011     public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
   3012             final boolean dismissShade) {
   3013         if (onlyProvisioned && !isDeviceProvisioned()) return;
   3014 
   3015         final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
   3016                 mContext, intent, mCurrentUserId);
   3017         final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing();
   3018         dismissKeyguardThenExecute(new OnDismissAction() {
   3019             @Override
   3020             public boolean onDismiss() {
   3021                 AsyncTask.execute(new Runnable() {
   3022                     public void run() {
   3023                         try {
   3024                             if (keyguardShowing && !afterKeyguardGone) {
   3025                                 ActivityManagerNative.getDefault()
   3026                                         .keyguardWaitingForActivityDrawn();
   3027                             }
   3028                             intent.setFlags(
   3029                                     Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
   3030                             mContext.startActivityAsUser(
   3031                                     intent, new UserHandle(UserHandle.USER_CURRENT));
   3032                             overrideActivityPendingAppTransition(
   3033                                     keyguardShowing && !afterKeyguardGone);
   3034                         } catch (RemoteException e) {
   3035                         }
   3036                     }
   3037                 });
   3038                 if (dismissShade) {
   3039                     animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
   3040                 }
   3041                 return true;
   3042             }
   3043         }, afterKeyguardGone);
   3044     }
   3045 
   3046     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
   3047         public void onReceive(Context context, Intent intent) {
   3048             if (DEBUG) Log.v(TAG, "onReceive: " + intent);
   3049             String action = intent.getAction();
   3050             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
   3051                 int flags = CommandQueue.FLAG_EXCLUDE_NONE;
   3052                 String reason = intent.getStringExtra("reason");
   3053                 if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
   3054                     flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
   3055                 }
   3056                 animateCollapsePanels(flags);
   3057             }
   3058             else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
   3059                 mScreenOn = false;
   3060                 notifyNavigationBarScreenOn(false);
   3061                 notifyHeadsUpScreenOn(false);
   3062                 finishBarAnimations();
   3063                 stopNotificationLogging();
   3064                 resetUserExpandedStates();
   3065             }
   3066             else if (Intent.ACTION_SCREEN_ON.equals(action)) {
   3067                 mScreenOn = true;
   3068                 // work around problem where mDisplay.getRotation() is not stable while screen is off (bug 7086018)
   3069                 repositionNavigationBar();
   3070                 notifyNavigationBarScreenOn(true);
   3071                 startNotificationLoggingIfScreenOnAndVisible();
   3072             }
   3073             else if (ACTION_DEMO.equals(action)) {
   3074                 Bundle bundle = intent.getExtras();
   3075                 if (bundle != null) {
   3076                     String command = bundle.getString("command", "").trim().toLowerCase();
   3077                     if (command.length() > 0) {
   3078                         try {
   3079                             dispatchDemoCommand(command, bundle);
   3080                         } catch (Throwable t) {
   3081                             Log.w(TAG, "Error running demo command, intent=" + intent, t);
   3082                         }
   3083                     }
   3084                 }
   3085             } else if ("fake_artwork".equals(action)) {
   3086                 if (DEBUG_MEDIA_FAKE_ARTWORK) {
   3087                     updateMediaMetaData(true);
   3088                 }
   3089             }
   3090         }
   3091     };
   3092 
   3093     private void resetUserExpandedStates() {
   3094         ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
   3095         final int notificationCount = activeNotifications.size();
   3096         for (int i = 0; i < notificationCount; i++) {
   3097             NotificationData.Entry entry = activeNotifications.get(i);
   3098             if (entry.row != null) {
   3099                 entry.row.resetUserExpansion();
   3100             }
   3101         }
   3102     }
   3103 
   3104     @Override
   3105     protected void dismissKeyguardThenExecute(final OnDismissAction action,
   3106             boolean afterKeyguardGone) {
   3107         if (mStatusBarKeyguardViewManager.isShowing()) {
   3108             if (UnlockMethodCache.getInstance(mContext).isMethodInsecure()
   3109                     && mNotificationPanel.isLaunchTransitionRunning() && !afterKeyguardGone) {
   3110                 action.onDismiss();
   3111                 mNotificationPanel.setLaunchTransitionEndRunnable(new Runnable() {
   3112                     @Override
   3113                     public void run() {
   3114                         mStatusBarKeyguardViewManager.dismiss();
   3115                     }
   3116                 });
   3117             } else {
   3118                 mStatusBarKeyguardViewManager.dismissWithAction(action, afterKeyguardGone);
   3119             }
   3120         } else {
   3121             action.onDismiss();
   3122         }
   3123     }
   3124 
   3125     // SystemUIService notifies SystemBars of configuration changes, which then calls down here
   3126     @Override
   3127     protected void onConfigurationChanged(Configuration newConfig) {
   3128         super.onConfigurationChanged(newConfig); // calls refreshLayout
   3129 
   3130         if (DEBUG) {
   3131             Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
   3132         }
   3133         updateDisplaySize(); // populates mDisplayMetrics
   3134 
   3135         updateResources();
   3136         updateClockSize();
   3137         repositionNavigationBar();
   3138         updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
   3139         updateShowSearchHoldoff();
   3140         updateRowStates();
   3141     }
   3142 
   3143     @Override
   3144     public void userSwitched(int newUserId) {
   3145         if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
   3146         animateCollapsePanels();
   3147         updateNotifications();
   3148         resetUserSetupObserver();
   3149         setControllerUsers();
   3150     }
   3151 
   3152     private void setControllerUsers() {
   3153         if (mZenModeController != null) {
   3154             mZenModeController.setUserId(mCurrentUserId);
   3155         }
   3156     }
   3157 
   3158     private void resetUserSetupObserver() {
   3159         mContext.getContentResolver().unregisterContentObserver(mUserSetupObserver);
   3160         mUserSetupObserver.onChange(false);
   3161         mContext.getContentResolver().registerContentObserver(
   3162                 Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), true,
   3163                 mUserSetupObserver,
   3164                 mCurrentUserId);
   3165     }
   3166 
   3167     private void setHeadsUpVisibility(boolean vis) {
   3168         if (!ENABLE_HEADS_UP) return;
   3169         if (DEBUG) Log.v(TAG, (vis ? "showing" : "hiding") + " heads up window");
   3170         EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_STATUS,
   3171                 vis ? mHeadsUpNotificationView.getKey() : "",
   3172                 vis ? 1 : 0);
   3173         mHeadsUpNotificationView.setVisibility(vis ? View.VISIBLE : View.GONE);
   3174     }
   3175 
   3176     public void onHeadsUpDismissed() {
   3177         mHeadsUpNotificationView.dismiss();
   3178     }
   3179 
   3180     /**
   3181      * Reload some of our resources when the configuration changes.
   3182      *
   3183      * We don't reload everything when the configuration changes -- we probably
   3184      * should, but getting that smooth is tough.  Someday we'll fix that.  In the
   3185      * meantime, just update the things that we know change.
   3186      */
   3187     void updateResources() {
   3188         // Update the quick setting tiles
   3189         if (mQSPanel != null) {
   3190             mQSPanel.updateResources();
   3191         }
   3192 
   3193         loadDimens();
   3194         mLinearOutSlowIn = AnimationUtils.loadInterpolator(
   3195                 mContext, android.R.interpolator.linear_out_slow_in);
   3196 
   3197         if (mNotificationPanel != null) {
   3198             mNotificationPanel.updateResources();
   3199         }
   3200         if (mHeadsUpNotificationView != null) {
   3201             mHeadsUpNotificationView.updateResources();
   3202         }
   3203         if (mBrightnessMirrorController != null) {
   3204             mBrightnessMirrorController.updateResources();
   3205         }
   3206     }
   3207 
   3208     private void updateClockSize() {
   3209         if (mStatusBarView == null) return;
   3210         TextView clock = (TextView) mStatusBarView.findViewById(R.id.clock);
   3211         if (clock != null) {
   3212             FontSizeUtils.updateFontSize(clock, R.dimen.status_bar_clock_size);
   3213         }
   3214     }
   3215     protected void loadDimens() {
   3216         final Resources res = mContext.getResources();
   3217 
   3218         mNaturalBarHeight = res.getDimensionPixelSize(
   3219                 com.android.internal.R.dimen.status_bar_height);
   3220 
   3221         int newIconSize = res.getDimensionPixelSize(
   3222             com.android.internal.R.dimen.status_bar_icon_size);
   3223         int newIconHPadding = res.getDimensionPixelSize(
   3224             R.dimen.status_bar_icon_padding);
   3225 
   3226         if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) {
   3227 //            Log.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding);
   3228             mIconHPadding = newIconHPadding;
   3229             mIconSize = newIconSize;
   3230             //reloadAllNotificationIcons(); // reload the tray
   3231         }
   3232 
   3233         mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
   3234 
   3235         mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity);
   3236         if (mNotificationPanelGravity <= 0) {
   3237             mNotificationPanelGravity = Gravity.START | Gravity.TOP;
   3238         }
   3239 
   3240         mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height);
   3241         mStatusBarHeaderHeight = res.getDimensionPixelSize(R.dimen.status_bar_header_height);
   3242 
   3243         mNotificationPanelMinHeightFrac = res.getFraction(R.dimen.notification_panel_min_height_frac, 1, 1);
   3244         if (mNotificationPanelMinHeightFrac < 0f || mNotificationPanelMinHeightFrac > 1f) {
   3245             mNotificationPanelMinHeightFrac = 0f;
   3246         }
   3247 
   3248         mHeadsUpNotificationDecay = res.getInteger(R.integer.heads_up_notification_decay);
   3249         mRowMinHeight =  res.getDimensionPixelSize(R.dimen.notification_min_height);
   3250         mRowMaxHeight =  res.getDimensionPixelSize(R.dimen.notification_max_height);
   3251 
   3252         mKeyguardMaxNotificationCount = res.getInteger(R.integer.keyguard_max_notification_count);
   3253 
   3254         if (DEBUG) Log.v(TAG, "updateResources");
   3255     }
   3256 
   3257     // Visibility reporting
   3258 
   3259     @Override
   3260     protected void visibilityChanged(boolean visible) {
   3261         mVisible = visible;
   3262         if (visible) {
   3263             startNotificationLoggingIfScreenOnAndVisible();
   3264         } else {
   3265             stopNotificationLogging();
   3266         }
   3267         super.visibilityChanged(visible);
   3268     }
   3269 
   3270     private void stopNotificationLogging() {
   3271         // Report all notifications as invisible and turn down the
   3272         // reporter.
   3273         if (!mCurrentlyVisibleNotifications.isEmpty()) {
   3274             logNotificationVisibilityChanges(
   3275                     Collections.<String>emptyList(), mCurrentlyVisibleNotifications);
   3276             mCurrentlyVisibleNotifications.clear();
   3277         }
   3278         mHandler.removeCallbacks(mVisibilityReporter);
   3279         mStackScroller.setChildLocationsChangedListener(null);
   3280     }
   3281 
   3282     private void startNotificationLoggingIfScreenOnAndVisible() {
   3283         if (mVisible && mScreenOn) {
   3284             mStackScroller.setChildLocationsChangedListener(mNotificationLocationsChangedListener);
   3285             // Some transitions like mScreenOn=false -> mScreenOn=true don't
   3286             // cause the scroller to emit child location events. Hence generate
   3287             // one ourselves to guarantee that we're reporting visible
   3288             // notifications.
   3289             // (Note that in cases where the scroller does emit events, this
   3290             // additional event doesn't break anything.)
   3291             mNotificationLocationsChangedListener.onChildLocationsChanged(mStackScroller);
   3292         }
   3293     }
   3294 
   3295     private void logNotificationVisibilityChanges(
   3296             Collection<String> newlyVisible, Collection<String> noLongerVisible) {
   3297         if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) {
   3298             return;
   3299         }
   3300         String[] newlyVisibleAr = newlyVisible.toArray(new String[newlyVisible.size()]);
   3301         String[] noLongerVisibleAr = noLongerVisible.toArray(new String[noLongerVisible.size()]);
   3302         try {
   3303             mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr);
   3304         } catch (RemoteException e) {
   3305             // Ignore.
   3306         }
   3307     }
   3308 
   3309     //
   3310     // tracing
   3311     //
   3312 
   3313     void postStartTracing() {
   3314         mHandler.postDelayed(mStartTracing, 3000);
   3315     }
   3316 
   3317     void vibrate() {
   3318         android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
   3319                 Context.VIBRATOR_SERVICE);
   3320         vib.vibrate(250, VIBRATION_ATTRIBUTES);
   3321     }
   3322 
   3323     Runnable mStartTracing = new Runnable() {
   3324         public void run() {
   3325             vibrate();
   3326             SystemClock.sleep(250);
   3327             Log.d(TAG, "startTracing");
   3328             android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
   3329             mHandler.postDelayed(mStopTracing, 10000);
   3330         }
   3331     };
   3332 
   3333     Runnable mStopTracing = new Runnable() {
   3334         public void run() {
   3335             android.os.Debug.stopMethodTracing();
   3336             Log.d(TAG, "stopTracing");
   3337             vibrate();
   3338         }
   3339     };
   3340 
   3341     @Override
   3342     protected void haltTicker() {
   3343         if (mTickerEnabled) {
   3344             mTicker.halt();
   3345         }
   3346     }
   3347 
   3348     @Override
   3349     protected boolean shouldDisableNavbarGestures() {
   3350         return !isDeviceProvisioned()
   3351                 || mExpandedVisible
   3352                 || (mDisabled & StatusBarManager.DISABLE_SEARCH) != 0;
   3353     }
   3354 
   3355     public void postStartSettingsActivity(final Intent intent, int delay) {
   3356         mHandler.postDelayed(new Runnable() {
   3357             @Override
   3358             public void run() {
   3359                 handleStartSettingsActivity(intent, true /*onlyProvisioned*/);
   3360             }
   3361         }, delay);
   3362     }
   3363 
   3364     private void handleStartSettingsActivity(Intent intent, boolean onlyProvisioned) {
   3365         startActivityDismissingKeyguard(intent, onlyProvisioned, true /* dismissShade */);
   3366     }
   3367 
   3368     private static class FastColorDrawable extends Drawable {
   3369         private final int mColor;
   3370 
   3371         public FastColorDrawable(int color) {
   3372             mColor = 0xff000000 | color;
   3373         }
   3374 
   3375         @Override
   3376         public void draw(Canvas canvas) {
   3377             canvas.drawColor(mColor, PorterDuff.Mode.SRC);
   3378         }
   3379 
   3380         @Override
   3381         public void setAlpha(int alpha) {
   3382         }
   3383 
   3384         @Override
   3385         public void setColorFilter(ColorFilter cf) {
   3386         }
   3387 
   3388         @Override
   3389         public int getOpacity() {
   3390             return PixelFormat.OPAQUE;
   3391         }
   3392 
   3393         @Override
   3394         public void setBounds(int left, int top, int right, int bottom) {
   3395         }
   3396 
   3397         @Override
   3398         public void setBounds(Rect bounds) {
   3399         }
   3400     }
   3401 
   3402     @Override
   3403     public void destroy() {
   3404         super.destroy();
   3405         if (mStatusBarWindow != null) {
   3406             mWindowManager.removeViewImmediate(mStatusBarWindow);
   3407             mStatusBarWindow = null;
   3408         }
   3409         if (mNavigationBarView != null) {
   3410             mWindowManager.removeViewImmediate(mNavigationBarView);
   3411             mNavigationBarView = null;
   3412         }
   3413         mContext.unregisterReceiver(mBroadcastReceiver);
   3414     }
   3415 
   3416     private boolean mDemoModeAllowed;
   3417     private boolean mDemoMode;
   3418     private DemoStatusIcons mDemoStatusIcons;
   3419 
   3420     @Override
   3421     public void dispatchDemoCommand(String command, Bundle args) {
   3422         if (!mDemoModeAllowed) {
   3423             mDemoModeAllowed = Settings.Global.getInt(mContext.getContentResolver(),
   3424                     "sysui_demo_allowed", 0) != 0;
   3425         }
   3426         if (!mDemoModeAllowed) return;
   3427         if (command.equals(COMMAND_ENTER)) {
   3428             mDemoMode = true;
   3429         } else if (command.equals(COMMAND_EXIT)) {
   3430             mDemoMode = false;
   3431             checkBarModes();
   3432         } else if (!mDemoMode) {
   3433             // automatically enter demo mode on first demo command
   3434             dispatchDemoCommand(COMMAND_ENTER, new Bundle());
   3435         }
   3436         boolean modeChange = command.equals(COMMAND_ENTER) || command.equals(COMMAND_EXIT);
   3437         if (modeChange || command.equals(COMMAND_CLOCK)) {
   3438             dispatchDemoCommandToView(command, args, R.id.clock);
   3439         }
   3440         if (modeChange || command.equals(COMMAND_BATTERY)) {
   3441             dispatchDemoCommandToView(command, args, R.id.battery);
   3442         }
   3443         if (modeChange || command.equals(COMMAND_STATUS)) {
   3444             if (mDemoStatusIcons == null) {
   3445                 mDemoStatusIcons = new DemoStatusIcons(mStatusIcons, mIconSize);
   3446             }
   3447             mDemoStatusIcons.dispatchDemoCommand(command, args);
   3448         }
   3449         if (mNetworkController != null && (modeChange || command.equals(COMMAND_NETWORK))) {
   3450             mNetworkController.dispatchDemoCommand(command, args);
   3451         }
   3452         if (modeChange || command.equals(COMMAND_NOTIFICATIONS)) {
   3453             View notifications = mStatusBarView == null ? null
   3454                     : mStatusBarView.findViewById(R.id.notification_icon_area);
   3455             if (notifications != null) {
   3456                 String visible = args.getString("visible");
   3457                 int vis = mDemoMode && "false".equals(visible) ? View.INVISIBLE : View.VISIBLE;
   3458                 notifications.setVisibility(vis);
   3459             }
   3460         }
   3461         if (command.equals(COMMAND_BARS)) {
   3462             String mode = args.getString("mode");
   3463             int barMode = "opaque".equals(mode) ? MODE_OPAQUE :
   3464                     "translucent".equals(mode) ? MODE_TRANSLUCENT :
   3465                     "semi-transparent".equals(mode) ? MODE_SEMI_TRANSPARENT :
   3466                     "transparent".equals(mode) ? MODE_TRANSPARENT :
   3467                     "warning".equals(mode) ? MODE_WARNING :
   3468                     -1;
   3469             if (barMode != -1) {
   3470                 boolean animate = true;
   3471                 if (mStatusBarView != null) {
   3472                     mStatusBarView.getBarTransitions().transitionTo(barMode, animate);
   3473                 }
   3474                 if (mNavigationBarView != null) {
   3475                     mNavigationBarView.getBarTransitions().transitionTo(barMode, animate);
   3476                 }
   3477             }
   3478         }
   3479     }
   3480 
   3481     private void dispatchDemoCommandToView(String command, Bundle args, int id) {
   3482         if (mStatusBarView == null) return;
   3483         View v = mStatusBarView.findViewById(id);
   3484         if (v instanceof DemoMode) {
   3485             ((DemoMode)v).dispatchDemoCommand(command, args);
   3486         }
   3487     }
   3488 
   3489     /**
   3490      * @return The {@link StatusBarState} the status bar is in.
   3491      */
   3492     public int getBarState() {
   3493         return mState;
   3494     }
   3495 
   3496     public void showKeyguard() {
   3497         if (mLaunchTransitionFadingAway) {
   3498             mNotificationPanel.animate().cancel();
   3499             mNotificationPanel.setAlpha(1f);
   3500             if (mLaunchTransitionEndRunnable != null) {
   3501                 mLaunchTransitionEndRunnable.run();
   3502             }
   3503             mLaunchTransitionEndRunnable = null;
   3504             mLaunchTransitionFadingAway = false;
   3505         }
   3506         setBarState(StatusBarState.KEYGUARD);
   3507         updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
   3508         if (!mScreenOnFromKeyguard) {
   3509 
   3510             // If the screen is off already, we need to disable touch events because these might
   3511             // collapse the panel after we expanded it, and thus we would end up with a blank
   3512             // Keyguard.
   3513             mNotificationPanel.setTouchDisabled(true);
   3514         }
   3515         instantExpandNotificationsPanel();
   3516         mLeaveOpenOnKeyguardHide = false;
   3517         if (mDraggedDownRow != null) {
   3518             mDraggedDownRow.setUserLocked(false);
   3519             mDraggedDownRow.notifyHeightChanged();
   3520             mDraggedDownRow = null;
   3521         }
   3522     }
   3523 
   3524     public boolean isCollapsing() {
   3525         return mNotificationPanel.isCollapsing();
   3526     }
   3527 
   3528     public void addPostCollapseAction(Runnable r) {
   3529         mPostCollapseRunnables.add(r);
   3530     }
   3531 
   3532     public boolean isInLaunchTransition() {
   3533         return mNotificationPanel.isLaunchTransitionRunning()
   3534                 || mNotificationPanel.isLaunchTransitionFinished();
   3535     }
   3536 
   3537     /**
   3538      * Fades the content of the keyguard away after the launch transition is done.
   3539      *
   3540      * @param beforeFading the runnable to be run when the circle is fully expanded and the fading
   3541      *                     starts
   3542      * @param endRunnable the runnable to be run when the transition is done
   3543      */
   3544     public void fadeKeyguardAfterLaunchTransition(final Runnable beforeFading,
   3545             Runnable endRunnable) {
   3546         mLaunchTransitionEndRunnable = endRunnable;
   3547         Runnable hideRunnable = new Runnable() {
   3548             @Override
   3549             public void run() {
   3550                 mLaunchTransitionFadingAway = true;
   3551                 if (beforeFading != null) {
   3552                     beforeFading.run();
   3553                 }
   3554                 mNotificationPanel.setAlpha(1);
   3555                 mNotificationPanel.animate()
   3556                         .alpha(0)
   3557                         .setStartDelay(FADE_KEYGUARD_START_DELAY)
   3558                         .setDuration(FADE_KEYGUARD_DURATION)
   3559                         .withLayer()
   3560                         .withEndAction(new Runnable() {
   3561                             @Override
   3562                             public void run() {
   3563                                 mNotificationPanel.setAlpha(1);
   3564                                 if (mLaunchTransitionEndRunnable != null) {
   3565                                     mLaunchTransitionEndRunnable.run();
   3566                                 }
   3567                                 mLaunchTransitionEndRunnable = null;
   3568                                 mLaunchTransitionFadingAway = false;
   3569                             }
   3570                         });
   3571             }
   3572         };
   3573         if (mNotificationPanel.isLaunchTransitionRunning()) {
   3574             mNotificationPanel.setLaunchTransitionEndRunnable(hideRunnable);
   3575         } else {
   3576             hideRunnable.run();
   3577         }
   3578     }
   3579 
   3580     /**
   3581      * @return true if we would like to stay in the shade, false if it should go away entirely
   3582      */
   3583     public boolean hideKeyguard() {
   3584         boolean staying = mLeaveOpenOnKeyguardHide;
   3585         setBarState(StatusBarState.SHADE);
   3586         if (mLeaveOpenOnKeyguardHide) {
   3587             mLeaveOpenOnKeyguardHide = false;
   3588             mNotificationPanel.animateToFullShade(calculateGoingToFullShadeDelay());
   3589             if (mDraggedDownRow != null) {
   3590                 mDraggedDownRow.setUserLocked(false);
   3591                 mDraggedDownRow = null;
   3592             }
   3593         } else {
   3594             instantCollapseNotificationPanel();
   3595         }
   3596         updateKeyguardState(staying, false /* fromShadeLocked */);
   3597         return staying;
   3598     }
   3599 
   3600     public long calculateGoingToFullShadeDelay() {
   3601         return mKeyguardFadingAwayDelay + mKeyguardFadingAwayDuration;
   3602     }
   3603 
   3604     /**
   3605      * Notifies the status bar the Keyguard is fading away with the specified timings.
   3606      *
   3607      * @param delay the animation delay in miliseconds
   3608      * @param fadeoutDuration the duration of the exit animation, in milliseconds
   3609      */
   3610     public void setKeyguardFadingAway(long delay, long fadeoutDuration) {
   3611         mKeyguardFadingAway = true;
   3612         mKeyguardFadingAwayDelay = delay;
   3613         mKeyguardFadingAwayDuration = fadeoutDuration;
   3614         mWaitingForKeyguardExit = false;
   3615         disable(mDisabledUnmodified, true /* animate */);
   3616     }
   3617 
   3618     public boolean isKeyguardFadingAway() {
   3619         return mKeyguardFadingAway;
   3620     }
   3621 
   3622     /**
   3623      * Notifies that the Keyguard fading away animation is done.
   3624      */
   3625     public void finishKeyguardFadingAway() {
   3626         mKeyguardFadingAway = false;
   3627     }
   3628 
   3629     private void updatePublicMode() {
   3630         setLockscreenPublicMode(
   3631                 (mStatusBarKeyguardViewManager.isShowing() ||
   3632                         mStatusBarKeyguardViewManager.isOccluded())
   3633                 && mStatusBarKeyguardViewManager.isSecure());
   3634     }
   3635 
   3636     private void updateKeyguardState(boolean goingToFullShade, boolean fromShadeLocked) {
   3637         if (mState == StatusBarState.KEYGUARD) {
   3638             mKeyguardIndicationController.setVisible(true);
   3639             mNotificationPanel.resetViews();
   3640             mKeyguardUserSwitcher.setKeyguard(true, fromShadeLocked);
   3641         } else {
   3642             mKeyguardIndicationController.setVisible(false);
   3643             mKeyguardUserSwitcher.setKeyguard(false,
   3644                     goingToFullShade || mState == StatusBarState.SHADE_LOCKED || fromShadeLocked);
   3645         }
   3646         if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
   3647             mScrimController.setKeyguardShowing(true);
   3648         } else {
   3649             mScrimController.setKeyguardShowing(false);
   3650         }
   3651         mNotificationPanel.setBarState(mState, mKeyguardFadingAway, goingToFullShade);
   3652         updateDozingState();
   3653         updatePublicMode();
   3654         updateStackScrollerState(goingToFullShade);
   3655         updateNotifications();
   3656         checkBarModes();
   3657         updateCarrierLabelVisibility(false);
   3658         updateMediaMetaData(false);
   3659         mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(),
   3660                 mStatusBarKeyguardViewManager.isSecure());
   3661     }
   3662 
   3663     private void updateDozingState() {
   3664         if (mState != StatusBarState.KEYGUARD && !mNotificationPanel.isDozing()) {
   3665             return;
   3666         }
   3667         mNotificationPanel.setDozing(mDozing);
   3668         if (mDozing) {
   3669             mKeyguardBottomArea.setVisibility(View.INVISIBLE);
   3670             mStackScroller.setDark(true, false /*animate*/);
   3671         } else {
   3672             mKeyguardBottomArea.setVisibility(View.VISIBLE);
   3673             mStackScroller.setDark(false, false /*animate*/);
   3674         }
   3675         mScrimController.setDozing(mDozing);
   3676     }
   3677 
   3678     public void updateStackScrollerState(boolean goingToFullShade) {
   3679         if (mStackScroller == null) return;
   3680         boolean onKeyguard = mState == StatusBarState.KEYGUARD;
   3681         mStackScroller.setHideSensitive(isLockscreenPublicMode(), goingToFullShade);
   3682         mStackScroller.setDimmed(onKeyguard, false /* animate */);
   3683         mStackScroller.setExpandingEnabled(!onKeyguard);
   3684         ActivatableNotificationView activatedChild = mStackScroller.getActivatedChild();
   3685         mStackScroller.setActivatedChild(null);
   3686         if (activatedChild != null) {
   3687             activatedChild.makeInactive(false /* animate */);
   3688         }
   3689     }
   3690 
   3691     public void userActivity() {
   3692         if (mState == StatusBarState.KEYGUARD) {
   3693             mKeyguardViewMediatorCallback.userActivity();
   3694         }
   3695     }
   3696 
   3697     public boolean interceptMediaKey(KeyEvent event) {
   3698         return mState == StatusBarState.KEYGUARD
   3699                 && mStatusBarKeyguardViewManager.interceptMediaKey(event);
   3700     }
   3701 
   3702     public boolean onMenuPressed() {
   3703         return mState == StatusBarState.KEYGUARD && mStatusBarKeyguardViewManager.onMenuPressed();
   3704     }
   3705 
   3706     public boolean onBackPressed() {
   3707         if (mStatusBarKeyguardViewManager.onBackPressed()) {
   3708             return true;
   3709         }
   3710         if (mNotificationPanel.isQsExpanded()) {
   3711             if (mNotificationPanel.isQsDetailShowing()) {
   3712                 mNotificationPanel.closeQsDetail();
   3713             } else {
   3714                 mNotificationPanel.animateCloseQs();
   3715             }
   3716             return true;
   3717         }
   3718         if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
   3719             animateCollapsePanels();
   3720             return true;
   3721         }
   3722         return false;
   3723     }
   3724 
   3725     public boolean onSpacePressed() {
   3726         if (mScreenOn != null && mScreenOn
   3727                 && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
   3728             animateCollapsePanels(0 /* flags */, true /* force */);
   3729             return true;
   3730         }
   3731         return false;
   3732     }
   3733 
   3734     private void showBouncer() {
   3735         if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
   3736             mWaitingForKeyguardExit = mStatusBarKeyguardViewManager.isShowing();
   3737             mStatusBarKeyguardViewManager.dismiss();
   3738         }
   3739     }
   3740 
   3741     private void instantExpandNotificationsPanel() {
   3742 
   3743         // Make our window larger and the panel expanded.
   3744         makeExpandedVisible(true);
   3745         mNotificationPanel.instantExpand();
   3746     }
   3747 
   3748     private void instantCollapseNotificationPanel() {
   3749         mNotificationPanel.instantCollapse();
   3750     }
   3751 
   3752     @Override
   3753     public void onActivated(ActivatableNotificationView view) {
   3754         mKeyguardIndicationController.showTransientIndication(R.string.notification_tap_again);
   3755         ActivatableNotificationView previousView = mStackScroller.getActivatedChild();
   3756         if (previousView != null) {
   3757             previousView.makeInactive(true /* animate */);
   3758         }
   3759         mStackScroller.setActivatedChild(view);
   3760     }
   3761 
   3762     /**
   3763      * @param state The {@link StatusBarState} to set.
   3764      */
   3765     public void setBarState(int state) {
   3766         mState = state;
   3767         mStatusBarWindowManager.setStatusBarState(state);
   3768     }
   3769 
   3770     @Override
   3771     public void onActivationReset(ActivatableNotificationView view) {
   3772         if (view == mStackScroller.getActivatedChild()) {
   3773             mKeyguardIndicationController.hideTransientIndication();
   3774             mStackScroller.setActivatedChild(null);
   3775         }
   3776     }
   3777 
   3778     public void onTrackingStarted() {
   3779         runPostCollapseRunnables();
   3780     }
   3781 
   3782     public void onUnlockHintStarted() {
   3783         mKeyguardIndicationController.showTransientIndication(R.string.keyguard_unlock);
   3784     }
   3785 
   3786     public void onHintFinished() {
   3787         // Delay the reset a bit so the user can read the text.
   3788         mKeyguardIndicationController.hideTransientIndicationDelayed(HINT_RESET_DELAY_MS);
   3789     }
   3790 
   3791     public void onCameraHintStarted() {
   3792         mKeyguardIndicationController.showTransientIndication(R.string.camera_hint);
   3793     }
   3794 
   3795     public void onPhoneHintStarted() {
   3796         mKeyguardIndicationController.showTransientIndication(R.string.phone_hint);
   3797     }
   3798 
   3799     public void onTrackingStopped(boolean expand) {
   3800         if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
   3801             if (!expand && !mUnlockMethodCache.isMethodInsecure()) {
   3802                 showBouncer();
   3803             }
   3804         }
   3805     }
   3806 
   3807     @Override
   3808     protected int getMaxKeyguardNotifications() {
   3809         return mKeyguardMaxNotificationCount;
   3810     }
   3811 
   3812     public NavigationBarView getNavigationBarView() {
   3813         return mNavigationBarView;
   3814     }
   3815 
   3816     // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
   3817 
   3818     @Override
   3819     public boolean onDraggedDown(View startingChild) {
   3820         if (hasActiveNotifications()) {
   3821 
   3822             // We have notifications, go to locked shade.
   3823             goToLockedShade(startingChild);
   3824             return true;
   3825         } else {
   3826 
   3827             // No notifications - abort gesture.
   3828             return false;
   3829         }
   3830     }
   3831 
   3832     @Override
   3833     public void onDragDownReset() {
   3834         mStackScroller.setDimmed(true /* dimmed */, true /* animated */);
   3835     }
   3836 
   3837     @Override
   3838     public void onThresholdReached() {
   3839         mStackScroller.setDimmed(false /* dimmed */, true /* animate */);
   3840     }
   3841 
   3842     @Override
   3843     public void onTouchSlopExceeded() {
   3844         mStackScroller.removeLongPressCallback();
   3845     }
   3846 
   3847     @Override
   3848     public void setEmptyDragAmount(float amount) {
   3849         mNotificationPanel.setEmptyDragAmount(amount);
   3850     }
   3851 
   3852     /**
   3853      * If secure with redaction: Show bouncer, go to unlocked shade.
   3854      *
   3855      * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p>
   3856      *
   3857      * @param expandView The view to expand after going to the shade.
   3858      */
   3859     public void goToLockedShade(View expandView) {
   3860         ExpandableNotificationRow row = null;
   3861         if (expandView instanceof ExpandableNotificationRow) {
   3862             row = (ExpandableNotificationRow) expandView;
   3863             row.setUserExpanded(true);
   3864         }
   3865         boolean fullShadeNeedsBouncer = !userAllowsPrivateNotificationsInPublic(mCurrentUserId)
   3866                 || !mShowLockscreenNotifications;
   3867         if (isLockscreenPublicMode() && fullShadeNeedsBouncer) {
   3868             mLeaveOpenOnKeyguardHide = true;
   3869             showBouncer();
   3870             mDraggedDownRow = row;
   3871         } else {
   3872             mNotificationPanel.animateToFullShade(0 /* delay */);
   3873             setBarState(StatusBarState.SHADE_LOCKED);
   3874             updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
   3875             if (row != null) {
   3876                 row.setUserLocked(false);
   3877             }
   3878         }
   3879     }
   3880 
   3881     /**
   3882      * Goes back to the keyguard after hanging around in {@link StatusBarState#SHADE_LOCKED}.
   3883      */
   3884     public void goToKeyguard() {
   3885         if (mState == StatusBarState.SHADE_LOCKED) {
   3886             mStackScroller.onGoToKeyguard();
   3887             setBarState(StatusBarState.KEYGUARD);
   3888             updateKeyguardState(false /* goingToFullShade */, true /* fromShadeLocked*/);
   3889         }
   3890     }
   3891 
   3892     /**
   3893      * @return a ViewGroup that spans the entire panel which contains the quick settings
   3894      */
   3895     public ViewGroup getQuickSettingsOverlayParent() {
   3896         return mNotificationPanel;
   3897     }
   3898 
   3899     public long getKeyguardFadingAwayDelay() {
   3900         return mKeyguardFadingAwayDelay;
   3901     }
   3902 
   3903     public long getKeyguardFadingAwayDuration() {
   3904         return mKeyguardFadingAwayDuration;
   3905     }
   3906 
   3907     public LinearLayout getSystemIcons() {
   3908         return mSystemIcons;
   3909     }
   3910 
   3911     public LinearLayout getSystemIconArea() {
   3912         return mSystemIconArea;
   3913     }
   3914 
   3915     @Override
   3916     public void setBouncerShowing(boolean bouncerShowing) {
   3917         super.setBouncerShowing(bouncerShowing);
   3918         disable(mDisabledUnmodified, true /* animate */);
   3919     }
   3920 
   3921     public void onScreenTurnedOff() {
   3922         mScreenOnFromKeyguard = false;
   3923         mScreenOnComingFromTouch = false;
   3924         mStackScroller.setAnimationsEnabled(false);
   3925     }
   3926 
   3927     public void onScreenTurnedOn() {
   3928         mScreenOnFromKeyguard = true;
   3929         mStackScroller.setAnimationsEnabled(true);
   3930         mNotificationPanel.onScreenTurnedOn();
   3931         mNotificationPanel.setTouchDisabled(false);
   3932     }
   3933 
   3934     /**
   3935      * This handles long-press of both back and recents.  They are
   3936      * handled together to capture them both being long-pressed
   3937      * at the same time to exit screen pinning (lock task).
   3938      *
   3939      * When accessibility mode is on, only a long-press from recents
   3940      * is required to exit.
   3941      *
   3942      * In all other circumstances we try to pass through long-press events
   3943      * for Back, so that apps can still use it.  Which can be from two things.
   3944      * 1) Not currently in screen pinning (lock task).
   3945      * 2) Back is long-pressed without recents.
   3946      */
   3947     private void handleLongPressBackRecents(View v) {
   3948         try {
   3949             boolean sendBackLongPress = false;
   3950             IActivityManager activityManager = ActivityManagerNative.getDefault();
   3951             boolean isAccessiblityEnabled = mAccessibilityManager.isEnabled();
   3952             if (activityManager.isInLockTaskMode() && !isAccessiblityEnabled) {
   3953                 long time = System.currentTimeMillis();
   3954                 // If we recently long-pressed the other button then they were
   3955                 // long-pressed 'together'
   3956                 if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) {
   3957                     activityManager.stopLockTaskModeOnCurrent();
   3958                 } else if ((v.getId() == R.id.back)
   3959                         && !mNavigationBarView.getRecentsButton().isPressed()) {
   3960                     // If we aren't pressing recents right now then they presses
   3961                     // won't be together, so send the standard long-press action.
   3962                     sendBackLongPress = true;
   3963                 }
   3964                 mLastLockToAppLongPress = time;
   3965             } else {
   3966                 // If this is back still need to handle sending the long-press event.
   3967                 if (v.getId() == R.id.back) {
   3968                     sendBackLongPress = true;
   3969                 } else if (isAccessiblityEnabled && activityManager.isInLockTaskMode()) {
   3970                     // When in accessibility mode a long press that is recents (not back)
   3971                     // should stop lock task.
   3972                     activityManager.stopLockTaskModeOnCurrent();
   3973                 }
   3974             }
   3975             if (sendBackLongPress) {
   3976                 KeyButtonView keyButtonView = (KeyButtonView) v;
   3977                 keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
   3978                 keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
   3979             }
   3980         } catch (RemoteException e) {
   3981             Log.d(TAG, "Unable to reach activity manager", e);
   3982         }
   3983     }
   3984 
   3985     // Recents
   3986 
   3987     @Override
   3988     protected void showRecents(boolean triggeredFromAltTab) {
   3989         // Set the recents visibility flag
   3990         mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
   3991         notifyUiVisibilityChanged(mSystemUiVisibility);
   3992         super.showRecents(triggeredFromAltTab);
   3993     }
   3994 
   3995     @Override
   3996     protected void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
   3997         // Unset the recents visibility flag
   3998         mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE;
   3999         notifyUiVisibilityChanged(mSystemUiVisibility);
   4000         super.hideRecents(triggeredFromAltTab, triggeredFromHomeKey);
   4001     }
   4002 
   4003     @Override
   4004     protected void toggleRecents() {
   4005         // Toggle the recents visibility flag
   4006         mSystemUiVisibility ^= View.RECENT_APPS_VISIBLE;
   4007         notifyUiVisibilityChanged(mSystemUiVisibility);
   4008         super.toggleRecents();
   4009     }
   4010 
   4011     @Override
   4012     public void onVisibilityChanged(boolean visible) {
   4013         // Update the recents visibility flag
   4014         if (visible) {
   4015             mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
   4016         } else {
   4017             mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE;
   4018         }
   4019         notifyUiVisibilityChanged(mSystemUiVisibility);
   4020     }
   4021 
   4022     public boolean hasActiveNotifications() {
   4023         return !mNotificationData.getActiveNotifications().isEmpty();
   4024     }
   4025 
   4026     public void wakeUpIfDozing(long time, boolean fromTouch) {
   4027         if (mDozing && mScrimController.isPulsing()) {
   4028             PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
   4029             pm.wakeUp(time);
   4030             if (fromTouch) {
   4031                 mScreenOnComingFromTouch = true;
   4032             }
   4033         }
   4034     }
   4035 
   4036     private final class ShadeUpdates {
   4037         private final ArraySet<String> mVisibleNotifications = new ArraySet<String>();
   4038         private final ArraySet<String> mNewVisibleNotifications = new ArraySet<String>();
   4039 
   4040         public void check() {
   4041             mNewVisibleNotifications.clear();
   4042             ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
   4043             for (int i = 0; i < activeNotifications.size(); i++) {
   4044                 final Entry entry = activeNotifications.get(i);
   4045                 final boolean visible = entry.row != null
   4046                         && entry.row.getVisibility() == View.VISIBLE;
   4047                 if (visible) {
   4048                     mNewVisibleNotifications.add(entry.key + entry.notification.getPostTime());
   4049                 }
   4050             }
   4051             final boolean updates = !mVisibleNotifications.containsAll(mNewVisibleNotifications);
   4052             mVisibleNotifications.clear();
   4053             mVisibleNotifications.addAll(mNewVisibleNotifications);
   4054 
   4055             // We have new notifications
   4056             if (updates && mDozeServiceHost != null) {
   4057                 mDozeServiceHost.fireNewNotifications();
   4058             }
   4059         }
   4060     }
   4061 
   4062     private final class DozeServiceHost implements DozeHost {
   4063         // Amount of time to allow to update the time shown on the screen before releasing
   4064         // the wakelock.  This timeout is design to compensate for the fact that we don't
   4065         // currently have a way to know when time display contents have actually been
   4066         // refreshed once we've finished rendering a new frame.
   4067         private static final long PROCESSING_TIME = 500;
   4068 
   4069         private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
   4070         private final H mHandler = new H();
   4071 
   4072         @Override
   4073         public String toString() {
   4074             return "PSB.DozeServiceHost[mCallbacks=" + mCallbacks.size() + "]";
   4075         }
   4076 
   4077         public void firePowerSaveChanged(boolean active) {
   4078             for (Callback callback : mCallbacks) {
   4079                 callback.onPowerSaveChanged(active);
   4080             }
   4081         }
   4082 
   4083         public void fireBuzzBeepBlinked() {
   4084             for (Callback callback : mCallbacks) {
   4085                 callback.onBuzzBeepBlinked();
   4086             }
   4087         }
   4088 
   4089         public void fireNotificationLight(boolean on) {
   4090             for (Callback callback : mCallbacks) {
   4091                 callback.onNotificationLight(on);
   4092             }
   4093         }
   4094 
   4095         public void fireNewNotifications() {
   4096             for (Callback callback : mCallbacks) {
   4097                 callback.onNewNotifications();
   4098             }
   4099         }
   4100 
   4101         @Override
   4102         public void addCallback(@NonNull Callback callback) {
   4103             mCallbacks.add(callback);
   4104         }
   4105 
   4106         @Override
   4107         public void removeCallback(@NonNull Callback callback) {
   4108             mCallbacks.remove(callback);
   4109         }
   4110 
   4111         @Override
   4112         public void startDozing(@NonNull Runnable ready) {
   4113             mHandler.obtainMessage(H.MSG_START_DOZING, ready).sendToTarget();
   4114         }
   4115 
   4116         @Override
   4117         public void pulseWhileDozing(@NonNull PulseCallback callback) {
   4118             mHandler.obtainMessage(H.MSG_PULSE_WHILE_DOZING, callback).sendToTarget();
   4119         }
   4120 
   4121         @Override
   4122         public void stopDozing() {
   4123             mHandler.obtainMessage(H.MSG_STOP_DOZING).sendToTarget();
   4124         }
   4125 
   4126         @Override
   4127         public boolean isPowerSaveActive() {
   4128             return mBatteryController != null && mBatteryController.isPowerSave();
   4129         }
   4130 
   4131         private void handleStartDozing(@NonNull Runnable ready) {
   4132             if (!mDozing) {
   4133                 mDozing = true;
   4134                 DozeLog.traceDozing(mContext, mDozing);
   4135                 updateDozingState();
   4136             }
   4137             ready.run();
   4138         }
   4139 
   4140         private void handlePulseWhileDozing(@NonNull PulseCallback callback) {
   4141             mScrimController.pulse(callback);
   4142         }
   4143 
   4144         private void handleStopDozing() {
   4145             if (mDozing) {
   4146                 mDozing = false;
   4147                 DozeLog.traceDozing(mContext, mDozing);
   4148                 updateDozingState();
   4149             }
   4150         }
   4151 
   4152         private final class H extends Handler {
   4153             private static final int MSG_START_DOZING = 1;
   4154             private static final int MSG_PULSE_WHILE_DOZING = 2;
   4155             private static final int MSG_STOP_DOZING = 3;
   4156 
   4157             @Override
   4158             public void handleMessage(Message msg) {
   4159                 switch (msg.what) {
   4160                     case MSG_START_DOZING:
   4161                         handleStartDozing((Runnable) msg.obj);
   4162                         break;
   4163                     case MSG_PULSE_WHILE_DOZING:
   4164                         handlePulseWhileDozing((PulseCallback) msg.obj);
   4165                         break;
   4166                     case MSG_STOP_DOZING:
   4167                         handleStopDozing();
   4168                         break;
   4169                 }
   4170             }
   4171         }
   4172     }
   4173 }
   4174