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