PhoneStatusBar.java revision f1ff209523a2474cb0e7aab0e083596c4afbe37f
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 imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
2216        int flags = mNavigationIconHints;
2217        if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) {
2218            flags |= NAVIGATION_HINT_BACK_ALT;
2219        } else {
2220            flags &= ~NAVIGATION_HINT_BACK_ALT;
2221        }
2222        if (imeShown) {
2223            flags |= NAVIGATION_HINT_IME_SHOWN;
2224        } else {
2225            flags &= ~NAVIGATION_HINT_IME_SHOWN;
2226        }
2227
2228        setNavigationIconHints(flags);
2229        if (mQS != null) mQS.setImeWindowStatus(vis > 0);
2230    }
2231
2232    @Override
2233    public void setHardKeyboardStatus(boolean available, boolean enabled) {}
2234
2235    @Override
2236    protected void tick(IBinder key, StatusBarNotification n, boolean firstTime) {
2237        // no ticking in lights-out mode
2238        if (!areLightsOn()) return;
2239
2240        // no ticking in Setup
2241        if (!isDeviceProvisioned()) return;
2242
2243        // not for you
2244        if (!notificationIsForCurrentProfiles(n)) return;
2245
2246        // Show the ticker if one is requested. Also don't do this
2247        // until status bar window is attached to the window manager,
2248        // because...  well, what's the point otherwise?  And trying to
2249        // run a ticker without being attached will crash!
2250        if (n.getNotification().tickerText != null && mStatusBarWindow != null
2251                && mStatusBarWindow.getWindowToken() != null) {
2252            if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
2253                            | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
2254                mTicker.addEntry(n);
2255            }
2256        }
2257    }
2258
2259    private class MyTicker extends Ticker {
2260        MyTicker(Context context, View sb) {
2261            super(context, sb);
2262        }
2263
2264        @Override
2265        public void tickerStarting() {
2266            mTicking = true;
2267            mStatusBarContents.setVisibility(View.GONE);
2268            mTickerView.setVisibility(View.VISIBLE);
2269            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
2270            mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
2271        }
2272
2273        @Override
2274        public void tickerDone() {
2275            mStatusBarContents.setVisibility(View.VISIBLE);
2276            mTickerView.setVisibility(View.GONE);
2277            mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
2278            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
2279                        mTickingDoneListener));
2280        }
2281
2282        public void tickerHalting() {
2283            if (mStatusBarContents.getVisibility() != View.VISIBLE) {
2284                mStatusBarContents.setVisibility(View.VISIBLE);
2285                mStatusBarContents
2286                        .startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
2287            }
2288            mTickerView.setVisibility(View.GONE);
2289            // we do not animate the ticker away at this point, just get rid of it (b/6992707)
2290        }
2291    }
2292
2293    Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
2294        public void onAnimationEnd(Animation animation) {
2295            mTicking = false;
2296        }
2297        public void onAnimationRepeat(Animation animation) {
2298        }
2299        public void onAnimationStart(Animation animation) {
2300        }
2301    };
2302
2303    private Animation loadAnim(int id, Animation.AnimationListener listener) {
2304        Animation anim = AnimationUtils.loadAnimation(mContext, id);
2305        if (listener != null) {
2306            anim.setAnimationListener(listener);
2307        }
2308        return anim;
2309    }
2310
2311    public static String viewInfo(View v) {
2312        return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
2313                + ") " + v.getWidth() + "x" + v.getHeight() + "]";
2314    }
2315
2316    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2317        synchronized (mQueueLock) {
2318            pw.println("Current Status Bar state:");
2319            pw.println("  mExpandedVisible=" + mExpandedVisible
2320                    + ", mTrackingPosition=" + mTrackingPosition);
2321            pw.println("  mTicking=" + mTicking);
2322            pw.println("  mTracking=" + mTracking);
2323            pw.println("  mDisplayMetrics=" + mDisplayMetrics);
2324            pw.println("  mStackScroller: " + viewInfo(mStackScroller));
2325            pw.println("  mTickerView: " + viewInfo(mTickerView));
2326            pw.println("  mStackScroller: " + viewInfo(mStackScroller)
2327                    + " scroll " + mStackScroller.getScrollX()
2328                    + "," + mStackScroller.getScrollY());
2329        }
2330
2331        pw.print("  mInteractingWindows="); pw.println(mInteractingWindows);
2332        pw.print("  mStatusBarWindowState=");
2333        pw.println(windowStateToString(mStatusBarWindowState));
2334        pw.print("  mStatusBarMode=");
2335        pw.println(BarTransitions.modeToString(mStatusBarMode));
2336        pw.print("  mZenMode=");
2337        pw.println(Settings.Global.zenModeToString(mZenMode));
2338        pw.print("  mUseHeadsUp=");
2339        pw.println(mUseHeadsUp);
2340        pw.print("  interrupting package: ");
2341        pw.println(hunStateToString(mInterruptingNotificationEntry));
2342        dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
2343        if (mNavigationBarView != null) {
2344            pw.print("  mNavigationBarWindowState=");
2345            pw.println(windowStateToString(mNavigationBarWindowState));
2346            pw.print("  mNavigationBarMode=");
2347            pw.println(BarTransitions.modeToString(mNavigationBarMode));
2348            dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
2349        }
2350
2351        pw.print("  mNavigationBarView=");
2352        if (mNavigationBarView == null) {
2353            pw.println("null");
2354        } else {
2355            mNavigationBarView.dump(fd, pw, args);
2356        }
2357
2358        pw.println("  Panels: ");
2359        if (mNotificationPanel != null) {
2360            pw.println("    mNotificationPanel=" +
2361                mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug(""));
2362            pw.print  ("      ");
2363            mNotificationPanel.dump(fd, pw, args);
2364        }
2365
2366        if (DUMPTRUCK) {
2367            synchronized (mNotificationData) {
2368                int N = mNotificationData.size();
2369                pw.println("  notification icons: " + N);
2370                for (int i=0; i<N; i++) {
2371                    NotificationData.Entry e = mNotificationData.get(i);
2372                    pw.println("    [" + i + "] key=" + e.key + " icon=" + e.icon);
2373                    StatusBarNotification n = e.notification;
2374                    pw.println("         pkg=" + n.getPackageName() + " id=" + n.getId() + " score=" + n.getScore());
2375                    pw.println("         notification=" + n.getNotification());
2376                    pw.println("         tickerText=\"" + n.getNotification().tickerText + "\"");
2377                }
2378            }
2379
2380            int N = mStatusIcons.getChildCount();
2381            pw.println("  system icons: " + N);
2382            for (int i=0; i<N; i++) {
2383                StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i);
2384                pw.println("    [" + i + "] icon=" + ic);
2385            }
2386
2387            if (false) {
2388                pw.println("see the logcat for a dump of the views we have created.");
2389                // must happen on ui thread
2390                mHandler.post(new Runnable() {
2391                        public void run() {
2392                            mStatusBarView.getLocationOnScreen(mAbsPos);
2393                            Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
2394                                    + ") " + mStatusBarView.getWidth() + "x"
2395                                    + getStatusBarHeight());
2396                            mStatusBarView.debug();
2397                        }
2398                    });
2399            }
2400        }
2401
2402        if (DEBUG_GESTURES) {
2403            pw.print("  status bar gestures: ");
2404            mGestureRec.dump(fd, pw, args);
2405        }
2406
2407        mNetworkController.dump(fd, pw, args);
2408    }
2409
2410    private String hunStateToString(Entry entry) {
2411        if (entry == null) return "null";
2412        if (entry.notification == null) return "corrupt";
2413        return entry.notification.getPackageName();
2414    }
2415
2416    private static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) {
2417        pw.print("  "); pw.print(var); pw.print(".BarTransitions.mMode=");
2418        pw.println(BarTransitions.modeToString(transitions.getMode()));
2419    }
2420
2421    @Override
2422    public void createAndAddWindows() {
2423        addStatusBarWindow();
2424    }
2425
2426    private void addStatusBarWindow() {
2427        makeStatusBarView();
2428        mStatusBarWindowManager = new StatusBarWindowManager(mContext);
2429        mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
2430    }
2431
2432    void setNotificationIconVisibility(boolean visible, int anim) {
2433        int old = mNotificationIcons.getVisibility();
2434        int v = visible ? View.VISIBLE : View.INVISIBLE;
2435        if (old != v) {
2436            mNotificationIcons.setVisibility(v);
2437            mNotificationIcons.startAnimation(loadAnim(anim, null));
2438        }
2439    }
2440
2441    void updateExpandedInvisiblePosition() {
2442        mTrackingPosition = -mDisplayMetrics.heightPixels;
2443    }
2444
2445    static final float saturate(float a) {
2446        return a < 0f ? 0f : (a > 1f ? 1f : a);
2447    }
2448
2449    @Override
2450    protected int getExpandedViewMaxHeight() {
2451        return mDisplayMetrics.heightPixels - mNotificationPanelMarginBottomPx;
2452    }
2453
2454    @Override
2455    public void updateExpandedViewPos(int thingy) {
2456        if (SPEW) Log.v(TAG, "updateExpandedViewPos");
2457
2458        // on larger devices, the notification panel is propped open a bit
2459        mNotificationPanel.setMinimumHeight(
2460                (int)(mNotificationPanelMinHeightFrac * mCurrentDisplaySize.y));
2461
2462        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mNotificationPanel.getLayoutParams();
2463        lp.gravity = mNotificationPanelGravity;
2464        lp.setMarginStart(mNotificationPanelMarginPx);
2465        mNotificationPanel.setLayoutParams(lp);
2466
2467        if (ENABLE_HEADS_UP && mHeadsUpNotificationView != null) {
2468            mHeadsUpNotificationView.setMargin(mNotificationPanelMarginPx);
2469            mStackScroller.getLocationOnScreen(mStackScrollerPosition);
2470            mHeadsUpVerticalOffset = mStackScrollerPosition[1] - mNaturalBarHeight;
2471        }
2472
2473        updateCarrierLabelVisibility(false);
2474    }
2475
2476    // called by makeStatusbar and also by PhoneStatusBarView
2477    void updateDisplaySize() {
2478        mDisplay.getMetrics(mDisplayMetrics);
2479        mDisplay.getSize(mCurrentDisplaySize);
2480        if (DEBUG_GESTURES) {
2481            mGestureRec.tag("display",
2482                    String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
2483        }
2484    }
2485
2486    private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
2487        public void onClick(View v) {
2488            synchronized (mNotificationData) {
2489                mPostCollapseCleanup = new Runnable() {
2490                    @Override
2491                    public void run() {
2492                        if (DEBUG) {
2493                            Log.v(TAG, "running post-collapse cleanup");
2494                        }
2495                        try {
2496                            mBarService.onClearAllNotifications(mCurrentUserId);
2497                        } catch (Exception ex) { }
2498                    }
2499                };
2500
2501                animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
2502                return;
2503                // TODO: Handle this better with notification stack scroller
2504            }
2505        }
2506    };
2507
2508    public void startActivityDismissingKeyguard(Intent intent, boolean onlyProvisioned) {
2509        if (onlyProvisioned && !isDeviceProvisioned()) return;
2510        try {
2511            // Dismiss the lock screen when Settings starts.
2512            ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
2513        } catch (RemoteException e) {
2514        }
2515        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
2516        mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
2517        animateCollapsePanels();
2518    }
2519
2520    private View.OnClickListener mSettingsButtonListener = new View.OnClickListener() {
2521        public void onClick(View v) {
2522            if (mHasQuickSettings) {
2523                animateExpandSettingsPanel();
2524            } else {
2525                startActivityDismissingKeyguard(
2526                        new Intent(android.provider.Settings.ACTION_SETTINGS), true);
2527            }
2528        }
2529    };
2530
2531    private View.OnClickListener mClockClickListener = new View.OnClickListener() {
2532        public void onClick(View v) {
2533            startActivityDismissingKeyguard(
2534                    new Intent(Intent.ACTION_QUICK_CLOCK), true); // have fun, everyone
2535        }
2536    };
2537
2538    private View.OnClickListener mNotificationButtonListener = new View.OnClickListener() {
2539        public void onClick(View v) {
2540            animateExpandNotificationsPanel();
2541        }
2542    };
2543
2544    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
2545        public void onReceive(Context context, Intent intent) {
2546            if (DEBUG) Log.v(TAG, "onReceive: " + intent);
2547            String action = intent.getAction();
2548            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
2549                int flags = CommandQueue.FLAG_EXCLUDE_NONE;
2550                if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
2551                    String reason = intent.getStringExtra("reason");
2552                    if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
2553                        flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
2554                    }
2555                }
2556                animateCollapsePanels(flags);
2557            }
2558            else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
2559                mScreenOn = false;
2560                notifyNavigationBarScreenOn(false);
2561                notifyHeadsUpScreenOn(false);
2562                finishBarAnimations();
2563            }
2564            else if (Intent.ACTION_SCREEN_ON.equals(action)) {
2565                mScreenOn = true;
2566                // work around problem where mDisplay.getRotation() is not stable while screen is off (bug 7086018)
2567                repositionNavigationBar();
2568                notifyNavigationBarScreenOn(true);
2569            }
2570            else if (ACTION_DEMO.equals(action)) {
2571                Bundle bundle = intent.getExtras();
2572                if (bundle != null) {
2573                    String command = bundle.getString("command", "").trim().toLowerCase();
2574                    if (command.length() > 0) {
2575                        try {
2576                            dispatchDemoCommand(command, bundle);
2577                        } catch (Throwable t) {
2578                            Log.w(TAG, "Error running demo command, intent=" + intent, t);
2579                        }
2580                    }
2581                }
2582            }
2583        }
2584    };
2585
2586    // SystemUIService notifies SystemBars of configuration changes, which then calls down here
2587    @Override
2588    protected void onConfigurationChanged(Configuration newConfig) {
2589        super.onConfigurationChanged(newConfig); // calls refreshLayout
2590
2591        if (DEBUG) {
2592            Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
2593        }
2594        updateDisplaySize(); // populates mDisplayMetrics
2595
2596        updateResources();
2597        repositionNavigationBar();
2598        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
2599        updateShowSearchHoldoff();
2600    }
2601
2602    @Override
2603    public void userSwitched(int newUserId) {
2604        if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
2605        animateCollapsePanels();
2606        updateNotificationIcons();
2607        resetUserSetupObserver();
2608    }
2609
2610    private void resetUserSetupObserver() {
2611        mContext.getContentResolver().unregisterContentObserver(mUserSetupObserver);
2612        mUserSetupObserver.onChange(false);
2613        mContext.getContentResolver().registerContentObserver(
2614                Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), true,
2615                mUserSetupObserver,
2616                mCurrentUserId);
2617    }
2618
2619    private void setHeadsUpVisibility(boolean vis) {
2620        if (!ENABLE_HEADS_UP) return;
2621        if (DEBUG) Log.v(TAG, (vis ? "showing" : "hiding") + " heads up window");
2622        mHeadsUpNotificationView.setVisibility(vis ? View.VISIBLE : View.GONE);
2623        if (!vis) {
2624            if (DEBUG) Log.d(TAG, "setting heads up entry to null");
2625            mInterruptingNotificationEntry = null;
2626        }
2627    }
2628
2629    public void animateHeadsUp(boolean animateInto, float frac) {
2630        if (!ENABLE_HEADS_UP || mHeadsUpNotificationView == null) return;
2631        frac = frac / 0.4f;
2632        frac = frac < 1.0f ? frac : 1.0f;
2633        float alpha = 1.0f - frac;
2634        float offset = mHeadsUpVerticalOffset * frac;
2635        offset = animateInto ? offset : 0f;
2636        mHeadsUpNotificationView.setAlpha(alpha);
2637        mHeadsUpNotificationView.setY(offset);
2638    }
2639
2640    public void onHeadsUpDismissed() {
2641        if (mInterruptingNotificationEntry == null) return;
2642        mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
2643        if (mHeadsUpNotificationView.isClearable()) {
2644            try {
2645                mBarService.onNotificationClear(
2646                        mInterruptingNotificationEntry.notification.getPackageName(),
2647                        mInterruptingNotificationEntry.notification.getTag(),
2648                        mInterruptingNotificationEntry.notification.getId(),
2649                        mInterruptingNotificationEntry.notification.getUserId());
2650            } catch (android.os.RemoteException ex) {
2651                // oh well
2652            }
2653        }
2654    }
2655
2656    /**
2657     * Reload some of our resources when the configuration changes.
2658     *
2659     * We don't reload everything when the configuration changes -- we probably
2660     * should, but getting that smooth is tough.  Someday we'll fix that.  In the
2661     * meantime, just update the things that we know change.
2662     */
2663    void updateResources() {
2664        final Context context = mContext;
2665        final Resources res = context.getResources();
2666
2667        if (mClearButton instanceof TextView) {
2668            ((TextView)mClearButton).setText(context.getText(R.string.status_bar_clear_all_button));
2669        }
2670
2671        // Update the QuickSettings container
2672        if (mQS != null) mQS.updateResources();
2673
2674        loadDimens();
2675    }
2676
2677    protected void loadDimens() {
2678        final Resources res = mContext.getResources();
2679
2680        mNaturalBarHeight = res.getDimensionPixelSize(
2681                com.android.internal.R.dimen.status_bar_height);
2682
2683        int newIconSize = res.getDimensionPixelSize(
2684            com.android.internal.R.dimen.status_bar_icon_size);
2685        int newIconHPadding = res.getDimensionPixelSize(
2686            R.dimen.status_bar_icon_padding);
2687
2688        if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) {
2689//            Log.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding);
2690            mIconHPadding = newIconHPadding;
2691            mIconSize = newIconSize;
2692            //reloadAllNotificationIcons(); // reload the tray
2693        }
2694
2695        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
2696
2697        mSelfExpandVelocityPx = res.getDimension(R.dimen.self_expand_velocity);
2698        mSelfCollapseVelocityPx = res.getDimension(R.dimen.self_collapse_velocity);
2699        mFlingExpandMinVelocityPx = res.getDimension(R.dimen.fling_expand_min_velocity);
2700        mFlingCollapseMinVelocityPx = res.getDimension(R.dimen.fling_collapse_min_velocity);
2701
2702        mCollapseMinDisplayFraction = res.getFraction(R.dimen.collapse_min_display_fraction, 1, 1);
2703        mExpandMinDisplayFraction = res.getFraction(R.dimen.expand_min_display_fraction, 1, 1);
2704
2705        mExpandAccelPx = res.getDimension(R.dimen.expand_accel);
2706        mCollapseAccelPx = res.getDimension(R.dimen.collapse_accel);
2707
2708        mFlingGestureMaxXVelocityPx = res.getDimension(R.dimen.fling_gesture_max_x_velocity);
2709
2710        mFlingGestureMaxOutputVelocityPx = res.getDimension(R.dimen.fling_gesture_max_output_velocity);
2711
2712        mNotificationPanelMarginBottomPx
2713            = (int) res.getDimension(R.dimen.notification_panel_margin_bottom);
2714        mNotificationPanelMarginPx
2715            = (int) res.getDimension(R.dimen.notification_panel_margin_left);
2716        mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity);
2717        if (mNotificationPanelGravity <= 0) {
2718            mNotificationPanelGravity = Gravity.START | Gravity.TOP;
2719        }
2720
2721        mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height);
2722        mNotificationHeaderHeight = res.getDimensionPixelSize(R.dimen.notification_panel_header_height);
2723
2724        mNotificationPanelMinHeightFrac = res.getFraction(R.dimen.notification_panel_min_height_frac, 1, 1);
2725        if (mNotificationPanelMinHeightFrac < 0f || mNotificationPanelMinHeightFrac > 1f) {
2726            mNotificationPanelMinHeightFrac = 0f;
2727        }
2728
2729        mHeadsUpNotificationDecay = res.getInteger(R.integer.heads_up_notification_decay);
2730        mRowMinHeight =  res.getDimensionPixelSize(R.dimen.notification_min_height);
2731        mRowMaxHeight =  res.getDimensionPixelSize(R.dimen.notification_max_height);
2732
2733        mKeyguardMaxNotificationCount = res.getInteger(R.integer.keyguard_max_notification_count);
2734
2735        if (false) Log.v(TAG, "updateResources");
2736    }
2737
2738    // Visibility reporting
2739
2740    @Override
2741    protected void visibilityChanged(boolean visible) {
2742        if (visible) {
2743            mStackScroller.setChildLocationsChangedListener(mNotificationLocationsChangedListener);
2744        } else {
2745            // Report all notifications as invisible and turn down the
2746            // reporter.
2747            if (!mCurrentlyVisibleNotifications.isEmpty()) {
2748                logNotificationVisibilityChanges(
2749                        Collections.<String>emptyList(), mCurrentlyVisibleNotifications);
2750                mCurrentlyVisibleNotifications.clear();
2751            }
2752            mHandler.removeCallbacks(mVisibilityReporter);
2753            mStackScroller.setChildLocationsChangedListener(null);
2754        }
2755        super.visibilityChanged(visible);
2756    }
2757
2758    private void logNotificationVisibilityChanges(
2759            Collection<String> newlyVisible, Collection<String> noLongerVisible) {
2760        if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) {
2761            return;
2762        }
2763
2764        String[] newlyVisibleAr = newlyVisible.toArray(new String[newlyVisible.size()]);
2765        String[] noLongerVisibleAr = noLongerVisible.toArray(new String[noLongerVisible.size()]);
2766        try {
2767            mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr);
2768        } catch (RemoteException e) {
2769            // Ignore.
2770        }
2771    }
2772
2773    //
2774    // tracing
2775    //
2776
2777    void postStartTracing() {
2778        mHandler.postDelayed(mStartTracing, 3000);
2779    }
2780
2781    void vibrate() {
2782        android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
2783                Context.VIBRATOR_SERVICE);
2784        vib.vibrate(250, AudioManager.STREAM_SYSTEM);
2785    }
2786
2787    Runnable mStartTracing = new Runnable() {
2788        public void run() {
2789            vibrate();
2790            SystemClock.sleep(250);
2791            Log.d(TAG, "startTracing");
2792            android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
2793            mHandler.postDelayed(mStopTracing, 10000);
2794        }
2795    };
2796
2797    Runnable mStopTracing = new Runnable() {
2798        public void run() {
2799            android.os.Debug.stopMethodTracing();
2800            Log.d(TAG, "stopTracing");
2801            vibrate();
2802        }
2803    };
2804
2805    @Override
2806    protected void haltTicker() {
2807        mTicker.halt();
2808    }
2809
2810    @Override
2811    protected boolean shouldDisableNavbarGestures() {
2812        return !isDeviceProvisioned()
2813                || mExpandedVisible
2814                || (mDisabled & StatusBarManager.DISABLE_SEARCH) != 0;
2815    }
2816
2817    public void startSettingsActivity(String action) {
2818        if (mQS != null) {
2819            mQS.startSettingsActivity(action);
2820        }
2821    }
2822
2823    private static class FastColorDrawable extends Drawable {
2824        private final int mColor;
2825
2826        public FastColorDrawable(int color) {
2827            mColor = 0xff000000 | color;
2828        }
2829
2830        @Override
2831        public void draw(Canvas canvas) {
2832            canvas.drawColor(mColor, PorterDuff.Mode.SRC);
2833        }
2834
2835        @Override
2836        public void setAlpha(int alpha) {
2837        }
2838
2839        @Override
2840        public void setColorFilter(ColorFilter cf) {
2841        }
2842
2843        @Override
2844        public int getOpacity() {
2845            return PixelFormat.OPAQUE;
2846        }
2847
2848        @Override
2849        public void setBounds(int left, int top, int right, int bottom) {
2850        }
2851
2852        @Override
2853        public void setBounds(Rect bounds) {
2854        }
2855    }
2856
2857    @Override
2858    public void destroy() {
2859        super.destroy();
2860        if (mStatusBarWindow != null) {
2861            mWindowManager.removeViewImmediate(mStatusBarWindow);
2862            mStatusBarWindow = null;
2863        }
2864        if (mNavigationBarView != null) {
2865            mWindowManager.removeViewImmediate(mNavigationBarView);
2866            mNavigationBarView = null;
2867        }
2868        mContext.unregisterReceiver(mBroadcastReceiver);
2869    }
2870
2871    private boolean mDemoModeAllowed;
2872    private boolean mDemoMode;
2873    private DemoStatusIcons mDemoStatusIcons;
2874
2875    @Override
2876    public void dispatchDemoCommand(String command, Bundle args) {
2877        if (!mDemoModeAllowed) {
2878            mDemoModeAllowed = Settings.Global.getInt(mContext.getContentResolver(),
2879                    "sysui_demo_allowed", 0) != 0;
2880        }
2881        if (!mDemoModeAllowed) return;
2882        if (command.equals(COMMAND_ENTER)) {
2883            mDemoMode = true;
2884        } else if (command.equals(COMMAND_EXIT)) {
2885            mDemoMode = false;
2886            checkBarModes();
2887        } else if (!mDemoMode) {
2888            // automatically enter demo mode on first demo command
2889            dispatchDemoCommand(COMMAND_ENTER, new Bundle());
2890        }
2891        boolean modeChange = command.equals(COMMAND_ENTER) || command.equals(COMMAND_EXIT);
2892        if (modeChange || command.equals(COMMAND_CLOCK)) {
2893            dispatchDemoCommandToView(command, args, R.id.clock);
2894        }
2895        if (modeChange || command.equals(COMMAND_BATTERY)) {
2896            dispatchDemoCommandToView(command, args, R.id.battery);
2897        }
2898        if (modeChange || command.equals(COMMAND_STATUS)) {
2899            if (mDemoStatusIcons == null) {
2900                mDemoStatusIcons = new DemoStatusIcons(mStatusIcons, mIconSize);
2901            }
2902            mDemoStatusIcons.dispatchDemoCommand(command, args);
2903        }
2904        if (mNetworkController != null && (modeChange || command.equals(COMMAND_NETWORK))) {
2905            mNetworkController.dispatchDemoCommand(command, args);
2906        }
2907        if (modeChange || command.equals(COMMAND_NOTIFICATIONS)) {
2908            View notifications = mStatusBarView == null ? null
2909                    : mStatusBarView.findViewById(R.id.notification_icon_area);
2910            if (notifications != null) {
2911                String visible = args.getString("visible");
2912                int vis = mDemoMode && "false".equals(visible) ? View.INVISIBLE : View.VISIBLE;
2913                notifications.setVisibility(vis);
2914            }
2915        }
2916        if (command.equals(COMMAND_BARS)) {
2917            String mode = args.getString("mode");
2918            int barMode = "opaque".equals(mode) ? MODE_OPAQUE :
2919                    "translucent".equals(mode) ? MODE_TRANSLUCENT :
2920                    "semi-transparent".equals(mode) ? MODE_SEMI_TRANSPARENT :
2921                    -1;
2922            if (barMode != -1) {
2923                boolean animate = true;
2924                if (mStatusBarView != null) {
2925                    mStatusBarView.getBarTransitions().transitionTo(barMode, animate);
2926                }
2927                if (mNavigationBarView != null) {
2928                    mNavigationBarView.getBarTransitions().transitionTo(barMode, animate);
2929                }
2930            }
2931        }
2932    }
2933
2934    private void dispatchDemoCommandToView(String command, Bundle args, int id) {
2935        if (mStatusBarView == null) return;
2936        View v = mStatusBarView.findViewById(id);
2937        if (v instanceof DemoMode) {
2938            ((DemoMode)v).dispatchDemoCommand(command, args);
2939        }
2940    }
2941
2942    public boolean isOnKeyguard() {
2943        return mOnKeyguard;
2944    }
2945
2946    public void showKeyguard() {
2947        mOnKeyguard = true;
2948        updateKeyguardState();
2949        instantExpandNotificationsPanel();
2950    }
2951
2952    public void hideKeyguard() {
2953        mOnKeyguard = false;
2954        updateKeyguardState();
2955        instantCollapseNotificationPanel();
2956    }
2957
2958    private void updatePublicMode() {
2959        setLockscreenPublicMode(mOnKeyguard && mStatusBarKeyguardViewManager.isSecure());
2960    }
2961
2962    private void updateKeyguardState() {
2963        if (mOnKeyguard) {
2964            if (isFlippedToSettings()) {
2965                flipToNotifications(false /*animate*/);
2966            }
2967            mKeyguardStatusView.setVisibility(View.VISIBLE);
2968            mKeyguardBottomArea.setVisibility(View.VISIBLE);
2969            mKeyguardIndicationTextView.setVisibility(View.VISIBLE);
2970            mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
2971            mKeyguardCarrierLabel.setVisibility(View.VISIBLE);
2972            mNotificationPanelHeader.setVisibility(View.GONE);
2973
2974            mKeyguardFlipper.setVisibility(View.VISIBLE);
2975            mSettingsContainer.setKeyguardShowing(true);
2976        } else {
2977            mKeyguardStatusView.setVisibility(View.GONE);
2978            mKeyguardBottomArea.setVisibility(View.GONE);
2979            mKeyguardIndicationTextView.setVisibility(View.GONE);
2980            mKeyguardCarrierLabel.setVisibility(View.GONE);
2981            mNotificationPanelHeader.setVisibility(View.VISIBLE);
2982
2983            mKeyguardFlipper.setVisibility(View.GONE);
2984            mSettingsContainer.setKeyguardShowing(false);
2985        }
2986
2987        updatePublicMode();
2988        updateRowStates();
2989        checkBarModes();
2990        updateNotificationIcons();
2991        updateCarrierLabelVisibility(false);
2992    }
2993
2994    public void userActivity() {
2995        if (mOnKeyguard) {
2996            mKeyguardViewMediatorCallback.userActivity();
2997        }
2998    }
2999
3000    public boolean onMenuPressed() {
3001        return mOnKeyguard && mStatusBarKeyguardViewManager.onMenuPressed();
3002    }
3003
3004    public boolean onBackPressed() {
3005        if (mOnKeyguard) {
3006            return mStatusBarKeyguardViewManager.onBackPressed();
3007        } else {
3008            animateCollapsePanels();
3009            return true;
3010        }
3011    }
3012
3013    private void showBouncer() {
3014        if (mOnKeyguard) {
3015            mStatusBarKeyguardViewManager.dismiss();
3016        }
3017    }
3018
3019    private void instantExpandNotificationsPanel() {
3020
3021        // Make our window larger and the panel visible.
3022        makeExpandedVisible(true);
3023        mNotificationPanel.setVisibility(View.VISIBLE);
3024
3025        // Wait for window manager to pickup the change, so we know the maximum height of the panel
3026        // then.
3027        mNotificationPanel.getViewTreeObserver().addOnGlobalLayoutListener(
3028                new ViewTreeObserver.OnGlobalLayoutListener() {
3029            @Override
3030            public void onGlobalLayout() {
3031                if (mStatusBarWindow.getHeight() != getStatusBarHeight()) {
3032                    mNotificationPanel.getViewTreeObserver().removeOnGlobalLayoutListener(this);
3033                    mNotificationPanel.setExpandedFraction(1);
3034                }
3035            }
3036        });
3037    }
3038
3039    private void instantCollapseNotificationPanel() {
3040        mNotificationPanel.setExpandedFraction(0);
3041    }
3042
3043    @Override
3044    public void onActivated(View view) {
3045        userActivity();
3046        mKeyguardIndicationTextView.switchIndication(R.string.notification_tap_again);
3047        super.onActivated(view);
3048    }
3049
3050    @Override
3051    public void onReset(View view) {
3052        super.onReset(view);
3053        mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
3054    }
3055
3056    public void onTrackingStarted() {
3057        if (mOnKeyguard) {
3058            mKeyguardIndicationTextView.switchIndication(R.string.keyguard_unlock);
3059        }
3060    }
3061
3062    public void onTrackingStopped() {
3063        if (mOnKeyguard) {
3064            mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
3065        }
3066    }
3067
3068    @Override
3069    protected int getMaxKeyguardNotifications() {
3070        return mKeyguardMaxNotificationCount;
3071    }
3072
3073    public NavigationBarView getNavigationBarView() {
3074        return mNavigationBarView;
3075    }
3076
3077    /**
3078     * @return a ViewGroup that spans the entire panel which contains the quick settings
3079     */
3080    public ViewGroup getQuickSettingsOverlayParent() {
3081        if (mHasQuickSettings) {
3082            return mNotificationPanel;
3083        } else {
3084            return null;
3085        }
3086    }
3087
3088    public static boolean inBounds(View view, MotionEvent event, boolean orAbove) {
3089        final int[] location = new int[2];
3090        view.getLocationInWindow(location);
3091        final int rx = (int) event.getRawX();
3092        final int ry = (int) event.getRawY();
3093        return rx >= location[0] && rx <= location[0] + view.getMeasuredWidth()
3094                && (orAbove || ry >= location[1]) && ry <= location[1] + view.getMeasuredHeight();
3095    }
3096
3097    private final class FlipperButton {
3098        private final View mHolder;
3099
3100        private ImageView mSettingsButton, mNotificationButton;
3101        private Animator mSettingsButtonAnim, mNotificationButtonAnim;
3102
3103        public FlipperButton(View holder) {
3104            mHolder = holder;
3105            mSettingsButton = (ImageView) holder.findViewById(R.id.settings_button);
3106            if (mSettingsButton != null) {
3107                mSettingsButton.setOnClickListener(mSettingsButtonListener);
3108                if (mHasQuickSettings) {
3109                    // the settings panel is hiding behind this button
3110                    mSettingsButton.setImageResource(R.drawable.ic_notify_quicksettings);
3111                    mSettingsButton.setVisibility(View.VISIBLE);
3112                } else {
3113                    // no settings panel, go straight to settings
3114                    mSettingsButton.setVisibility(View.VISIBLE);
3115                    mSettingsButton.setImageResource(R.drawable.ic_notify_settings);
3116                }
3117            }
3118            mNotificationButton = (ImageView) holder.findViewById(R.id.notification_button);
3119            if (mNotificationButton != null) {
3120                mNotificationButton.setOnClickListener(mNotificationButtonListener);
3121            }
3122        }
3123
3124        public boolean inHolderBounds(MotionEvent event) {
3125            return inBounds(mHolder, event, false);
3126        }
3127
3128        public void provisionCheck(boolean provisioned) {
3129            if (mSettingsButton != null) {
3130                mSettingsButton.setEnabled(provisioned);
3131            }
3132        }
3133
3134        public void userSetup(boolean userSetup) {
3135            if (mSettingsButton != null) {
3136                mSettingsButton.setVisibility(userSetup ? View.VISIBLE : View.INVISIBLE);
3137            }
3138        }
3139
3140        public void reset() {
3141            cancel();
3142            mSettingsButton.setVisibility(View.VISIBLE);
3143            mNotificationButton.setVisibility(View.GONE);
3144        }
3145
3146        public void refreshLayout() {
3147            if (mSettingsButton != null) {
3148                // Force asset reloading
3149                mSettingsButton.setImageDrawable(null);
3150                mSettingsButton.setImageResource(R.drawable.ic_notify_quicksettings);
3151            }
3152
3153            if (mNotificationButton != null) {
3154                // Force asset reloading
3155                mNotificationButton.setImageDrawable(null);
3156                mNotificationButton.setImageResource(R.drawable.ic_notifications);
3157            }
3158        }
3159
3160        public void flipToSettings(boolean animate) {
3161            mNotificationButton.setVisibility(View.VISIBLE);
3162            if (animate) {
3163                mSettingsButtonAnim = start(
3164                    setVisibilityWhenDone(
3165                        ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 0f)
3166                            .setDuration(FLIP_DURATION_OUT),
3167                        mStackScroller, View.INVISIBLE));
3168                mNotificationButtonAnim = start(
3169                    startDelay(FLIP_DURATION_OUT,
3170                        ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 1f)
3171                            .setDuration(FLIP_DURATION_IN)));
3172            } else {
3173                mSettingsButton.setAlpha(0f);
3174                mSettingsButton.setVisibility(View.INVISIBLE);
3175                mNotificationButton.setAlpha(1f);
3176            }
3177        }
3178
3179        public void flipToNotifications(boolean animate) {
3180            mSettingsButton.setVisibility(View.VISIBLE);
3181            if (animate) {
3182                mNotificationButtonAnim = start(
3183                    setVisibilityWhenDone(
3184                        ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 0f)
3185                            .setDuration(FLIP_DURATION_OUT),
3186                        mNotificationButton, View.INVISIBLE));
3187
3188                mSettingsButtonAnim = start(
3189                    startDelay(FLIP_DURATION_OUT,
3190                        ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 1f)
3191                            .setDuration(FLIP_DURATION_IN)));
3192            } else {
3193                mNotificationButton.setVisibility(View.INVISIBLE);
3194                mNotificationButton.setAlpha(0f);
3195                mSettingsButton.setAlpha(1f);
3196            }
3197        }
3198
3199        public void cancel() {
3200            cancelAnim(mSettingsButtonAnim);
3201            cancelAnim(mNotificationButtonAnim);
3202        }
3203
3204        public void setVisibility(int vis) {
3205            mHolder.setVisibility(vis);
3206        }
3207    }
3208}
3209