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