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