2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.server.status;
19 import com.android.internal.R;
20 import com.android.internal.util.CharSequences;
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;
64 import java.io.FileDescriptor;
65 import java.io.PrintWriter;
66 import java.util.ArrayList;
67 import java.util.HashMap;
70 import android.provider.Settings;
71 import java.lang.reflect.Field;
75 * The public (ok, semi-public) service for the status bar.
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.
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.
89 public class StatusBarService extends IStatusBar.Stub
91 static final String TAG = "StatusBar";
92 static final boolean SPEW = false;
94 private boolean mShowPlmnSb;
95 private boolean mShowSpnSb;
97 static final int EXPANDED_LEAVE_ALONE = -10000;
98 static final int EXPANDED_FULL_OPEN = -10001;
100 private static final int MSG_ANIMATE = 1000;
101 private static final int MSG_ANIMATE_REVEAL = 1001;
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 {
114 NotificationData notificationData;
119 private class DisableRecord implements IBinder.DeathRecipient {
124 public void binderDied() {
125 Slog.i(TAG, "binder died for pkg=" + pkg);
126 disable(0, token, pkg);
127 token.unlinkToDeath(this, 0);
131 public interface NotificationCallbacks {
132 void onSetDisabled(int status);
134 void onNotificationClick(String pkg, String tag, int id);
135 void onPanelRevealed();
138 private class ExpandedDialog extends Dialog {
139 ExpandedDialog(Context context) {
140 super(context, com.android.internal.R.style.Theme_Light_NoTitleBar);
144 public boolean dispatchKeyEvent(KeyEvent event) {
145 boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
146 switch (event.getKeyCode()) {
147 case KeyEvent.KEYCODE_BACK:
149 StatusBarService.this.deactivate();
153 return super.dispatchKeyEvent(event);
157 final Context mContext;
158 final Display mDisplay;
159 StatusBarView mStatusBarView;
161 H mHandler = new H();
162 Object mQueueLock = new Object();
163 ArrayList<PendingOp> mQueue = new ArrayList<PendingOp>();
164 NotificationCallbacks mNotificationCallbacks;
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.
172 HashMap<IBinder,StatusBarIcon> mIconMap = new HashMap<IBinder,StatusBarIcon>();
173 ArrayList<StatusBarIcon> mIconList = new ArrayList<StatusBarIcon>();
174 String[] mRightIconSlots;
175 StatusBarIcon[] mRightIcons;
177 IconMerger mNotificationIcons;
178 LinearLayout mStatusIcons;
179 StatusBarIcon mMoreIcon;
180 private UninstallReceiver mUninstallReceiver;
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;
196 TextView mClearButton;
197 View mExpandedContents;
198 CloseDragHandle mCloseView;
199 int[] mPositionTmp = new int[2];
201 boolean mExpandedVisible;
207 TrackingView mTrackingView;
208 WindowManager.LayoutParams mTrackingParams;
209 int mTrackingPosition; // the position of the top of the tracking view.
212 private Ticker mTicker;
213 private View mTickerView;
214 private boolean mTicking;
215 private TickerView tickerView;
217 // Tracking finger for opening/closing.
218 int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
220 VelocityTracker mVelocityTracker;
222 static final int ANIM_FRAME_DURATION = (1000/60);
225 long mCurAnimationTime;
226 float mDisplayHeight;
231 boolean mAnimatingReveal = false;
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;
240 // for disabling the status bar
241 ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
245 * Construct the service, add the status bar view to the window manager
247 public StatusBarService(Context 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);
256 mUninstallReceiver = new UninstallReceiver();
259 public void setNotificationCallbacks(NotificationCallbacks listener) {
260 mNotificationCallbacks = listener;
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];
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);
278 // figure out which pixel-format to use for the status bar.
279 mPixelFormat = PixelFormat.TRANSLUCENT;
280 Drawable bg = sb.getBackground();
282 mPixelFormat = bg.getOpacity();
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);
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);
308 mOngoingTitle.setVisibility(View.GONE);
309 mLatestTitle.setVisibility(View.GONE);
311 mTicker = new MyTicker(context, sb);
313 tickerView = (TickerView)sb.findViewById(R.id.tickerText);
314 tickerView.mTicker = mTicker;
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;
322 mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
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);
332 // set the inital view visibility
333 setAreThereNotifications();
334 mDateView.setVisibility(View.INVISIBLE);
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);
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);
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,
361 lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
362 lp.setTitle("StatusBar");
363 lp.windowAnimations = R.style.Animation_StatusBar;
365 WindowManagerImpl.getDefault().addView(view, lp);
368 // ================================================================================
370 // ================================================================================
371 public void activate() {
372 enforceExpandStatusBar();
373 addPendingOp(OP_EXPAND, null, true);
376 public void deactivate() {
377 enforceExpandStatusBar();
378 addPendingOp(OP_EXPAND, null, false);
381 public void toggle() {
382 enforceExpandStatusBar();
383 addPendingOp(OP_TOGGLE, null, false);
386 public void disable(int what, IBinder token, String pkg) {
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.
394 synchronized (mDisableRecords) {
395 manageDisableListLocked(what, token, pkg);
396 net = gatherDisableActionsLocked();
397 mNotificationCallbacks.onSetDisabled(net);
399 addPendingOp(OP_DISABLE, net);
403 public IBinder addIcon(String slot, String iconPackage, int iconId, int iconLevel) {
405 return addIcon(IconData.makeIcon(slot, iconPackage, iconId, iconLevel, 0), null);
408 public void updateIcon(IBinder key,
409 String slot, String iconPackage, int iconId, int iconLevel) {
411 updateIcon(key, IconData.makeIcon(slot, iconPackage, iconId, iconLevel, 0), null);
414 public void removeIcon(IBinder key) {
416 addPendingOp(OP_REMOVE_ICON, key, null, null, -1);
419 private void enforceStatusBar() {
420 mContext.enforceCallingOrSelfPermission(
421 android.Manifest.permission.STATUS_BAR,
425 private void enforceExpandStatusBar() {
426 mContext.enforceCallingOrSelfPermission(
427 android.Manifest.permission.EXPAND_STATUS_BAR,
431 // ================================================================================
432 // Can be called from any thread
433 // ================================================================================
434 public IBinder addIcon(IconData data, NotificationData n) {
436 // assert early-on if they using a slot that doesn't exist.
437 if (data != null && n == null) {
438 slot = getRightIconIndex(data.slot);
440 throw new SecurityException("invalid status bar icon slot: "
441 + (data.slot != null ? "'" + data.slot + "'" : "null"));
446 IBinder key = new Binder();
447 addPendingOp(OP_ADD_ICON, key, data, n, -1);
451 public void updateIcon(IBinder key, IconData data, NotificationData n) {
452 addPendingOp(OP_UPDATE_ICON, key, data, n, -1);
455 public void setIconVisibility(IBinder key, boolean visible) {
456 addPendingOp(OP_SET_VISIBLE, key, visible);
459 private void addPendingOp(int code, IBinder key, IconData data, NotificationData n, int i) {
460 synchronized (mQueueLock) {
461 PendingOp op = new PendingOp();
464 op.iconData = data == null ? null : data.clone();
465 op.notificationData = n;
468 if (mQueue.size() == 1) {
469 mHandler.sendEmptyMessage(2);
474 private void addPendingOp(int code, IBinder key, boolean visible) {
475 synchronized (mQueueLock) {
476 PendingOp op = new PendingOp();
479 op.visible = visible;
481 if (mQueue.size() == 1) {
482 mHandler.sendEmptyMessage(1);
487 private void addPendingOp(int code, int integer) {
488 synchronized (mQueueLock) {
489 PendingOp op = new PendingOp();
491 op.integer = integer;
493 if (mQueue.size() == 1) {
494 mHandler.sendEmptyMessage(1);
499 // lock on mDisableRecords
500 void manageDisableListLocked(int what, IBinder token, String pkg) {
502 Slog.d(TAG, "manageDisableList what=0x" + Integer.toHexString(what)
506 synchronized (mDisableRecords) {
507 final int N = mDisableRecords.size();
508 DisableRecord tok = null;
510 for (i=0; i<N; i++) {
511 DisableRecord t = mDisableRecords.get(i);
512 if (t.token == token) {
517 if (what == 0 || !token.isBinderAlive()) {
519 mDisableRecords.remove(i);
520 tok.token.unlinkToDeath(tok, 0);
524 tok = new DisableRecord();
526 token.linkToDeath(tok, 0);
528 catch (RemoteException ex) {
531 mDisableRecords.add(tok);
540 // lock on mDisableRecords
541 int gatherDisableActionsLocked() {
542 final int N = mDisableRecords.size();
543 // gather the new net flags
545 for (int i=0; i<N; i++) {
546 net |= mDisableRecords.get(i).what;
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)) {
561 // ================================================================================
562 // Always called from UI thread
563 // ================================================================================
565 * All changes to the status bar and notifications funnel through here and are batched.
567 private class H extends Handler {
568 public void handleMessage(Message m) {
569 if (m.what == MSG_ANIMATE) {
573 if (m.what == MSG_ANIMATE_REVEAL) {
578 ArrayList<PendingOp> queue;
579 synchronized (mQueueLock) {
581 mQueue = new ArrayList<PendingOp>();
584 boolean wasExpanded = mExpanded;
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;
592 int N = queue.size();
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) {
600 visible = op.visible;
602 else if (op.code == OP_EXPAND) {
606 else if (op.code == OP_TOGGLE) {
614 if (alwaysHandle(op.code)) {
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) {
623 else if (o.code == OP_EXPAND) {
629 op.iconData = o.iconData;
630 op.notificationData = o.notificationData;
646 performAddUpdateIcon(op.key, op.iconData, op.notificationData);
649 performRemoveIcon(op.key);
653 disableWhat = op.integer;
657 if (doVisibility && op.code != OP_REMOVE_ICON) {
658 performSetIconVisibility(op.key, visible);
662 if (queue.size() != 0) {
663 throw new RuntimeException("Assertion failed: queue.size=" + queue.size());
666 // this is last so that we capture all of the pending changes before doing it
674 performDisableActions(disableWhat);
679 private boolean alwaysHandle(int code) {
680 return code == OP_DISABLE;
683 /* private */ void performAddUpdateIcon(IBinder key, IconData data, NotificationData n)
684 throws StatusBarException {
686 Slog.d(TAG, "performAddUpdateIcon icon=" + data + " notification=" + n + " key=" + key);
690 StatusBarNotification notification = getNotification(key);
691 NotificationData oldData = null;
692 if (notification == null) {
694 notification = new StatusBarNotification();
695 notification.key = key;
696 notification.data = n;
697 synchronized (mNotificationData) {
698 mNotificationData.add(notification);
700 addNotificationView(notification);
701 setAreThereNotifications();
704 oldData = notification.data;
705 notification.data = n;
706 updateNotificationView(notification, oldData);
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
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);
722 updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
726 synchronized (mIconMap) {
727 StatusBarIcon icon = mIconMap.get(key);
730 LinearLayout v = n == null ? mStatusIcons : mNotificationIcons;
732 icon = new StatusBarIcon(mContext, data, v);
733 mIconMap.put(key, icon);
737 int slotIndex = getRightIconIndex(data.slot);
738 StatusBarIcon[] rightIcons = mRightIcons;
739 if (rightIcons[slotIndex] == null) {
741 for (int i=mRightIcons.length-1; i>slotIndex; i--) {
742 StatusBarIcon ic = rightIcons[i];
747 rightIcons[slotIndex] = icon;
748 mStatusIcons.addView(icon.view, pos);
750 Slog.e(TAG, "duplicate icon in slot " + slotIndex + "/" + data.slot);
751 mIconMap.remove(key);
752 mIconList.remove(icon);
756 int iconIndex = mNotificationData.getIconIndex(n);
757 mNotificationIcons.addView(icon.view, iconIndex);
761 // right hand side icons -- these don't reorder
762 icon.update(mContext, data);
765 ViewGroup parent = (ViewGroup)icon.view.getParent();
766 parent.removeView(icon.view);
768 icon.update(mContext, data);
769 int iconIndex = mNotificationData.getIconIndex(n);
770 mNotificationIcons.addView(icon.view, iconIndex);
776 /* private */ void performSetIconVisibility(IBinder key, boolean visible) {
777 synchronized (mIconMap) {
779 Slog.d(TAG, "performSetIconVisibility key=" + key + " visible=" + visible);
781 StatusBarIcon icon = mIconMap.get(key);
782 icon.view.setVisibility(visible ? View.VISIBLE : View.GONE);
786 /* private */ void performRemoveIcon(IBinder key) {
787 synchronized (this) {
789 Slog.d(TAG, "performRemoveIcon key=" + key);
791 StatusBarIcon icon = mIconMap.remove(key);
792 mIconList.remove(icon);
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;
801 StatusBarNotification notification = getNotification(key);
802 if (notification != null) {
803 removeNotificationView(notification);
804 synchronized (mNotificationData) {
805 mNotificationData.remove(notification);
807 setAreThereNotifications();
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);
824 return icon.getNumber();
832 StatusBarNotification getNotification(IBinder key) {
833 synchronized (mNotificationData) {
834 return mNotificationData.get(key);
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);
846 View makeNotificationView(StatusBarNotification notification, ViewGroup parent) {
847 NotificationData n = notification.data;
848 RemoteViews remoteViews = n.contentView;
849 if (remoteViews == null) {
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);
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));
868 Exception exception = null;
870 child = remoteViews.apply(mContext, content);
872 catch (RuntimeException e) {
876 Slog.e(TAG, "couldn't inflate view for package " + n.pkg, exception);
880 //Try-catch cause it does not work with QuickSettings, possibly other apps too :( (Won't work thru XML as well)
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);
889 catch (Exception e) {
892 content.addView(child);
894 row.setDrawingCacheEnabled(true);
896 notification.view = row;
897 notification.contentView = child;
902 void addNotificationView(StatusBarNotification notification) {
903 if (notification.view != null) {
904 throw new RuntimeException("Assertion failed: notification.view="
905 + notification.view);
908 LinearLayout parent = notification.data.ongoingEvent ? mOngoingItems : mLatestItems;
910 View child = makeNotificationView(notification, parent);
915 int index = mNotificationData.getExpandedIndex(notification);
916 parent.addView(child, index);
920 * Remove the old one and put the new one in its place.
921 * @param notification the notification
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);
936 n.contentView.reapply(mContext, notification.contentView);
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));
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);
952 mNotificationData.update(notification);
953 removeNotificationView(notification);
954 addNotificationView(notification);
956 setAreThereNotifications();
959 void removeNotificationView(StatusBarNotification notification) {
960 View v = notification.view;
962 ViewGroup parent = (ViewGroup)v.getParent();
963 parent.removeView(v);
964 notification.view = null;
968 private void setAreThereNotifications() {
969 boolean ongoing = mOngoingItems.getChildCount() != 0;
970 boolean latest = mLatestItems.getChildCount() != 0;
972 if (mNotificationData.hasClearableItems()) {
973 mClearButton.setVisibility(View.VISIBLE);
975 mClearButton.setVisibility(View.INVISIBLE);
978 mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE);
979 mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE);
981 if (ongoing || latest) {
982 mNoNotificationsTitle.setVisibility(View.GONE);
984 mNoNotificationsTitle.setVisibility(View.VISIBLE);
988 private void makeExpandedVisible() {
989 if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
990 if (mExpandedVisible) {
993 mExpandedVisible = true;
994 panelSlightlyVisible(true);
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);
1004 setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
1008 void animateExpand() {
1009 if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded);
1010 if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
1017 prepareTracking(0, true);
1018 performFling(0, 2000.0f, true);
1021 void animateCollapse() {
1023 Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
1024 + " mExpandedVisible=" + mExpandedVisible
1025 + " mExpanded=" + mExpanded
1026 + " mAnimating=" + mAnimating
1027 + " mAnimY=" + mAnimY
1028 + " mAnimVel=" + mAnimVel);
1031 if (!mExpandedVisible) {
1039 y = mDisplay.getHeight()-1;
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.
1044 prepareTracking(y, false);
1045 performFling(y, -2000.0f, true);
1048 void performExpand() {
1049 if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded);
1050 if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
1057 // It seems strange to sometimes not expand...
1059 synchronized (mNotificationData) {
1060 if (mNotificationData.size() == 0) {
1067 makeExpandedVisible();
1068 updateExpandedViewPos(EXPANDED_FULL_OPEN);
1070 if (false) postStartTracing();
1073 void performCollapse() {
1074 if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded
1075 + " mExpandedVisible=" + mExpandedVisible);
1077 if (!mExpandedVisible) {
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);
1087 if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
1088 setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1090 setDateViewVisibility(false, com.android.internal.R.anim.fade_out);
1098 void doAnimation() {
1100 if (SPEW) Slog.d(TAG, "doAnimation");
1101 if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
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.");
1107 updateExpandedViewPos(EXPANDED_FULL_OPEN);
1110 else if (mAnimY < mStatusBarView.getHeight()) {
1111 if (SPEW) Slog.d(TAG, "Animation completed to collapsed state.");
1113 updateExpandedViewPos(0);
1117 updateExpandedViewPos((int)mAnimY);
1118 mCurAnimationTime += ANIM_FRAME_DURATION;
1119 mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
1124 void stopTracking() {
1126 mVelocityTracker.recycle();
1127 mVelocityTracker = null;
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);
1143 void doRevealAnimation() {
1144 final int h = mCloseView.getHeight() + mStatusBarView.getHeight();
1145 if (mAnimatingReveal && mAnimating && mAnimY < h) {
1149 updateExpandedViewPos((int)mAnimY);
1151 updateExpandedViewPos((int)mAnimY);
1152 mCurAnimationTime += ANIM_FRAME_DURATION;
1153 mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
1159 void prepareTracking(int y, boolean opening) {
1161 mVelocityTracker = VelocityTracker.obtain();
1163 mAnimAccel = 2000.0f;
1165 mAnimY = mStatusBarView.getHeight();
1166 updateExpandedViewPos((int)mAnimY);
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;
1175 mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
1177 makeExpandedVisible();
1179 // it's open, close it?
1182 mHandler.removeMessages(MSG_ANIMATE);
1184 updateExpandedViewPos(y + mViewDelta);
1188 void performFling(int y, float vel, boolean always) {
1189 mAnimatingReveal = false;
1190 mDisplayHeight = mDisplay.getHeight();
1195 //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
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;
1209 // We are expanded and are now going to animate away.
1210 mAnimAccel = -2000.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;
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;
1235 //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
1236 // + " mAnimAccel=" + mAnimAccel);
1238 long now = SystemClock.uptimeMillis();
1239 mAnimLastTime = now;
1240 mCurAnimationTime = now + ANIM_FRAME_DURATION;
1242 mHandler.removeMessages(MSG_ANIMATE);
1243 mHandler.removeMessages(MSG_ANIMATE_REVEAL);
1244 mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
1248 boolean interceptTouchEvent(MotionEvent event) {
1250 Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
1254 if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
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();
1264 mViewDelta = statusBarSize - y;
1266 mTrackingView.getLocationOnScreen(mAbsPos);
1267 mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y;
1269 if ((!mExpanded && y < hitSize) ||
1270 (mExpanded && y > (mDisplay.getHeight()-hitSize))) {
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);
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) {
1290 mAnimatingReveal = false;
1291 updateExpandedViewPos(y + mViewDelta);
1293 } else if (event.getAction() == MotionEvent.ACTION_UP) {
1294 mVelocityTracker.computeCurrentVelocity(1000);
1296 float yVel = mVelocityTracker.getYVelocity();
1297 boolean negative = yVel < 0;
1299 float xVel = mVelocityTracker.getXVelocity();
1303 if (xVel > 150.0f) {
1304 xVel = 150.0f; // limit how much we care about the x axis
1307 float vel = (float)Math.hypot(yVel, xVel);
1312 performFling((int)event.getRawY(), vel, false);
1319 private class Launcher implements View.OnClickListener {
1320 private PendingIntent mIntent;
1321 private String mPkg;
1322 private String mTag;
1325 Launcher(PendingIntent intent, String pkg, String tag, int id) {
1332 public void onClick(View v) {
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) {
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()));
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);
1357 private class MyTicker extends Ticker {
1358 MyTicker(Context context, StatusBarView sb) {
1363 void tickerStarting() {
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);
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);
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);
1398 Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
1399 public void onAnimationEnd(Animation animation) {
1402 public void onAnimationRepeat(Animation animation) {
1404 public void onAnimationStart(Animation animation) {
1408 private Animation loadAnim(int id, Animation.AnimationListener listener) {
1409 Animation anim = AnimationUtils.loadAnimation(mContext, id);
1410 if (listener != null) {
1411 anim.setAnimationListener(listener);
1416 public String viewInfo(View v) {
1417 return "(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
1418 + " " + v.getWidth() + "x" + v.getHeight() + ")";
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());
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="
1451 pw.println(" iconData=" + op.iconData);
1452 pw.println(" notificationData=" + op.notificationData);
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));
1470 synchronized (mIconMap) {
1471 final int N = mIconMap.size();
1472 pw.println(" mIconMap.size=" + N);
1473 Set<IBinder> keys = mIconMap.keySet();
1475 for (IBinder key: keys) {
1476 StatusBarIcon icon = mIconMap.get(key);
1477 pw.println(" [" + i + "] key=" + key);
1478 pw.println(" data=" + icon.mData);
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);
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);
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);
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() {
1514 mStatusBarView.getLocationOnScreen(mAbsPos);
1515 Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
1516 + ") " + mStatusBarView.getWidth() + "x"
1517 + mStatusBarView.getHeight());
1518 mStatusBarView.debug();
1520 mExpandedView.getLocationOnScreen(mAbsPos);
1521 Slog.d(TAG, "mExpandedView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
1522 + ") " + mExpandedView.getWidth() + "x"
1523 + mExpandedView.getHeight());
1524 mExpandedView.debug();
1526 mTrackingView.getLocationOnScreen(mAbsPos);
1527 Slog.d(TAG, "mTrackingView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
1528 + ") " + mTrackingView.getWidth() + "x"
1529 + mTrackingView.getHeight());
1530 mTrackingView.debug();
1536 void onBarViewAttached() {
1537 WindowManager.LayoutParams lp;
1541 /// ---------- Tracking View --------------
1542 pixelFormat = PixelFormat.RGBX_8888;
1543 bg = mTrackingView.getBackground();
1545 pixelFormat = bg.getOpacity();
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,
1556 // lp.token = mStatusBarView.getWindowToken();
1557 lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
1558 lp.setTitle("TrackingView");
1559 lp.y = mTrackingPosition;
1560 mTrackingParams = lp;
1562 WindowManagerImpl.getDefault().addView(mTrackingView, lp);
1565 void onTrackingViewAttached() {
1566 WindowManager.LayoutParams lp;
1570 /// ---------- Expanded View --------------
1571 pixelFormat = PixelFormat.TRANSLUCENT;
1573 final int disph = mDisplay.getHeight();
1574 lp = mExpandedDialog.getWindow().getAttributes();
1575 lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
1576 lp.height = getExpandedHeight();
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;
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();
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));
1607 void setNotificationIconVisibility(boolean visible, int anim) {
1608 int old = mNotificationIcons.getVisibility();
1609 int v = visible ? View.VISIBLE : View.INVISIBLE;
1611 mNotificationIcons.setVisibility(v);
1612 mNotificationIcons.startAnimation(loadAnim(anim, null));
1616 void updateExpandedViewPos(int expandedPosition) {
1618 Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition
1619 + " mTrackingParams.y=" + mTrackingParams.y
1620 + " mTrackingPosition=" + mTrackingPosition);
1623 int h = mStatusBarView.getHeight();
1624 int disph = mDisplay.getHeight();
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);
1636 if (mExpandedParams != null) {
1637 mExpandedParams.y = -disph;
1638 mExpandedDialog.getWindow().setAttributes(mExpandedParams);
1645 if (expandedPosition == EXPANDED_FULL_OPEN) {
1648 else if (expandedPosition == EXPANDED_LEAVE_ALONE) {
1649 pos = mTrackingPosition;
1652 if (expandedPosition <= disph) {
1653 pos = expandedPosition;
1659 mTrackingPosition = mTrackingParams.y = pos;
1660 mTrackingParams.height = disph-h;
1661 WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
1663 if (mExpandedParams != null) {
1664 mCloseView.getLocationInWindow(mPositionTmp);
1665 final int closePos = mPositionTmp[1];
1667 mExpandedContents.getLocationInWindow(mPositionTmp);
1668 final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight();
1670 mExpandedParams.y = pos + mTrackingView.getHeight()
1671 - (mTrackingParams.height-closePos) - contentsBottom;
1673 if (mExpandedParams.y > max) {
1674 mExpandedParams.y = max;
1676 int min = mTrackingPosition;
1677 if (mExpandedParams.y < min) {
1678 mExpandedParams.y = min;
1681 boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h;
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;
1687 panelSlightlyVisible(visible);
1688 mExpandedDialog.getWindow().setAttributes(mExpandedParams);
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);
1700 int getExpandedHeight() {
1701 return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight();
1704 void updateExpandedHeight() {
1705 if (mExpandedView != null) {
1706 mExpandedParams.height = getExpandedHeight();
1707 mExpandedDialog.getWindow().setAttributes(mExpandedParams);
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)
1718 private boolean mPanelSlightlyVisible;
1719 void panelSlightlyVisible(boolean visible) {
1720 if (mPanelSlightlyVisible != visible) {
1721 mPanelSlightlyVisible = visible;
1723 // tell the notification manager to turn off the lights.
1724 mNotificationCallbacks.onPanelRevealed();
1729 void performDisableActions(int net) {
1730 int old = mDisabled;
1731 int diff = net ^ old;
1735 if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
1736 if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
1737 Slog.d(TAG, "DISABLE_EXPAND: yes");
1741 if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1742 if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1743 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
1745 mNotificationIcons.setVisibility(View.INVISIBLE);
1748 setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
1751 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
1752 if (!mExpandedVisible) {
1753 setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1756 } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1757 if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
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));
1774 private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
1775 public void onClick(View v) {
1776 mNotificationCallbacks.onClearAll();
1777 addPendingOp(OP_EXPAND, null, false);
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)) {
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));
1794 else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1800 void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) {
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);
1805 Slog.d(TAG, "updateNetworkName showSpn=" + showSpn + " spn=" + spn
1806 + " showPlmn=" + showPlmn + " plmn=" + plmn);
1808 boolean something = false;
1809 // Double carrier - bcrook
1810 if (showPlmn && mShowPlmnSb) {
1811 mPlmnLabel.setVisibility(View.VISIBLE);
1813 mPlmnLabel.setText(plmn);
1815 mPlmnLabel.setText(R.string.lockscreen_carrier_default);
1818 mPlmnLabel.setText("");
1819 mPlmnLabel.setVisibility(View.GONE);
1821 // Double carrier - bcrook, refinements from Wysie
1822 if (showSpn && spn != null && mShowSpnSb) {
1823 mSpnLabel.setText(spn);
1824 mSpnLabel.setVisibility(View.VISIBLE);
1827 mSpnLabel.setText("");
1828 mSpnLabel.setVisibility(View.GONE);
1833 * Reload some of our resources when the configuration changes.
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.
1839 void updateResources() {
1840 Resources res = mContext.getResources();
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));
1847 mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
1849 if (false) Slog.v(TAG, "updateResources");
1856 void postStartTracing() {
1857 mHandler.postDelayed(mStartTracing, 3000);
1861 android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
1862 Context.VIBRATOR_SERVICE);
1866 Runnable mStartTracing = new Runnable() {
1869 SystemClock.sleep(250);
1870 Slog.d(TAG, "startTracing");
1871 android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
1872 mHandler.postDelayed(mStopTracing, 10000);
1876 Runnable mStopTracing = new Runnable() {
1878 android.os.Debug.stopMethodTracing();
1879 Slog.d(TAG, "stopTracing");
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);
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);
1901 Uri data = intent.getData();
1903 String pkg = data.getSchemeSpecificPart();
1905 pkgList = new String[]{pkg};
1909 ArrayList<StatusBarNotification> list = null;
1910 if (pkgList != null) {
1911 synchronized (StatusBarService.this) {
1912 for (String pkg : pkgList) {
1913 list = mNotificationData.notificationsForPackage(pkg);
1919 final int N = list.size();
1920 for (int i=0; i<N; i++) {
1921 removeIcon(list.get(i).key);