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