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