• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.systemui.statusbar;
18 
19 import android.app.Service;
20 import com.android.internal.statusbar.IStatusBar;
21 import com.android.internal.statusbar.IStatusBarService;
22 import com.android.internal.statusbar.StatusBarIcon;
23 import com.android.internal.statusbar.StatusBarIconList;
24 import com.android.internal.statusbar.StatusBarNotification;
25 
26 import android.app.ActivityManagerNative;
27 import android.app.Dialog;
28 import android.app.Notification;
29 import android.app.PendingIntent;
30 import android.app.Service;
31 import android.app.StatusBarManager;
32 import android.content.BroadcastReceiver;
33 import android.content.Context;
34 import android.content.Intent;
35 import android.content.IntentFilter;
36 import android.content.pm.PackageManager;
37 import android.content.res.Resources;
38 import android.graphics.PixelFormat;
39 import android.graphics.Rect;
40 import android.graphics.drawable.Drawable;
41 import android.net.Uri;
42 import android.os.IBinder;
43 import android.os.RemoteException;
44 import android.os.Binder;
45 import android.os.Handler;
46 import android.os.Message;
47 import android.os.ServiceManager;
48 import android.os.SystemClock;
49 import android.text.TextUtils;
50 import android.util.Slog;
51 import android.util.Log;
52 import android.view.Display;
53 import android.view.Gravity;
54 import android.view.KeyEvent;
55 import android.view.LayoutInflater;
56 import android.view.MotionEvent;
57 import android.view.VelocityTracker;
58 import android.view.View;
59 import android.view.ViewGroup;
60 import android.view.Window;
61 import android.view.WindowManager;
62 import android.view.WindowManagerImpl;
63 import android.view.animation.Animation;
64 import android.view.animation.AnimationUtils;
65 import android.widget.ImageView;
66 import android.widget.LinearLayout;
67 import android.widget.RemoteViews;
68 import android.widget.ScrollView;
69 import android.widget.TextView;
70 import android.widget.FrameLayout;
71 
72 import java.io.FileDescriptor;
73 import java.io.PrintWriter;
74 import java.util.ArrayList;
75 import java.util.HashMap;
76 import java.util.Set;
77 
78 import com.android.systemui.R;
79 import com.android.systemui.statusbar.policy.StatusBarPolicy;
80 
81 
82 
83 public class StatusBarService extends Service implements CommandQueue.Callbacks {
84     static final String TAG = "StatusBarService";
85     static final boolean SPEW_ICONS = false;
86     static final boolean SPEW = false;
87 
88     public static final String ACTION_STATUSBAR_START
89             = "com.android.internal.policy.statusbar.START";
90 
91     static final int EXPANDED_LEAVE_ALONE = -10000;
92     static final int EXPANDED_FULL_OPEN = -10001;
93 
94     private static final int MSG_ANIMATE = 1000;
95     private static final int MSG_ANIMATE_REVEAL = 1001;
96 
97     StatusBarPolicy mIconPolicy;
98 
99     CommandQueue mCommandQueue;
100     IStatusBarService mBarService;
101 
102     int mIconSize;
103     Display mDisplay;
104     StatusBarView mStatusBarView;
105     int mPixelFormat;
106     H mHandler = new H();
107     Object mQueueLock = new Object();
108 
109     // icons
110     LinearLayout mIcons;
111     IconMerger mNotificationIcons;
112     LinearLayout mStatusIcons;
113 
114     // expanded notifications
115     Dialog mExpandedDialog;
116     ExpandedView mExpandedView;
117     WindowManager.LayoutParams mExpandedParams;
118     ScrollView mScrollView;
119     View mNotificationLinearLayout;
120     View mExpandedContents;
121     // top bar
122     TextView mNoNotificationsTitle;
123     TextView mClearButton;
124     // drag bar
125     CloseDragHandle mCloseView;
126     // ongoing
127     NotificationData mOngoing = new NotificationData();
128     TextView mOngoingTitle;
129     LinearLayout mOngoingItems;
130     // latest
131     NotificationData mLatest = new NotificationData();
132     TextView mLatestTitle;
133     LinearLayout mLatestItems;
134     // position
135     int[] mPositionTmp = new int[2];
136     boolean mExpanded;
137     boolean mExpandedVisible;
138 
139     // the date view
140     DateView mDateView;
141 
142     // the tracker view
143     TrackingView mTrackingView;
144     WindowManager.LayoutParams mTrackingParams;
145     int mTrackingPosition; // the position of the top of the tracking view.
146     private boolean mPanelSlightlyVisible;
147 
148     // ticker
149     private Ticker mTicker;
150     private View mTickerView;
151     private boolean mTicking;
152 
153     // Tracking finger for opening/closing.
154     int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
155     boolean mTracking;
156     VelocityTracker mVelocityTracker;
157 
158     static final int ANIM_FRAME_DURATION = (1000/60);
159 
160     boolean mAnimating;
161     long mCurAnimationTime;
162     float mDisplayHeight;
163     float mAnimY;
164     float mAnimVel;
165     float mAnimAccel;
166     long mAnimLastTime;
167     boolean mAnimatingReveal = false;
168     int mViewDelta;
169     int[] mAbsPos = new int[2];
170 
171     // for disabling the status bar
172     int mDisabled = 0;
173 
174     private class ExpandedDialog extends Dialog {
ExpandedDialog(Context context)175         ExpandedDialog(Context context) {
176             super(context, com.android.internal.R.style.Theme_Light_NoTitleBar);
177         }
178 
179         @Override
dispatchKeyEvent(KeyEvent event)180         public boolean dispatchKeyEvent(KeyEvent event) {
181             boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
182             switch (event.getKeyCode()) {
183             case KeyEvent.KEYCODE_BACK:
184                 if (!down) {
185                     animateCollapse();
186                 }
187                 return true;
188             }
189             return super.dispatchKeyEvent(event);
190         }
191     }
192 
193 
194     @Override
onCreate()195     public void onCreate() {
196         // First set up our views and stuff.
197         mDisplay = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
198         makeStatusBarView(this);
199 
200         // Connect in to the status bar manager service
201         StatusBarIconList iconList = new StatusBarIconList();
202         ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();
203         ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();
204         mCommandQueue = new CommandQueue(this, iconList);
205         mBarService = IStatusBarService.Stub.asInterface(
206                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
207         try {
208             mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications);
209         } catch (RemoteException ex) {
210             // If the system process isn't there we're doomed anyway.
211         }
212 
213         // Set up the initial icon state
214         int N = iconList.size();
215         int viewIndex = 0;
216         for (int i=0; i<N; i++) {
217             StatusBarIcon icon = iconList.getIcon(i);
218             if (icon != null) {
219                 addIcon(iconList.getSlot(i), i, viewIndex, icon);
220                 viewIndex++;
221             }
222         }
223 
224         // Set up the initial notification state
225         N = notificationKeys.size();
226         if (N == notifications.size()) {
227             for (int i=0; i<N; i++) {
228                 addNotification(notificationKeys.get(i), notifications.get(i));
229             }
230         } else {
231             Log.wtf(TAG, "Notification list length mismatch: keys=" + N
232                     + " notifications=" + notifications.size());
233         }
234 
235         // Put up the view
236         addStatusBarView();
237 
238         // Lastly, call to the icon policy to install/update all the icons.
239         mIconPolicy = new StatusBarPolicy(this);
240     }
241 
242     @Override
onDestroy()243     public void onDestroy() {
244         // we're never destroyed
245     }
246 
247     /**
248      * Nobody binds to us.
249      */
250     @Override
onBind(Intent intent)251     public IBinder onBind(Intent intent) {
252         return null;
253     }
254 
255     // ================================================================================
256     // Constructing the view
257     // ================================================================================
makeStatusBarView(Context context)258     private void makeStatusBarView(Context context) {
259         Resources res = context.getResources();
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         expanded.mService = this;
266 
267         StatusBarView sb = (StatusBarView)View.inflate(context, R.layout.status_bar, null);
268         sb.mService = this;
269 
270         // figure out which pixel-format to use for the status bar.
271         mPixelFormat = PixelFormat.TRANSLUCENT;
272         Drawable bg = sb.getBackground();
273         if (bg != null) {
274             mPixelFormat = bg.getOpacity();
275         }
276 
277         mStatusBarView = sb;
278         mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons);
279         mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons);
280         mIcons = (LinearLayout)sb.findViewById(R.id.icons);
281         mTickerView = sb.findViewById(R.id.ticker);
282         mDateView = (DateView)sb.findViewById(R.id.date);
283 
284         mExpandedDialog = new ExpandedDialog(context);
285         mExpandedView = expanded;
286         mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout);
287         mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle);
288         mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems);
289         mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle);
290         mLatestItems = (LinearLayout)expanded.findViewById(R.id.latestItems);
291         mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle);
292         mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button);
293         mClearButton.setOnClickListener(mClearButtonListener);
294         mScrollView = (ScrollView)expanded.findViewById(R.id.scroll);
295         mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout);
296 
297         mExpandedView.setVisibility(View.GONE);
298         mOngoingTitle.setVisibility(View.GONE);
299         mLatestTitle.setVisibility(View.GONE);
300 
301         mTicker = new MyTicker(context, sb);
302 
303         TickerView tickerView = (TickerView)sb.findViewById(R.id.tickerText);
304         tickerView.mTicker = mTicker;
305 
306         mTrackingView = (TrackingView)View.inflate(context, R.layout.status_bar_tracking, null);
307         mTrackingView.mService = this;
308         mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close);
309         mCloseView.mService = this;
310 
311         mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
312 
313         // set the inital view visibility
314         setAreThereNotifications();
315         mDateView.setVisibility(View.INVISIBLE);
316 
317         // receive broadcasts
318         IntentFilter filter = new IntentFilter();
319         filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
320         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
321         filter.addAction(Intent.ACTION_SCREEN_OFF);
322         context.registerReceiver(mBroadcastReceiver, filter);
323     }
324 
addStatusBarView()325     protected void addStatusBarView() {
326         Resources res = getResources();
327         final int height= res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
328 
329         final StatusBarView view = mStatusBarView;
330         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
331                 ViewGroup.LayoutParams.MATCH_PARENT,
332                 height,
333                 WindowManager.LayoutParams.TYPE_STATUS_BAR,
334                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
335                     | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING,
336                 PixelFormat.RGBX_8888);
337         lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
338         lp.setTitle("StatusBar");
339         lp.windowAnimations = com.android.internal.R.style.Animation_StatusBar;
340 
341         WindowManagerImpl.getDefault().addView(view, lp);
342     }
343 
addIcon(String slot, int index, int viewIndex, StatusBarIcon icon)344     public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
345         if (SPEW_ICONS) {
346             Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
347                     + " icon=" + icon);
348         }
349         StatusBarIconView view = new StatusBarIconView(this, slot);
350         view.set(icon);
351         mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize));
352     }
353 
updateIcon(String slot, int index, int viewIndex, StatusBarIcon old, StatusBarIcon icon)354     public void updateIcon(String slot, int index, int viewIndex,
355             StatusBarIcon old, StatusBarIcon icon) {
356         if (SPEW_ICONS) {
357             Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
358                     + " old=" + old + " icon=" + icon);
359         }
360         StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex);
361         view.set(icon);
362     }
363 
removeIcon(String slot, int index, int viewIndex)364     public void removeIcon(String slot, int index, int viewIndex) {
365         if (SPEW_ICONS) {
366             Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
367         }
368         mStatusIcons.removeViewAt(viewIndex);
369     }
370 
addNotification(IBinder key, StatusBarNotification notification)371     public void addNotification(IBinder key, StatusBarNotification notification) {
372         boolean shouldTick = true;
373         if (notification.notification.fullScreenIntent != null) {
374             shouldTick = false;
375             Slog.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
376             try {
377                 notification.notification.fullScreenIntent.send();
378             } catch (PendingIntent.CanceledException e) {
379             }
380         }
381 
382         StatusBarIconView iconView = addNotificationViews(key, notification);
383         if (iconView == null) return;
384 
385         if (shouldTick) {
386             tick(notification);
387         }
388 
389         // Recalculate the position of the sliding windows and the titles.
390         setAreThereNotifications();
391         updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
392     }
393 
updateNotification(IBinder key, StatusBarNotification notification)394     public void updateNotification(IBinder key, StatusBarNotification notification) {
395         NotificationData oldList;
396         int oldIndex = mOngoing.findEntry(key);
397         if (oldIndex >= 0) {
398             oldList = mOngoing;
399         } else {
400             oldIndex = mLatest.findEntry(key);
401             if (oldIndex < 0) {
402                 Slog.w(TAG, "updateNotification for unknown key: " + key);
403                 return;
404             }
405             oldList = mLatest;
406         }
407         final NotificationData.Entry oldEntry = oldList.getEntryAt(oldIndex);
408         final StatusBarNotification oldNotification = oldEntry.notification;
409         final RemoteViews oldContentView = oldNotification.notification.contentView;
410 
411         final RemoteViews contentView = notification.notification.contentView;
412 
413         if (false) {
414             Slog.d(TAG, "old notification: when=" + oldNotification.notification.when
415                     + " ongoing=" + oldNotification.isOngoing()
416                     + " expanded=" + oldEntry.expanded
417                     + " contentView=" + oldContentView);
418             Slog.d(TAG, "new notification: when=" + notification.notification.when
419                     + " ongoing=" + oldNotification.isOngoing()
420                     + " contentView=" + contentView);
421         }
422 
423         // Can we just reapply the RemoteViews in place?  If when didn't change, the order
424         // didn't change.
425         if (notification.notification.when == oldNotification.notification.when
426                 && notification.isOngoing() == oldNotification.isOngoing()
427                 && oldEntry.expanded != null
428                 && contentView != null && oldContentView != null
429                 && contentView.getPackage() != null
430                 && oldContentView.getPackage() != null
431                 && oldContentView.getPackage().equals(contentView.getPackage())
432                 && oldContentView.getLayoutId() == contentView.getLayoutId()) {
433             if (SPEW) Slog.d(TAG, "reusing notification");
434             oldEntry.notification = notification;
435             try {
436                 // Reapply the RemoteViews
437                 contentView.reapply(this, oldEntry.content);
438                 // update the contentIntent
439                 final PendingIntent contentIntent = notification.notification.contentIntent;
440                 if (contentIntent != null) {
441                     oldEntry.content.setOnClickListener(new Launcher(contentIntent,
442                                 notification.pkg, notification.tag, notification.id));
443                 }
444                 // Update the icon.
445                 final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
446                         notification.notification.icon, notification.notification.iconLevel,
447                         notification.notification.number);
448                 if (!oldEntry.icon.set(ic)) {
449                     handleNotificationError(key, notification, "Couldn't update icon: " + ic);
450                     return;
451                 }
452             }
453             catch (RuntimeException e) {
454                 // It failed to add cleanly.  Log, and remove the view from the panel.
455                 Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
456                 removeNotificationViews(key);
457                 addNotificationViews(key, notification);
458             }
459         } else {
460             if (SPEW) Slog.d(TAG, "not reusing notification");
461             removeNotificationViews(key);
462             addNotificationViews(key, notification);
463         }
464 
465         // Restart the ticker if it's still running
466         if (notification.notification.tickerText != null
467                 && !TextUtils.equals(notification.notification.tickerText,
468                     oldEntry.notification.notification.tickerText)) {
469             tick(notification);
470         }
471 
472         // Recalculate the position of the sliding windows and the titles.
473         setAreThereNotifications();
474         updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
475     }
476 
removeNotification(IBinder key)477     public void removeNotification(IBinder key) {
478         if (SPEW) Slog.d(TAG, "removeNotification key=" + key);
479         StatusBarNotification old = removeNotificationViews(key);
480 
481         if (old != null) {
482             // Cancel the ticker if it's still running
483             mTicker.removeEntry(old);
484 
485             // Recalculate the position of the sliding windows and the titles.
486             setAreThereNotifications();
487             updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
488         }
489     }
490 
chooseIconIndex(boolean isOngoing, int viewIndex)491     private int chooseIconIndex(boolean isOngoing, int viewIndex) {
492         final int latestSize = mLatest.size();
493         if (isOngoing) {
494             return latestSize + (mOngoing.size() - viewIndex);
495         } else {
496             return latestSize - viewIndex;
497         }
498     }
499 
makeNotificationView(StatusBarNotification notification, ViewGroup parent)500     View[] makeNotificationView(StatusBarNotification notification, ViewGroup parent) {
501         Notification n = notification.notification;
502         RemoteViews remoteViews = n.contentView;
503         if (remoteViews == null) {
504             return null;
505         }
506 
507         // create the row view
508         LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
509         View row = inflater.inflate(R.layout.status_bar_latest_event, parent, false);
510 
511         // bind the click event to the content area
512         ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
513         content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
514         content.setOnFocusChangeListener(mFocusChangeListener);
515         PendingIntent contentIntent = n.contentIntent;
516         if (contentIntent != null) {
517             content.setOnClickListener(new Launcher(contentIntent, notification.pkg,
518                         notification.tag, notification.id));
519         }
520 
521         View expanded = null;
522         Exception exception = null;
523         try {
524             expanded = remoteViews.apply(this, content);
525         }
526         catch (RuntimeException e) {
527             exception = e;
528         }
529         if (expanded == null) {
530             String ident = notification.pkg + "/0x" + Integer.toHexString(notification.id);
531             Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
532             return null;
533         } else {
534             content.addView(expanded);
535             row.setDrawingCacheEnabled(true);
536         }
537 
538         return new View[] { row, content, expanded };
539     }
540 
addNotificationViews(IBinder key, StatusBarNotification notification)541     StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
542         NotificationData list;
543         ViewGroup parent;
544         final boolean isOngoing = notification.isOngoing();
545         if (isOngoing) {
546             list = mOngoing;
547             parent = mOngoingItems;
548         } else {
549             list = mLatest;
550             parent = mLatestItems;
551         }
552         // Construct the expanded view.
553         final View[] views = makeNotificationView(notification, parent);
554         if (views == null) {
555             handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
556                     + notification);
557             return null;
558         }
559         final View row = views[0];
560         final View content = views[1];
561         final View expanded = views[2];
562         // Construct the icon.
563         final StatusBarIconView iconView = new StatusBarIconView(this,
564                 notification.pkg + "/0x" + Integer.toHexString(notification.id));
565         final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon,
566                     notification.notification.iconLevel, notification.notification.number);
567         if (!iconView.set(ic)) {
568             handleNotificationError(key, notification, "Coulding create icon: " + ic);
569             return null;
570         }
571         // Add the expanded view.
572         final int viewIndex = list.add(key, notification, row, content, expanded, iconView);
573         parent.addView(row, viewIndex);
574         // Add the icon.
575         final int iconIndex = chooseIconIndex(isOngoing, viewIndex);
576         mNotificationIcons.addView(iconView, iconIndex);
577         return iconView;
578     }
579 
removeNotificationViews(IBinder key)580     StatusBarNotification removeNotificationViews(IBinder key) {
581         NotificationData.Entry entry = mOngoing.remove(key);
582         if (entry == null) {
583             entry = mLatest.remove(key);
584             if (entry == null) {
585                 Slog.w(TAG, "removeNotification for unknown key: " + key);
586                 return null;
587             }
588         }
589         // Remove the expanded view.
590         ((ViewGroup)entry.row.getParent()).removeView(entry.row);
591         // Remove the icon.
592         ((ViewGroup)entry.icon.getParent()).removeView(entry.icon);
593 
594         return entry.notification;
595     }
596 
setAreThereNotifications()597     private void setAreThereNotifications() {
598         boolean ongoing = mOngoing.hasVisibleItems();
599         boolean latest = mLatest.hasVisibleItems();
600 
601         // (no ongoing notifications are clearable)
602         if (mLatest.hasClearableItems()) {
603             mClearButton.setVisibility(View.VISIBLE);
604         } else {
605             mClearButton.setVisibility(View.INVISIBLE);
606         }
607 
608         mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE);
609         mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE);
610 
611         if (ongoing || latest) {
612             mNoNotificationsTitle.setVisibility(View.GONE);
613         } else {
614             mNoNotificationsTitle.setVisibility(View.VISIBLE);
615         }
616     }
617 
618 
619     /**
620      * State is one or more of the DISABLE constants from StatusBarManager.
621      */
disable(int state)622     public void disable(int state) {
623         final int old = mDisabled;
624         final int diff = state ^ old;
625         mDisabled = state;
626 
627         if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
628             if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
629                 if (SPEW) Slog.d(TAG, "DISABLE_EXPAND: yes");
630                 animateCollapse();
631             }
632         }
633         if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
634             if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
635                 if (SPEW) Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
636                 if (mTicking) {
637                     mTicker.halt();
638                 } else {
639                     setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
640                 }
641             } else {
642                 if (SPEW) Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
643                 if (!mExpandedVisible) {
644                     setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
645                 }
646             }
647         } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
648             if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
649                 if (SPEW) Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes");
650                 mTicker.halt();
651             }
652         }
653     }
654 
655     /**
656      * All changes to the status bar and notifications funnel through here and are batched.
657      */
658     private class H extends Handler {
handleMessage(Message m)659         public void handleMessage(Message m) {
660             switch (m.what) {
661                 case MSG_ANIMATE:
662                     doAnimation();
663                     break;
664                 case MSG_ANIMATE_REVEAL:
665                     doRevealAnimation();
666                     break;
667             }
668         }
669     }
670 
671     View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
672         public void onFocusChange(View v, boolean hasFocus) {
673             // Because 'v' is a ViewGroup, all its children will be (un)selected
674             // too, which allows marqueeing to work.
675             v.setSelected(hasFocus);
676         }
677     };
678 
makeExpandedVisible()679     private void makeExpandedVisible() {
680         if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
681         if (mExpandedVisible) {
682             return;
683         }
684         mExpandedVisible = true;
685         visibilityChanged(true);
686 
687         updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
688         mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
689         mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
690         mExpandedDialog.getWindow().setAttributes(mExpandedParams);
691         mExpandedView.requestFocus(View.FOCUS_FORWARD);
692         mTrackingView.setVisibility(View.VISIBLE);
693         mExpandedView.setVisibility(View.VISIBLE);
694 
695         if (!mTicking) {
696             setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
697         }
698     }
699 
animateExpand()700     public void animateExpand() {
701         if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded);
702         if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
703             return ;
704         }
705         if (mExpanded) {
706             return;
707         }
708 
709         prepareTracking(0, true);
710         performFling(0, 2000.0f, true);
711     }
712 
animateCollapse()713     public void animateCollapse() {
714         if (SPEW) {
715             Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
716                     + " mExpandedVisible=" + mExpandedVisible
717                     + " mExpanded=" + mExpanded
718                     + " mAnimating=" + mAnimating
719                     + " mAnimY=" + mAnimY
720                     + " mAnimVel=" + mAnimVel);
721         }
722 
723         if (!mExpandedVisible) {
724             return;
725         }
726 
727         int y;
728         if (mAnimating) {
729             y = (int)mAnimY;
730         } else {
731             y = mDisplay.getHeight()-1;
732         }
733         // Let the fling think that we're open so it goes in the right direction
734         // and doesn't try to re-open the windowshade.
735         mExpanded = true;
736         prepareTracking(y, false);
737         performFling(y, -2000.0f, true);
738     }
739 
performExpand()740     void performExpand() {
741         if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded);
742         if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
743             return ;
744         }
745         if (mExpanded) {
746             return;
747         }
748 
749         mExpanded = true;
750         makeExpandedVisible();
751         updateExpandedViewPos(EXPANDED_FULL_OPEN);
752 
753         if (false) postStartTracing();
754     }
755 
performCollapse()756     void performCollapse() {
757         if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded
758                 + " mExpandedVisible=" + mExpandedVisible
759                 + " mTicking=" + mTicking);
760 
761         if (!mExpandedVisible) {
762             return;
763         }
764         mExpandedVisible = false;
765         visibilityChanged(false);
766         mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
767         mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
768         mExpandedDialog.getWindow().setAttributes(mExpandedParams);
769         mTrackingView.setVisibility(View.GONE);
770         mExpandedView.setVisibility(View.GONE);
771 
772         if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
773             setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
774         }
775         if (mDateView.getVisibility() == View.VISIBLE) {
776             setDateViewVisibility(false, com.android.internal.R.anim.fade_out);
777         }
778 
779         if (!mExpanded) {
780             return;
781         }
782         mExpanded = false;
783     }
784 
doAnimation()785     void doAnimation() {
786         if (mAnimating) {
787             if (SPEW) Slog.d(TAG, "doAnimation");
788             if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
789             incrementAnim();
790             if (SPEW) Slog.d(TAG, "doAnimation after  mAnimY=" + mAnimY);
791             if (mAnimY >= mDisplay.getHeight()-1) {
792                 if (SPEW) Slog.d(TAG, "Animation completed to expanded state.");
793                 mAnimating = false;
794                 updateExpandedViewPos(EXPANDED_FULL_OPEN);
795                 performExpand();
796             }
797             else if (mAnimY < mStatusBarView.getHeight()) {
798                 if (SPEW) Slog.d(TAG, "Animation completed to collapsed state.");
799                 mAnimating = false;
800                 updateExpandedViewPos(0);
801                 performCollapse();
802             }
803             else {
804                 updateExpandedViewPos((int)mAnimY);
805                 mCurAnimationTime += ANIM_FRAME_DURATION;
806                 mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
807             }
808         }
809     }
810 
stopTracking()811     void stopTracking() {
812         mTracking = false;
813         mVelocityTracker.recycle();
814         mVelocityTracker = null;
815     }
816 
incrementAnim()817     void incrementAnim() {
818         long now = SystemClock.uptimeMillis();
819         float t = ((float)(now - mAnimLastTime)) / 1000;            // ms -> s
820         final float y = mAnimY;
821         final float v = mAnimVel;                                   // px/s
822         final float a = mAnimAccel;                                 // px/s/s
823         mAnimY = y + (v*t) + (0.5f*a*t*t);                          // px
824         mAnimVel = v + (a*t);                                       // px/s
825         mAnimLastTime = now;                                        // ms
826         //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
827         //        + " mAnimAccel=" + mAnimAccel);
828     }
829 
doRevealAnimation()830     void doRevealAnimation() {
831         final int h = mCloseView.getHeight() + mStatusBarView.getHeight();
832         if (mAnimatingReveal && mAnimating && mAnimY < h) {
833             incrementAnim();
834             if (mAnimY >= h) {
835                 mAnimY = h;
836                 updateExpandedViewPos((int)mAnimY);
837             } else {
838                 updateExpandedViewPos((int)mAnimY);
839                 mCurAnimationTime += ANIM_FRAME_DURATION;
840                 mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
841                         mCurAnimationTime);
842             }
843         }
844     }
845 
prepareTracking(int y, boolean opening)846     void prepareTracking(int y, boolean opening) {
847         mTracking = true;
848         mVelocityTracker = VelocityTracker.obtain();
849         if (opening) {
850             mAnimAccel = 2000.0f;
851             mAnimVel = 200;
852             mAnimY = mStatusBarView.getHeight();
853             updateExpandedViewPos((int)mAnimY);
854             mAnimating = true;
855             mAnimatingReveal = true;
856             mHandler.removeMessages(MSG_ANIMATE);
857             mHandler.removeMessages(MSG_ANIMATE_REVEAL);
858             long now = SystemClock.uptimeMillis();
859             mAnimLastTime = now;
860             mCurAnimationTime = now + ANIM_FRAME_DURATION;
861             mAnimating = true;
862             mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
863                     mCurAnimationTime);
864             makeExpandedVisible();
865         } else {
866             // it's open, close it?
867             if (mAnimating) {
868                 mAnimating = false;
869                 mHandler.removeMessages(MSG_ANIMATE);
870             }
871             updateExpandedViewPos(y + mViewDelta);
872         }
873     }
874 
performFling(int y, float vel, boolean always)875     void performFling(int y, float vel, boolean always) {
876         mAnimatingReveal = false;
877         mDisplayHeight = mDisplay.getHeight();
878 
879         mAnimY = y;
880         mAnimVel = vel;
881 
882         //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
883 
884         if (mExpanded) {
885             if (!always && (
886                     vel > 200.0f
887                     || (y > (mDisplayHeight-25) && vel > -200.0f))) {
888                 // We are expanded, but they didn't move sufficiently to cause
889                 // us to retract.  Animate back to the expanded position.
890                 mAnimAccel = 2000.0f;
891                 if (vel < 0) {
892                     mAnimVel = 0;
893                 }
894             }
895             else {
896                 // We are expanded and are now going to animate away.
897                 mAnimAccel = -2000.0f;
898                 if (vel > 0) {
899                     mAnimVel = 0;
900                 }
901             }
902         } else {
903             if (always || (
904                     vel > 200.0f
905                     || (y > (mDisplayHeight/2) && vel > -200.0f))) {
906                 // We are collapsed, and they moved enough to allow us to
907                 // expand.  Animate in the notifications.
908                 mAnimAccel = 2000.0f;
909                 if (vel < 0) {
910                     mAnimVel = 0;
911                 }
912             }
913             else {
914                 // We are collapsed, but they didn't move sufficiently to cause
915                 // us to retract.  Animate back to the collapsed position.
916                 mAnimAccel = -2000.0f;
917                 if (vel > 0) {
918                     mAnimVel = 0;
919                 }
920             }
921         }
922         //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
923         //        + " mAnimAccel=" + mAnimAccel);
924 
925         long now = SystemClock.uptimeMillis();
926         mAnimLastTime = now;
927         mCurAnimationTime = now + ANIM_FRAME_DURATION;
928         mAnimating = true;
929         mHandler.removeMessages(MSG_ANIMATE);
930         mHandler.removeMessages(MSG_ANIMATE_REVEAL);
931         mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
932         stopTracking();
933     }
934 
interceptTouchEvent(MotionEvent event)935     boolean interceptTouchEvent(MotionEvent event) {
936         if (SPEW) {
937             Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
938                 + mDisabled);
939         }
940 
941         if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
942             return false;
943         }
944 
945         final int statusBarSize = mStatusBarView.getHeight();
946         final int hitSize = statusBarSize*2;
947         if (event.getAction() == MotionEvent.ACTION_DOWN) {
948             final int y = (int)event.getRawY();
949 
950             if (!mExpanded) {
951                 mViewDelta = statusBarSize - y;
952             } else {
953                 mTrackingView.getLocationOnScreen(mAbsPos);
954                 mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y;
955             }
956             if ((!mExpanded && y < hitSize) ||
957                     (mExpanded && y > (mDisplay.getHeight()-hitSize))) {
958 
959                 // We drop events at the edge of the screen to make the windowshade come
960                 // down by accident less, especially when pushing open a device with a keyboard
961                 // that rotates (like g1 and droid)
962                 int x = (int)event.getRawX();
963                 final int edgeBorder = mEdgeBorder;
964                 if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) {
965                     prepareTracking(y, !mExpanded);// opening if we're not already fully visible
966                     mVelocityTracker.addMovement(event);
967                 }
968             }
969         } else if (mTracking) {
970             mVelocityTracker.addMovement(event);
971             final int minY = statusBarSize + mCloseView.getHeight();
972             if (event.getAction() == MotionEvent.ACTION_MOVE) {
973                 int y = (int)event.getRawY();
974                 if (mAnimatingReveal && y < minY) {
975                     // nothing
976                 } else  {
977                     mAnimatingReveal = false;
978                     updateExpandedViewPos(y + mViewDelta);
979                 }
980             } else if (event.getAction() == MotionEvent.ACTION_UP) {
981                 mVelocityTracker.computeCurrentVelocity(1000);
982 
983                 float yVel = mVelocityTracker.getYVelocity();
984                 boolean negative = yVel < 0;
985 
986                 float xVel = mVelocityTracker.getXVelocity();
987                 if (xVel < 0) {
988                     xVel = -xVel;
989                 }
990                 if (xVel > 150.0f) {
991                     xVel = 150.0f; // limit how much we care about the x axis
992                 }
993 
994                 float vel = (float)Math.hypot(yVel, xVel);
995                 if (negative) {
996                     vel = -vel;
997                 }
998 
999                 performFling((int)event.getRawY(), vel, false);
1000             }
1001 
1002         }
1003         return false;
1004     }
1005 
1006     private class Launcher implements View.OnClickListener {
1007         private PendingIntent mIntent;
1008         private String mPkg;
1009         private String mTag;
1010         private int mId;
1011 
1012         Launcher(PendingIntent intent, String pkg, String tag, int id) {
1013             mIntent = intent;
1014             mPkg = pkg;
1015             mTag = tag;
1016             mId = id;
1017         }
1018 
1019         public void onClick(View v) {
1020             try {
1021                 // The intent we are sending is for the application, which
1022                 // won't have permission to immediately start an activity after
1023                 // the user switches to home.  We know it is safe to do at this
1024                 // point, so make sure new activity switches are now allowed.
1025                 ActivityManagerNative.getDefault().resumeAppSwitches();
1026             } catch (RemoteException e) {
1027             }
1028 
1029             if (mIntent != null) {
1030                 int[] pos = new int[2];
1031                 v.getLocationOnScreen(pos);
1032                 Intent overlay = new Intent();
1033                 overlay.setSourceBounds(
1034                         new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
1035                 try {
1036                     mIntent.send(StatusBarService.this, 0, overlay);
1037                 } catch (PendingIntent.CanceledException e) {
1038                     // the stack trace isn't very helpful here.  Just log the exception message.
1039                     Slog.w(TAG, "Sending contentIntent failed: " + e);
1040                 }
1041             }
1042 
1043             try {
1044                 mBarService.onNotificationClick(mPkg, mTag, mId);
1045             } catch (RemoteException ex) {
1046                 // system process is dead if we're here.
1047             }
1048 
1049             // close the shade if it was open
1050             animateCollapse();
1051         }
1052     }
1053 
1054     private void tick(StatusBarNotification n) {
1055         // Show the ticker if one is requested. Also don't do this
1056         // until status bar window is attached to the window manager,
1057         // because...  well, what's the point otherwise?  And trying to
1058         // run a ticker without being attached will crash!
1059         if (n.notification.tickerText != null && mStatusBarView.getWindowToken() != null) {
1060             if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
1061                             | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
1062                 mTicker.addEntry(n);
1063             }
1064         }
1065     }
1066 
1067     /**
1068      * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
1069      * about the failure.
1070      *
1071      * WARNING: this will call back into us.  Don't hold any locks.
1072      */
1073     void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
1074         removeNotification(key);
1075         try {
1076             mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message);
1077         } catch (RemoteException ex) {
1078             // The end is nigh.
1079         }
1080     }
1081 
1082     private class MyTicker extends Ticker {
1083         MyTicker(Context context, StatusBarView sb) {
1084             super(context, sb);
1085         }
1086 
1087         @Override
1088         void tickerStarting() {
1089             if (SPEW) Slog.d(TAG, "tickerStarting");
1090             mTicking = true;
1091             mIcons.setVisibility(View.GONE);
1092             mTickerView.setVisibility(View.VISIBLE);
1093             mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
1094             mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
1095             if (mExpandedVisible) {
1096                 setDateViewVisibility(false, com.android.internal.R.anim.push_up_out);
1097             }
1098         }
1099 
1100         @Override
1101         void tickerDone() {
1102             if (SPEW) Slog.d(TAG, "tickerDone");
1103             mTicking = false;
1104             mIcons.setVisibility(View.VISIBLE);
1105             mTickerView.setVisibility(View.GONE);
1106             mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
1107             mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out, null));
1108             if (mExpandedVisible) {
1109                 setDateViewVisibility(true, com.android.internal.R.anim.push_down_in);
1110             }
1111         }
1112 
1113         void tickerHalting() {
1114             if (SPEW) Slog.d(TAG, "tickerHalting");
1115             mTicking = false;
1116             mIcons.setVisibility(View.VISIBLE);
1117             mTickerView.setVisibility(View.GONE);
1118             mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
1119             mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out, null));
1120             if (mExpandedVisible) {
1121                 setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
1122             }
1123         }
1124     }
1125 
1126     private Animation loadAnim(int id, Animation.AnimationListener listener) {
1127         Animation anim = AnimationUtils.loadAnimation(StatusBarService.this, id);
1128         if (listener != null) {
1129             anim.setAnimationListener(listener);
1130         }
1131         return anim;
1132     }
1133 
1134     public String viewInfo(View v) {
1135         return "(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
1136                 + " " + v.getWidth() + "x" + v.getHeight() + ")";
1137     }
1138 
1139     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1140         if (checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1141                 != PackageManager.PERMISSION_GRANTED) {
1142             pw.println("Permission Denial: can't dump StatusBar from from pid="
1143                     + Binder.getCallingPid()
1144                     + ", uid=" + Binder.getCallingUid());
1145             return;
1146         }
1147 
1148         synchronized (mQueueLock) {
1149             pw.println("Current Status Bar state:");
1150             pw.println("  mExpanded=" + mExpanded
1151                     + ", mExpandedVisible=" + mExpandedVisible);
1152             pw.println("  mTicking=" + mTicking);
1153             pw.println("  mTracking=" + mTracking);
1154             pw.println("  mAnimating=" + mAnimating
1155                     + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
1156                     + ", mAnimAccel=" + mAnimAccel);
1157             pw.println("  mCurAnimationTime=" + mCurAnimationTime
1158                     + " mAnimLastTime=" + mAnimLastTime);
1159             pw.println("  mDisplayHeight=" + mDisplayHeight
1160                     + " mAnimatingReveal=" + mAnimatingReveal
1161                     + " mViewDelta=" + mViewDelta);
1162             pw.println("  mDisplayHeight=" + mDisplayHeight);
1163             pw.println("  mExpandedParams: " + mExpandedParams);
1164             pw.println("  mExpandedView: " + viewInfo(mExpandedView));
1165             pw.println("  mExpandedDialog: " + mExpandedDialog);
1166             pw.println("  mTrackingParams: " + mTrackingParams);
1167             pw.println("  mTrackingView: " + viewInfo(mTrackingView));
1168             pw.println("  mOngoingTitle: " + viewInfo(mOngoingTitle));
1169             pw.println("  mOngoingItems: " + viewInfo(mOngoingItems));
1170             pw.println("  mLatestTitle: " + viewInfo(mLatestTitle));
1171             pw.println("  mLatestItems: " + viewInfo(mLatestItems));
1172             pw.println("  mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle));
1173             pw.println("  mCloseView: " + viewInfo(mCloseView));
1174             pw.println("  mTickerView: " + viewInfo(mTickerView));
1175             pw.println("  mScrollView: " + viewInfo(mScrollView)
1176                     + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
1177             pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout));
1178         }
1179 
1180         if (true) {
1181             // must happen on ui thread
1182             mHandler.post(new Runnable() {
1183                     public void run() {
1184                         Slog.d(TAG, "mStatusIcons:");
1185                         mStatusIcons.debug();
1186                     }
1187                 });
1188         }
1189 
1190     }
1191 
1192     void onBarViewAttached() {
1193         WindowManager.LayoutParams lp;
1194         int pixelFormat;
1195         Drawable bg;
1196 
1197         /// ---------- Tracking View --------------
1198         pixelFormat = PixelFormat.RGBX_8888;
1199         bg = mTrackingView.getBackground();
1200         if (bg != null) {
1201             pixelFormat = bg.getOpacity();
1202         }
1203 
1204         lp = new WindowManager.LayoutParams(
1205                 ViewGroup.LayoutParams.MATCH_PARENT,
1206                 ViewGroup.LayoutParams.MATCH_PARENT,
1207                 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
1208                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
1209                 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
1210                 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
1211                 pixelFormat);
1212 //        lp.token = mStatusBarView.getWindowToken();
1213         lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
1214         lp.setTitle("TrackingView");
1215         lp.y = mTrackingPosition;
1216         mTrackingParams = lp;
1217 
1218         WindowManagerImpl.getDefault().addView(mTrackingView, lp);
1219     }
1220 
1221     void onTrackingViewAttached() {
1222         WindowManager.LayoutParams lp;
1223         int pixelFormat;
1224         Drawable bg;
1225 
1226         /// ---------- Expanded View --------------
1227         pixelFormat = PixelFormat.TRANSLUCENT;
1228 
1229         final int disph = mDisplay.getHeight();
1230         lp = mExpandedDialog.getWindow().getAttributes();
1231         lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
1232         lp.height = getExpandedHeight();
1233         lp.x = 0;
1234         mTrackingPosition = lp.y = -disph; // sufficiently large negative
1235         lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
1236         lp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
1237                 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
1238                 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
1239                 | WindowManager.LayoutParams.FLAG_DITHER
1240                 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1241         lp.format = pixelFormat;
1242         lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
1243         lp.setTitle("StatusBarExpanded");
1244         mExpandedDialog.getWindow().setAttributes(lp);
1245         mExpandedDialog.getWindow().setFormat(pixelFormat);
1246         mExpandedParams = lp;
1247 
1248         mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
1249         mExpandedDialog.setContentView(mExpandedView,
1250                 new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
1251                                            ViewGroup.LayoutParams.MATCH_PARENT));
1252         mExpandedDialog.getWindow().setBackgroundDrawable(null);
1253         mExpandedDialog.show();
1254         FrameLayout hack = (FrameLayout)mExpandedView.getParent();
1255     }
1256 
1257     void setDateViewVisibility(boolean visible, int anim) {
1258         mDateView.setUpdates(visible);
1259         mDateView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1260         mDateView.startAnimation(loadAnim(anim, null));
1261     }
1262 
1263     void setNotificationIconVisibility(boolean visible, int anim) {
1264         int old = mNotificationIcons.getVisibility();
1265         int v = visible ? View.VISIBLE : View.INVISIBLE;
1266         if (old != v) {
1267             mNotificationIcons.setVisibility(v);
1268             mNotificationIcons.startAnimation(loadAnim(anim, null));
1269         }
1270     }
1271 
1272     void updateExpandedViewPos(int expandedPosition) {
1273         if (SPEW) {
1274             Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition
1275                     + " mTrackingParams.y="
1276                     + ((mTrackingParams == null) ? "???" : mTrackingParams.y)
1277                     + " mTrackingPosition=" + mTrackingPosition);
1278         }
1279 
1280         int h = mStatusBarView.getHeight();
1281         int disph = mDisplay.getHeight();
1282 
1283         // If the expanded view is not visible, make sure they're still off screen.
1284         // Maybe the view was resized.
1285         if (!mExpandedVisible) {
1286             if (mTrackingView != null) {
1287                 mTrackingPosition = -disph;
1288                 if (mTrackingParams != null) {
1289                     mTrackingParams.y = mTrackingPosition;
1290                     WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
1291                 }
1292             }
1293             if (mExpandedParams != null) {
1294                 mExpandedParams.y = -disph;
1295                 mExpandedDialog.getWindow().setAttributes(mExpandedParams);
1296             }
1297             return;
1298         }
1299 
1300         // tracking view...
1301         int pos;
1302         if (expandedPosition == EXPANDED_FULL_OPEN) {
1303             pos = h;
1304         }
1305         else if (expandedPosition == EXPANDED_LEAVE_ALONE) {
1306             pos = mTrackingPosition;
1307         }
1308         else {
1309             if (expandedPosition <= disph) {
1310                 pos = expandedPosition;
1311             } else {
1312                 pos = disph;
1313             }
1314             pos -= disph-h;
1315         }
1316         mTrackingPosition = mTrackingParams.y = pos;
1317         mTrackingParams.height = disph-h;
1318         WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
1319 
1320         if (mExpandedParams != null) {
1321             mCloseView.getLocationInWindow(mPositionTmp);
1322             final int closePos = mPositionTmp[1];
1323 
1324             mExpandedContents.getLocationInWindow(mPositionTmp);
1325             final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight();
1326 
1327             if (expandedPosition != EXPANDED_LEAVE_ALONE) {
1328                 mExpandedParams.y = pos + mTrackingView.getHeight()
1329                         - (mTrackingParams.height-closePos) - contentsBottom;
1330                 int max = h;
1331                 if (mExpandedParams.y > max) {
1332                     mExpandedParams.y = max;
1333                 }
1334                 int min = mTrackingPosition;
1335                 if (mExpandedParams.y < min) {
1336                     mExpandedParams.y = min;
1337                 }
1338 
1339                 boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h;
1340                 if (!visible) {
1341                     // if the contents aren't visible, move the expanded view way off screen
1342                     // because the window itself extends below the content view.
1343                     mExpandedParams.y = -disph;
1344                 }
1345                 mExpandedDialog.getWindow().setAttributes(mExpandedParams);
1346 
1347                 if (SPEW) Slog.d(TAG, "updateExpandedViewPos visibilityChanged(" + visible + ")");
1348                 visibilityChanged(visible);
1349             }
1350         }
1351 
1352         if (SPEW) {
1353             Slog.d(TAG, "updateExpandedViewPos after  expandedPosition=" + expandedPosition
1354                     + " mTrackingParams.y=" + mTrackingParams.y
1355                     + " mTrackingPosition=" + mTrackingPosition
1356                     + " mExpandedParams.y=" + mExpandedParams.y
1357                     + " mExpandedParams.height=" + mExpandedParams.height);
1358         }
1359     }
1360 
1361     int getExpandedHeight() {
1362         return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight();
1363     }
1364 
1365     void updateExpandedHeight() {
1366         if (mExpandedView != null) {
1367             mExpandedParams.height = getExpandedHeight();
1368             mExpandedDialog.getWindow().setAttributes(mExpandedParams);
1369         }
1370     }
1371 
1372     /**
1373      * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
1374      * This was added last-minute and is inconsistent with the way the rest of the notifications
1375      * are handled, because the notification isn't really cancelled.  The lights are just
1376      * turned off.  If any other notifications happen, the lights will turn back on.  Steve says
1377      * this is what he wants. (see bug 1131461)
1378      */
1379     void visibilityChanged(boolean visible) {
1380         if (mPanelSlightlyVisible != visible) {
1381             mPanelSlightlyVisible = visible;
1382             try {
1383                 mBarService.onPanelRevealed();
1384             } catch (RemoteException ex) {
1385                 // Won't fail unless the world has ended.
1386             }
1387         }
1388     }
1389 
1390     void performDisableActions(int net) {
1391         int old = mDisabled;
1392         int diff = net ^ old;
1393         mDisabled = net;
1394 
1395         // act accordingly
1396         if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
1397             if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
1398                 Slog.d(TAG, "DISABLE_EXPAND: yes");
1399                 animateCollapse();
1400             }
1401         }
1402         if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1403             if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1404                 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
1405                 if (mTicking) {
1406                     mNotificationIcons.setVisibility(View.INVISIBLE);
1407                     mTicker.halt();
1408                 } else {
1409                     setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
1410                 }
1411             } else {
1412                 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
1413                 if (!mExpandedVisible) {
1414                     setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1415                 }
1416             }
1417         } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1418             Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: "
1419                 + (((net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0)
1420                     ? "yes" : "no"));
1421             if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1422                 mTicker.halt();
1423             }
1424         }
1425     }
1426 
1427     private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
1428         public void onClick(View v) {
1429             try {
1430                 mBarService.onClearAllNotifications();
1431             } catch (RemoteException ex) {
1432                 // system process is dead if we're here.
1433             }
1434             animateCollapse();
1435         }
1436     };
1437 
1438     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1439         public void onReceive(Context context, Intent intent) {
1440             String action = intent.getAction();
1441             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
1442                     || Intent.ACTION_SCREEN_OFF.equals(action)) {
1443                 animateCollapse();
1444             }
1445             else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1446                 updateResources();
1447             }
1448         }
1449     };
1450 
1451     /**
1452      * Reload some of our resources when the configuration changes.
1453      *
1454      * We don't reload everything when the configuration changes -- we probably
1455      * should, but getting that smooth is tough.  Someday we'll fix that.  In the
1456      * meantime, just update the things that we know change.
1457      */
1458     void updateResources() {
1459         Resources res = getResources();
1460 
1461         mClearButton.setText(getText(R.string.status_bar_clear_all_button));
1462         mOngoingTitle.setText(getText(R.string.status_bar_ongoing_events_title));
1463         mLatestTitle.setText(getText(R.string.status_bar_latest_events_title));
1464         mNoNotificationsTitle.setText(getText(R.string.status_bar_no_notifications_title));
1465 
1466         mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
1467 
1468         if (false) Slog.v(TAG, "updateResources");
1469     }
1470 
1471     //
1472     // tracing
1473     //
1474 
1475     void postStartTracing() {
1476         mHandler.postDelayed(mStartTracing, 3000);
1477     }
1478 
1479     void vibrate() {
1480         android.os.Vibrator vib = (android.os.Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
1481         vib.vibrate(250);
1482     }
1483 
1484     Runnable mStartTracing = new Runnable() {
1485         public void run() {
1486             vibrate();
1487             SystemClock.sleep(250);
1488             Slog.d(TAG, "startTracing");
1489             android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
1490             mHandler.postDelayed(mStopTracing, 10000);
1491         }
1492     };
1493 
1494     Runnable mStopTracing = new Runnable() {
1495         public void run() {
1496             android.os.Debug.stopMethodTracing();
1497             Slog.d(TAG, "stopTracing");
1498             vibrate();
1499         }
1500     };
1501 }
1502