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