blob: b1c6ad89a2bd9aee7fbd63c9ecb6fcf43917beaf [file] [log] [blame]
Joe Onorato2314aab2010-04-08 16:41:23 -05001/*
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
Joe Onorato79de0c52010-05-26 17:03:26 -040017package com.android.systemui.statusbar;
Joe Onorato2314aab2010-04-08 16:41:23 -050018
19import android.app.Service;
Joe Onorato0cbda992010-05-02 16:28:15 -070020import com.android.internal.statusbar.IStatusBar;
21import com.android.internal.statusbar.IStatusBarService;
22import com.android.internal.statusbar.StatusBarIcon;
23import com.android.internal.statusbar.StatusBarIconList;
Joe Onorato75199e32010-05-29 17:22:51 -040024import com.android.internal.statusbar.StatusBarNotification;
25
Joe Onorato1c95ecb2010-06-28 17:19:12 -040026import android.app.ActivityManagerNative;
27import android.app.Dialog;
28import android.app.Notification;
29import android.app.PendingIntent;
30import android.app.Service;
31import android.app.StatusBarManager;
32import android.content.BroadcastReceiver;
33import android.content.Context;
34import android.content.Intent;
35import android.content.IntentFilter;
36import android.content.pm.PackageManager;
37import android.content.res.Resources;
38import android.graphics.PixelFormat;
39import android.graphics.Rect;
40import android.graphics.drawable.Drawable;
41import android.net.Uri;
42import android.os.IBinder;
43import android.os.RemoteException;
44import android.os.Binder;
45import android.os.Handler;
46import android.os.Message;
47import android.os.ServiceManager;
48import android.os.SystemClock;
Joe Onoratoda695522010-09-07 14:13:12 -040049import android.text.TextUtils;
Joe Onorato1c95ecb2010-06-28 17:19:12 -040050import android.util.Slog;
51import android.util.Log;
52import android.view.Display;
53import android.view.Gravity;
54import android.view.KeyEvent;
55import android.view.LayoutInflater;
56import android.view.MotionEvent;
57import android.view.VelocityTracker;
58import android.view.View;
59import android.view.ViewGroup;
60import android.view.Window;
61import android.view.WindowManager;
62import android.view.WindowManagerImpl;
63import android.view.animation.Animation;
64import android.view.animation.AnimationUtils;
65import android.widget.ImageView;
66import android.widget.LinearLayout;
67import android.widget.RemoteViews;
68import android.widget.ScrollView;
69import android.widget.TextView;
70import android.widget.FrameLayout;
Joe Onorato0cbda992010-05-02 16:28:15 -070071
Joe Onorato1c95ecb2010-06-28 17:19:12 -040072import java.io.FileDescriptor;
73import java.io.PrintWriter;
74import java.util.ArrayList;
75import java.util.HashMap;
76import java.util.Set;
77
78import com.android.systemui.R;
79import com.android.systemui.statusbar.policy.StatusBarPolicy;
80
81
82
83public class StatusBarService extends Service implements CommandQueue.Callbacks {
84 static final String TAG = "StatusBarService";
Joe Onorato795f2842010-09-27 11:34:46 -070085 static final boolean SPEW_ICONS = false;
Joe Onoratof9ec03c2010-09-23 15:09:18 -070086 static final boolean SPEW = false;
Joe Onorato1c95ecb2010-06-28 17:19:12 -040087
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;
Joe Onorato1c95ecb2010-06-28 17:19:12 -040096
97 StatusBarPolicy mIconPolicy;
Joe Onorato2314aab2010-04-08 16:41:23 -050098
Joe Onorato0cbda992010-05-02 16:28:15 -070099 CommandQueue mCommandQueue;
Joe Onorato25f95f92010-04-08 18:37:10 -0500100 IStatusBarService mBarService;
Joe Onorato2314aab2010-04-08 16:41:23 -0500101
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400102 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 {
175 ExpandedDialog(Context context) {
176 super(context, com.android.internal.R.style.Theme_Light_NoTitleBar);
177 }
178
179 @Override
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
Joe Onorato2314aab2010-04-08 16:41:23 -0500194 @Override
195 public void onCreate() {
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400196 // First set up our views and stuff.
197 mDisplay = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
198 makeStatusBarView(this);
199
Joe Onorato2314aab2010-04-08 16:41:23 -0500200 // Connect in to the status bar manager service
Joe Onorato0cbda992010-05-02 16:28:15 -0700201 StatusBarIconList iconList = new StatusBarIconList();
Joe Onorato75199e32010-05-29 17:22:51 -0400202 ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();
203 ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();
Joe Onorato66d7d012010-05-14 10:05:10 -0700204 mCommandQueue = new CommandQueue(this, iconList);
Joe Onorato25f95f92010-04-08 18:37:10 -0500205 mBarService = IStatusBarService.Stub.asInterface(
Joe Onorato2314aab2010-04-08 16:41:23 -0500206 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
207 try {
Joe Onorato75199e32010-05-29 17:22:51 -0400208 mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications);
Joe Onorato2314aab2010-04-08 16:41:23 -0500209 } catch (RemoteException ex) {
210 // If the system process isn't there we're doomed anyway.
211 }
Joe Onorato0cbda992010-05-02 16:28:15 -0700212
213 // Set up the initial icon state
Joe Onorato75199e32010-05-29 17:22:51 -0400214 int N = iconList.size();
Joe Onorato0cbda992010-05-02 16:28:15 -0700215 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
Joe Onorato75199e32010-05-29 17:22:51 -0400224 // Set up the initial notification state
225 N = notificationKeys.size();
Joe Onorato75199e32010-05-29 17:22:51 -0400226 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
Joe Onorato0cbda992010-05-02 16:28:15 -0700235 // Put up the view
236 addStatusBarView();
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400237
238 // Lastly, call to the icon policy to install/update all the icons.
239 mIconPolicy = new StatusBarPolicy(this);
Joe Onorato2314aab2010-04-08 16:41:23 -0500240 }
241
242 @Override
243 public void onDestroy() {
244 // we're never destroyed
245 }
246
247 /**
248 * Nobody binds to us.
249 */
250 @Override
251 public IBinder onBind(Intent intent) {
252 return null;
253 }
254
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400255 // ================================================================================
256 // Constructing the view
257 // ================================================================================
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
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400267 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
Joe Onorato0d7349b2010-10-11 16:24:50 -0700297 mExpandedView.setVisibility(View.GONE);
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400298 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
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400313 // 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
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");
Joe Onoratobb60d1e2010-10-11 16:35:48 -0700339 lp.windowAnimations = com.android.internal.R.style.Animation_StatusBar;
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400340
341 WindowManagerImpl.getDefault().addView(view, lp);
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400342 }
343
344 public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
Joe Onoratof9ec03c2010-09-23 15:09:18 -0700345 if (SPEW_ICONS) {
346 Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
347 + " icon=" + icon);
348 }
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400349 StatusBarIconView view = new StatusBarIconView(this, slot);
350 view.set(icon);
351 mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize));
352 }
353
354 public void updateIcon(String slot, int index, int viewIndex,
355 StatusBarIcon old, StatusBarIcon icon) {
Joe Onoratof9ec03c2010-09-23 15:09:18 -0700356 if (SPEW_ICONS) {
357 Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex
358 + " old=" + old + " icon=" + icon);
359 }
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400360 StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex);
361 view.set(icon);
362 }
363
364 public void removeIcon(String slot, int index, int viewIndex) {
Joe Onoratof9ec03c2010-09-23 15:09:18 -0700365 if (SPEW_ICONS) {
366 Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex);
367 }
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400368 mStatusIcons.removeViewAt(viewIndex);
369 }
370
371 public void addNotification(IBinder key, StatusBarNotification notification) {
Daniel Sandlerd02bdaa2010-08-26 10:28:46 -0400372 boolean shouldTick = true;
373 if (notification.notification.fullScreenIntent != null) {
374 shouldTick = false;
375 Slog.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400376 try {
377 notification.notification.fullScreenIntent.send();
378 } catch (PendingIntent.CanceledException e) {
379 }
Daniel Sandlerd02bdaa2010-08-26 10:28:46 -0400380 }
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400381
Daniel Sandlerd02bdaa2010-08-26 10:28:46 -0400382 StatusBarIconView iconView = addNotificationViews(key, notification);
383 if (iconView == null) return;
384
385 if (shouldTick) {
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400386 tick(notification);
387 }
Daniel Sandlerd02bdaa2010-08-26 10:28:46 -0400388
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400389 // Recalculate the position of the sliding windows and the titles.
390 setAreThereNotifications();
391 updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
392 }
393
394 public void updateNotification(IBinder key, StatusBarNotification notification) {
395 Slog.d(TAG, "updateNotification key=" + key + " notification=" + notification);
396
397 NotificationData oldList;
398 int oldIndex = mOngoing.findEntry(key);
399 if (oldIndex >= 0) {
400 oldList = mOngoing;
401 } else {
402 oldIndex = mLatest.findEntry(key);
403 if (oldIndex < 0) {
404 Slog.w(TAG, "updateNotification for unknown key: " + key);
405 return;
406 }
407 oldList = mLatest;
408 }
409 final NotificationData.Entry oldEntry = oldList.getEntryAt(oldIndex);
410 final StatusBarNotification oldNotification = oldEntry.notification;
411 final RemoteViews oldContentView = oldNotification.notification.contentView;
412
413 final RemoteViews contentView = notification.notification.contentView;
414
415 if (false) {
416 Slog.d(TAG, "old notification: when=" + oldNotification.notification.when
417 + " ongoing=" + oldNotification.isOngoing()
418 + " expanded=" + oldEntry.expanded
419 + " contentView=" + oldContentView);
420 Slog.d(TAG, "new notification: when=" + notification.notification.when
421 + " ongoing=" + oldNotification.isOngoing()
422 + " contentView=" + contentView);
423 }
424
425 // Can we just reapply the RemoteViews in place? If when didn't change, the order
426 // didn't change.
427 if (notification.notification.when == oldNotification.notification.when
428 && notification.isOngoing() == oldNotification.isOngoing()
429 && oldEntry.expanded != null
430 && contentView != null && oldContentView != null
431 && contentView.getPackage() != null
432 && oldContentView.getPackage() != null
433 && oldContentView.getPackage().equals(contentView.getPackage())
434 && oldContentView.getLayoutId() == contentView.getLayoutId()) {
Joe Onorato6be47392010-06-30 14:48:54 -0400435 if (SPEW) Slog.d(TAG, "reusing notification");
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400436 oldEntry.notification = notification;
437 try {
438 // Reapply the RemoteViews
439 contentView.reapply(this, oldEntry.content);
440 // update the contentIntent
441 final PendingIntent contentIntent = notification.notification.contentIntent;
442 if (contentIntent != null) {
443 oldEntry.content.setOnClickListener(new Launcher(contentIntent,
444 notification.pkg, notification.tag, notification.id));
445 }
446 // Update the icon.
447 final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
448 notification.notification.icon, notification.notification.iconLevel,
449 notification.notification.number);
450 if (!oldEntry.icon.set(ic)) {
451 handleNotificationError(key, notification, "Couldn't update icon: " + ic);
452 return;
453 }
454 }
455 catch (RuntimeException e) {
456 // It failed to add cleanly. Log, and remove the view from the panel.
457 Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
458 removeNotificationViews(key);
459 addNotificationViews(key, notification);
460 }
461 } else {
Joe Onorato6be47392010-06-30 14:48:54 -0400462 if (SPEW) Slog.d(TAG, "not reusing notification");
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400463 removeNotificationViews(key);
464 addNotificationViews(key, notification);
465 }
466
467 // Restart the ticker if it's still running
Joe Onoratoda695522010-09-07 14:13:12 -0400468 if (notification.notification.tickerText != null
469 && !TextUtils.equals(notification.notification.tickerText,
470 oldEntry.notification.notification.tickerText)) {
471 tick(notification);
472 }
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400473
474 // Recalculate the position of the sliding windows and the titles.
475 setAreThereNotifications();
476 updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
477 }
478
479 public void removeNotification(IBinder key) {
Joe Onorato6be47392010-06-30 14:48:54 -0400480 if (SPEW) Slog.d(TAG, "removeNotification key=" + key);
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400481 StatusBarNotification old = removeNotificationViews(key);
482
483 if (old != null) {
484 // Cancel the ticker if it's still running
485 mTicker.removeEntry(old);
486
487 // Recalculate the position of the sliding windows and the titles.
488 setAreThereNotifications();
489 updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
490 }
491 }
492
493 private int chooseIconIndex(boolean isOngoing, int viewIndex) {
494 final int latestSize = mLatest.size();
495 if (isOngoing) {
496 return latestSize + (mOngoing.size() - viewIndex);
497 } else {
498 return latestSize - viewIndex;
499 }
500 }
501
502 View[] makeNotificationView(StatusBarNotification notification, ViewGroup parent) {
503 Notification n = notification.notification;
504 RemoteViews remoteViews = n.contentView;
505 if (remoteViews == null) {
506 return null;
507 }
508
509 // create the row view
510 LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
511 View row = inflater.inflate(R.layout.status_bar_latest_event, parent, false);
512
513 // bind the click event to the content area
514 ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
515 content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
516 content.setOnFocusChangeListener(mFocusChangeListener);
517 PendingIntent contentIntent = n.contentIntent;
518 if (contentIntent != null) {
519 content.setOnClickListener(new Launcher(contentIntent, notification.pkg,
520 notification.tag, notification.id));
521 }
522
523 View expanded = null;
524 Exception exception = null;
525 try {
526 expanded = remoteViews.apply(this, content);
527 }
528 catch (RuntimeException e) {
529 exception = e;
530 }
531 if (expanded == null) {
532 String ident = notification.pkg + "/0x" + Integer.toHexString(notification.id);
Steve Howardce945842010-07-01 17:54:16 -0700533 Slog.e(TAG, "couldn't inflate view for notification " + ident, exception);
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400534 return null;
535 } else {
536 content.addView(expanded);
537 row.setDrawingCacheEnabled(true);
538 }
539
540 return new View[] { row, content, expanded };
541 }
542
543 StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
544 NotificationData list;
545 ViewGroup parent;
546 final boolean isOngoing = notification.isOngoing();
547 if (isOngoing) {
548 list = mOngoing;
549 parent = mOngoingItems;
550 } else {
551 list = mLatest;
552 parent = mLatestItems;
553 }
554 // Construct the expanded view.
555 final View[] views = makeNotificationView(notification, parent);
556 if (views == null) {
557 handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
558 + notification);
559 return null;
560 }
561 final View row = views[0];
562 final View content = views[1];
563 final View expanded = views[2];
564 // Construct the icon.
565 final StatusBarIconView iconView = new StatusBarIconView(this,
566 notification.pkg + "/0x" + Integer.toHexString(notification.id));
567 final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon,
568 notification.notification.iconLevel, notification.notification.number);
569 if (!iconView.set(ic)) {
570 handleNotificationError(key, notification, "Coulding create icon: " + ic);
571 return null;
572 }
573 // Add the expanded view.
574 final int viewIndex = list.add(key, notification, row, content, expanded, iconView);
575 parent.addView(row, viewIndex);
576 // Add the icon.
577 final int iconIndex = chooseIconIndex(isOngoing, viewIndex);
Joe Onorato6c01a112010-10-04 17:38:47 -0400578 mNotificationIcons.addView(iconView, iconIndex);
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400579 return iconView;
580 }
581
582 StatusBarNotification removeNotificationViews(IBinder key) {
583 NotificationData.Entry entry = mOngoing.remove(key);
584 if (entry == null) {
585 entry = mLatest.remove(key);
586 if (entry == null) {
587 Slog.w(TAG, "removeNotification for unknown key: " + key);
588 return null;
589 }
590 }
591 // Remove the expanded view.
592 ((ViewGroup)entry.row.getParent()).removeView(entry.row);
593 // Remove the icon.
594 ((ViewGroup)entry.icon.getParent()).removeView(entry.icon);
595
596 return entry.notification;
597 }
598
599 private void setAreThereNotifications() {
600 boolean ongoing = mOngoing.hasVisibleItems();
601 boolean latest = mLatest.hasVisibleItems();
602
603 // (no ongoing notifications are clearable)
604 if (mLatest.hasClearableItems()) {
605 mClearButton.setVisibility(View.VISIBLE);
606 } else {
607 mClearButton.setVisibility(View.INVISIBLE);
608 }
609
610 mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE);
611 mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE);
612
613 if (ongoing || latest) {
614 mNoNotificationsTitle.setVisibility(View.GONE);
615 } else {
616 mNoNotificationsTitle.setVisibility(View.VISIBLE);
617 }
618 }
619
620
Joe Onorato94c98c02010-04-12 09:03:27 -0700621 /**
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400622 * State is one or more of the DISABLE constants from StatusBarManager.
Joe Onorato94c98c02010-04-12 09:03:27 -0700623 */
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400624 public void disable(int state) {
625 final int old = mDisabled;
626 final int diff = state ^ old;
627 mDisabled = state;
628
629 if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
630 if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
631 Slog.d(TAG, "DISABLE_EXPAND: yes");
632 animateCollapse();
633 }
634 }
635 if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
636 if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
637 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
638 if (mTicking) {
639 mTicker.halt();
640 } else {
641 setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
642 }
643 } else {
644 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
645 if (!mExpandedVisible) {
646 setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
647 }
648 }
649 } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
650 if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
651 Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes");
652 mTicker.halt();
653 }
654 }
655 }
656
657 /**
658 * All changes to the status bar and notifications funnel through here and are batched.
659 */
660 private class H extends Handler {
661 public void handleMessage(Message m) {
662 switch (m.what) {
663 case MSG_ANIMATE:
664 doAnimation();
665 break;
666 case MSG_ANIMATE_REVEAL:
667 doRevealAnimation();
668 break;
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400669 }
670 }
671 }
672
673 View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
674 public void onFocusChange(View v, boolean hasFocus) {
675 // Because 'v' is a ViewGroup, all its children will be (un)selected
676 // too, which allows marqueeing to work.
677 v.setSelected(hasFocus);
678 }
679 };
680
681 private void makeExpandedVisible() {
682 if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
683 if (mExpandedVisible) {
684 return;
685 }
686 mExpandedVisible = true;
687 visibilityChanged(true);
688
689 updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
690 mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
691 mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
692 mExpandedDialog.getWindow().setAttributes(mExpandedParams);
693 mExpandedView.requestFocus(View.FOCUS_FORWARD);
694 mTrackingView.setVisibility(View.VISIBLE);
Joe Onorato0d7349b2010-10-11 16:24:50 -0700695 mExpandedView.setVisibility(View.VISIBLE);
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400696
697 if (!mTicking) {
698 setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
699 }
700 }
701
702 public void animateExpand() {
703 if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded);
704 if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
705 return ;
706 }
707 if (mExpanded) {
708 return;
709 }
710
711 prepareTracking(0, true);
712 performFling(0, 2000.0f, true);
713 }
714
715 public void animateCollapse() {
716 if (SPEW) {
717 Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
718 + " mExpandedVisible=" + mExpandedVisible
719 + " mExpanded=" + mExpanded
720 + " mAnimating=" + mAnimating
721 + " mAnimY=" + mAnimY
722 + " mAnimVel=" + mAnimVel);
723 }
724
725 if (!mExpandedVisible) {
726 return;
727 }
728
729 int y;
730 if (mAnimating) {
731 y = (int)mAnimY;
732 } else {
733 y = mDisplay.getHeight()-1;
734 }
735 // Let the fling think that we're open so it goes in the right direction
736 // and doesn't try to re-open the windowshade.
737 mExpanded = true;
738 prepareTracking(y, false);
739 performFling(y, -2000.0f, true);
740 }
741
742 void performExpand() {
743 if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded);
744 if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
745 return ;
746 }
747 if (mExpanded) {
748 return;
749 }
750
751 mExpanded = true;
752 makeExpandedVisible();
753 updateExpandedViewPos(EXPANDED_FULL_OPEN);
754
755 if (false) postStartTracing();
756 }
757
758 void performCollapse() {
759 if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded
Daniel Sandler0398bf72010-08-19 14:55:38 -0400760 + " mExpandedVisible=" + mExpandedVisible
761 + " mTicking=" + mTicking);
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400762
763 if (!mExpandedVisible) {
764 return;
765 }
766 mExpandedVisible = false;
767 visibilityChanged(false);
768 mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
769 mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
770 mExpandedDialog.getWindow().setAttributes(mExpandedParams);
771 mTrackingView.setVisibility(View.GONE);
Joe Onorato0d7349b2010-10-11 16:24:50 -0700772 mExpandedView.setVisibility(View.GONE);
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400773
774 if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
775 setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
776 }
Daniel Sandler0398bf72010-08-19 14:55:38 -0400777 if (mDateView.getVisibility() == View.VISIBLE) {
778 setDateViewVisibility(false, com.android.internal.R.anim.fade_out);
779 }
Joe Onorato1c95ecb2010-06-28 17:19:12 -0400780
781 if (!mExpanded) {
782 return;
783 }
784 mExpanded = false;
785 }
786
787 void doAnimation() {
788 if (mAnimating) {
789 if (SPEW) Slog.d(TAG, "doAnimation");
790 if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
791 incrementAnim();
792 if (SPEW) Slog.d(TAG, "doAnimation after mAnimY=" + mAnimY);
793 if (mAnimY >= mDisplay.getHeight()-1) {
794 if (SPEW) Slog.d(TAG, "Animation completed to expanded state.");
795 mAnimating = false;
796 updateExpandedViewPos(EXPANDED_FULL_OPEN);
797 performExpand();
798 }
799 else if (mAnimY < mStatusBarView.getHeight()) {
800 if (SPEW) Slog.d(TAG, "Animation completed to collapsed state.");
801 mAnimating = false;
802 updateExpandedViewPos(0);
803 performCollapse();
804 }
805 else {
806 updateExpandedViewPos((int)mAnimY);
807 mCurAnimationTime += ANIM_FRAME_DURATION;
808 mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
809 }
810 }
811 }
812
813 void stopTracking() {
814 mTracking = false;
815 mVelocityTracker.recycle();
816 mVelocityTracker = null;
817 }
818
819 void incrementAnim() {
820 long now = SystemClock.uptimeMillis();
821 float t = ((float)(now - mAnimLastTime)) / 1000; // ms -> s
822 final float y = mAnimY;
823 final float v = mAnimVel; // px/s
824 final float a = mAnimAccel; // px/s/s
825 mAnimY = y + (v*t) + (0.5f*a*t*t); // px
826 mAnimVel = v + (a*t); // px/s
827 mAnimLastTime = now; // ms
828 //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
829 // + " mAnimAccel=" + mAnimAccel);
830 }
831
832 void doRevealAnimation() {
833 final int h = mCloseView.getHeight() + mStatusBarView.getHeight();
834 if (mAnimatingReveal && mAnimating && mAnimY < h) {
835 incrementAnim();
836 if (mAnimY >= h) {
837 mAnimY = h;
838 updateExpandedViewPos((int)mAnimY);
839 } else {
840 updateExpandedViewPos((int)mAnimY);
841 mCurAnimationTime += ANIM_FRAME_DURATION;
842 mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
843 mCurAnimationTime);
844 }
845 }
846 }
847
848 void prepareTracking(int y, boolean opening) {
849 mTracking = true;
850 mVelocityTracker = VelocityTracker.obtain();
851 if (opening) {
852 mAnimAccel = 2000.0f;
853 mAnimVel = 200;
854 mAnimY = mStatusBarView.getHeight();
855 updateExpandedViewPos((int)mAnimY);
856 mAnimating = true;
857 mAnimatingReveal = true;
858 mHandler.removeMessages(MSG_ANIMATE);
859 mHandler.removeMessages(MSG_ANIMATE_REVEAL);
860 long now = SystemClock.uptimeMillis();
861 mAnimLastTime = now;
862 mCurAnimationTime = now + ANIM_FRAME_DURATION;
863 mAnimating = true;
864 mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
865 mCurAnimationTime);
866 makeExpandedVisible();
867 } else {
868 // it's open, close it?
869 if (mAnimating) {
870 mAnimating = false;
871 mHandler.removeMessages(MSG_ANIMATE);
872 }
873 updateExpandedViewPos(y + mViewDelta);
874 }
875 }
876
877 void performFling(int y, float vel, boolean always) {
878 mAnimatingReveal = false;
879 mDisplayHeight = mDisplay.getHeight();
880
881 mAnimY = y;
882 mAnimVel = vel;
883
884 //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
885
886 if (mExpanded) {
887 if (!always && (
888 vel > 200.0f
889 || (y > (mDisplayHeight-25) && vel > -200.0f))) {
890 // We are expanded, but they didn't move sufficiently to cause
891 // us to retract. Animate back to the expanded position.
892 mAnimAccel = 2000.0f;
893 if (vel < 0) {
894 mAnimVel = 0;
895 }
896 }
897 else {
898 // We are expanded and are now going to animate away.
899 mAnimAccel = -2000.0f;
900 if (vel > 0) {
901 mAnimVel = 0;
902 }
903 }
904 } else {
905 if (always || (
906 vel > 200.0f
907 || (y > (mDisplayHeight/2) && vel > -200.0f))) {
908 // We are collapsed, and they moved enough to allow us to
909 // expand. Animate in the notifications.
910 mAnimAccel = 2000.0f;
911 if (vel < 0) {
912 mAnimVel = 0;
913 }
914 }
915 else {
916 // We are collapsed, but they didn't move sufficiently to cause
917 // us to retract. Animate back to the collapsed position.
918 mAnimAccel = -2000.0f;
919 if (vel > 0) {
920 mAnimVel = 0;
921 }
922 }
923 }
924 //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
925 // + " mAnimAccel=" + mAnimAccel);
926
927 long now = SystemClock.uptimeMillis();
928 mAnimLastTime = now;
929 mCurAnimationTime = now + ANIM_FRAME_DURATION;
930 mAnimating = true;
931 mHandler.removeMessages(MSG_ANIMATE);
932 mHandler.removeMessages(MSG_ANIMATE_REVEAL);
933 mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
934 stopTracking();
935 }
936
937 boolean interceptTouchEvent(MotionEvent event) {
938 if (SPEW) {
939 Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
940 + mDisabled);
941 }
942
943 if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
944 return false;
945 }
946
947 final int statusBarSize = mStatusBarView.getHeight();
948 final int hitSize = statusBarSize*2;
949 if (event.getAction() == MotionEvent.ACTION_DOWN) {
950 final int y = (int)event.getRawY();
951
952 if (!mExpanded) {
953 mViewDelta = statusBarSize - y;
954 } else {
955 mTrackingView.getLocationOnScreen(mAbsPos);
956 mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y;
957 }
958 if ((!mExpanded && y < hitSize) ||
959 (mExpanded && y > (mDisplay.getHeight()-hitSize))) {
960
961 // We drop events at the edge of the screen to make the windowshade come
962 // down by accident less, especially when pushing open a device with a keyboard
963 // that rotates (like g1 and droid)
964 int x = (int)event.getRawX();
965 final int edgeBorder = mEdgeBorder;
966 if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) {
967 prepareTracking(y, !mExpanded);// opening if we're not already fully visible
968 mVelocityTracker.addMovement(event);
969 }
970 }
971 } else if (mTracking) {
972 mVelocityTracker.addMovement(event);
973 final int minY = statusBarSize + mCloseView.getHeight();
974 if (event.getAction() == MotionEvent.ACTION_MOVE) {
975 int y = (int)event.getRawY();
976 if (mAnimatingReveal && y < minY) {
977 // nothing
978 } else {
979 mAnimatingReveal = false;
980 updateExpandedViewPos(y + mViewDelta);
981 }
982 } else if (event.getAction() == MotionEvent.ACTION_UP) {
983 mVelocityTracker.computeCurrentVelocity(1000);
984
985 float yVel = mVelocityTracker.getYVelocity();
986 boolean negative = yVel < 0;
987
988 float xVel = mVelocityTracker.getXVelocity();
989 if (xVel < 0) {
990 xVel = -xVel;
991 }
992 if (xVel > 150.0f) {
993 xVel = 150.0f; // limit how much we care about the x axis
994 }
995
996 float vel = (float)Math.hypot(yVel, xVel);
997 if (negative) {
998 vel = -vel;
999 }
1000
1001 performFling((int)event.getRawY(), vel, false);
1002 }
1003
1004 }
1005 return false;
1006 }
1007
1008 private class Launcher implements View.OnClickListener {
1009 private PendingIntent mIntent;
1010 private String mPkg;
1011 private String mTag;
1012 private int mId;
1013
1014 Launcher(PendingIntent intent, String pkg, String tag, int id) {
1015 mIntent = intent;
1016 mPkg = pkg;
1017 mTag = tag;
1018 mId = id;
1019 }
1020
1021 public void onClick(View v) {
1022 try {
1023 // The intent we are sending is for the application, which
1024 // won't have permission to immediately start an activity after
1025 // the user switches to home. We know it is safe to do at this
1026 // point, so make sure new activity switches are now allowed.
1027 ActivityManagerNative.getDefault().resumeAppSwitches();
1028 } catch (RemoteException e) {
1029 }
Daniel Sandler27a8be42010-06-29 15:19:54 -04001030
1031 if (mIntent != null) {
1032 int[] pos = new int[2];
1033 v.getLocationOnScreen(pos);
1034 Intent overlay = new Intent();
1035 overlay.setSourceBounds(
1036 new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
1037 try {
1038 mIntent.send(StatusBarService.this, 0, overlay);
1039 } catch (PendingIntent.CanceledException e) {
1040 // the stack trace isn't very helpful here. Just log the exception message.
1041 Slog.w(TAG, "Sending contentIntent failed: " + e);
1042 }
Joe Onorato1c95ecb2010-06-28 17:19:12 -04001043 }
Daniel Sandler27a8be42010-06-29 15:19:54 -04001044
Joe Onorato1c95ecb2010-06-28 17:19:12 -04001045 try {
1046 mBarService.onNotificationClick(mPkg, mTag, mId);
1047 } catch (RemoteException ex) {
1048 // system process is dead if we're here.
1049 }
Daniel Sandler27a8be42010-06-29 15:19:54 -04001050
1051 // close the shade if it was open
Joe Onorato1c95ecb2010-06-28 17:19:12 -04001052 animateCollapse();
1053 }
1054 }
1055
1056 private void tick(StatusBarNotification n) {
1057 // Show the ticker if one is requested. Also don't do this
1058 // until status bar window is attached to the window manager,
1059 // because... well, what's the point otherwise? And trying to
1060 // run a ticker without being attached will crash!
1061 if (n.notification.tickerText != null && mStatusBarView.getWindowToken() != null) {
1062 if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
1063 | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
1064 mTicker.addEntry(n);
1065 }
1066 }
1067 }
1068
1069 /**
1070 * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
1071 * about the failure.
1072 *
1073 * WARNING: this will call back into us. Don't hold any locks.
1074 */
1075 void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
1076 removeNotification(key);
1077 try {
1078 mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message);
1079 } catch (RemoteException ex) {
1080 // The end is nigh.
1081 }
1082 }
1083
1084 private class MyTicker extends Ticker {
1085 MyTicker(Context context, StatusBarView sb) {
1086 super(context, sb);
1087 }
1088
1089 @Override
1090 void tickerStarting() {
Daniel Sandler0398bf72010-08-19 14:55:38 -04001091 if (SPEW) Slog.d(TAG, "tickerStarting");
Joe Onorato1c95ecb2010-06-28 17:19:12 -04001092 mTicking = true;
1093 mIcons.setVisibility(View.GONE);
1094 mTickerView.setVisibility(View.VISIBLE);
1095 mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
1096 mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
1097 if (mExpandedVisible) {
1098 setDateViewVisibility(false, com.android.internal.R.anim.push_up_out);
1099 }
1100 }
1101
1102 @Override
1103 void tickerDone() {
Daniel Sandler0398bf72010-08-19 14:55:38 -04001104 if (SPEW) Slog.d(TAG, "tickerDone");
1105 mTicking = false;
Joe Onorato1c95ecb2010-06-28 17:19:12 -04001106 mIcons.setVisibility(View.VISIBLE);
1107 mTickerView.setVisibility(View.GONE);
1108 mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
Daniel Sandler0398bf72010-08-19 14:55:38 -04001109 mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out, null));
Joe Onorato1c95ecb2010-06-28 17:19:12 -04001110 if (mExpandedVisible) {
1111 setDateViewVisibility(true, com.android.internal.R.anim.push_down_in);
1112 }
1113 }
1114
1115 void tickerHalting() {
Daniel Sandler0398bf72010-08-19 14:55:38 -04001116 if (SPEW) Slog.d(TAG, "tickerHalting");
1117 mTicking = false;
Joe Onorato1c95ecb2010-06-28 17:19:12 -04001118 mIcons.setVisibility(View.VISIBLE);
1119 mTickerView.setVisibility(View.GONE);
1120 mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
Daniel Sandler0398bf72010-08-19 14:55:38 -04001121 mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out, null));
Joe Onorato1c95ecb2010-06-28 17:19:12 -04001122 if (mExpandedVisible) {
1123 setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
1124 }
1125 }
1126 }
1127
Joe Onorato1c95ecb2010-06-28 17:19:12 -04001128 private Animation loadAnim(int id, Animation.AnimationListener listener) {
1129 Animation anim = AnimationUtils.loadAnimation(StatusBarService.this, id);
1130 if (listener != null) {
1131 anim.setAnimationListener(listener);
1132 }
1133 return anim;
1134 }
1135
1136 public String viewInfo(View v) {
1137 return "(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
1138 + " " + v.getWidth() + "x" + v.getHeight() + ")";
1139 }
1140
1141 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1142 if (checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1143 != PackageManager.PERMISSION_GRANTED) {
1144 pw.println("Permission Denial: can't dump StatusBar from from pid="
1145 + Binder.getCallingPid()
1146 + ", uid=" + Binder.getCallingUid());
1147 return;
1148 }
1149
1150 synchronized (mQueueLock) {
1151 pw.println("Current Status Bar state:");
1152 pw.println(" mExpanded=" + mExpanded
1153 + ", mExpandedVisible=" + mExpandedVisible);
1154 pw.println(" mTicking=" + mTicking);
1155 pw.println(" mTracking=" + mTracking);
1156 pw.println(" mAnimating=" + mAnimating
1157 + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
1158 + ", mAnimAccel=" + mAnimAccel);
1159 pw.println(" mCurAnimationTime=" + mCurAnimationTime
1160 + " mAnimLastTime=" + mAnimLastTime);
1161 pw.println(" mDisplayHeight=" + mDisplayHeight
1162 + " mAnimatingReveal=" + mAnimatingReveal
1163 + " mViewDelta=" + mViewDelta);
1164 pw.println(" mDisplayHeight=" + mDisplayHeight);
1165 pw.println(" mExpandedParams: " + mExpandedParams);
1166 pw.println(" mExpandedView: " + viewInfo(mExpandedView));
1167 pw.println(" mExpandedDialog: " + mExpandedDialog);
1168 pw.println(" mTrackingParams: " + mTrackingParams);
1169 pw.println(" mTrackingView: " + viewInfo(mTrackingView));
1170 pw.println(" mOngoingTitle: " + viewInfo(mOngoingTitle));
1171 pw.println(" mOngoingItems: " + viewInfo(mOngoingItems));
1172 pw.println(" mLatestTitle: " + viewInfo(mLatestTitle));
1173 pw.println(" mLatestItems: " + viewInfo(mLatestItems));
1174 pw.println(" mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle));
1175 pw.println(" mCloseView: " + viewInfo(mCloseView));
1176 pw.println(" mTickerView: " + viewInfo(mTickerView));
1177 pw.println(" mScrollView: " + viewInfo(mScrollView)
1178 + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
1179 pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout));
1180 }
Joe Onorato1c95ecb2010-06-28 17:19:12 -04001181
Joe Onoratof9ec03c2010-09-23 15:09:18 -07001182 if (true) {
Joe Onorato1c95ecb2010-06-28 17:19:12 -04001183 // must happen on ui thread
1184 mHandler.post(new Runnable() {
1185 public void run() {
Joe Onoratof9ec03c2010-09-23 15:09:18 -07001186 Slog.d(TAG, "mStatusIcons:");
1187 mStatusIcons.debug();
Joe Onorato1c95ecb2010-06-28 17:19:12 -04001188 }
1189 });
1190 }
Joe Onoratof9ec03c2010-09-23 15:09:18 -07001191
Joe Onorato1c95ecb2010-06-28 17:19:12 -04001192 }
1193
1194 void onBarViewAttached() {
1195 WindowManager.LayoutParams lp;
1196 int pixelFormat;
1197 Drawable bg;
1198
1199 /// ---------- Tracking View --------------
1200 pixelFormat = PixelFormat.RGBX_8888;
1201 bg = mTrackingView.getBackground();
1202 if (bg != null) {
1203 pixelFormat = bg.getOpacity();
1204 }
1205
1206 lp = new WindowManager.LayoutParams(
1207 ViewGroup.LayoutParams.MATCH_PARENT,
1208 ViewGroup.LayoutParams.MATCH_PARENT,
1209 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
1210 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
1211 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
1212 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
1213 pixelFormat);
1214// lp.token = mStatusBarView.getWindowToken();
1215 lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
1216 lp.setTitle("TrackingView");
1217 lp.y = mTrackingPosition;
1218 mTrackingParams = lp;
1219
1220 WindowManagerImpl.getDefault().addView(mTrackingView, lp);
1221 }
1222
1223 void onTrackingViewAttached() {
1224 WindowManager.LayoutParams lp;
1225 int pixelFormat;
1226 Drawable bg;
1227
1228 /// ---------- Expanded View --------------
1229 pixelFormat = PixelFormat.TRANSLUCENT;
1230
1231 final int disph = mDisplay.getHeight();
1232 lp = mExpandedDialog.getWindow().getAttributes();
1233 lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
1234 lp.height = getExpandedHeight();
1235 lp.x = 0;
1236 mTrackingPosition = lp.y = -disph; // sufficiently large negative
1237 lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
1238 lp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
1239 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
1240 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
1241 | WindowManager.LayoutParams.FLAG_DITHER
1242 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
1243 lp.format = pixelFormat;
1244 lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
1245 lp.setTitle("StatusBarExpanded");
1246 mExpandedDialog.getWindow().setAttributes(lp);
1247 mExpandedDialog.getWindow().setFormat(pixelFormat);
1248 mExpandedParams = lp;
1249
1250 mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
1251 mExpandedDialog.setContentView(mExpandedView,
1252 new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
1253 ViewGroup.LayoutParams.MATCH_PARENT));
1254 mExpandedDialog.getWindow().setBackgroundDrawable(null);
1255 mExpandedDialog.show();
1256 FrameLayout hack = (FrameLayout)mExpandedView.getParent();
1257 }
1258
1259 void setDateViewVisibility(boolean visible, int anim) {
1260 mDateView.setUpdates(visible);
1261 mDateView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1262 mDateView.startAnimation(loadAnim(anim, null));
1263 }
1264
1265 void setNotificationIconVisibility(boolean visible, int anim) {
1266 int old = mNotificationIcons.getVisibility();
1267 int v = visible ? View.VISIBLE : View.INVISIBLE;
1268 if (old != v) {
1269 mNotificationIcons.setVisibility(v);
1270 mNotificationIcons.startAnimation(loadAnim(anim, null));
1271 }
1272 }
1273
1274 void updateExpandedViewPos(int expandedPosition) {
1275 if (SPEW) {
1276 Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition
Daniel Sandler0398bf72010-08-19 14:55:38 -04001277 + " mTrackingParams.y="
1278 + ((mTrackingParams == null) ? "???" : mTrackingParams.y)
Joe Onorato1c95ecb2010-06-28 17:19:12 -04001279 + " mTrackingPosition=" + mTrackingPosition);
1280 }
1281
1282 int h = mStatusBarView.getHeight();
1283 int disph = mDisplay.getHeight();
1284
1285 // If the expanded view is not visible, make sure they're still off screen.
1286 // Maybe the view was resized.
1287 if (!mExpandedVisible) {
1288 if (mTrackingView != null) {
1289 mTrackingPosition = -disph;
1290 if (mTrackingParams != null) {
1291 mTrackingParams.y = mTrackingPosition;
1292 WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
1293 }
1294 }
1295 if (mExpandedParams != null) {
1296 mExpandedParams.y = -disph;
1297 mExpandedDialog.getWindow().setAttributes(mExpandedParams);
1298 }
1299 return;
1300 }
1301
1302 // tracking view...
1303 int pos;
1304 if (expandedPosition == EXPANDED_FULL_OPEN) {
1305 pos = h;
1306 }
1307 else if (expandedPosition == EXPANDED_LEAVE_ALONE) {
1308 pos = mTrackingPosition;
1309 }
1310 else {
1311 if (expandedPosition <= disph) {
1312 pos = expandedPosition;
1313 } else {
1314 pos = disph;
1315 }
1316 pos -= disph-h;
1317 }
1318 mTrackingPosition = mTrackingParams.y = pos;
1319 mTrackingParams.height = disph-h;
1320 WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
1321
1322 if (mExpandedParams != null) {
1323 mCloseView.getLocationInWindow(mPositionTmp);
1324 final int closePos = mPositionTmp[1];
1325
1326 mExpandedContents.getLocationInWindow(mPositionTmp);
1327 final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight();
1328
Joe Onorato1c95ecb2010-06-28 17:19:12 -04001329 if (expandedPosition != EXPANDED_LEAVE_ALONE) {
Joe Onorato0d7349b2010-10-11 16:24:50 -07001330 mExpandedParams.y = pos + mTrackingView.getHeight()
1331 - (mTrackingParams.height-closePos) - contentsBottom;
1332 int max = h;
1333 if (mExpandedParams.y > max) {
1334 mExpandedParams.y = max;
1335 }
1336 int min = mTrackingPosition;
1337 if (mExpandedParams.y < min) {
1338 mExpandedParams.y = min;
1339 }
1340
1341 boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h;
1342 if (!visible) {
1343 // if the contents aren't visible, move the expanded view way off screen
1344 // because the window itself extends below the content view.
1345 mExpandedParams.y = -disph;
1346 }
1347 mExpandedDialog.getWindow().setAttributes(mExpandedParams);
1348
Joe Onorato6be47392010-06-30 14:48:54 -04001349 if (SPEW) Slog.d(TAG, "updateExpandedViewPos visibilityChanged(" + visible + ")");
Joe Onorato1c95ecb2010-06-28 17:19:12 -04001350 visibilityChanged(visible);
1351 }
1352 }
1353
1354 if (SPEW) {
1355 Slog.d(TAG, "updateExpandedViewPos after expandedPosition=" + expandedPosition
1356 + " mTrackingParams.y=" + mTrackingParams.y
1357 + " mTrackingPosition=" + mTrackingPosition
1358 + " mExpandedParams.y=" + mExpandedParams.y
1359 + " mExpandedParams.height=" + mExpandedParams.height);
1360 }
1361 }
1362
1363 int getExpandedHeight() {
1364 return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight();
1365 }
1366
1367 void updateExpandedHeight() {
1368 if (mExpandedView != null) {
1369 mExpandedParams.height = getExpandedHeight();
1370 mExpandedDialog.getWindow().setAttributes(mExpandedParams);
1371 }
1372 }
1373
1374 /**
1375 * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
1376 * This was added last-minute and is inconsistent with the way the rest of the notifications
1377 * are handled, because the notification isn't really cancelled. The lights are just
1378 * turned off. If any other notifications happen, the lights will turn back on. Steve says
1379 * this is what he wants. (see bug 1131461)
1380 */
1381 void visibilityChanged(boolean visible) {
1382 if (mPanelSlightlyVisible != visible) {
1383 mPanelSlightlyVisible = visible;
1384 try {
1385 mBarService.onPanelRevealed();
1386 } catch (RemoteException ex) {
1387 // Won't fail unless the world has ended.
1388 }
1389 }
1390 }
1391
1392 void performDisableActions(int net) {
1393 int old = mDisabled;
1394 int diff = net ^ old;
1395 mDisabled = net;
1396
1397 // act accordingly
1398 if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
1399 if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
1400 Slog.d(TAG, "DISABLE_EXPAND: yes");
1401 animateCollapse();
1402 }
1403 }
1404 if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1405 if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
1406 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
1407 if (mTicking) {
1408 mNotificationIcons.setVisibility(View.INVISIBLE);
1409 mTicker.halt();
1410 } else {
1411 setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
1412 }
1413 } else {
1414 Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
1415 if (!mExpandedVisible) {
1416 setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
1417 }
1418 }
1419 } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
Daniel Sandler0398bf72010-08-19 14:55:38 -04001420 Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: "
1421 + (((net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0)
1422 ? "yes" : "no"));
Joe Onorato1c95ecb2010-06-28 17:19:12 -04001423 if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
1424 mTicker.halt();
1425 }
1426 }
1427 }
1428
1429 private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
1430 public void onClick(View v) {
1431 try {
1432 mBarService.onClearAllNotifications();
1433 } catch (RemoteException ex) {
1434 // system process is dead if we're here.
1435 }
1436 animateCollapse();
1437 }
1438 };
1439
1440 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1441 public void onReceive(Context context, Intent intent) {
1442 String action = intent.getAction();
1443 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
1444 || Intent.ACTION_SCREEN_OFF.equals(action)) {
Joe Onorato246ba112010-08-26 16:52:26 -04001445 animateCollapse();
Joe Onorato1c95ecb2010-06-28 17:19:12 -04001446 }
1447 else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
1448 updateResources();
1449 }
1450 }
1451 };
1452
Joe Onorato1c95ecb2010-06-28 17:19:12 -04001453 /**
1454 * Reload some of our resources when the configuration changes.
1455 *
1456 * We don't reload everything when the configuration changes -- we probably
1457 * should, but getting that smooth is tough. Someday we'll fix that. In the
1458 * meantime, just update the things that we know change.
1459 */
1460 void updateResources() {
1461 Resources res = getResources();
1462
1463 mClearButton.setText(getText(R.string.status_bar_clear_all_button));
1464 mOngoingTitle.setText(getText(R.string.status_bar_ongoing_events_title));
1465 mLatestTitle.setText(getText(R.string.status_bar_latest_events_title));
1466 mNoNotificationsTitle.setText(getText(R.string.status_bar_no_notifications_title));
1467
1468 mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
1469
1470 if (false) Slog.v(TAG, "updateResources");
1471 }
1472
1473 //
1474 // tracing
1475 //
1476
1477 void postStartTracing() {
1478 mHandler.postDelayed(mStartTracing, 3000);
1479 }
1480
1481 void vibrate() {
1482 android.os.Vibrator vib = (android.os.Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
1483 vib.vibrate(250);
1484 }
1485
1486 Runnable mStartTracing = new Runnable() {
1487 public void run() {
1488 vibrate();
1489 SystemClock.sleep(250);
1490 Slog.d(TAG, "startTracing");
1491 android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
1492 mHandler.postDelayed(mStopTracing, 10000);
1493 }
1494 };
1495
1496 Runnable mStopTracing = new Runnable() {
1497 public void run() {
1498 android.os.Debug.stopMethodTracing();
1499 Slog.d(TAG, "stopTracing");
1500 vibrate();
1501 }
1502 };
Joe Onorato2314aab2010-04-08 16:41:23 -05001503}