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