PhoneStatusBar.java revision 67e7d90376150042b9f464e8ca44f4b3a032f11c
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
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.AnimatorSet;
22import android.animation.ObjectAnimator;
23import android.app.ActivityManager;
24import android.app.ActivityManagerNative;
25import android.app.Dialog;
26import android.app.KeyguardManager;
27import android.app.Notification;
28import android.app.PendingIntent;
29import android.app.StatusBarManager;
30import android.content.BroadcastReceiver;
31import android.content.Context;
32import android.content.Intent;
33import android.content.IntentFilter;
34import android.content.res.Configuration;
35import android.content.res.Resources;
36import android.graphics.Canvas;
37import android.graphics.ColorFilter;
38import android.graphics.PixelFormat;
39import android.graphics.PorterDuff;
40import android.graphics.Rect;
41import android.graphics.drawable.Drawable;
42import android.graphics.drawable.NinePatchDrawable;
43import android.inputmethodservice.InputMethodService;
44import android.os.IBinder;
45import android.os.Message;
46import android.os.RemoteException;
47import android.os.ServiceManager;
48import android.os.SystemClock;
49import android.provider.Settings;
50import android.util.DisplayMetrics;
51import android.util.Log;
52import android.util.Slog;
53import android.view.Choreographer;
54import android.view.Display;
55import android.view.Gravity;
56import android.view.IWindowManager;
57import android.view.KeyEvent;
58import android.view.MotionEvent;
59import android.view.VelocityTracker;
60import android.view.View;
61import android.view.ViewGroup;
62import android.view.ViewGroup.LayoutParams;
63import android.view.WindowManager;
64import android.view.WindowManagerImpl;
65import android.view.animation.AccelerateInterpolator;
66import android.view.animation.Animation;
67import android.view.animation.AnimationUtils;
68import android.view.animation.DecelerateInterpolator;
69import android.widget.FrameLayout;
70import android.widget.ImageView;
71import android.widget.LinearLayout;
72import android.widget.ScrollView;
73import android.widget.TextView;
74import com.android.internal.statusbar.StatusBarIcon;
75import com.android.internal.statusbar.StatusBarNotification;
76import com.android.systemui.R;
77import com.android.systemui.recent.RecentTasksLoader;
78import com.android.systemui.statusbar.BaseStatusBar;
79import com.android.systemui.statusbar.NotificationData;
80import com.android.systemui.statusbar.NotificationData.Entry;
81import com.android.systemui.statusbar.CommandQueue;
82import com.android.systemui.statusbar.RotationToggle;
83import com.android.systemui.statusbar.SignalClusterView;
84import com.android.systemui.statusbar.StatusBarIconView;
85import com.android.systemui.statusbar.policy.BatteryController;
86import com.android.systemui.statusbar.policy.DateView;
87import com.android.systemui.statusbar.policy.IntruderAlertView;
88import com.android.systemui.statusbar.policy.LocationController;
89import com.android.systemui.statusbar.policy.NetworkController;
90import com.android.systemui.statusbar.policy.NotificationRowLayout;
91
92import java.io.FileDescriptor;
93import java.io.PrintWriter;
94import java.util.ArrayList;
95
96public class PhoneStatusBar extends BaseStatusBar {
97    static final String TAG = "PhoneStatusBar";
98    public static final boolean DEBUG = false;
99    public static final boolean SPEW = DEBUG;
100    public static final boolean DUMPTRUCK = true; // extra dumpsys info
101
102    // additional instrumentation for testing purposes; intended to be left on during development
103    public static final boolean CHATTY = DEBUG;
104
105    public static final String ACTION_STATUSBAR_START
106            = "com.android.internal.policy.statusbar.START";
107
108    private static final boolean DIM_BEHIND_EXPANDED_PANEL = false;
109
110    private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
111    private static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001;
112    // 1020-1030 reserved for BaseStatusBar
113
114    // will likely move to a resource or other tunable param at some point
115    private static final int INTRUDER_ALERT_DECAY_MS = 0; // disabled, was 10000;
116
117    private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;
118
119    private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; // see NotificationManagerService
120    private static final int HIDE_ICONS_BELOW_SCORE = Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER;
121
122    // fling gesture tuning parameters, scaled to display density
123    private float mSelfExpandVelocityPx; // classic value: 2000px/s
124    private float mSelfCollapseVelocityPx; // classic value: 2000px/s (will be negated to collapse "up")
125    private float mFlingExpandMinVelocityPx; // classic value: 200px/s
126    private float mFlingCollapseMinVelocityPx; // classic value: 200px/s
127    private float mCollapseMinDisplayFraction; // classic value: 0.08 (25px/min(320px,480px) on G1)
128    private float mExpandMinDisplayFraction; // classic value: 0.5 (drag open halfway to expand)
129    private float mFlingGestureMaxXVelocityPx; // classic value: 150px/s
130
131    private float mExpandAccelPx; // classic value: 2000px/s/s
132    private float mCollapseAccelPx; // classic value: 2000px/s/s (will be negated to collapse "up")
133
134    PhoneStatusBarPolicy mIconPolicy;
135
136    // These are no longer handled by the policy, because we need custom strategies for them
137    BatteryController mBatteryController;
138    LocationController mLocationController;
139    NetworkController mNetworkController;
140
141    int mNaturalBarHeight = -1;
142    int mIconSize = -1;
143    int mIconHPadding = -1;
144    Display mDisplay;
145
146    IWindowManager mWindowManager;
147
148    StatusBarWindowView mStatusBarWindow;
149    PhoneStatusBarView mStatusBarView;
150
151    int mPixelFormat;
152    Object mQueueLock = new Object();
153
154    // icons
155    LinearLayout mIcons;
156    IconMerger mNotificationIcons;
157    View mMoreIcon;
158    LinearLayout mStatusIcons;
159
160    // expanded notifications
161    View mNotificationPanel; // the sliding/resizing panel within the notification window
162    ScrollView mScrollView;
163    View mExpandedContents;
164    int mNotificationPanelMarginBottomPx, mNotificationPanelMarginLeftPx;
165    int mNotificationPanelGravity;
166    int mNotificationPanelMinHeight;
167
168    // top bar
169    View mClearButton;
170    View mSettingsButton;
171    RotationToggle mRotationButton;
172
173    // drag bar
174    CloseDragHandle mCloseView;
175    private int mCloseViewHeight;
176
177    // position
178    int[] mPositionTmp = new int[2];
179    boolean mExpanded;
180    boolean mExpandedVisible;
181
182    // the date view
183    DateView mDateView;
184
185    // for immersive activities
186    private IntruderAlertView mIntruderAlertView;
187
188    // on-screen navigation buttons
189    private NavigationBarView mNavigationBarView = null;
190
191    // the tracker view
192    int mTrackingPosition; // the position of the top of the tracking view.
193    private boolean mPanelSlightlyVisible;
194
195    // ticker
196    private Ticker mTicker;
197    private View mTickerView;
198    private boolean mTicking;
199
200    // Tracking finger for opening/closing.
201    int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
202    boolean mTracking;
203    VelocityTracker mVelocityTracker;
204
205    Choreographer mChoreographer;
206    boolean mAnimating;
207    boolean mClosing; // only valid when mAnimating; indicates the initial acceleration
208    float mAnimY;
209    float mAnimVel;
210    float mAnimAccel;
211    long mAnimLastTimeNanos;
212    boolean mAnimatingReveal = false;
213    int mViewDelta;
214    float mFlingVelocity;
215    int mFlingY;
216    int[] mAbsPos = new int[2];
217    Runnable mPostCollapseCleanup = null;
218
219    private AnimatorSet mLightsOutAnimation;
220    private AnimatorSet mLightsOnAnimation;
221
222    // for disabling the status bar
223    int mDisabled = 0;
224
225    // tracking calls to View.setSystemUiVisibility()
226    int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
227
228    DisplayMetrics mDisplayMetrics = new DisplayMetrics();
229
230    private int mNavigationIconHints = 0;
231    private final Animator.AnimatorListener mMakeIconsInvisible = new AnimatorListenerAdapter() {
232        @Override
233        public void onAnimationEnd(Animator animation) {
234            // double-check to avoid races
235            if (mIcons.getAlpha() == 0) {
236                Slog.d(TAG, "makeIconsInvisible");
237                mIcons.setVisibility(View.INVISIBLE);
238            }
239        }
240    };
241
242    private final Runnable mStartRevealAnimation = new Runnable() {
243        @Override
244        public void run() {
245            mAnimAccel = mExpandAccelPx;
246            mAnimVel = mFlingExpandMinVelocityPx;
247            mAnimY = getStatusBarHeight();
248            updateExpandedViewPos((int)mAnimY);
249
250            mAnimating = true;
251            mAnimatingReveal = true;
252            resetLastAnimTime();
253            mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION,
254                mAnimationCallback, null);
255            mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION,
256                mRevealAnimationCallback, null);
257            mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION,
258                mRevealAnimationCallback, null);
259        }
260    };
261
262    private final Runnable mPerformFling = new Runnable() {
263        @Override
264        public void run() {
265            performFling(mFlingY + mViewDelta, mFlingVelocity, false);
266        }
267    };
268
269    private class ExpandedDialog extends Dialog {
270        ExpandedDialog(Context context) {
271            super(context, com.android.internal.R.style.Theme_Translucent_NoTitleBar);
272        }
273
274        @Override
275        public boolean dispatchKeyEvent(KeyEvent event) {
276            boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
277            switch (event.getKeyCode()) {
278            case KeyEvent.KEYCODE_BACK:
279                if (!down) {
280                    animateCollapse();
281                }
282                return true;
283            }
284            return super.dispatchKeyEvent(event);
285        }
286    }
287
288    @Override
289    public void start() {
290        mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
291                .getDefaultDisplay();
292
293        mWindowManager = IWindowManager.Stub.asInterface(
294                ServiceManager.getService(Context.WINDOW_SERVICE));
295
296        super.start(); // calls createAndAddWindows()
297
298        addNavigationBar();
299
300        if (ENABLE_INTRUDERS) addIntruderView();
301
302        // Lastly, call to the icon policy to install/update all the icons.
303        mIconPolicy = new PhoneStatusBarPolicy(mContext);
304    }
305
306    // ================================================================================
307    // Constructing the view
308    // ================================================================================
309    protected PhoneStatusBarView makeStatusBarView() {
310        final Context context = mContext;
311
312        Resources res = context.getResources();
313
314        updateDisplaySize(); // populates mDisplayMetrics
315        loadDimens();
316
317        mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
318
319        mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
320                R.layout.super_status_bar, null);
321        if (DEBUG) {
322            mStatusBarWindow.setBackgroundColor(0x6000FF80);
323        }
324        mStatusBarWindow.mService = this;
325        mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() {
326            @Override
327            public boolean onTouch(View v, MotionEvent event) {
328                if (event.getAction() == MotionEvent.ACTION_DOWN) {
329                    if (mExpanded && !mAnimating) {
330                        animateCollapse();
331                    }
332                }
333                return mStatusBarWindow.onTouchEvent(event);
334            }});
335
336        mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
337        mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel);
338        // don't allow clicks on the panel to pass through to the background where they will cause the panel to close
339        mNotificationPanel.setOnTouchListener(new View.OnTouchListener() {
340            @Override
341            public boolean onTouch(View v, MotionEvent event) {
342                return true;
343            }
344        });
345        mNotificationPanel.setSystemUiVisibility(
346                  View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER
347                | View.STATUS_BAR_DISABLE_SYSTEM_INFO);
348
349        if (!ActivityManager.isHighEndGfx(mDisplay)) {
350            mStatusBarWindow.setBackground(null);
351            mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor(
352                    R.color.notification_panel_solid_background)));
353        }
354        if (ENABLE_INTRUDERS) {
355            mIntruderAlertView = (IntruderAlertView) View.inflate(context, R.layout.intruder_alert, null);
356            mIntruderAlertView.setVisibility(View.GONE);
357            mIntruderAlertView.setBar(this);
358        }
359
360        mStatusBarView.mService = this;
361
362        mChoreographer = Choreographer.getInstance();
363
364        try {
365            boolean showNav = mWindowManager.hasNavigationBar();
366            if (DEBUG) Slog.v(TAG, "hasNavigationBar=" + showNav);
367            if (showNav) {
368                mNavigationBarView =
369                    (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null);
370
371                mNavigationBarView.setDisabledFlags(mDisabled);
372                mNavigationBarView.setBar(this);
373            }
374        } catch (RemoteException ex) {
375            // no window manager? good luck with that
376        }
377
378        // figure out which pixel-format to use for the status bar.
379        mPixelFormat = PixelFormat.OPAQUE;
380        mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons);
381        mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);
382        mNotificationIcons.setOverflowIndicator(mMoreIcon);
383        mIcons = (LinearLayout)mStatusBarView.findViewById(R.id.icons);
384        mTickerView = mStatusBarView.findViewById(R.id.ticker);
385
386        mPile = (NotificationRowLayout)mStatusBarWindow.findViewById(R.id.latestItems);
387        mPile.setLongPressListener(getNotificationLongClicker());
388        mExpandedContents = mPile; // was: expanded.findViewById(R.id.notificationLinearLayout);
389
390        mClearButton = mStatusBarWindow.findViewById(R.id.clear_all_button);
391        mClearButton.setOnClickListener(mClearButtonListener);
392        mClearButton.setAlpha(0f);
393        mClearButton.setVisibility(View.INVISIBLE);
394        mClearButton.setEnabled(false);
395        mDateView = (DateView)mStatusBarWindow.findViewById(R.id.date);
396        mSettingsButton = mStatusBarWindow.findViewById(R.id.settings_button);
397        mSettingsButton.setOnClickListener(mSettingsButtonListener);
398        mRotationButton = (RotationToggle) mStatusBarWindow.findViewById(R.id.rotation_lock_button);
399
400        mScrollView = (ScrollView)mStatusBarWindow.findViewById(R.id.scroll);
401        mScrollView.setVerticalScrollBarEnabled(false); // less drawing during pulldowns
402
403        mTicker = new MyTicker(context, mStatusBarView);
404
405        TickerView tickerView = (TickerView)mStatusBarView.findViewById(R.id.tickerText);
406        tickerView.mTicker = mTicker;
407
408        mCloseView = (CloseDragHandle)mStatusBarWindow.findViewById(R.id.close);
409        mCloseView.mService = this;
410        mCloseViewHeight = res.getDimensionPixelSize(R.dimen.close_handle_height);
411
412        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
413
414        // set the inital view visibility
415        setAreThereNotifications();
416
417        // Other icons
418        mLocationController = new LocationController(mContext); // will post a notification
419        mBatteryController = new BatteryController(mContext);
420        mBatteryController.addIconView((ImageView)mStatusBarView.findViewById(R.id.battery));
421        mNetworkController = new NetworkController(mContext);
422        final SignalClusterView signalCluster =
423                (SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster);
424        mNetworkController.addSignalCluster(signalCluster);
425        signalCluster.setNetworkController(mNetworkController);
426//        final ImageView wimaxRSSI =
427//                (ImageView)sb.findViewById(R.id.wimax_signal);
428//        if (wimaxRSSI != null) {
429//            mNetworkController.addWimaxIconView(wimaxRSSI);
430//        }
431        // Recents Panel
432        mRecentTasksLoader = new RecentTasksLoader(context);
433        updateRecentsPanel();
434
435        // receive broadcasts
436        IntentFilter filter = new IntentFilter();
437        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
438        filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
439        filter.addAction(Intent.ACTION_SCREEN_OFF);
440        context.registerReceiver(mBroadcastReceiver, filter);
441
442        return mStatusBarView;
443    }
444
445    @Override
446    protected WindowManager.LayoutParams getRecentsLayoutParams(LayoutParams layoutParams) {
447        boolean opaque = false;
448        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
449                layoutParams.width,
450                layoutParams.height,
451                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
452                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
453                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
454                | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
455                (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
456        if (ActivityManager.isHighEndGfx(mDisplay)) {
457            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
458        } else {
459            lp.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
460            lp.dimAmount = 0.75f;
461        }
462        lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
463        lp.setTitle("RecentsPanel");
464        lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications;
465        lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
466        | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
467        return lp;
468    }
469
470    @Override
471    protected WindowManager.LayoutParams getSearchLayoutParams(LayoutParams layoutParams) {
472        boolean opaque = false;
473        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
474                LayoutParams.MATCH_PARENT,
475                LayoutParams.MATCH_PARENT,
476                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
477                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
478                | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
479                | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
480                (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
481        if (ActivityManager.isHighEndGfx(mDisplay)) {
482            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
483        }
484        lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
485        lp.setTitle("SearchPanel");
486        // TODO: Define custom animation for Search panel
487        lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications;
488        lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED
489        | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
490        return lp;
491    }
492
493    protected void updateRecentsPanel() {
494        super.updateRecentsPanel(R.layout.status_bar_recent_panel);
495        // Make .03 alpha the minimum so you always see the item a bit-- slightly below
496        // .03, the item disappears entirely (as if alpha = 0) and that discontinuity looks
497        // a bit jarring
498        mRecentsPanel.setMinSwipeAlpha(0.03f);
499        if (mNavigationBarView != null) {
500            mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPanel);
501        }
502    }
503
504    @Override
505    protected void updateSearchPanel() {
506        super.updateSearchPanel();
507        mSearchPanelView.setStatusBarView(mNavigationBarView);
508        mNavigationBarView.setDelegateView(mSearchPanelView);
509    }
510
511    @Override
512    public void showSearchPanel() {
513        super.showSearchPanel();
514        WindowManager.LayoutParams lp =
515            (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
516        lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
517        WindowManagerImpl.getDefault().updateViewLayout(mNavigationBarView, lp);
518    }
519
520    @Override
521    public void hideSearchPanel() {
522        super.hideSearchPanel();
523        WindowManager.LayoutParams lp =
524            (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams();
525        lp.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
526        WindowManagerImpl.getDefault().updateViewLayout(mNavigationBarView, lp);
527    }
528
529    protected int getStatusBarGravity() {
530        return Gravity.TOP | Gravity.FILL_HORIZONTAL;
531    }
532
533    public int getStatusBarHeight() {
534        if (mNaturalBarHeight < 0) {
535            final Resources res = mContext.getResources();
536            mNaturalBarHeight =
537                    res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
538        }
539        return mNaturalBarHeight;
540    }
541
542    private int getCloseViewHeight() {
543        return mCloseViewHeight;
544    }
545
546    private View.OnClickListener mRecentsClickListener = new View.OnClickListener() {
547        public void onClick(View v) {
548            toggleRecentApps();
549        }
550    };
551
552    private int mShowSearchHoldoff = 0;
553    private Runnable mShowSearchPanel = new Runnable() {
554        public void run() {
555            showSearchPanel();
556        }
557    };
558
559    View.OnTouchListener mHomeSearchActionListener = new View.OnTouchListener() {
560        public boolean onTouch(View v, MotionEvent event) {
561            switch(event.getAction()) {
562            case MotionEvent.ACTION_DOWN:
563                if (!shouldDisableNavbarGestures()) {
564                    mHandler.removeCallbacks(mShowSearchPanel);
565                    mHandler.postDelayed(mShowSearchPanel, mShowSearchHoldoff);
566                }
567            break;
568
569            case MotionEvent.ACTION_UP:
570            case MotionEvent.ACTION_CANCEL:
571                mHandler.removeCallbacks(mShowSearchPanel);
572            break;
573        }
574        return false;
575        }
576    };
577
578    private void prepareNavigationBarView() {
579        mNavigationBarView.reorient();
580
581        mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
582        mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPanel);
583        mNavigationBarView.getHomeButton().setOnTouchListener(mHomeSearchActionListener);
584        updateSearchPanel();
585    }
586
587    // For small-screen devices (read: phones) that lack hardware navigation buttons
588    private void addNavigationBar() {
589        if (DEBUG) Slog.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);
590        if (mNavigationBarView == null) return;
591
592        prepareNavigationBarView();
593
594        WindowManagerImpl.getDefault().addView(
595                mNavigationBarView, getNavigationBarLayoutParams());
596    }
597
598    private void repositionNavigationBar() {
599        if (mNavigationBarView == null) return;
600
601        prepareNavigationBarView();
602
603        WindowManagerImpl.getDefault().updateViewLayout(
604                mNavigationBarView, getNavigationBarLayoutParams());
605    }
606
607    private WindowManager.LayoutParams getNavigationBarLayoutParams() {
608        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
609                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
610                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
611                    0
612                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
613                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
614                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
615                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
616                PixelFormat.OPAQUE);
617        // this will allow the navbar to run in an overlay on devices that support this
618        if (ActivityManager.isHighEndGfx(mDisplay)) {
619            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
620        }
621
622        lp.setTitle("NavigationBar");
623        lp.windowAnimations = 0;
624
625        return lp;
626    }
627
628    private void addIntruderView() {
629        final int height = getStatusBarHeight();
630
631        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
632                ViewGroup.LayoutParams.MATCH_PARENT,
633                ViewGroup.LayoutParams.WRAP_CONTENT,
634                WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar!
635                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
636                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
637                    | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
638                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
639                    | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
640                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
641                PixelFormat.TRANSLUCENT);
642        lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
643        //lp.y += height * 1.5; // FIXME
644        lp.setTitle("IntruderAlert");
645        lp.packageName = mContext.getPackageName();
646        lp.windowAnimations = R.style.Animation_StatusBar_IntruderAlert;
647
648        WindowManagerImpl.getDefault().addView(mIntruderAlertView, lp);
649    }
650
651    public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
652        if (SPEW) Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
653                + " icon=" + icon);
654        StatusBarIconView view = new StatusBarIconView(mContext, slot, null);
655        view.set(icon);
656        mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize));
657    }
658
659    public void updateIcon(String slot, int index, int viewIndex,
660            StatusBarIcon old, StatusBarIcon icon) {
661        if (SPEW) Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
662                + " old=" + old + " icon=" + icon);
663        StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex);
664        view.set(icon);
665    }
666
667    public void removeIcon(String slot, int index, int viewIndex) {
668        if (SPEW) Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
669        mStatusIcons.removeViewAt(viewIndex);
670    }
671
672    public void addNotification(IBinder key, StatusBarNotification notification) {
673        /* if (DEBUG) */ Slog.d(TAG, "addNotification score=" + notification.score);
674        StatusBarIconView iconView = addNotificationViews(key, notification);
675        if (iconView == null) return;
676
677        boolean immersive = false;
678        try {
679            immersive = ActivityManagerNative.getDefault().isTopActivityImmersive();
680            if (DEBUG) {
681                Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive"));
682            }
683        } catch (RemoteException ex) {
684        }
685
686        /*
687         * DISABLED due to missing API
688        if (ENABLE_INTRUDERS && (
689                   // TODO(dsandler): Only if the screen is on
690                notification.notification.intruderView != null)) {
691            Slog.d(TAG, "Presenting high-priority notification");
692            // special new transient ticker mode
693            // 1. Populate mIntruderAlertView
694
695            if (notification.notification.intruderView == null) {
696                Slog.e(TAG, notification.notification.toString() + " wanted to intrude but intruderView was null");
697                return;
698            }
699
700            // bind the click event to the content area
701            PendingIntent contentIntent = notification.notification.contentIntent;
702            final View.OnClickListener listener = (contentIntent != null)
703                    ? new NotificationClicker(contentIntent,
704                            notification.pkg, notification.tag, notification.id)
705                    : null;
706
707            mIntruderAlertView.applyIntruderContent(notification.notification.intruderView, listener);
708
709            mCurrentlyIntrudingNotification = notification;
710
711            // 2. Animate mIntruderAlertView in
712            mHandler.sendEmptyMessage(MSG_SHOW_INTRUDER);
713
714            // 3. Set alarm to age the notification off (TODO)
715            mHandler.removeMessages(MSG_HIDE_INTRUDER);
716            if (INTRUDER_ALERT_DECAY_MS > 0) {
717                mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS);
718            }
719        } else
720         */
721
722        if (notification.notification.fullScreenIntent != null) {
723            // not immersive & a full-screen alert should be shown
724            Slog.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
725            try {
726                notification.notification.fullScreenIntent.send();
727            } catch (PendingIntent.CanceledException e) {
728            }
729        } else {
730            // usual case: status bar visible & not immersive
731
732            // show the ticker if there isn't an intruder too
733            if (mCurrentlyIntrudingNotification == null) {
734                tick(null, notification, true);
735            }
736        }
737
738        // Recalculate the position of the sliding windows and the titles.
739        setAreThereNotifications();
740        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
741    }
742
743    public void removeNotification(IBinder key) {
744        StatusBarNotification old = removeNotificationViews(key);
745        if (SPEW) Slog.d(TAG, "removeNotification key=" + key + " old=" + old);
746
747        if (old != null) {
748            // Cancel the ticker if it's still running
749            mTicker.removeEntry(old);
750
751            // Recalculate the position of the sliding windows and the titles.
752            updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
753
754            if (ENABLE_INTRUDERS && old == mCurrentlyIntrudingNotification) {
755                mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
756            }
757
758            if (CLOSE_PANEL_WHEN_EMPTIED && mNotificationData.size() == 0 && !mAnimating) {
759                animateCollapse();
760            }
761        }
762
763        setAreThereNotifications();
764    }
765
766    @Override
767    protected void onConfigurationChanged(Configuration newConfig) {
768        updateRecentsPanel();
769        mShowSearchHoldoff  = mContext.getResources().getInteger(
770                R.integer.config_show_search_delay);
771    }
772
773    private void loadNotificationShade() {
774        if (mPile == null) return;
775
776        int N = mNotificationData.size();
777
778        ArrayList<View> toShow = new ArrayList<View>();
779
780        final boolean provisioned = isDeviceProvisioned();
781        // If the device hasn't been through Setup, we only show system notifications
782        for (int i=0; i<N; i++) {
783            Entry ent = mNotificationData.get(N-i-1);
784            if (provisioned || "android".equals(ent.notification.pkg)) {
785                toShow.add(ent.row);
786            }
787        }
788
789        ArrayList<View> toRemove = new ArrayList<View>();
790        for (int i=0; i<mPile.getChildCount(); i++) {
791            View child = mPile.getChildAt(i);
792            if (!toShow.contains(child)) {
793                toRemove.add(child);
794            }
795        }
796
797        for (View remove : toRemove) {
798            mPile.removeView(remove);
799        }
800
801        for (int i=0; i<toShow.size(); i++) {
802            View v = toShow.get(i);
803            if (v.getParent() == null) {
804                mPile.addView(v, i);
805            }
806        }
807
808        mSettingsButton.setEnabled(isDeviceProvisioned());
809    }
810
811    private void reloadAllNotificationIcons() {
812        if (mNotificationIcons == null) return;
813        mNotificationIcons.removeAllViews();
814        updateNotificationIcons();
815    }
816
817    @Override
818    protected void updateNotificationIcons() {
819        if (mNotificationIcons == null) return;
820
821        loadNotificationShade();
822
823        final LinearLayout.LayoutParams params
824            = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
825
826        int N = mNotificationData.size();
827
828        if (DEBUG) {
829            Slog.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" + mNotificationIcons);
830        }
831
832        ArrayList<View> toShow = new ArrayList<View>();
833
834        final boolean provisioned = isDeviceProvisioned();
835        // If the device hasn't been through Setup, we only show system notifications
836        for (int i=0; i<N; i++) {
837            Entry ent = mNotificationData.get(N-i-1);
838            if ((provisioned && ent.notification.score >= HIDE_ICONS_BELOW_SCORE)
839                    || "android".equals(ent.notification.pkg)) {
840                toShow.add(ent.icon);
841            }
842        }
843
844        ArrayList<View> toRemove = new ArrayList<View>();
845        for (int i=0; i<mNotificationIcons.getChildCount(); i++) {
846            View child = mNotificationIcons.getChildAt(i);
847            if (!toShow.contains(child)) {
848                toRemove.add(child);
849            }
850        }
851
852        for (View remove : toRemove) {
853            mNotificationIcons.removeView(remove);
854        }
855
856        for (int i=0; i<toShow.size(); i++) {
857            View v = toShow.get(i);
858            if (v.getParent() == null) {
859                mNotificationIcons.addView(v, i, params);
860            }
861        }
862    }
863
864    @Override
865    protected void setAreThereNotifications() {
866        final boolean any = mNotificationData.size() > 0;
867
868        final boolean clearable = any && mNotificationData.hasClearableItems();
869
870        if (DEBUG) {
871            Slog.d(TAG, "setAreThereNotifications: N=" + mNotificationData.size()
872                    + " any=" + any + " clearable=" + clearable);
873        }
874
875        if (mClearButton.isShown()) {
876            if (clearable != (mClearButton.getAlpha() == 1.0f)) {
877                ObjectAnimator clearAnimation = ObjectAnimator.ofFloat(
878                        mClearButton, "alpha", clearable ? 1.0f : 0.0f).setDuration(250);
879                clearAnimation.addListener(new AnimatorListenerAdapter() {
880                    @Override
881                    public void onAnimationEnd(Animator animation) {
882                        if (mClearButton.getAlpha() <= 0.0f) {
883                            mClearButton.setVisibility(View.INVISIBLE);
884                        }
885                    }
886
887                    @Override
888                    public void onAnimationStart(Animator animation) {
889                        if (mClearButton.getAlpha() <= 0.0f) {
890                            mClearButton.setVisibility(View.VISIBLE);
891                        }
892                    }
893                });
894                clearAnimation.start();
895            }
896        } else {
897            mClearButton.setAlpha(clearable ? 1.0f : 0.0f);
898            mClearButton.setVisibility(clearable ? View.VISIBLE : View.INVISIBLE);
899        }
900        mClearButton.setEnabled(clearable);
901
902        final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out);
903        final boolean showDot = (any&&!areLightsOn());
904        if (showDot != (nlo.getAlpha() == 1.0f)) {
905            if (showDot) {
906                nlo.setAlpha(0f);
907                nlo.setVisibility(View.VISIBLE);
908            }
909            nlo.animate()
910                .alpha(showDot?1:0)
911                .setDuration(showDot?750:250)
912                .setInterpolator(new AccelerateInterpolator(2.0f))
913                .setListener(showDot ? null : new AnimatorListenerAdapter() {
914                    @Override
915                    public void onAnimationEnd(Animator _a) {
916                        nlo.setVisibility(View.GONE);
917                    }
918                })
919                .start();
920        }
921    }
922
923    public void showClock(boolean show) {
924        if (mStatusBarView == null) return;
925        View clock = mStatusBarView.findViewById(R.id.clock);
926        if (clock != null) {
927            clock.setVisibility(show ? View.VISIBLE : View.GONE);
928        }
929    }
930
931    /**
932     * State is one or more of the DISABLE constants from StatusBarManager.
933     */
934    public void disable(int state) {
935        final int old = mDisabled;
936        final int diff = state ^ old;
937        mDisabled = state;
938
939        if (DEBUG) {
940            Slog.d(TAG, String.format("disable: 0x%08x -> 0x%08x (diff: 0x%08x)",
941                old, state, diff));
942        }
943
944        StringBuilder flagdbg = new StringBuilder();
945        flagdbg.append("disable: < ");
946        flagdbg.append(((state & StatusBarManager.DISABLE_EXPAND) != 0) ? "EXPAND" : "expand");
947        flagdbg.append(((diff  & StatusBarManager.DISABLE_EXPAND) != 0) ? "* " : " ");
948        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "ICONS" : "icons");
949        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "* " : " ");
950        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "ALERTS" : "alerts");
951        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "* " : " ");
952        flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) ? "TICKER" : "ticker");
953        flagdbg.append(((diff  & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) ? "* " : " ");
954        flagdbg.append(((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info");
955        flagdbg.append(((diff  & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " ");
956        flagdbg.append(((state & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back");
957        flagdbg.append(((diff  & StatusBarManager.DISABLE_BACK) != 0) ? "* " : " ");
958        flagdbg.append(((state & StatusBarManager.DISABLE_HOME) != 0) ? "HOME" : "home");
959        flagdbg.append(((diff  & StatusBarManager.DISABLE_HOME) != 0) ? "* " : " ");
960        flagdbg.append(((state & StatusBarManager.DISABLE_RECENT) != 0) ? "RECENT" : "recent");
961        flagdbg.append(((diff  & StatusBarManager.DISABLE_RECENT) != 0) ? "* " : " ");
962        flagdbg.append(((state & StatusBarManager.DISABLE_CLOCK) != 0) ? "CLOCK" : "clock");
963        flagdbg.append(((diff  & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " ");
964        flagdbg.append(">");
965        Slog.d(TAG, flagdbg.toString());
966
967        if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
968            mIcons.animate().cancel();
969            if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
970                if (mTicking) {
971                    mTicker.halt();
972                }
973                mIcons.animate()
974                    .alpha(0f)
975                    .translationY(mNaturalBarHeight*0.5f)
976                    //.setStartDelay(100)
977                    .setDuration(175)
978                    .setInterpolator(new DecelerateInterpolator(1.5f))
979                    .setListener(mMakeIconsInvisible)
980                    .start();
981            } else {
982                mIcons.setVisibility(View.VISIBLE);
983                mIcons.animate()
984                    .alpha(1f)
985                    .translationY(0)
986                    .setStartDelay(0)
987                    .setInterpolator(new DecelerateInterpolator(1.5f))
988                    .setDuration(175)
989                    .start();
990            }
991        }
992
993        if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) {
994            boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0;
995            showClock(show);
996        }
997        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
998            if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
999                animateCollapse();
1000            }
1001        }
1002
1003        if ((diff & (StatusBarManager.DISABLE_HOME
1004                        | StatusBarManager.DISABLE_RECENT
1005                        | StatusBarManager.DISABLE_BACK)) != 0) {
1006            // the nav bar will take care of these
1007            if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state);
1008
1009            if ((state & StatusBarManager.DISABLE_RECENT) != 0) {
1010                // close recents if it's visible
1011                mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
1012                mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
1013            }
1014        }
1015
1016        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1017            if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1018                if (mTicking) {
1019                    mTicker.halt();
1020                } else {
1021                    setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
1022                }
1023            } else {
1024                if (!mExpandedVisible) {
1025                    setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1026                }
1027            }
1028        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1029            if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1030                mTicker.halt();
1031            }
1032        }
1033    }
1034
1035    @Override
1036    protected BaseStatusBar.H createHandler() {
1037        return new PhoneStatusBar.H();
1038    }
1039
1040    /**
1041     * All changes to the status bar and notifications funnel through here and are batched.
1042     */
1043    private class H extends BaseStatusBar.H {
1044        public void handleMessage(Message m) {
1045            super.handleMessage(m);
1046            switch (m.what) {
1047                case MSG_OPEN_NOTIFICATION_PANEL:
1048                    animateExpand();
1049                    break;
1050                case MSG_CLOSE_NOTIFICATION_PANEL:
1051                    animateCollapse();
1052                    break;
1053                case MSG_SHOW_INTRUDER:
1054                    setIntruderAlertVisibility(true);
1055                    break;
1056                case MSG_HIDE_INTRUDER:
1057                    setIntruderAlertVisibility(false);
1058                    mCurrentlyIntrudingNotification = null;
1059                    break;
1060            }
1061        }
1062    }
1063
1064    final Runnable mAnimationCallback = new Runnable() {
1065        @Override
1066        public void run() {
1067            doAnimation(mChoreographer.getFrameTimeNanos());
1068        }
1069    };
1070
1071    final Runnable mRevealAnimationCallback = new Runnable() {
1072        @Override
1073        public void run() {
1074            doRevealAnimation(mChoreographer.getFrameTimeNanos());
1075        }
1076    };
1077
1078    View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
1079        public void onFocusChange(View v, boolean hasFocus) {
1080            // Because 'v' is a ViewGroup, all its children will be (un)selected
1081            // too, which allows marqueeing to work.
1082            v.setSelected(hasFocus);
1083        }
1084    };
1085
1086    private void makeExpandedVisible(boolean revealAfterDraw) {
1087        if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
1088        if (mExpandedVisible) {
1089            return;
1090        }
1091
1092        mExpandedVisible = true;
1093        makeSlippery(mNavigationBarView, true);
1094
1095        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
1096
1097        // Expand the window to encompass the full screen in anticipation of the drag.
1098        // This is only possible to do atomically because the status bar is at the top of the screen!
1099        WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams();
1100        lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1101        lp.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1102        lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
1103        final WindowManager wm = WindowManagerImpl.getDefault();
1104        wm.updateViewLayout(mStatusBarWindow, lp);
1105
1106        // Updating the window layout will force an expensive traversal/redraw.
1107        // Kick off the reveal animation after this is complete to avoid animation latency.
1108        if (revealAfterDraw) {
1109            mHandler.post(mStartRevealAnimation);
1110        }
1111
1112        visibilityChanged(true);
1113    }
1114
1115    private static void makeSlippery(View view, boolean slippery) {
1116        if (view == null) {
1117            return;
1118        }
1119        WindowManager.LayoutParams lp = (WindowManager.LayoutParams) view.getLayoutParams();
1120        if (slippery) {
1121            lp.flags |= WindowManager.LayoutParams.FLAG_SLIPPERY;
1122        } else {
1123            lp.flags &= ~WindowManager.LayoutParams.FLAG_SLIPPERY;
1124        }
1125        WindowManagerImpl.getDefault().updateViewLayout(view, lp);
1126    }
1127
1128    public void animateExpand() {
1129        if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded);
1130        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
1131            return ;
1132        }
1133        if (mExpanded) {
1134            return;
1135        }
1136
1137        prepareTracking(0, true);
1138        performFling(0, mSelfExpandVelocityPx, true);
1139    }
1140
1141    public void animateCollapse() {
1142        animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
1143    }
1144
1145    public void animateCollapse(int flags) {
1146        animateCollapse(flags, 1.0f);
1147    }
1148
1149    public void animateCollapse(int flags, float velocityMultiplier) {
1150        if (SPEW) {
1151            Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
1152                    + " mExpandedVisible=" + mExpandedVisible
1153                    + " mExpanded=" + mExpanded
1154                    + " mAnimating=" + mAnimating
1155                    + " mAnimY=" + mAnimY
1156                    + " mAnimVel=" + mAnimVel
1157                    + " flags=" + flags);
1158        }
1159
1160        if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
1161            mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
1162            mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
1163        }
1164
1165        if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) {
1166            mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL);
1167            mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL);
1168        }
1169
1170        if (!mExpandedVisible) {
1171            return;
1172        }
1173
1174        int y;
1175        if (mAnimating) {
1176            y = (int)mAnimY;
1177        } else {
1178            y = getExpandedViewMaxHeight()-1;
1179        }
1180        // Let the fling think that we're open so it goes in the right direction
1181        // and doesn't try to re-open the windowshade.
1182        mExpanded = true;
1183        prepareTracking(y, false);
1184        performFling(y, -mSelfCollapseVelocityPx*velocityMultiplier, true);
1185    }
1186
1187    void performExpand() {
1188        if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded);
1189        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
1190            return ;
1191        }
1192        if (mExpanded) {
1193            return;
1194        }
1195
1196        mExpanded = true;
1197        makeExpandedVisible(false);
1198        updateExpandedViewPos(EXPANDED_FULL_OPEN);
1199
1200        if (false) postStartTracing();
1201    }
1202
1203    void performCollapse() {
1204        if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded
1205                + " mExpandedVisible=" + mExpandedVisible);
1206
1207        if (!mExpandedVisible) {
1208            return;
1209        }
1210        mExpandedVisible = false;
1211        visibilityChanged(false);
1212        makeSlippery(mNavigationBarView, false);
1213
1214        // Shrink the window to the size of the status bar only
1215        WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams();
1216        lp.height = getStatusBarHeight();
1217        lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1218        lp.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1219        final WindowManager wm = WindowManagerImpl.getDefault();
1220        wm.updateViewLayout(mStatusBarWindow, lp);
1221
1222        if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
1223            setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1224        }
1225
1226        if (!mExpanded) {
1227            return;
1228        }
1229        mExpanded = false;
1230
1231        // Close any "App info" popups that might have snuck on-screen
1232        dismissPopups();
1233
1234        if (mPostCollapseCleanup != null) {
1235            mPostCollapseCleanup.run();
1236            mPostCollapseCleanup = null;
1237        }
1238    }
1239
1240    void resetLastAnimTime() {
1241        mAnimLastTimeNanos = System.nanoTime();
1242        if (SPEW) {
1243            Throwable t = new Throwable();
1244            t.fillInStackTrace();
1245            Slog.d(TAG, "resetting last anim time=" + mAnimLastTimeNanos, t);
1246        }
1247    }
1248
1249    void doAnimation(long frameTimeNanos) {
1250        if (mAnimating) {
1251            if (SPEW) Slog.d(TAG, "doAnimation dt=" + (frameTimeNanos - mAnimLastTimeNanos));
1252            if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
1253            incrementAnim(frameTimeNanos);
1254            if (SPEW) {
1255                Slog.d(TAG, "doAnimation after  mAnimY=" + mAnimY);
1256                Slog.d(TAG, "doAnimation expandedViewMax=" + getExpandedViewMaxHeight());
1257            }
1258
1259            if (mAnimY >= getExpandedViewMaxHeight()-1 && !mClosing) {
1260                if (SPEW) Slog.d(TAG, "Animation completed to expanded state.");
1261                mAnimating = false;
1262                updateExpandedViewPos(EXPANDED_FULL_OPEN);
1263                performExpand();
1264                return;
1265            }
1266
1267            if (mAnimY == 0 && mAnimAccel == 0 && mClosing) {
1268                if (SPEW) Slog.d(TAG, "Animation completed to collapsed state.");
1269                mAnimating = false;
1270                performCollapse();
1271                return;
1272            }
1273
1274            if (mAnimY < getStatusBarHeight() && mClosing) {
1275                // Draw one more frame with the bar positioned at the top of the screen
1276                // before ending the animation so that the user sees the bar in
1277                // its final position.  The call to performCollapse() causes a window
1278                // relayout which takes time and might cause the animation to skip
1279                // on the very last frame before the bar disappears if we did it now.
1280                mAnimY = 0;
1281                mAnimAccel = 0;
1282                mAnimVel = 0;
1283            }
1284
1285            updateExpandedViewPos((int)mAnimY);
1286            mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION,
1287                    mAnimationCallback, null);
1288        }
1289    }
1290
1291    void stopTracking() {
1292        if (!mTracking)
1293            return;
1294        mTracking = false;
1295        mPile.setLayerType(View.LAYER_TYPE_NONE, null);
1296        mVelocityTracker.recycle();
1297        mVelocityTracker = null;
1298        mCloseView.setPressed(false);
1299    }
1300
1301    void incrementAnim(long frameTimeNanos) {
1302        final long deltaNanos = Math.max(frameTimeNanos - mAnimLastTimeNanos, 0);
1303        final float t = deltaNanos * 0.000000001f;                  // ns -> s
1304        final float y = mAnimY;
1305        final float v = mAnimVel;                                   // px/s
1306        final float a = mAnimAccel;                                 // px/s/s
1307        mAnimY = y + (v*t) + (0.5f*a*t*t);                          // px
1308        mAnimVel = v + (a*t);                                       // px/s
1309        mAnimLastTimeNanos = frameTimeNanos;                        // ns
1310        //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
1311        //        + " mAnimAccel=" + mAnimAccel);
1312    }
1313
1314    void doRevealAnimation(long frameTimeNanos) {
1315        if (SPEW) {
1316            Slog.d(TAG, "doRevealAnimation: dt=" + (frameTimeNanos - mAnimLastTimeNanos));
1317        }
1318        final int h = mNotificationPanelMinHeight;
1319        if (mAnimatingReveal && mAnimating && mAnimY < h) {
1320            incrementAnim(frameTimeNanos);
1321            if (mAnimY >= h) {
1322                mAnimY = h;
1323                updateExpandedViewPos((int)mAnimY);
1324            } else {
1325                updateExpandedViewPos((int)mAnimY);
1326                mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION,
1327                        mRevealAnimationCallback, null);
1328            }
1329        }
1330    }
1331
1332    void prepareTracking(int y, boolean opening) {
1333        if (CHATTY) {
1334            Slog.d(TAG, "panel: beginning to track the user's touch, y=" + y + " opening=" + opening);
1335        }
1336
1337        mCloseView.setPressed(true);
1338
1339        mTracking = true;
1340        mPile.setLayerType(View.LAYER_TYPE_HARDWARE, null);
1341        mVelocityTracker = VelocityTracker.obtain();
1342        if (opening) {
1343            makeExpandedVisible(true);
1344        } else {
1345            // it's open, close it?
1346            if (mAnimating) {
1347                mAnimating = false;
1348                mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION,
1349                        mAnimationCallback, null);
1350            }
1351            updateExpandedViewPos(y + mViewDelta);
1352        }
1353    }
1354
1355    void performFling(int y, float vel, boolean always) {
1356        if (CHATTY) {
1357            Slog.d(TAG, "panel: will fling, y=" + y + " vel=" + vel + " mExpanded=" + mExpanded);
1358        }
1359
1360        mAnimatingReveal = false;
1361
1362        mAnimY = y;
1363        mAnimVel = vel;
1364
1365        //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
1366
1367        if (mExpanded) {
1368            if (!always && (
1369                    vel > mFlingCollapseMinVelocityPx
1370                    || (y > (getExpandedViewMaxHeight()*(1f-mCollapseMinDisplayFraction)) &&
1371                        vel > -mFlingExpandMinVelocityPx))) {
1372                // We are expanded, but they didn't move sufficiently to cause
1373                // us to retract.  Animate back to the expanded position.
1374                mAnimAccel = mExpandAccelPx;
1375                if (vel < 0) {
1376                    mAnimVel = 0;
1377                }
1378            }
1379            else {
1380                // We are expanded and are now going to animate away.
1381                mAnimAccel = -mCollapseAccelPx;
1382                if (vel > 0) {
1383                    mAnimVel = 0;
1384                }
1385            }
1386        } else {
1387            if (always || (
1388                    vel > mFlingExpandMinVelocityPx
1389                    || (y > (getExpandedViewMaxHeight()*(1f-mExpandMinDisplayFraction)) &&
1390                        vel > -mFlingCollapseMinVelocityPx))) {
1391                // We are collapsed, and they moved enough to allow us to
1392                // expand.  Animate in the notifications.
1393                mAnimAccel = mExpandAccelPx;
1394                if (vel < 0) {
1395                    mAnimVel = 0;
1396                }
1397            }
1398            else {
1399                // We are collapsed, but they didn't move sufficiently to cause
1400                // us to retract.  Animate back to the collapsed position.
1401                mAnimAccel = -mCollapseAccelPx;
1402                if (vel > 0) {
1403                    mAnimVel = 0;
1404                }
1405            }
1406        }
1407        //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
1408        //        + " mAnimAccel=" + mAnimAccel);
1409
1410        resetLastAnimTime();
1411        mAnimating = true;
1412        mClosing = mAnimAccel < 0;
1413
1414        mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION,
1415                mAnimationCallback, null);
1416        mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION,
1417                mRevealAnimationCallback, null);
1418        mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION,
1419                mAnimationCallback, null);
1420        stopTracking();
1421    }
1422
1423    boolean interceptTouchEvent(MotionEvent event) {
1424        if (SPEW) {
1425            Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
1426                + mDisabled + " mTracking=" + mTracking);
1427        } else if (CHATTY) {
1428            if (event.getAction() != MotionEvent.ACTION_MOVE) {
1429                Slog.d(TAG, String.format(
1430                            "panel: %s at (%f, %f) mDisabled=0x%08x",
1431                            MotionEvent.actionToString(event.getAction()),
1432                            event.getRawX(), event.getRawY(), mDisabled));
1433            }
1434        }
1435
1436        if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
1437            return false;
1438        }
1439
1440        final int action = event.getAction();
1441        final int statusBarSize = getStatusBarHeight();
1442        final int hitSize = statusBarSize*2;
1443        final int y = (int)event.getRawY();
1444        if (action == MotionEvent.ACTION_DOWN) {
1445            if (!areLightsOn()) {
1446                setLightsOn(true);
1447            }
1448
1449            if (!mExpanded) {
1450                mViewDelta = statusBarSize - y;
1451            } else {
1452                mCloseView.getLocationOnScreen(mAbsPos);
1453                mViewDelta = mAbsPos[1] + statusBarSize + getCloseViewHeight() - y; // XXX: not closeViewHeight, but paddingBottom from the 9patch
1454            }
1455            if ((!mExpanded && y < hitSize) ||
1456                    // @@ add taps outside the panel if it's not full-screen
1457                    (mExpanded && y > (getExpandedViewMaxHeight()-hitSize))) {
1458
1459                // We drop events at the edge of the screen to make the windowshade come
1460                // down by accident less, especially when pushing open a device with a keyboard
1461                // that rotates (like g1 and droid)
1462                int x = (int)event.getRawX();
1463                final int edgeBorder = mEdgeBorder;
1464                if (x >= edgeBorder && x < mDisplayMetrics.widthPixels - edgeBorder) {
1465                    prepareTracking(y, !mExpanded);// opening if we're not already fully visible
1466                    trackMovement(event);
1467                }
1468            }
1469        } else if (mTracking) {
1470            trackMovement(event);
1471            if (action == MotionEvent.ACTION_MOVE) {
1472                if (mAnimatingReveal && (y + mViewDelta) < mNotificationPanelMinHeight) {
1473                    // nothing
1474                } else  {
1475                    mAnimatingReveal = false;
1476                    updateExpandedViewPos(y + mViewDelta);
1477                }
1478            } else if (action == MotionEvent.ACTION_UP
1479                    || action == MotionEvent.ACTION_CANCEL) {
1480                mVelocityTracker.computeCurrentVelocity(1000);
1481
1482                float yVel = mVelocityTracker.getYVelocity();
1483                boolean negative = yVel < 0;
1484
1485                float xVel = mVelocityTracker.getXVelocity();
1486                if (xVel < 0) {
1487                    xVel = -xVel;
1488                }
1489                if (xVel > mFlingGestureMaxXVelocityPx) {
1490                    xVel = mFlingGestureMaxXVelocityPx; // limit how much we care about the x axis
1491                }
1492
1493                float vel = (float)Math.hypot(yVel, xVel);
1494                if (negative) {
1495                    vel = -vel;
1496                }
1497
1498                if (CHATTY) {
1499                    Slog.d(TAG, String.format("gesture: vraw=(%f,%f) vnorm=(%f,%f) vlinear=%f",
1500                        mVelocityTracker.getXVelocity(),
1501                        mVelocityTracker.getYVelocity(),
1502                        xVel, yVel,
1503                        vel));
1504                }
1505
1506                if (mTrackingPosition == mNotificationPanelMinHeight) {
1507                    // start the fling from the tracking position, ignore y and view delta
1508                    mFlingY = mTrackingPosition;
1509                    mViewDelta = 0;
1510                } else {
1511                    mFlingY = y;
1512                }
1513                mFlingVelocity = vel;
1514                mHandler.post(mPerformFling);
1515            }
1516
1517        }
1518        return false;
1519    }
1520
1521    private void trackMovement(MotionEvent event) {
1522        // Add movement to velocity tracker using raw screen X and Y coordinates instead
1523        // of window coordinates because the window frame may be moving at the same time.
1524        float deltaX = event.getRawX() - event.getX();
1525        float deltaY = event.getRawY() - event.getY();
1526        event.offsetLocation(deltaX, deltaY);
1527        mVelocityTracker.addMovement(event);
1528        event.offsetLocation(-deltaX, -deltaY);
1529    }
1530
1531    @Override // CommandQueue
1532    public void setNavigationIconHints(int hints) {
1533        if (hints == mNavigationIconHints) return;
1534
1535        mNavigationIconHints = hints;
1536
1537        if (mNavigationBarView != null) {
1538            mNavigationBarView.setNavigationIconHints(hints);
1539        }
1540    }
1541
1542    @Override // CommandQueue
1543    public void setSystemUiVisibility(int vis, int mask) {
1544        final int oldVal = mSystemUiVisibility;
1545        final int newVal = (oldVal&~mask) | (vis&mask);
1546        final int diff = newVal ^ oldVal;
1547
1548        if (diff != 0) {
1549            mSystemUiVisibility = newVal;
1550
1551            if (0 != (diff & View.SYSTEM_UI_FLAG_LOW_PROFILE)) {
1552                final boolean lightsOut = (0 != (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE));
1553                if (lightsOut) {
1554                    animateCollapse();
1555                    if (mTicking) {
1556                        mTicker.halt();
1557                    }
1558                }
1559
1560                if (mNavigationBarView != null) {
1561                    mNavigationBarView.setLowProfile(lightsOut);
1562                }
1563
1564                setStatusBarLowProfile(lightsOut);
1565            }
1566
1567            notifyUiVisibilityChanged();
1568        }
1569    }
1570
1571    private void setStatusBarLowProfile(boolean lightsOut) {
1572        if (mLightsOutAnimation == null) {
1573            final View notifications = mStatusBarView.findViewById(R.id.notification_icon_area);
1574            final View systemIcons = mStatusBarView.findViewById(R.id.statusIcons);
1575            final View signal = mStatusBarView.findViewById(R.id.signal_cluster);
1576            final View battery = mStatusBarView.findViewById(R.id.battery);
1577            final View clock = mStatusBarView.findViewById(R.id.clock);
1578
1579            mLightsOutAnimation = new AnimatorSet();
1580            mLightsOutAnimation.playTogether(
1581                    ObjectAnimator.ofFloat(notifications, View.ALPHA, 0),
1582                    ObjectAnimator.ofFloat(systemIcons, View.ALPHA, 0),
1583                    ObjectAnimator.ofFloat(signal, View.ALPHA, 0),
1584                    ObjectAnimator.ofFloat(battery, View.ALPHA, 0.5f),
1585                    ObjectAnimator.ofFloat(clock, View.ALPHA, 0.5f)
1586                );
1587            mLightsOutAnimation.setDuration(750);
1588
1589            mLightsOnAnimation = new AnimatorSet();
1590            mLightsOnAnimation.playTogether(
1591                    ObjectAnimator.ofFloat(notifications, View.ALPHA, 1),
1592                    ObjectAnimator.ofFloat(systemIcons, View.ALPHA, 1),
1593                    ObjectAnimator.ofFloat(signal, View.ALPHA, 1),
1594                    ObjectAnimator.ofFloat(battery, View.ALPHA, 1),
1595                    ObjectAnimator.ofFloat(clock, View.ALPHA, 1)
1596                );
1597            mLightsOnAnimation.setDuration(250);
1598        }
1599
1600        mLightsOutAnimation.cancel();
1601        mLightsOnAnimation.cancel();
1602
1603        final Animator a = lightsOut ? mLightsOutAnimation : mLightsOnAnimation;
1604        a.start();
1605
1606        setAreThereNotifications();
1607    }
1608
1609    private boolean areLightsOn() {
1610        return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
1611    }
1612
1613    public void setLightsOn(boolean on) {
1614        Log.v(TAG, "setLightsOn(" + on + ")");
1615        if (on) {
1616            setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE);
1617        } else {
1618            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE);
1619        }
1620    }
1621
1622    private void notifyUiVisibilityChanged() {
1623        try {
1624            mWindowManager.statusBarVisibilityChanged(mSystemUiVisibility);
1625        } catch (RemoteException ex) {
1626        }
1627    }
1628
1629    public void topAppWindowChanged(boolean showMenu) {
1630        if (DEBUG) {
1631            Slog.d(TAG, (showMenu?"showing":"hiding") + " the MENU button");
1632        }
1633        if (mNavigationBarView != null) {
1634            mNavigationBarView.setMenuVisibility(showMenu);
1635        }
1636
1637        // See above re: lights-out policy for legacy apps.
1638        if (showMenu) setLightsOn(true);
1639    }
1640
1641    @Override
1642    public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
1643        boolean altBack = (backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS)
1644            || ((vis & InputMethodService.IME_VISIBLE) != 0);
1645
1646        mCommandQueue.setNavigationIconHints(
1647                altBack ? (mNavigationIconHints | StatusBarManager.NAVIGATION_HINT_BACK_ALT)
1648                        : (mNavigationIconHints & ~StatusBarManager.NAVIGATION_HINT_BACK_ALT));
1649    }
1650
1651    @Override
1652    public void setHardKeyboardStatus(boolean available, boolean enabled) { }
1653
1654    private class NotificationClicker implements View.OnClickListener {
1655        private PendingIntent mIntent;
1656        private String mPkg;
1657        private String mTag;
1658        private int mId;
1659
1660        NotificationClicker(PendingIntent intent, String pkg, String tag, int id) {
1661            mIntent = intent;
1662            mPkg = pkg;
1663            mTag = tag;
1664            mId = id;
1665        }
1666
1667        public void onClick(View v) {
1668            try {
1669                // The intent we are sending is for the application, which
1670                // won't have permission to immediately start an activity after
1671                // the user switches to home.  We know it is safe to do at this
1672                // point, so make sure new activity switches are now allowed.
1673                ActivityManagerNative.getDefault().resumeAppSwitches();
1674                // Also, notifications can be launched from the lock screen,
1675                // so dismiss the lock screen when the activity starts.
1676                ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
1677            } catch (RemoteException e) {
1678            }
1679
1680            if (mIntent != null) {
1681                int[] pos = new int[2];
1682                v.getLocationOnScreen(pos);
1683                Intent overlay = new Intent();
1684                overlay.setSourceBounds(
1685                        new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
1686                try {
1687                    mIntent.send(mContext, 0, overlay);
1688                } catch (PendingIntent.CanceledException e) {
1689                    // the stack trace isn't very helpful here.  Just log the exception message.
1690                    Slog.w(TAG, "Sending contentIntent failed: " + e);
1691                }
1692
1693                KeyguardManager kgm =
1694                    (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
1695                if (kgm != null) kgm.exitKeyguardSecurely(null);
1696            }
1697
1698            try {
1699                mBarService.onNotificationClick(mPkg, mTag, mId);
1700            } catch (RemoteException ex) {
1701                // system process is dead if we're here.
1702            }
1703
1704            // close the shade if it was open
1705            animateCollapse();
1706
1707            // If this click was on the intruder alert, hide that instead
1708            mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
1709        }
1710    }
1711
1712    @Override
1713    protected void tick(IBinder key, StatusBarNotification n, boolean firstTime) {
1714        // no ticking in lights-out mode
1715        if (!areLightsOn()) return;
1716
1717        // no ticking in Setup
1718        if (!isDeviceProvisioned()) return;
1719
1720        // Show the ticker if one is requested. Also don't do this
1721        // until status bar window is attached to the window manager,
1722        // because...  well, what's the point otherwise?  And trying to
1723        // run a ticker without being attached will crash!
1724        if (n.notification.tickerText != null && mStatusBarWindow.getWindowToken() != null) {
1725            if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
1726                            | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
1727                mTicker.addEntry(n);
1728            }
1729        }
1730    }
1731
1732    private class MyTicker extends Ticker {
1733        MyTicker(Context context, View sb) {
1734            super(context, sb);
1735        }
1736
1737        @Override
1738        public void tickerStarting() {
1739            mTicking = true;
1740            mIcons.setVisibility(View.GONE);
1741            mTickerView.setVisibility(View.VISIBLE);
1742            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
1743            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
1744        }
1745
1746        @Override
1747        public void tickerDone() {
1748            mIcons.setVisibility(View.VISIBLE);
1749            mTickerView.setVisibility(View.GONE);
1750            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
1751            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
1752                        mTickingDoneListener));
1753        }
1754
1755        public void tickerHalting() {
1756            mIcons.setVisibility(View.VISIBLE);
1757            mTickerView.setVisibility(View.GONE);
1758            mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
1759            mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out,
1760                        mTickingDoneListener));
1761        }
1762    }
1763
1764    Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
1765        public void onAnimationEnd(Animation animation) {
1766            mTicking = false;
1767        }
1768        public void onAnimationRepeat(Animation animation) {
1769        }
1770        public void onAnimationStart(Animation animation) {
1771        }
1772    };
1773
1774    private Animation loadAnim(int id, Animation.AnimationListener listener) {
1775        Animation anim = AnimationUtils.loadAnimation(mContext, id);
1776        if (listener != null) {
1777            anim.setAnimationListener(listener);
1778        }
1779        return anim;
1780    }
1781
1782    public static String viewInfo(View v) {
1783        return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
1784                + ") " + v.getWidth() + "x" + v.getHeight() + "]";
1785    }
1786
1787    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1788        synchronized (mQueueLock) {
1789            pw.println("Current Status Bar state:");
1790            pw.println("  mExpanded=" + mExpanded
1791                    + ", mExpandedVisible=" + mExpandedVisible);
1792            pw.println("  mTicking=" + mTicking);
1793            pw.println("  mTracking=" + mTracking);
1794            pw.println("  mAnimating=" + mAnimating
1795                    + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
1796                    + ", mAnimAccel=" + mAnimAccel);
1797            pw.println("  mAnimLastTimeNanos=" + mAnimLastTimeNanos);
1798            pw.println("  mAnimatingReveal=" + mAnimatingReveal
1799                    + " mViewDelta=" + mViewDelta);
1800            pw.println("  mDisplayMetrics=" + mDisplayMetrics);
1801            pw.println("  mPile: " + viewInfo(mPile));
1802            pw.println("  mCloseView: " + viewInfo(mCloseView));
1803            pw.println("  mTickerView: " + viewInfo(mTickerView));
1804            pw.println("  mScrollView: " + viewInfo(mScrollView)
1805                    + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
1806        }
1807
1808        pw.print("  mNavigationBarView=");
1809        if (mNavigationBarView == null) {
1810            pw.println("null");
1811        } else {
1812            mNavigationBarView.dump(fd, pw, args);
1813        }
1814
1815        if (DUMPTRUCK) {
1816            synchronized (mNotificationData) {
1817                int N = mNotificationData.size();
1818                pw.println("  notification icons: " + N);
1819                for (int i=0; i<N; i++) {
1820                    NotificationData.Entry e = mNotificationData.get(i);
1821                    pw.println("    [" + i + "] key=" + e.key + " icon=" + e.icon);
1822                    StatusBarNotification n = e.notification;
1823                    pw.println("         pkg=" + n.pkg + " id=" + n.id + " score=" + n.score);
1824                    pw.println("         notification=" + n.notification);
1825                    pw.println("         tickerText=\"" + n.notification.tickerText + "\"");
1826                }
1827            }
1828
1829            int N = mStatusIcons.getChildCount();
1830            pw.println("  system icons: " + N);
1831            for (int i=0; i<N; i++) {
1832                StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i);
1833                pw.println("    [" + i + "] icon=" + ic);
1834            }
1835
1836            if (false) {
1837                pw.println("see the logcat for a dump of the views we have created.");
1838                // must happen on ui thread
1839                mHandler.post(new Runnable() {
1840                        public void run() {
1841                            mStatusBarView.getLocationOnScreen(mAbsPos);
1842                            Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
1843                                    + ") " + mStatusBarView.getWidth() + "x"
1844                                    + getStatusBarHeight());
1845                            mStatusBarView.debug();
1846                        }
1847                    });
1848            }
1849        }
1850
1851        mNetworkController.dump(fd, pw, args);
1852    }
1853
1854    @Override
1855    public void createAndAddWindows() {
1856        addStatusBarWindow();
1857    }
1858
1859    private void addStatusBarWindow() {
1860        // Put up the view
1861        final int height = getStatusBarHeight();
1862
1863        // Now that the status bar window encompasses the sliding panel and its
1864        // translucent backdrop, the entire thing is made TRANSLUCENT and is
1865        // hardware-accelerated.
1866        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
1867                ViewGroup.LayoutParams.MATCH_PARENT,
1868                height,
1869                WindowManager.LayoutParams.TYPE_STATUS_BAR,
1870                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1871                    | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
1872                    | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
1873                PixelFormat.TRANSLUCENT);
1874
1875        if (ActivityManager.isHighEndGfx(mDisplay)) {
1876            lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
1877        }
1878
1879        lp.gravity = getStatusBarGravity();
1880        lp.setTitle("StatusBar");
1881        lp.packageName = mContext.getPackageName();
1882
1883        makeStatusBarView();
1884        WindowManagerImpl.getDefault().addView(mStatusBarWindow, lp);
1885    }
1886
1887    void setNotificationIconVisibility(boolean visible, int anim) {
1888        int old = mNotificationIcons.getVisibility();
1889        int v = visible ? View.VISIBLE : View.INVISIBLE;
1890        if (old != v) {
1891            mNotificationIcons.setVisibility(v);
1892            mNotificationIcons.startAnimation(loadAnim(anim, null));
1893        }
1894    }
1895
1896    void updateExpandedInvisiblePosition() {
1897        mTrackingPosition = -mDisplayMetrics.heightPixels;
1898    }
1899
1900    static final float saturate(float a) {
1901        return a < 0f ? 0f : (a > 1f ? 1f : a);
1902    }
1903
1904    @Override
1905    protected int getExpandedViewMaxHeight() {
1906        return mDisplayMetrics.heightPixels - mNotificationPanelMarginBottomPx;
1907    }
1908
1909    @Override
1910    protected void updateExpandedViewPos(int expandedPosition) {
1911        if (SPEW) {
1912            Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition
1913                    //+ " mTrackingParams.y=" + ((mTrackingParams == null) ? "?" : mTrackingParams.y)
1914                    + " mTrackingPosition=" + mTrackingPosition
1915                    + " gravity=" + mNotificationPanelGravity);
1916        }
1917        int panelh = 0;
1918        final int disph = getExpandedViewMaxHeight();
1919
1920        // If the expanded view is not visible, make sure they're still off screen.
1921        // Maybe the view was resized.
1922        if (!mExpandedVisible) {
1923            updateExpandedInvisiblePosition();
1924            return;
1925        }
1926
1927        // tracking view...
1928        int pos;
1929        if (expandedPosition == EXPANDED_FULL_OPEN) {
1930            panelh = disph;
1931        }
1932        else if (expandedPosition == EXPANDED_LEAVE_ALONE) {
1933            panelh = mTrackingPosition;
1934        }
1935        else {
1936            if (expandedPosition <= disph) {
1937                panelh = expandedPosition;
1938            } else {
1939                panelh = disph;
1940            }
1941        }
1942
1943        // catch orientation changes and other peculiar cases
1944        if (panelh > disph || (panelh < disph && !mTracking && !mAnimating)) {
1945            panelh = disph;
1946        } else if (panelh < 0) {
1947            panelh = 0;
1948        }
1949
1950        mTrackingPosition = panelh;
1951
1952        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mNotificationPanel.getLayoutParams();
1953        lp.height = panelh;
1954        lp.gravity = mNotificationPanelGravity;
1955        lp.leftMargin = mNotificationPanelMarginLeftPx;
1956        if (SPEW) {
1957            Slog.v(TAG, "updated cropView height=" + panelh + " grav=" + lp.gravity);
1958        }
1959        mNotificationPanel.setLayoutParams(lp);
1960
1961        if (DIM_BEHIND_EXPANDED_PANEL && ActivityManager.isHighEndGfx(mDisplay)) {
1962            // woo, special effects
1963            final int barh = getCloseViewHeight() + getStatusBarHeight();
1964            final float frac = saturate((float)(panelh - barh) / (disph - barh));
1965            final int color = ((int)(0xB0 * Math.sin(frac * 1.57f))) << 24;
1966            mStatusBarWindow.setBackgroundColor(color);
1967        }
1968    }
1969
1970    void updateDisplaySize() {
1971        mDisplay.getMetrics(mDisplayMetrics);
1972    }
1973
1974    void performDisableActions(int net) {
1975        int old = mDisabled;
1976        int diff = net ^ old;
1977        mDisabled = net;
1978
1979        // act accordingly
1980        if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
1981            if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
1982                Slog.d(TAG, "DISABLE_EXPAND: yes");
1983                animateCollapse();
1984            }
1985        }
1986        if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1987            if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1988                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
1989                if (mTicking) {
1990                    mNotificationIcons.setVisibility(View.INVISIBLE);
1991                    mTicker.halt();
1992                } else {
1993                    setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
1994                }
1995            } else {
1996                Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
1997                if (!mExpandedVisible) {
1998                    setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1999                }
2000            }
2001        } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
2002            if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
2003                mTicker.halt();
2004            }
2005        }
2006    }
2007
2008    private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
2009        final int mini(int a, int b) {
2010            return (b>a?a:b);
2011        }
2012        public void onClick(View v) {
2013            synchronized (mNotificationData) {
2014                // animate-swipe all dismissable notifications, then animate the shade closed
2015                int numChildren = mPile.getChildCount();
2016
2017                int scrollTop = mScrollView.getScrollY();
2018                int scrollBottom = scrollTop + mScrollView.getHeight();
2019                final ArrayList<View> snapshot = new ArrayList<View>(numChildren);
2020                for (int i=0; i<numChildren; i++) {
2021                    final View child = mPile.getChildAt(i);
2022                    if (mPile.canChildBeDismissed(child) && child.getBottom() > scrollTop &&
2023                            child.getTop() < scrollBottom) {
2024                        snapshot.add(child);
2025                    }
2026                }
2027                if (snapshot.isEmpty()) {
2028                    animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
2029                    return;
2030                }
2031                new Thread(new Runnable() {
2032                    @Override
2033                    public void run() {
2034                        // Decrease the delay for every row we animate to give the sense of
2035                        // accelerating the swipes
2036                        final int ROW_DELAY_DECREMENT = 10;
2037                        int currentDelay = 140;
2038                        int totalDelay = 0;
2039
2040                        // Set the shade-animating state to avoid doing other work during
2041                        // all of these animations. In particular, avoid layout and
2042                        // redrawing when collapsing the shade.
2043                        mPile.setViewRemoval(false);
2044
2045                        mPostCollapseCleanup = new Runnable() {
2046                            @Override
2047                            public void run() {
2048                                try {
2049                                    mPile.setViewRemoval(true);
2050                                    mBarService.onClearAllNotifications();
2051                                } catch (Exception ex) { }
2052                            }
2053                        };
2054
2055                        View sampleView = snapshot.get(0);
2056                        int width = sampleView.getWidth();
2057                        final int velocity = width * 8; // 1000/8 = 125 ms duration
2058                        for (final View _v : snapshot) {
2059                            mHandler.postDelayed(new Runnable() {
2060                                @Override
2061                                public void run() {
2062                                    mPile.dismissRowAnimated(_v, velocity);
2063                                }
2064                            }, totalDelay);
2065                            currentDelay = Math.max(50, currentDelay - ROW_DELAY_DECREMENT);
2066                            totalDelay += currentDelay;
2067                        }
2068                        // Delay the collapse animation until after all swipe animations have
2069                        // finished. Provide some buffer because there may be some extra delay
2070                        // before actually starting each swipe animation. Ideally, we'd
2071                        // synchronize the end of those animations with the start of the collaps
2072                        // exactly.
2073                        mHandler.postDelayed(new Runnable() {
2074                            @Override
2075                            public void run() {
2076                                animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE);
2077                            }
2078                        }, totalDelay + 225);
2079                    }
2080                }).start();
2081            }
2082        }
2083    };
2084
2085    private View.OnClickListener mSettingsButtonListener = new View.OnClickListener() {
2086        public void onClick(View v) {
2087            // We take this as a good indicator that Setup is running and we shouldn't
2088            // allow you to go somewhere else
2089            if (!isDeviceProvisioned()) return;
2090            try {
2091                // Dismiss the lock screen when Settings starts.
2092                ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
2093            } catch (RemoteException e) {
2094            }
2095            v.getContext().startActivity(new Intent(Settings.ACTION_SETTINGS)
2096                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
2097            animateCollapse();
2098        }
2099    };
2100
2101    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
2102        public void onReceive(Context context, Intent intent) {
2103            String action = intent.getAction();
2104            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
2105                    || Intent.ACTION_SCREEN_OFF.equals(action)) {
2106                int flags = CommandQueue.FLAG_EXCLUDE_NONE;
2107                if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
2108                    String reason = intent.getStringExtra("reason");
2109                    if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
2110                        flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
2111                    }
2112                }
2113                animateCollapse(flags);
2114            }
2115            else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
2116                updateResources();
2117                repositionNavigationBar();
2118                updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
2119            }
2120        }
2121    };
2122
2123    private void setIntruderAlertVisibility(boolean vis) {
2124        if (!ENABLE_INTRUDERS) return;
2125        if (DEBUG) {
2126            Slog.v(TAG, (vis ? "showing" : "hiding") + " intruder alert window");
2127        }
2128        mIntruderAlertView.setVisibility(vis ? View.VISIBLE : View.GONE);
2129    }
2130
2131    public void dismissIntruder() {
2132        if (mCurrentlyIntrudingNotification == null) return;
2133
2134        try {
2135            mBarService.onNotificationClear(
2136                    mCurrentlyIntrudingNotification.pkg,
2137                    mCurrentlyIntrudingNotification.tag,
2138                    mCurrentlyIntrudingNotification.id);
2139        } catch (android.os.RemoteException ex) {
2140            // oh well
2141        }
2142    }
2143
2144    /**
2145     * Reload some of our resources when the configuration changes.
2146     *
2147     * We don't reload everything when the configuration changes -- we probably
2148     * should, but getting that smooth is tough.  Someday we'll fix that.  In the
2149     * meantime, just update the things that we know change.
2150     */
2151    void updateResources() {
2152        final Context context = mContext;
2153        final Resources res = context.getResources();
2154
2155        if (mClearButton instanceof TextView) {
2156            ((TextView)mClearButton).setText(context.getText(R.string.status_bar_clear_all_button));
2157        }
2158        loadDimens();
2159    }
2160
2161    protected void loadDimens() {
2162        final Resources res = mContext.getResources();
2163
2164        mNaturalBarHeight = res.getDimensionPixelSize(
2165                com.android.internal.R.dimen.status_bar_height);
2166
2167        int newIconSize = res.getDimensionPixelSize(
2168            com.android.internal.R.dimen.status_bar_icon_size);
2169        int newIconHPadding = res.getDimensionPixelSize(
2170            R.dimen.status_bar_icon_padding);
2171
2172        if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) {
2173//            Slog.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding);
2174            mIconHPadding = newIconHPadding;
2175            mIconSize = newIconSize;
2176            //reloadAllNotificationIcons(); // reload the tray
2177        }
2178
2179        mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
2180
2181        mSelfExpandVelocityPx = res.getDimension(R.dimen.self_expand_velocity);
2182        mSelfCollapseVelocityPx = res.getDimension(R.dimen.self_collapse_velocity);
2183        mFlingExpandMinVelocityPx = res.getDimension(R.dimen.fling_expand_min_velocity);
2184        mFlingCollapseMinVelocityPx = res.getDimension(R.dimen.fling_collapse_min_velocity);
2185
2186        mCollapseMinDisplayFraction = res.getFraction(R.dimen.collapse_min_display_fraction, 1, 1);
2187        mExpandMinDisplayFraction = res.getFraction(R.dimen.expand_min_display_fraction, 1, 1);
2188
2189        mExpandAccelPx = res.getDimension(R.dimen.expand_accel);
2190        mCollapseAccelPx = res.getDimension(R.dimen.collapse_accel);
2191
2192        mFlingGestureMaxXVelocityPx = res.getDimension(R.dimen.fling_gesture_max_x_velocity);
2193
2194        mNotificationPanelMarginBottomPx
2195            = (int) res.getDimension(R.dimen.notification_panel_margin_bottom);
2196        mNotificationPanelMarginLeftPx
2197            = (int) res.getDimension(R.dimen.notification_panel_margin_left);
2198        mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity);
2199        if (mNotificationPanelGravity <= 0) {
2200            mNotificationPanelGravity = Gravity.CENTER_VERTICAL | Gravity.TOP;
2201        }
2202        mNotificationPanelMinHeight =
2203              res.getDimensionPixelSize(R.dimen.notification_panel_padding_top)
2204            + res.getDimensionPixelSize(R.dimen.notification_panel_header_height)
2205            + res.getDimensionPixelSize(R.dimen.close_handle_underlap)
2206            + getNinePatchPadding(res.getDrawable(R.drawable.notification_panel_bg)).bottom;
2207
2208        if (false) Slog.v(TAG, "updateResources");
2209    }
2210
2211    private static Rect getNinePatchPadding(Drawable d) {
2212        Rect padding = new Rect();
2213        if (d instanceof NinePatchDrawable) {
2214            NinePatchDrawable ninePatch = (NinePatchDrawable) d;
2215            ninePatch.getPadding(padding);
2216        }
2217        return padding;
2218    }
2219
2220    //
2221    // tracing
2222    //
2223
2224    void postStartTracing() {
2225        mHandler.postDelayed(mStartTracing, 3000);
2226    }
2227
2228    void vibrate() {
2229        android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
2230                Context.VIBRATOR_SERVICE);
2231        vib.vibrate(250);
2232    }
2233
2234    Runnable mStartTracing = new Runnable() {
2235        public void run() {
2236            vibrate();
2237            SystemClock.sleep(250);
2238            Slog.d(TAG, "startTracing");
2239            android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
2240            mHandler.postDelayed(mStopTracing, 10000);
2241        }
2242    };
2243
2244    Runnable mStopTracing = new Runnable() {
2245        public void run() {
2246            android.os.Debug.stopMethodTracing();
2247            Slog.d(TAG, "stopTracing");
2248            vibrate();
2249        }
2250    };
2251
2252    @Override
2253    protected void haltTicker() {
2254        mTicker.halt();
2255    }
2256
2257    @Override
2258    protected boolean shouldDisableNavbarGestures() {
2259        return mExpanded || (mDisabled & StatusBarManager.DISABLE_HOME) != 0;
2260    }
2261
2262    private static class FastColorDrawable extends Drawable {
2263        private final int mColor;
2264
2265        public FastColorDrawable(int color) {
2266            mColor = 0xff000000 | color;
2267        }
2268
2269        @Override
2270        public void draw(Canvas canvas) {
2271            canvas.drawColor(mColor, PorterDuff.Mode.SRC);
2272        }
2273
2274        @Override
2275        public void setAlpha(int alpha) {
2276        }
2277
2278        @Override
2279        public void setColorFilter(ColorFilter cf) {
2280        }
2281
2282        @Override
2283        public int getOpacity() {
2284            return PixelFormat.OPAQUE;
2285        }
2286
2287        @Override
2288        public void setBounds(int left, int top, int right, int bottom) {
2289        }
2290
2291        @Override
2292        public void setBounds(Rect bounds) {
2293        }
2294    }
2295}
2296