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