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