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