OSDN Git Service

Add Wysie's colors and canadiancow's battery percentage.
[android-x86/frameworks-base.git] / services / java / com / android / server / status / StatusBarService.java
1 /*
2  * Copyright (C) 2007 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
17 package com.android.server.status;
18
19 import com.android.internal.R;
20 import com.android.internal.util.CharSequences;
21
22 import android.app.ActivityManagerNative;
23 import android.app.Dialog;
24 import android.app.IStatusBar;
25 import android.app.PendingIntent;
26 import android.app.StatusBarManager;
27 import android.content.BroadcastReceiver;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.content.pm.PackageManager;
32 import android.content.res.Resources;
33 import android.graphics.PixelFormat;
34 import android.graphics.Rect;
35 import android.graphics.drawable.Drawable;
36 import android.net.Uri;
37 import android.os.IBinder;
38 import android.os.RemoteException;
39 import android.os.Binder;
40 import android.os.Handler;
41 import android.os.Message;
42 import android.os.SystemClock;
43 import android.provider.Telephony;
44 import android.util.Slog;
45 import android.view.Display;
46 import android.view.Gravity;
47 import android.view.KeyEvent;
48 import android.view.LayoutInflater;
49 import android.view.MotionEvent;
50 import android.view.VelocityTracker;
51 import android.view.View;
52 import android.view.ViewGroup;
53 import android.view.Window;
54 import android.view.WindowManager;
55 import android.view.WindowManagerImpl;
56 import android.view.animation.Animation;
57 import android.view.animation.AnimationUtils;
58 import android.widget.LinearLayout;
59 import android.widget.RemoteViews;
60 import android.widget.ScrollView;
61 import android.widget.TextView;
62 import android.widget.FrameLayout;
63
64 import java.io.FileDescriptor;
65 import java.io.PrintWriter;
66 import java.util.ArrayList;
67 import java.util.HashMap;
68 import java.util.Set;
69
70 import android.provider.Settings;
71 import java.lang.reflect.Field;
72
73
74 /**
75  * The public (ok, semi-public) service for the status bar.
76  * <p>
77  * This interesting thing to note about this class is that most of the methods that
78  * are called from other classes just post a message, and everything else is batched
79  * and coalesced into a series of calls to methods that all start with "perform."
80  * There are two reasons for this.  The first is that some of the methods (activate/deactivate)
81  * are on IStatusBar, so they're called from the thread pool and they need to make their
82  * way onto the UI thread.  The second is that the message queue is stopped while animations
83  * are happening in order to make for smoother transitions.
84  * <p>
85  * Each icon is either an icon or an icon and a notification.  They're treated mostly
86  * separately throughout the code, although they both use the same key, which is assigned
87  * when they are created.
88  */
89 public class StatusBarService extends IStatusBar.Stub
90 {
91     static final String TAG = "StatusBar";
92     static final boolean SPEW = false;
93
94     private boolean mShowPlmnSb;
95     private boolean mShowSpnSb;
96
97     static final int EXPANDED_LEAVE_ALONE = -10000;
98     static final int EXPANDED_FULL_OPEN = -10001;
99
100     private static final int MSG_ANIMATE = 1000;
101     private static final int MSG_ANIMATE_REVEAL = 1001;
102
103     private static final int OP_ADD_ICON = 1;
104     private static final int OP_UPDATE_ICON = 2;
105     private static final int OP_REMOVE_ICON = 3;
106     private static final int OP_SET_VISIBLE = 4;
107     private static final int OP_EXPAND = 5;
108     private static final int OP_TOGGLE = 6;
109     private static final int OP_DISABLE = 7;
110     private class PendingOp {
111         IBinder key;
112         int code;
113         IconData iconData;
114         NotificationData notificationData;
115         boolean visible;
116         int integer;
117     }
118
119     private class DisableRecord implements IBinder.DeathRecipient {
120         String pkg;
121         int what;
122         IBinder token;
123
124         public void binderDied() {
125             Slog.i(TAG, "binder died for pkg=" + pkg);
126             disable(0, token, pkg);
127             token.unlinkToDeath(this, 0);
128         }
129     }
130
131     public interface NotificationCallbacks {
132         void onSetDisabled(int status);
133         void onClearAll();
134         void onNotificationClick(String pkg, String tag, int id);
135         void onPanelRevealed();
136     }
137
138     private class ExpandedDialog extends Dialog {
139         ExpandedDialog(Context context) {
140             super(context, com.android.internal.R.style.Theme_Light_NoTitleBar);
141         }
142
143         @Override
144         public boolean dispatchKeyEvent(KeyEvent event) {
145             boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
146             switch (event.getKeyCode()) {
147             case KeyEvent.KEYCODE_BACK:
148                 if (!down) {
149                     StatusBarService.this.deactivate();
150                 }
151                 return true;
152             }
153             return super.dispatchKeyEvent(event);
154         }
155     }
156     
157     final Context mContext;
158     final Display mDisplay;
159     StatusBarView mStatusBarView;
160     int mPixelFormat;
161     H mHandler = new H();
162     Object mQueueLock = new Object();
163     ArrayList<PendingOp> mQueue = new ArrayList<PendingOp>();
164     NotificationCallbacks mNotificationCallbacks;
165     
166     // All accesses to mIconMap and mNotificationData are syncronized on those objects,
167     // but this is only so dump() can work correctly.  Modifying these outside of the UI
168     // thread will not work, there are places in the code that unlock and reaquire between
169     // reads and require them to not be modified.
170
171     // icons
172     HashMap<IBinder,StatusBarIcon> mIconMap = new HashMap<IBinder,StatusBarIcon>();
173     ArrayList<StatusBarIcon> mIconList = new ArrayList<StatusBarIcon>();
174     String[] mRightIconSlots;
175     StatusBarIcon[] mRightIcons;
176     LinearLayout mIcons;
177     IconMerger mNotificationIcons;
178     LinearLayout mStatusIcons;
179     StatusBarIcon mMoreIcon;
180     private UninstallReceiver mUninstallReceiver;
181
182     // expanded notifications
183     NotificationViewList mNotificationData = new NotificationViewList();
184     Dialog mExpandedDialog;
185     ExpandedView mExpandedView;
186     WindowManager.LayoutParams mExpandedParams;
187     ScrollView mScrollView;
188     View mNotificationLinearLayout;
189     TextView mOngoingTitle;
190     LinearLayout mOngoingItems;
191     TextView mLatestTitle;
192     LinearLayout mLatestItems;
193     TextView mNoNotificationsTitle;
194     TextView mSpnLabel;
195     TextView mPlmnLabel;
196     TextView mClearButton;
197     View mExpandedContents;
198     CloseDragHandle mCloseView;
199     int[] mPositionTmp = new int[2];
200     boolean mExpanded;
201     boolean mExpandedVisible;
202
203     // the date view
204     DateView mDateView;
205
206     // the tracker view
207     TrackingView mTrackingView;
208     WindowManager.LayoutParams mTrackingParams;
209     int mTrackingPosition; // the position of the top of the tracking view.
210
211     // ticker
212     private Ticker mTicker;
213     private View mTickerView;
214     private boolean mTicking;
215     private TickerView tickerView;
216     
217     // Tracking finger for opening/closing.
218     int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
219     boolean mTracking;
220     VelocityTracker mVelocityTracker;
221     
222     static final int ANIM_FRAME_DURATION = (1000/60);
223     
224     boolean mAnimating;
225     long mCurAnimationTime;
226     float mDisplayHeight;
227     float mAnimY;
228     float mAnimVel;
229     float mAnimAccel;
230     long mAnimLastTime;
231     boolean mAnimatingReveal = false;
232     int mViewDelta;
233     int[] mAbsPos = new int[2];
234     private int blackColor = 0xff000000;
235     private int whiteColor = 0xffffffff;
236     private int notificationTitleColor = blackColor;
237     private int notificationTextColor = blackColor;
238     private int notificationTimeColor = blackColor;
239     
240     // for disabling the status bar
241     ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
242     int mDisabled = 0;
243
244     /**
245      * Construct the service, add the status bar view to the window manager
246      */
247     public StatusBarService(Context context) {
248         mContext = context;
249         notificationTitleColor = Settings.System.getInt(mContext.getContentResolver(), Settings.System.NOTIF_ITEM_TITLE_COLOR, blackColor);
250         notificationTextColor = Settings.System.getInt(mContext.getContentResolver(), Settings.System.NOTIF_ITEM_TEXT_COLOR, blackColor);
251         notificationTimeColor = Settings.System.getInt(mContext.getContentResolver(), Settings.System.NOTIF_ITEM_TIME_COLOR, blackColor);
252         mDisplay = ((WindowManager)context.getSystemService(
253                 Context.WINDOW_SERVICE)).getDefaultDisplay();
254         makeStatusBarView(context);
255         updateColors();
256         mUninstallReceiver = new UninstallReceiver();
257     }
258
259     public void setNotificationCallbacks(NotificationCallbacks listener) {
260         mNotificationCallbacks = listener;
261     }
262
263     // ================================================================================
264     // Constructing the view
265     // ================================================================================
266     private void makeStatusBarView(Context context) {
267         Resources res = context.getResources();
268         mRightIconSlots = res.getStringArray(com.android.internal.R.array.status_bar_icon_order);
269         mRightIcons = new StatusBarIcon[mRightIconSlots.length];
270
271         ExpandedView expanded = (ExpandedView)View.inflate(context,
272                 com.android.internal.R.layout.status_bar_expanded, null);
273         expanded.mService = this;
274         StatusBarView sb = (StatusBarView)View.inflate(context,
275                 com.android.internal.R.layout.status_bar, null);
276         sb.mService = this;
277
278         // figure out which pixel-format to use for the status bar.
279         mPixelFormat = PixelFormat.TRANSLUCENT;
280         Drawable bg = sb.getBackground();
281         if (bg != null) {
282             mPixelFormat = bg.getOpacity();
283         }
284
285         mStatusBarView = sb;
286         mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons);
287         mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons);
288         mNotificationIcons.service = this;
289         mIcons = (LinearLayout)sb.findViewById(R.id.icons);
290         mTickerView = sb.findViewById(R.id.ticker);
291         mDateView = (DateView)sb.findViewById(R.id.date);
292
293         mExpandedDialog = new ExpandedDialog(context);
294         mExpandedView = expanded;
295         mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout);
296         mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle);
297         mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems);
298         mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle);
299         mLatestItems = (LinearLayout)expanded.findViewById(R.id.latestItems);
300         mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle);
301         mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button);
302         mClearButton.setOnClickListener(mClearButtonListener);
303         mSpnLabel = (TextView)expanded.findViewById(R.id.spnLabel);
304         mPlmnLabel = (TextView)expanded.findViewById(R.id.plmnLabel);
305         mScrollView = (ScrollView)expanded.findViewById(R.id.scroll);
306         mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout);
307
308         mOngoingTitle.setVisibility(View.GONE);
309         mLatestTitle.setVisibility(View.GONE);
310         
311         mTicker = new MyTicker(context, sb);
312
313         tickerView = (TickerView)sb.findViewById(R.id.tickerText);
314         tickerView.mTicker = mTicker;
315
316         mTrackingView = (TrackingView)View.inflate(context,
317                 com.android.internal.R.layout.status_bar_tracking, null);
318         mTrackingView.mService = this;
319         mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close);
320         mCloseView.mService = this;
321
322         mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
323
324         // add the more icon for the notifications
325         IconData moreData = IconData.makeIcon(null, context.getPackageName(),
326                 R.drawable.stat_notify_more, 0, 42);
327         mMoreIcon = new StatusBarIcon(context, moreData, mNotificationIcons);
328         mMoreIcon.view.setId(R.drawable.stat_notify_more);
329         mNotificationIcons.moreIcon = mMoreIcon;
330         mNotificationIcons.addView(mMoreIcon.view);
331
332         // set the inital view visibility
333         setAreThereNotifications();
334         mDateView.setVisibility(View.INVISIBLE);
335
336         // before we register for broadcasts
337         mPlmnLabel.setText(R.string.lockscreen_carrier_default);
338         mPlmnLabel.setVisibility(View.VISIBLE);
339         mSpnLabel.setText("");
340         mSpnLabel.setVisibility(View.GONE);
341
342         // receive broadcasts
343         IntentFilter filter = new IntentFilter();
344         filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
345         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
346         filter.addAction(Intent.ACTION_SCREEN_OFF);
347         filter.addAction(Telephony.Intents.SPN_STRINGS_UPDATED_ACTION);
348         context.registerReceiver(mBroadcastReceiver, filter);
349     }
350
351     public void systemReady() {
352         final StatusBarView view = mStatusBarView;
353         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
354                 ViewGroup.LayoutParams.MATCH_PARENT,
355                 view.getContext().getResources().getDimensionPixelSize(
356                         com.android.internal.R.dimen.status_bar_height),
357                 WindowManager.LayoutParams.TYPE_STATUS_BAR,
358                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
359                 WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING,
360                 mPixelFormat);
361         lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
362         lp.setTitle("StatusBar");
363         lp.windowAnimations = R.style.Animation_StatusBar;
364
365         WindowManagerImpl.getDefault().addView(view, lp);
366     }
367     
368     // ================================================================================
369     // From IStatusBar
370     // ================================================================================
371     public void activate() {
372         enforceExpandStatusBar();
373         addPendingOp(OP_EXPAND, null, true);
374     }
375
376     public void deactivate() {
377         enforceExpandStatusBar();
378         addPendingOp(OP_EXPAND, null, false);
379     }
380
381     public void toggle() {
382         enforceExpandStatusBar();
383         addPendingOp(OP_TOGGLE, null, false);
384     }
385
386     public void disable(int what, IBinder token, String pkg) {
387         enforceStatusBar();
388         synchronized (mNotificationCallbacks) {
389             // This is a little gross, but I think it's safe as long as nobody else
390             // synchronizes on mNotificationCallbacks.  It's important that the the callback
391             // and the pending op get done in the correct order and not interleaved with
392             // other calls, otherwise they'll get out of sync.
393             int net;
394             synchronized (mDisableRecords) {
395                 manageDisableListLocked(what, token, pkg);
396                 net = gatherDisableActionsLocked();
397                 mNotificationCallbacks.onSetDisabled(net);
398             }
399             addPendingOp(OP_DISABLE, net);
400         }
401     }
402
403     public IBinder addIcon(String slot, String iconPackage, int iconId, int iconLevel) {
404         enforceStatusBar();
405         return addIcon(IconData.makeIcon(slot, iconPackage, iconId, iconLevel, 0), null);
406     }
407
408     public void updateIcon(IBinder key,
409             String slot, String iconPackage, int iconId, int iconLevel) {
410         enforceStatusBar();
411         updateIcon(key, IconData.makeIcon(slot, iconPackage, iconId, iconLevel, 0), null);
412     }
413
414     public void removeIcon(IBinder key) {
415         enforceStatusBar();
416         addPendingOp(OP_REMOVE_ICON, key, null, null, -1);
417     }
418
419     private void enforceStatusBar() {
420         mContext.enforceCallingOrSelfPermission(
421                 android.Manifest.permission.STATUS_BAR,
422                 "StatusBarService");
423     }
424
425     private void enforceExpandStatusBar() {
426         mContext.enforceCallingOrSelfPermission(
427                 android.Manifest.permission.EXPAND_STATUS_BAR,
428                 "StatusBarService");
429     }
430
431     // ================================================================================
432     // Can be called from any thread
433     // ================================================================================
434     public IBinder addIcon(IconData data, NotificationData n) {
435         int slot;
436         // assert early-on if they using a slot that doesn't exist.
437         if (data != null && n == null) {
438             slot = getRightIconIndex(data.slot);
439             if (slot < 0) {
440                 throw new SecurityException("invalid status bar icon slot: "
441                         + (data.slot != null ? "'" + data.slot + "'" : "null"));
442             }
443         } else {
444             slot = -1;
445         }
446         IBinder key = new Binder();
447         addPendingOp(OP_ADD_ICON, key, data, n, -1);
448         return key;
449     }
450
451     public void updateIcon(IBinder key, IconData data, NotificationData n) {
452         addPendingOp(OP_UPDATE_ICON, key, data, n, -1);
453     }
454
455     public void setIconVisibility(IBinder key, boolean visible) {
456         addPendingOp(OP_SET_VISIBLE, key, visible);
457     }
458
459     private void addPendingOp(int code, IBinder key, IconData data, NotificationData n, int i) {
460         synchronized (mQueueLock) {
461             PendingOp op = new PendingOp();
462             op.key = key;
463             op.code = code;
464             op.iconData = data == null ? null : data.clone();
465             op.notificationData = n;
466             op.integer = i;
467             mQueue.add(op);
468             if (mQueue.size() == 1) {
469                 mHandler.sendEmptyMessage(2);
470             }
471         }
472     }
473
474     private void addPendingOp(int code, IBinder key, boolean visible) {
475         synchronized (mQueueLock) {
476             PendingOp op = new PendingOp();
477             op.key = key;
478             op.code = code;
479             op.visible = visible;
480             mQueue.add(op);
481             if (mQueue.size() == 1) {
482                 mHandler.sendEmptyMessage(1);
483             }
484         }
485     }
486
487     private void addPendingOp(int code, int integer) {
488         synchronized (mQueueLock) {
489             PendingOp op = new PendingOp();
490             op.code = code;
491             op.integer = integer;
492             mQueue.add(op);
493             if (mQueue.size() == 1) {
494                 mHandler.sendEmptyMessage(1);
495             }
496         }
497     }
498
499     // lock on mDisableRecords
500     void manageDisableListLocked(int what, IBinder token, String pkg) {
501         if (SPEW) {
502             Slog.d(TAG, "manageDisableList what=0x" + Integer.toHexString(what)
503                     + " pkg=" + pkg);
504         }
505         // update the list
506         synchronized (mDisableRecords) {
507             final int N = mDisableRecords.size();
508             DisableRecord tok = null;
509             int i;
510             for (i=0; i<N; i++) {
511                 DisableRecord t = mDisableRecords.get(i);
512                 if (t.token == token) {
513                     tok = t;
514                     break;
515                 }
516             }
517             if (what == 0 || !token.isBinderAlive()) {
518                 if (tok != null) {
519                     mDisableRecords.remove(i);
520                     tok.token.unlinkToDeath(tok, 0);
521                 }
522             } else {
523                 if (tok == null) {
524                     tok = new DisableRecord();
525                     try {
526                         token.linkToDeath(tok, 0);
527                     }
528                     catch (RemoteException ex) {
529                         return; // give up
530                     }
531                     mDisableRecords.add(tok);
532                 }
533                 tok.what = what;
534                 tok.token = token;
535                 tok.pkg = pkg;
536             }
537         }
538     }
539
540     // lock on mDisableRecords
541     int gatherDisableActionsLocked() {
542         final int N = mDisableRecords.size();
543         // gather the new net flags
544         int net = 0;
545         for (int i=0; i<N; i++) {
546             net |= mDisableRecords.get(i).what;
547         }
548         return net;
549     }
550
551     private int getRightIconIndex(String slot) {
552         final int N = mRightIconSlots.length;
553         for (int i=0; i<N; i++) {
554             if (mRightIconSlots[i].equals(slot)) {
555                 return i;
556             }
557         }
558         return -1;
559     }
560
561     // ================================================================================
562     // Always called from UI thread
563     // ================================================================================
564     /**
565      * All changes to the status bar and notifications funnel through here and are batched.
566      */
567     private class H extends Handler {
568         public void handleMessage(Message m) {
569             if (m.what == MSG_ANIMATE) {
570                 doAnimation();
571                 return;
572             }
573             if (m.what == MSG_ANIMATE_REVEAL) {
574                 doRevealAnimation();
575                 return;
576             }
577
578             ArrayList<PendingOp> queue;
579             synchronized (mQueueLock) {
580                 queue = mQueue;
581                 mQueue = new ArrayList<PendingOp>();
582             }
583
584             boolean wasExpanded = mExpanded;
585
586             // for each one in the queue, find all of the ones with the same key
587             // and collapse that down into a final op and/or call to setVisibility, etc
588             boolean expand = wasExpanded;
589             boolean doExpand = false;
590             boolean doDisable = false;
591             int disableWhat = 0;
592             int N = queue.size();
593             while (N > 0) {
594                 PendingOp op = queue.get(0);
595                 boolean doOp = false;
596                 boolean visible = false;
597                 boolean doVisibility = false;
598                 if (op.code == OP_SET_VISIBLE) {
599                     doVisibility = true;
600                     visible = op.visible;
601                 }
602                 else if (op.code == OP_EXPAND) {
603                     doExpand = true;
604                     expand = op.visible;
605                 }
606                 else if (op.code == OP_TOGGLE) {
607                     doExpand = true;
608                     expand = !expand;
609                 }
610                 else {
611                     doOp = true;
612                 }
613
614                 if (alwaysHandle(op.code)) {
615                     // coalesce these
616                     for (int i=1; i<N; i++) {
617                         PendingOp o = queue.get(i);
618                         if (!alwaysHandle(o.code) && o.key == op.key) {
619                             if (o.code == OP_SET_VISIBLE) {
620                                 visible = o.visible;
621                                 doVisibility = true;
622                             }
623                             else if (o.code == OP_EXPAND) {
624                                 expand = o.visible;
625                                 doExpand = true;
626                             }
627                             else {
628                                 op.code = o.code;
629                                 op.iconData = o.iconData;
630                                 op.notificationData = o.notificationData;
631                             }
632                             queue.remove(i);
633                             i--;
634                             N--;
635                         }
636                     }
637                 }
638
639                 queue.remove(0);
640                 N--;
641
642                 if (doOp) {
643                     switch (op.code) {
644                         case OP_ADD_ICON:
645                         case OP_UPDATE_ICON:
646                             performAddUpdateIcon(op.key, op.iconData, op.notificationData);
647                             break;
648                         case OP_REMOVE_ICON:
649                             performRemoveIcon(op.key);
650                             break;
651                         case OP_DISABLE:
652                             doDisable = true;
653                             disableWhat = op.integer;
654                             break;
655                     }
656                 }
657                 if (doVisibility && op.code != OP_REMOVE_ICON) {
658                     performSetIconVisibility(op.key, visible);
659                 }
660             }
661
662             if (queue.size() != 0) {
663                 throw new RuntimeException("Assertion failed: queue.size=" + queue.size());
664             }
665             if (doExpand) {
666                 // this is last so that we capture all of the pending changes before doing it
667                 if (expand) {
668                     animateExpand();
669                 } else {
670                     animateCollapse();
671                 }
672             }
673             if (doDisable) {
674                 performDisableActions(disableWhat);
675             }
676         }
677     }
678
679     private boolean alwaysHandle(int code) {
680         return code == OP_DISABLE;
681     }
682
683     /* private */ void performAddUpdateIcon(IBinder key, IconData data, NotificationData n)
684                         throws StatusBarException {
685         if (SPEW) {
686             Slog.d(TAG, "performAddUpdateIcon icon=" + data + " notification=" + n + " key=" + key);
687         }
688         // notification
689         if (n != null) {
690             StatusBarNotification notification = getNotification(key);
691             NotificationData oldData = null;
692             if (notification == null) {
693                 // add
694                 notification = new StatusBarNotification();
695                 notification.key = key;
696                 notification.data = n;
697                 synchronized (mNotificationData) {
698                     mNotificationData.add(notification);
699                 }
700                 addNotificationView(notification);
701                 setAreThereNotifications();
702             } else {
703                 // update
704                 oldData = notification.data;
705                 notification.data = n;
706                 updateNotificationView(notification, oldData);
707             }
708             // Show the ticker if one is requested, and the text is different
709             // than the currently displayed ticker.  Also don't do this
710             // until status bar window is attached to the window manager,
711             // because...  well, what's the point otherwise?  And trying to
712             // run a ticker without being attached will crash!
713             if (n.tickerText != null && mStatusBarView.getWindowToken() != null
714                     && (oldData == null
715                         || oldData.tickerText == null
716                         || !CharSequences.equals(oldData.tickerText, n.tickerText))) {
717                 if (0 == (mDisabled & 
718                     (StatusBarManager.DISABLE_NOTIFICATION_ICONS | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
719                     mTicker.addEntry(n, StatusBarIcon.getIcon(mContext, data), n.tickerText);
720                 }
721             }
722             updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
723         }
724
725         // icon
726         synchronized (mIconMap) {
727             StatusBarIcon icon = mIconMap.get(key);
728             if (icon == null) {
729                 // add
730                 LinearLayout v = n == null ? mStatusIcons : mNotificationIcons;
731
732                 icon = new StatusBarIcon(mContext, data, v);
733                 mIconMap.put(key, icon);
734                 mIconList.add(icon);
735
736                 if (n == null) {
737                     int slotIndex = getRightIconIndex(data.slot);
738                     StatusBarIcon[] rightIcons = mRightIcons;
739                     if (rightIcons[slotIndex] == null) {
740                         int pos = 0;
741                         for (int i=mRightIcons.length-1; i>slotIndex; i--) {
742                             StatusBarIcon ic = rightIcons[i];
743                             if (ic != null) {
744                                 pos++;
745                             }
746                         }
747                         rightIcons[slotIndex] = icon;
748                         mStatusIcons.addView(icon.view, pos);
749                     } else {
750                         Slog.e(TAG, "duplicate icon in slot " + slotIndex + "/" + data.slot);
751                         mIconMap.remove(key);
752                         mIconList.remove(icon);
753                         return ;
754                     }
755                 } else {
756                     int iconIndex = mNotificationData.getIconIndex(n);
757                     mNotificationIcons.addView(icon.view, iconIndex);
758                 }
759             } else {
760                 if (n == null) {
761                     // right hand side icons -- these don't reorder
762                     icon.update(mContext, data);
763                 } else {
764                     // remove old
765                     ViewGroup parent = (ViewGroup)icon.view.getParent();
766                     parent.removeView(icon.view);
767                     // add new
768                     icon.update(mContext, data);
769                     int iconIndex = mNotificationData.getIconIndex(n);
770                     mNotificationIcons.addView(icon.view, iconIndex);
771                 }
772             }
773         }
774     }
775
776     /* private */ void performSetIconVisibility(IBinder key, boolean visible) {
777         synchronized (mIconMap) {
778             if (SPEW) {
779                 Slog.d(TAG, "performSetIconVisibility key=" + key + " visible=" + visible);
780             }
781             StatusBarIcon icon = mIconMap.get(key);
782             icon.view.setVisibility(visible ? View.VISIBLE : View.GONE);
783         }
784     }
785     
786     /* private */ void performRemoveIcon(IBinder key) {
787         synchronized (this) {
788             if (SPEW) {
789                 Slog.d(TAG, "performRemoveIcon key=" + key);
790             }
791             StatusBarIcon icon = mIconMap.remove(key);
792             mIconList.remove(icon);
793             if (icon != null) {
794                 ViewGroup parent = (ViewGroup)icon.view.getParent();
795                 parent.removeView(icon.view);
796                 int slotIndex = getRightIconIndex(icon.mData.slot);
797                 if (slotIndex >= 0) {
798                     mRightIcons[slotIndex] = null;
799                 }
800             }
801             StatusBarNotification notification = getNotification(key);
802             if (notification != null) {
803                 removeNotificationView(notification);
804                 synchronized (mNotificationData) {
805                     mNotificationData.remove(notification);
806                 }
807                 setAreThereNotifications();
808             }
809         }
810     }
811
812     int getIconNumberForView(View v) {
813         synchronized (mIconMap) {
814             StatusBarIcon icon = null;
815             final int N = mIconList.size();
816             for (int i=0; i<N; i++) {
817                 StatusBarIcon ic = mIconList.get(i);
818                 if (ic.view == v) {
819                     icon = ic;
820                     break;
821                 }
822             }
823             if (icon != null) {
824                 return icon.getNumber();
825             } else {
826                 return -1;
827             }
828         }
829     }
830
831
832     StatusBarNotification getNotification(IBinder key) {
833         synchronized (mNotificationData) {
834             return mNotificationData.get(key);
835         }
836     }
837
838     View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
839         public void onFocusChange(View v, boolean hasFocus) {
840             // Because 'v' is a ViewGroup, all its children will be (un)selected
841             // too, which allows marqueeing to work.
842             v.setSelected(hasFocus);
843         }
844     };
845     
846     View makeNotificationView(StatusBarNotification notification, ViewGroup parent) {
847         NotificationData n = notification.data;
848         RemoteViews remoteViews = n.contentView;
849         if (remoteViews == null) {
850             return null;
851         }
852
853         // create the row view
854         LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
855                 Context.LAYOUT_INFLATER_SERVICE);
856         View row = inflater.inflate(com.android.internal.R.layout.status_bar_latest_event, parent, false);
857
858         // bind the click event to the content area
859         ViewGroup content = (ViewGroup)row.findViewById(com.android.internal.R.id.content);
860         content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
861         content.setOnFocusChangeListener(mFocusChangeListener);
862         PendingIntent contentIntent = n.contentIntent;
863         if (contentIntent != null) {
864             content.setOnClickListener(new Launcher(contentIntent, n.pkg, n.tag, n.id));
865         }
866
867         View child = null;
868         Exception exception = null;
869         try {
870             child = remoteViews.apply(mContext, content);
871         }
872         catch (RuntimeException e) {
873             exception = e;
874         }
875         if (child == null) {
876             Slog.e(TAG, "couldn't inflate view for package " + n.pkg, exception);
877             return null;
878         }
879
880         //Try-catch cause it does not work with QuickSettings, possibly other apps too :( (Won't work thru XML as well)
881         try {
882             TextView tv1 = (TextView) child.findViewById(com.android.internal.R.id.title);
883             TextView tv2 = (TextView) child.findViewById(com.android.internal.R.id.text);
884             TextView tv3 = (TextView) child.findViewById(com.android.internal.R.id.time);
885             tv1.setTextColor(notificationTitleColor);
886             tv2.setTextColor(notificationTextColor);
887             tv3.setTextColor(notificationTimeColor);
888         }
889         catch (Exception e) {
890         }
891
892         content.addView(child);
893
894         row.setDrawingCacheEnabled(true);
895
896         notification.view = row;
897         notification.contentView = child;
898
899         return row;
900     }
901
902     void addNotificationView(StatusBarNotification notification) {
903         if (notification.view != null) {
904             throw new RuntimeException("Assertion failed: notification.view="
905                     + notification.view);
906         }
907
908         LinearLayout parent = notification.data.ongoingEvent ? mOngoingItems : mLatestItems;
909
910         View child = makeNotificationView(notification, parent);
911         if (child == null) {
912             return ;
913         }
914
915         int index = mNotificationData.getExpandedIndex(notification);
916         parent.addView(child, index);
917     }
918
919     /**
920      * Remove the old one and put the new one in its place.
921      * @param notification the notification
922      */
923     void updateNotificationView(StatusBarNotification notification, NotificationData oldData) {
924         NotificationData n = notification.data;
925         if (oldData != null && n != null
926                 && n.when == oldData.when
927                 && n.ongoingEvent == oldData.ongoingEvent
928                 && n.contentView != null && oldData.contentView != null
929                 && n.contentView.getPackage() != null
930                 && oldData.contentView.getPackage() != null
931                 && oldData.contentView.getPackage().equals(n.contentView.getPackage())
932                 && oldData.contentView.getLayoutId() == n.contentView.getLayoutId()
933                 && notification.view != null) {
934             mNotificationData.update(notification);
935             try {
936                 n.contentView.reapply(mContext, notification.contentView);
937
938                 // update the contentIntent
939                 ViewGroup content = (ViewGroup)notification.view.findViewById(
940                         com.android.internal.R.id.content);
941                 PendingIntent contentIntent = n.contentIntent;
942                 if (contentIntent != null) {
943                     content.setOnClickListener(new Launcher(contentIntent, n.pkg, n.tag, n.id));
944                 }
945             }
946             catch (RuntimeException e) {
947                 // It failed to add cleanly.  Log, and remove the view from the panel.
948                 Slog.w(TAG, "couldn't reapply views for package " + n.contentView.getPackage(), e);
949                 removeNotificationView(notification);
950             }
951         } else {
952             mNotificationData.update(notification);
953             removeNotificationView(notification);
954             addNotificationView(notification);
955         }
956         setAreThereNotifications();
957     }
958
959     void removeNotificationView(StatusBarNotification notification) {
960         View v = notification.view;
961         if (v != null) {
962             ViewGroup parent = (ViewGroup)v.getParent();
963             parent.removeView(v);
964             notification.view = null;
965         }
966     }
967
968     private void setAreThereNotifications() {
969         boolean ongoing = mOngoingItems.getChildCount() != 0;
970         boolean latest = mLatestItems.getChildCount() != 0;
971
972         if (mNotificationData.hasClearableItems()) {
973             mClearButton.setVisibility(View.VISIBLE);
974         } else {
975             mClearButton.setVisibility(View.INVISIBLE);
976         }
977
978         mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE);
979         mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE);
980
981         if (ongoing || latest) {
982             mNoNotificationsTitle.setVisibility(View.GONE);
983         } else {
984             mNoNotificationsTitle.setVisibility(View.VISIBLE);
985         }
986     }
987
988     private void makeExpandedVisible() {
989         if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
990         if (mExpandedVisible) {
991             return;
992         }
993         mExpandedVisible = true;
994         panelSlightlyVisible(true);
995         
996         updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
997         mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
998         mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
999         mExpandedDialog.getWindow().setAttributes(mExpandedParams);
1000         mExpandedView.requestFocus(View.FOCUS_FORWARD);
1001         mTrackingView.setVisibility(View.VISIBLE);
1002         
1003         if (!mTicking) {
1004             setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
1005         }
1006     }
1007     
1008     void animateExpand() {
1009         if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded);
1010         if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
1011             return ;
1012         }
1013         if (mExpanded) {
1014             return;
1015         }
1016
1017         prepareTracking(0, true);
1018         performFling(0, 2000.0f, true);
1019     }
1020     
1021     void animateCollapse() {
1022         if (SPEW) {
1023             Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
1024                     + " mExpandedVisible=" + mExpandedVisible
1025                     + " mExpanded=" + mExpanded
1026                     + " mAnimating=" + mAnimating
1027                     + " mAnimY=" + mAnimY
1028                     + " mAnimVel=" + mAnimVel);
1029         }
1030         
1031         if (!mExpandedVisible) {
1032             return;
1033         }
1034
1035         int y;
1036         if (mAnimating) {
1037             y = (int)mAnimY;
1038         } else {
1039             y = mDisplay.getHeight()-1;
1040         }
1041         // Let the fling think that we're open so it goes in the right direction
1042         // and doesn't try to re-open the windowshade.
1043         mExpanded = true;
1044         prepareTracking(y, false);
1045         performFling(y, -2000.0f, true);
1046     }
1047     
1048     void performExpand() {
1049         if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded);
1050         if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
1051             return ;
1052         }
1053         if (mExpanded) {
1054             return;
1055         }
1056
1057         // It seems strange to sometimes not expand...
1058         if (false) {
1059             synchronized (mNotificationData) {
1060                 if (mNotificationData.size() == 0) {
1061                     return;
1062                 }
1063             }
1064         }
1065         
1066         mExpanded = true;
1067         makeExpandedVisible();
1068         updateExpandedViewPos(EXPANDED_FULL_OPEN);
1069
1070         if (false) postStartTracing();
1071     }
1072
1073     void performCollapse() {
1074         if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded
1075                 + " mExpandedVisible=" + mExpandedVisible);
1076         
1077         if (!mExpandedVisible) {
1078             return;
1079         }
1080         mExpandedVisible = false;
1081         panelSlightlyVisible(false);
1082         mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1083         mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
1084         mExpandedDialog.getWindow().setAttributes(mExpandedParams);
1085         mTrackingView.setVisibility(View.GONE);
1086
1087         if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
1088             setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1089         }
1090         setDateViewVisibility(false, com.android.internal.R.anim.fade_out);
1091         
1092         if (!mExpanded) {
1093             return;
1094         }
1095         mExpanded = false;
1096     }
1097
1098     void doAnimation() {
1099         if (mAnimating) {
1100             if (SPEW) Slog.d(TAG, "doAnimation");
1101             if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
1102             incrementAnim();
1103             if (SPEW) Slog.d(TAG, "doAnimation after  mAnimY=" + mAnimY);
1104             if (mAnimY >= mDisplay.getHeight()-1) {
1105                 if (SPEW) Slog.d(TAG, "Animation completed to expanded state.");
1106                 mAnimating = false;
1107                 updateExpandedViewPos(EXPANDED_FULL_OPEN);
1108                 performExpand();
1109             }
1110             else if (mAnimY < mStatusBarView.getHeight()) {
1111                 if (SPEW) Slog.d(TAG, "Animation completed to collapsed state.");
1112                 mAnimating = false;
1113                 updateExpandedViewPos(0);
1114                 performCollapse();
1115             }
1116             else {
1117                 updateExpandedViewPos((int)mAnimY);
1118                 mCurAnimationTime += ANIM_FRAME_DURATION;
1119                 mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
1120             }
1121         }
1122     }
1123
1124     void stopTracking() {
1125         mTracking = false;
1126         mVelocityTracker.recycle();
1127         mVelocityTracker = null;
1128     }
1129
1130     void incrementAnim() {
1131         long now = SystemClock.uptimeMillis();
1132         float t = ((float)(now - mAnimLastTime)) / 1000;            // ms -> s
1133         final float y = mAnimY;
1134         final float v = mAnimVel;                                   // px/s
1135         final float a = mAnimAccel;                                 // px/s/s
1136         mAnimY = y + (v*t) + (0.5f*a*t*t);                          // px
1137         mAnimVel = v + (a*t);                                       // px/s
1138         mAnimLastTime = now;                                        // ms
1139         //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
1140         //        + " mAnimAccel=" + mAnimAccel);
1141     }
1142
1143     void doRevealAnimation() {
1144         final int h = mCloseView.getHeight() + mStatusBarView.getHeight();
1145         if (mAnimatingReveal && mAnimating && mAnimY < h) {
1146             incrementAnim();
1147             if (mAnimY >= h) {
1148                 mAnimY = h;
1149                 updateExpandedViewPos((int)mAnimY);
1150             } else {
1151                 updateExpandedViewPos((int)mAnimY);
1152                 mCurAnimationTime += ANIM_FRAME_DURATION;
1153                 mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
1154                         mCurAnimationTime);
1155             }
1156         }
1157     }
1158     
1159     void prepareTracking(int y, boolean opening) {
1160         mTracking = true;
1161         mVelocityTracker = VelocityTracker.obtain();
1162         if (opening) {
1163             mAnimAccel = 2000.0f;
1164             mAnimVel = 200;
1165             mAnimY = mStatusBarView.getHeight();
1166             updateExpandedViewPos((int)mAnimY);
1167             mAnimating = true;
1168             mAnimatingReveal = true;
1169             mHandler.removeMessages(MSG_ANIMATE);
1170             mHandler.removeMessages(MSG_ANIMATE_REVEAL);
1171             long now = SystemClock.uptimeMillis();
1172             mAnimLastTime = now;
1173             mCurAnimationTime = now + ANIM_FRAME_DURATION;
1174             mAnimating = true;
1175             mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
1176                     mCurAnimationTime);
1177             makeExpandedVisible();
1178         } else {
1179             // it's open, close it?
1180             if (mAnimating) {
1181                 mAnimating = false;
1182                 mHandler.removeMessages(MSG_ANIMATE);
1183             }
1184             updateExpandedViewPos(y + mViewDelta);
1185         }
1186     }
1187     
1188     void performFling(int y, float vel, boolean always) {
1189         mAnimatingReveal = false;
1190         mDisplayHeight = mDisplay.getHeight();
1191
1192         mAnimY = y;
1193         mAnimVel = vel;
1194
1195         //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
1196
1197         if (mExpanded) {
1198             if (!always && (
1199                     vel > 200.0f
1200                     || (y > (mDisplayHeight-25) && vel > -200.0f))) {
1201                 // We are expanded, but they didn't move sufficiently to cause
1202                 // us to retract.  Animate back to the expanded position.
1203                 mAnimAccel = 2000.0f;
1204                 if (vel < 0) {
1205                     mAnimVel = 0;
1206                 }
1207             }
1208             else {
1209                 // We are expanded and are now going to animate away.
1210                 mAnimAccel = -2000.0f;
1211                 if (vel > 0) {
1212                     mAnimVel = 0;
1213                 }
1214             }
1215         } else {
1216             if (always || (
1217                     vel > 200.0f
1218                     || (y > (mDisplayHeight/2) && vel > -200.0f))) {
1219                 // We are collapsed, and they moved enough to allow us to
1220                 // expand.  Animate in the notifications.
1221                 mAnimAccel = 2000.0f;
1222                 if (vel < 0) {
1223                     mAnimVel = 0;
1224                 }
1225             }
1226             else {
1227                 // We are collapsed, but they didn't move sufficiently to cause
1228                 // us to retract.  Animate back to the collapsed position.
1229                 mAnimAccel = -2000.0f;
1230                 if (vel > 0) {
1231                     mAnimVel = 0;
1232                 }
1233             }
1234         }
1235         //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
1236         //        + " mAnimAccel=" + mAnimAccel);
1237
1238         long now = SystemClock.uptimeMillis();
1239         mAnimLastTime = now;
1240         mCurAnimationTime = now + ANIM_FRAME_DURATION;
1241         mAnimating = true;
1242         mHandler.removeMessages(MSG_ANIMATE);
1243         mHandler.removeMessages(MSG_ANIMATE_REVEAL);
1244         mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
1245         stopTracking();
1246     }
1247     
1248     boolean interceptTouchEvent(MotionEvent event) {
1249         if (SPEW) {
1250             Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
1251                 + mDisabled);
1252         }
1253
1254         if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
1255             return false;
1256         }
1257         
1258         final int statusBarSize = mStatusBarView.getHeight();
1259         final int hitSize = statusBarSize*2;
1260         if (event.getAction() == MotionEvent.ACTION_DOWN) {
1261             final int y = (int)event.getRawY();
1262
1263             if (!mExpanded) {
1264                 mViewDelta = statusBarSize - y;
1265             } else {
1266                 mTrackingView.getLocationOnScreen(mAbsPos);
1267                 mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y;
1268             }
1269             if ((!mExpanded && y < hitSize) ||
1270                     (mExpanded && y > (mDisplay.getHeight()-hitSize))) {
1271
1272                 // We drop events at the edge of the screen to make the windowshade come
1273                 // down by accident less, especially when pushing open a device with a keyboard
1274                 // that rotates (like g1 and droid)
1275                 int x = (int)event.getRawX();
1276                 final int edgeBorder = mEdgeBorder;
1277                 if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) {
1278                     prepareTracking(y, !mExpanded);// opening if we're not already fully visible
1279                     mVelocityTracker.addMovement(event);
1280                 }
1281             }
1282         } else if (mTracking) {
1283             mVelocityTracker.addMovement(event);
1284             final int minY = statusBarSize + mCloseView.getHeight();
1285             if (event.getAction() == MotionEvent.ACTION_MOVE) {
1286                 int y = (int)event.getRawY();
1287                 if (mAnimatingReveal && y < minY) {
1288                     // nothing
1289                 } else  {
1290                     mAnimatingReveal = false;
1291                     updateExpandedViewPos(y + mViewDelta);
1292                 }
1293             } else if (event.getAction() == MotionEvent.ACTION_UP) {
1294                 mVelocityTracker.computeCurrentVelocity(1000);
1295
1296                 float yVel = mVelocityTracker.getYVelocity();
1297                 boolean negative = yVel < 0;
1298
1299                 float xVel = mVelocityTracker.getXVelocity();
1300                 if (xVel < 0) {
1301                     xVel = -xVel;
1302                 }
1303                 if (xVel > 150.0f) {
1304                     xVel = 150.0f; // limit how much we care about the x axis
1305                 }
1306
1307                 float vel = (float)Math.hypot(yVel, xVel);
1308                 if (negative) {
1309                     vel = -vel;
1310                 }
1311                 
1312                 performFling((int)event.getRawY(), vel, false);
1313             }
1314             
1315         }
1316         return false;
1317     }
1318
1319     private class Launcher implements View.OnClickListener {
1320         private PendingIntent mIntent;
1321         private String mPkg;
1322         private String mTag;
1323         private int mId;
1324
1325         Launcher(PendingIntent intent, String pkg, String tag, int id) {
1326             mIntent = intent;
1327             mPkg = pkg;
1328             mTag = tag;
1329             mId = id;
1330         }
1331
1332         public void onClick(View v) {
1333             try {
1334                 // The intent we are sending is for the application, which
1335                 // won't have permission to immediately start an activity after
1336                 // the user switches to home.  We know it is safe to do at this
1337                 // point, so make sure new activity switches are now allowed.
1338                 ActivityManagerNative.getDefault().resumeAppSwitches();
1339             } catch (RemoteException e) {
1340             }
1341             int[] pos = new int[2];
1342             v.getLocationOnScreen(pos);
1343             Intent overlay = new Intent();
1344             overlay.setSourceBounds(
1345                     new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
1346             try {
1347                 mIntent.send(mContext, 0, overlay);
1348                 mNotificationCallbacks.onNotificationClick(mPkg, mTag, mId);
1349             } catch (PendingIntent.CanceledException e) {
1350                 // the stack trace isn't very helpful here.  Just log the exception message.
1351                 Slog.w(TAG, "Sending contentIntent failed: " + e);
1352             }
1353             deactivate();
1354         }
1355     }
1356
1357     private class MyTicker extends Ticker {
1358         MyTicker(Context context, StatusBarView sb) {
1359             super(context, sb);
1360         }
1361         
1362         @Override
1363         void tickerStarting() {
1364             mTicking = true;
1365             mIcons.setVisibility(View.GONE);
1366             mTickerView.setVisibility(View.VISIBLE);
1367             mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
1368             mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
1369             if (mExpandedVisible) {
1370                 setDateViewVisibility(false, com.android.internal.R.anim.push_up_out);
1371             }
1372         }
1373
1374         @Override
1375         void tickerDone() {
1376             mIcons.setVisibility(View.VISIBLE);
1377             mTickerView.setVisibility(View.GONE);
1378             mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
1379             mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
1380                         mTickingDoneListener));
1381             if (mExpandedVisible) {
1382                 setDateViewVisibility(true, com.android.internal.R.anim.push_down_in);
1383             }
1384         }
1385
1386         void tickerHalting() {
1387             mIcons.setVisibility(View.VISIBLE);
1388             mTickerView.setVisibility(View.GONE);
1389             mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
1390             mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out,
1391                         mTickingDoneListener));
1392             if (mExpandedVisible) {
1393                 setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
1394             }
1395         }
1396     }
1397
1398     Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
1399         public void onAnimationEnd(Animation animation) {
1400             mTicking = false;
1401         }
1402         public void onAnimationRepeat(Animation animation) {
1403         }
1404         public void onAnimationStart(Animation animation) {
1405         }
1406     };
1407
1408     private Animation loadAnim(int id, Animation.AnimationListener listener) {
1409         Animation anim = AnimationUtils.loadAnimation(mContext, id);
1410         if (listener != null) {
1411             anim.setAnimationListener(listener);
1412         }
1413         return anim;
1414     }
1415
1416     public String viewInfo(View v) {
1417         return "(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
1418                 + " " + v.getWidth() + "x" + v.getHeight() + ")";
1419     }
1420
1421     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1422         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1423                 != PackageManager.PERMISSION_GRANTED) {
1424             pw.println("Permission Denial: can't dump StatusBar from from pid="
1425                     + Binder.getCallingPid()
1426                     + ", uid=" + Binder.getCallingUid());
1427             return;
1428         }
1429         
1430         synchronized (mQueueLock) {
1431             pw.println("Current Status Bar state:");
1432             pw.println("  mExpanded=" + mExpanded
1433                     + ", mExpandedVisible=" + mExpandedVisible);
1434             pw.println("  mTicking=" + mTicking);
1435             pw.println("  mTracking=" + mTracking);
1436             pw.println("  mAnimating=" + mAnimating
1437                     + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
1438                     + ", mAnimAccel=" + mAnimAccel);
1439             pw.println("  mCurAnimationTime=" + mCurAnimationTime
1440                     + " mAnimLastTime=" + mAnimLastTime);
1441             pw.println("  mDisplayHeight=" + mDisplayHeight
1442                     + " mAnimatingReveal=" + mAnimatingReveal
1443                     + " mViewDelta=" + mViewDelta);
1444             pw.println("  mDisplayHeight=" + mDisplayHeight);
1445             final int N = mQueue.size();
1446             pw.println("  mQueue.size=" + N);
1447             for (int i=0; i<N; i++) {
1448                 PendingOp op = mQueue.get(i);
1449                 pw.println("    [" + i + "] key=" + op.key + " code=" + op.code + " visible="
1450                         + op.visible);
1451                 pw.println("           iconData=" + op.iconData);
1452                 pw.println("           notificationData=" + op.notificationData);
1453             }
1454             pw.println("  mExpandedParams: " + mExpandedParams);
1455             pw.println("  mExpandedView: " + viewInfo(mExpandedView));
1456             pw.println("  mExpandedDialog: " + mExpandedDialog);
1457             pw.println("  mTrackingParams: " + mTrackingParams);
1458             pw.println("  mTrackingView: " + viewInfo(mTrackingView));
1459             pw.println("  mOngoingTitle: " + viewInfo(mOngoingTitle));
1460             pw.println("  mOngoingItems: " + viewInfo(mOngoingItems));
1461             pw.println("  mLatestTitle: " + viewInfo(mLatestTitle));
1462             pw.println("  mLatestItems: " + viewInfo(mLatestItems));
1463             pw.println("  mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle));
1464             pw.println("  mCloseView: " + viewInfo(mCloseView));
1465             pw.println("  mTickerView: " + viewInfo(mTickerView));
1466             pw.println("  mScrollView: " + viewInfo(mScrollView)
1467                     + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
1468             pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout));
1469         }
1470         synchronized (mIconMap) {
1471             final int N = mIconMap.size();
1472             pw.println("  mIconMap.size=" + N);
1473             Set<IBinder> keys = mIconMap.keySet();
1474             int i=0;
1475             for (IBinder key: keys) {
1476                 StatusBarIcon icon = mIconMap.get(key);
1477                 pw.println("    [" + i + "] key=" + key);
1478                 pw.println("           data=" + icon.mData);
1479                 i++;
1480             }
1481         }
1482         synchronized (mNotificationData) {
1483             int N = mNotificationData.ongoingCount();
1484             pw.println("  ongoingCount.size=" + N);
1485             for (int i=0; i<N; i++) {
1486                 StatusBarNotification n = mNotificationData.getOngoing(i);
1487                 pw.println("    [" + i + "] key=" + n.key + " view=" + n.view);
1488                 pw.println("           data=" + n.data);
1489             }
1490             N = mNotificationData.latestCount();
1491             pw.println("  ongoingCount.size=" + N);
1492             for (int i=0; i<N; i++) {
1493                 StatusBarNotification n = mNotificationData.getLatest(i);
1494                 pw.println("    [" + i + "] key=" + n.key + " view=" + n.view);
1495                 pw.println("           data=" + n.data);
1496             }
1497         }
1498         synchronized (mDisableRecords) {
1499             final int N = mDisableRecords.size();
1500             pw.println("  mDisableRecords.size=" + N
1501                     + " mDisabled=0x" + Integer.toHexString(mDisabled));
1502             for (int i=0; i<N; i++) {
1503                 DisableRecord tok = mDisableRecords.get(i);
1504                 pw.println("    [" + i + "] what=0x" + Integer.toHexString(tok.what)
1505                                 + " pkg=" + tok.pkg + " token=" + tok.token);
1506             }
1507         }
1508         
1509         if (false) {
1510             pw.println("see the logcat for a dump of the views we have created.");
1511             // must happen on ui thread
1512             mHandler.post(new Runnable() {
1513                     public void run() {
1514                         mStatusBarView.getLocationOnScreen(mAbsPos);
1515                         Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
1516                                 + ") " + mStatusBarView.getWidth() + "x"
1517                                 + mStatusBarView.getHeight());
1518                         mStatusBarView.debug();
1519
1520                         mExpandedView.getLocationOnScreen(mAbsPos);
1521                         Slog.d(TAG, "mExpandedView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
1522                                 + ") " + mExpandedView.getWidth() + "x"
1523                                 + mExpandedView.getHeight());
1524                         mExpandedView.debug();
1525
1526                         mTrackingView.getLocationOnScreen(mAbsPos);
1527                         Slog.d(TAG, "mTrackingView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
1528                                 + ") " + mTrackingView.getWidth() + "x"
1529                                 + mTrackingView.getHeight());
1530                         mTrackingView.debug();
1531                     }
1532                 });
1533         }
1534     }
1535
1536     void onBarViewAttached() {
1537         WindowManager.LayoutParams lp;
1538         int pixelFormat;
1539         Drawable bg;
1540
1541         /// ---------- Tracking View --------------
1542         pixelFormat = PixelFormat.RGBX_8888;
1543         bg = mTrackingView.getBackground();
1544         if (bg != null) {
1545             pixelFormat = bg.getOpacity();
1546         }
1547
1548         lp = new WindowManager.LayoutParams(
1549                 ViewGroup.LayoutParams.MATCH_PARENT,
1550                 ViewGroup.LayoutParams.MATCH_PARENT,
1551                 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
1552                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
1553                 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
1554                 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
1555                 pixelFormat);
1556 //        lp.token = mStatusBarView.getWindowToken();
1557         lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
1558         lp.setTitle("TrackingView");
1559         lp.y = mTrackingPosition;
1560         mTrackingParams = lp;
1561
1562         WindowManagerImpl.getDefault().addView(mTrackingView, lp);
1563     }
1564
1565     void onTrackingViewAttached() {
1566         WindowManager.LayoutParams lp;
1567         int pixelFormat;
1568         Drawable bg;
1569
1570         /// ---------- Expanded View --------------
1571         pixelFormat = PixelFormat.TRANSLUCENT;
1572
1573         final int disph = mDisplay.getHeight();
1574         lp = mExpandedDialog.getWindow().getAttributes();
1575         lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
1576         lp.height = getExpandedHeight();
1577         lp.x = 0;
1578         mTrackingPosition = lp.y = -disph; // sufficiently large negative
1579         lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
1580         lp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
1581                 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
1582                 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
1583                 | WindowManager.LayoutParams.FLAG_DITHER
1584                 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1585         lp.format = pixelFormat;
1586         lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
1587         lp.setTitle("StatusBarExpanded");
1588         mExpandedDialog.getWindow().setAttributes(lp);
1589         mExpandedDialog.getWindow().setFormat(pixelFormat);
1590         mExpandedParams = lp;
1591
1592         mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
1593         mExpandedDialog.setContentView(mExpandedView,
1594                 new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
1595                                            ViewGroup.LayoutParams.MATCH_PARENT));
1596         mExpandedDialog.getWindow().setBackgroundDrawable(null);
1597         mExpandedDialog.show();
1598         FrameLayout hack = (FrameLayout)mExpandedView.getParent();
1599     }
1600
1601     void setDateViewVisibility(boolean visible, int anim) {
1602         mDateView.setUpdates(visible);
1603         mDateView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1604         mDateView.startAnimation(loadAnim(anim, null));
1605     }
1606
1607     void setNotificationIconVisibility(boolean visible, int anim) {
1608         int old = mNotificationIcons.getVisibility();
1609         int v = visible ? View.VISIBLE : View.INVISIBLE;
1610         if (old != v) {
1611             mNotificationIcons.setVisibility(v);
1612             mNotificationIcons.startAnimation(loadAnim(anim, null));
1613         }
1614     }
1615
1616     void updateExpandedViewPos(int expandedPosition) {
1617         if (SPEW) {
1618             Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition
1619                     + " mTrackingParams.y=" + mTrackingParams.y
1620                     + " mTrackingPosition=" + mTrackingPosition);
1621         }
1622
1623         int h = mStatusBarView.getHeight();
1624         int disph = mDisplay.getHeight();
1625
1626         // If the expanded view is not visible, make sure they're still off screen.
1627         // Maybe the view was resized.
1628         if (!mExpandedVisible) {
1629             if (mTrackingView != null) {
1630                 mTrackingPosition = -disph;
1631                 if (mTrackingParams != null) {
1632                     mTrackingParams.y = mTrackingPosition;
1633                     WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
1634                 }
1635             }
1636             if (mExpandedParams != null) {
1637                 mExpandedParams.y = -disph;
1638                 mExpandedDialog.getWindow().setAttributes(mExpandedParams);
1639             }
1640             return;
1641         }
1642
1643         // tracking view...
1644         int pos;
1645         if (expandedPosition == EXPANDED_FULL_OPEN) {
1646             pos = h;
1647         }
1648         else if (expandedPosition == EXPANDED_LEAVE_ALONE) {
1649             pos = mTrackingPosition;
1650         }
1651         else {
1652             if (expandedPosition <= disph) {
1653                 pos = expandedPosition;
1654             } else {
1655                 pos = disph;
1656             }
1657             pos -= disph-h;
1658         }
1659         mTrackingPosition = mTrackingParams.y = pos;
1660         mTrackingParams.height = disph-h;
1661         WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
1662
1663         if (mExpandedParams != null) {
1664             mCloseView.getLocationInWindow(mPositionTmp);
1665             final int closePos = mPositionTmp[1];
1666
1667             mExpandedContents.getLocationInWindow(mPositionTmp);
1668             final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight();
1669
1670             mExpandedParams.y = pos + mTrackingView.getHeight()
1671                     - (mTrackingParams.height-closePos) - contentsBottom;
1672             int max = h;
1673             if (mExpandedParams.y > max) {
1674                 mExpandedParams.y = max;
1675             }
1676             int min = mTrackingPosition;
1677             if (mExpandedParams.y < min) {
1678                 mExpandedParams.y = min;
1679             }
1680
1681             boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h;
1682             if (!visible) {
1683                 // if the contents aren't visible, move the expanded view way off screen
1684                 // because the window itself extends below the content view.
1685                 mExpandedParams.y = -disph;
1686             }
1687             panelSlightlyVisible(visible);
1688             mExpandedDialog.getWindow().setAttributes(mExpandedParams);
1689         }
1690
1691         if (SPEW) {
1692             Slog.d(TAG, "updateExpandedViewPos after  expandedPosition=" + expandedPosition
1693                     + " mTrackingParams.y=" + mTrackingParams.y
1694                     + " mTrackingPosition=" + mTrackingPosition
1695                     + " mExpandedParams.y=" + mExpandedParams.y
1696                     + " mExpandedParams.height=" + mExpandedParams.height);
1697         }
1698     }
1699
1700     int getExpandedHeight() {
1701         return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight();
1702     }
1703
1704     void updateExpandedHeight() {
1705         if (mExpandedView != null) {
1706             mExpandedParams.height = getExpandedHeight();
1707             mExpandedDialog.getWindow().setAttributes(mExpandedParams);
1708         }
1709     }
1710
1711     /**
1712      * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
1713      * This was added last-minute and is inconsistent with the way the rest of the notifications
1714      * are handled, because the notification isn't really cancelled.  The lights are just
1715      * turned off.  If any other notifications happen, the lights will turn back on.  Steve says
1716      * this is what he wants. (see bug 1131461)
1717      */
1718     private boolean mPanelSlightlyVisible;
1719     void panelSlightlyVisible(boolean visible) {
1720         if (mPanelSlightlyVisible != visible) {
1721             mPanelSlightlyVisible = visible;
1722             if (visible) {
1723                 // tell the notification manager to turn off the lights.
1724                 mNotificationCallbacks.onPanelRevealed();
1725             }
1726         }
1727     }
1728
1729     void performDisableActions(int net) {
1730         int old = mDisabled;
1731         int diff = net ^ old;
1732         mDisabled = net;
1733
1734         // act accordingly
1735         if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
1736             if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
1737                 Slog.d(TAG, "DISABLE_EXPAND: yes");
1738                 animateCollapse();
1739             }
1740         }
1741         if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1742             if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1743                 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
1744                 if (mTicking) {
1745                     mNotificationIcons.setVisibility(View.INVISIBLE);
1746                     mTicker.halt();
1747                 } else {
1748                     setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
1749                 }
1750             } else {
1751                 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
1752                 if (!mExpandedVisible) {
1753                     setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1754                 }
1755             }
1756         } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1757             if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1758                 mTicker.halt();
1759             }
1760         }
1761     }
1762
1763     private void updateColors() {
1764         mDateView.setTextColor(Settings.System.getInt(mContext.getContentResolver(), Settings.System.DATE_COLOR, blackColor));
1765         mNoNotificationsTitle.setTextColor(Settings.System.getInt(mContext.getContentResolver(), Settings.System.NO_NOTIF_COLOR, whiteColor));
1766         mLatestTitle.setTextColor(Settings.System.getInt(mContext.getContentResolver(), Settings.System.LATEST_NOTIF_COLOR, whiteColor));
1767         mOngoingTitle.setTextColor(Settings.System.getInt(mContext.getContentResolver(), Settings.System.ONGOING_NOTIF_COLOR, whiteColor));
1768         mSpnLabel.setTextColor(Settings.System.getInt(mContext.getContentResolver(), Settings.System.SPN_LABEL_COLOR, blackColor));
1769         mPlmnLabel.setTextColor(Settings.System.getInt(mContext.getContentResolver(), Settings.System.PLMN_LABEL_COLOR, blackColor));
1770         mClearButton.setTextColor(Settings.System.getInt(mContext.getContentResolver(), Settings.System.CLEAR_BUTTON_LABEL_COLOR, blackColor));
1771         tickerView.updateColors(Settings.System.getInt(mContext.getContentResolver(), Settings.System.NEW_NOTIF_TICKER_COLOR, blackColor));
1772     }
1773
1774     private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
1775         public void onClick(View v) {
1776             mNotificationCallbacks.onClearAll();
1777             addPendingOp(OP_EXPAND, null, false);
1778         }
1779     };
1780
1781     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1782         public void onReceive(Context context, Intent intent) {
1783             String action = intent.getAction();
1784             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
1785                     || Intent.ACTION_SCREEN_OFF.equals(action)) {
1786                 deactivate();
1787             }
1788             else if (Telephony.Intents.SPN_STRINGS_UPDATED_ACTION.equals(action)) {
1789                 updateNetworkName(intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_SPN, false),
1790                         intent.getStringExtra(Telephony.Intents.EXTRA_SPN),
1791                         intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_PLMN, false),
1792                         intent.getStringExtra(Telephony.Intents.EXTRA_PLMN));
1793             }
1794             else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1795                 updateResources();
1796             }
1797         }
1798     };
1799
1800     void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) {
1801         // Double carrier
1802         mShowPlmnSb = (Settings.System.getInt(mContext.getContentResolver(), Settings.System.SHOW_PLMN_SB, 1) == 1);
1803         mShowSpnSb = (Settings.System.getInt(mContext.getContentResolver(), Settings.System.SHOW_SPN_SB, 1) == 1);
1804         if (false) {
1805             Slog.d(TAG, "updateNetworkName showSpn=" + showSpn + " spn=" + spn
1806                     + " showPlmn=" + showPlmn + " plmn=" + plmn);
1807         }
1808         boolean something = false;
1809         // Double carrier - bcrook
1810         if (showPlmn && mShowPlmnSb) {
1811             mPlmnLabel.setVisibility(View.VISIBLE);
1812             if (plmn != null) {
1813                 mPlmnLabel.setText(plmn);
1814             } else {
1815                 mPlmnLabel.setText(R.string.lockscreen_carrier_default);
1816             }
1817         } else {
1818             mPlmnLabel.setText("");
1819             mPlmnLabel.setVisibility(View.GONE);
1820         }
1821         // Double carrier - bcrook, refinements from Wysie
1822         if (showSpn && spn != null && mShowSpnSb) {
1823             mSpnLabel.setText(spn);
1824             mSpnLabel.setVisibility(View.VISIBLE);
1825             something = true;
1826         } else {
1827             mSpnLabel.setText("");
1828             mSpnLabel.setVisibility(View.GONE);
1829         }
1830     }
1831
1832     /**
1833      * Reload some of our resources when the configuration changes.
1834      * 
1835      * We don't reload everything when the configuration changes -- we probably
1836      * should, but getting that smooth is tough.  Someday we'll fix that.  In the
1837      * meantime, just update the things that we know change.
1838      */
1839     void updateResources() {
1840         Resources res = mContext.getResources();
1841
1842         mClearButton.setText(mContext.getText(R.string.status_bar_clear_all_button));
1843         mOngoingTitle.setText(mContext.getText(R.string.status_bar_ongoing_events_title));
1844         mLatestTitle.setText(mContext.getText(R.string.status_bar_latest_events_title));
1845         mNoNotificationsTitle.setText(mContext.getText(R.string.status_bar_no_notifications_title));
1846
1847         mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
1848
1849         if (false) Slog.v(TAG, "updateResources");
1850     }
1851
1852     //
1853     // tracing
1854     //
1855
1856     void postStartTracing() {
1857         mHandler.postDelayed(mStartTracing, 3000);
1858     }
1859
1860     void vibrate() {
1861         android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
1862                 Context.VIBRATOR_SERVICE);
1863         vib.vibrate(250);
1864     }
1865
1866     Runnable mStartTracing = new Runnable() {
1867         public void run() {
1868             vibrate();
1869             SystemClock.sleep(250);
1870             Slog.d(TAG, "startTracing");
1871             android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
1872             mHandler.postDelayed(mStopTracing, 10000);
1873         }
1874     };
1875
1876     Runnable mStopTracing = new Runnable() {
1877         public void run() {
1878             android.os.Debug.stopMethodTracing();
1879             Slog.d(TAG, "stopTracing");
1880             vibrate();
1881         }
1882     };
1883     
1884     class UninstallReceiver extends BroadcastReceiver {
1885         public UninstallReceiver() {
1886             IntentFilter filter = new IntentFilter();
1887             filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1888             filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1889             filter.addDataScheme("package");
1890             mContext.registerReceiver(this, filter);
1891             IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
1892             mContext.registerReceiver(this, sdFilter);
1893         }
1894         
1895         @Override
1896         public void onReceive(Context context, Intent intent) {
1897             String pkgList[] = null;
1898             if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())) {
1899                 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
1900             } else {
1901                 Uri data = intent.getData();
1902                 if (data != null) {
1903                     String pkg = data.getSchemeSpecificPart();
1904                     if (pkg != null) {
1905                         pkgList = new String[]{pkg};
1906                     }
1907                 }
1908             }
1909             ArrayList<StatusBarNotification> list = null;
1910             if (pkgList != null) {
1911                 synchronized (StatusBarService.this) {
1912                     for (String pkg : pkgList) {
1913                         list = mNotificationData.notificationsForPackage(pkg);
1914                     }
1915                 }
1916             }
1917             
1918             if (list != null) {
1919                 final int N = list.size();
1920                 for (int i=0; i<N; i++) {
1921                     removeIcon(list.get(i).key);
1922                 }
1923             }
1924         }
1925     }
1926 }