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