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