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