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