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