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