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