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