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