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