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