PhoneStatusBar.java revision ea56251d92050e9a672d1f66d0d4621e4dd4136e
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 19 20import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT; 21import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN; 22import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN; 23import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; 24import static android.app.StatusBarManager.windowStateToString; 25import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT; 26import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; 27import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT; 28import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT; 29import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT; 30 31import android.animation.Animator; 32import android.animation.AnimatorListenerAdapter; 33import android.animation.TimeInterpolator; 34import android.app.ActivityManager; 35import android.app.ActivityManagerNative; 36import android.app.Notification; 37import android.app.PendingIntent; 38import android.app.StatusBarManager; 39import android.content.BroadcastReceiver; 40import android.content.Context; 41import android.content.Intent; 42import android.content.IntentFilter; 43import android.content.res.Configuration; 44import android.content.res.Resources; 45import android.database.ContentObserver; 46import android.graphics.Canvas; 47import android.graphics.ColorFilter; 48import android.graphics.PixelFormat; 49import android.graphics.Point; 50import android.graphics.PorterDuff; 51import android.graphics.Rect; 52import android.graphics.drawable.Drawable; 53import android.inputmethodservice.InputMethodService; 54import android.media.AudioManager; 55import android.os.Bundle; 56import android.os.Handler; 57import android.os.IBinder; 58import android.os.Message; 59import android.os.PowerManager; 60import android.os.RemoteException; 61import android.os.SystemClock; 62import android.os.UserHandle; 63import android.provider.Settings; 64import android.provider.Settings.Global; 65import android.service.notification.StatusBarNotification; 66import android.util.ArraySet; 67import android.util.DisplayMetrics; 68import android.util.EventLog; 69import android.util.Log; 70import android.view.Display; 71import android.view.Gravity; 72import android.view.KeyEvent; 73import android.view.LayoutInflater; 74import android.view.MotionEvent; 75import android.view.VelocityTracker; 76import android.view.View; 77import android.view.ViewGroup; 78import android.view.ViewGroup.LayoutParams; 79import android.view.ViewPropertyAnimator; 80import android.view.ViewTreeObserver; 81import android.view.WindowManager; 82import android.view.animation.AccelerateInterpolator; 83import android.view.animation.Animation; 84import android.view.animation.AnimationUtils; 85import android.view.animation.DecelerateInterpolator; 86import android.widget.FrameLayout; 87import android.widget.ImageView; 88import android.widget.LinearLayout; 89import android.widget.TextView; 90 91import com.android.internal.statusbar.StatusBarIcon; 92import com.android.keyguard.ViewMediatorCallback; 93import com.android.systemui.DemoMode; 94import com.android.systemui.EventLogTags; 95import com.android.systemui.R; 96import com.android.systemui.keyguard.KeyguardViewMediator; 97import com.android.systemui.qs.QSPanel; 98import com.android.systemui.qs.QSTile; 99import com.android.systemui.statusbar.BaseStatusBar; 100import com.android.systemui.statusbar.CommandQueue; 101import com.android.systemui.statusbar.DragDownHelper; 102import com.android.systemui.statusbar.ExpandableNotificationRow; 103import com.android.systemui.statusbar.GestureRecorder; 104import com.android.systemui.statusbar.InterceptedNotifications; 105import com.android.systemui.statusbar.NotificationData; 106import com.android.systemui.statusbar.NotificationData.Entry; 107import com.android.systemui.statusbar.NotificationOverflowContainer; 108import com.android.systemui.statusbar.SignalClusterView; 109import com.android.systemui.statusbar.StatusBarIconView; 110import com.android.systemui.statusbar.StatusBarState; 111import com.android.systemui.statusbar.policy.BatteryController; 112import com.android.systemui.statusbar.policy.BluetoothControllerImpl; 113import com.android.systemui.statusbar.policy.CastControllerImpl; 114import com.android.systemui.statusbar.policy.DateView; 115import com.android.systemui.statusbar.policy.HeadsUpNotificationView; 116import com.android.systemui.statusbar.policy.UserInfoController; 117import com.android.systemui.statusbar.policy.LocationControllerImpl; 118import com.android.systemui.statusbar.policy.NetworkControllerImpl; 119import com.android.systemui.statusbar.policy.RotationLockControllerImpl; 120import com.android.systemui.statusbar.policy.ZenModeControllerImpl; 121import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; 122import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener; 123import com.android.systemui.statusbar.stack.StackScrollState.ViewState; 124 125import java.io.FileDescriptor; 126import java.io.PrintWriter; 127import java.util.ArrayList; 128import java.util.Collection; 129import java.util.Collections; 130 131public class PhoneStatusBar extends BaseStatusBar implements DemoMode, 132 DragDownHelper.OnDragDownListener, ActivityStarter { 133 static final String TAG = "PhoneStatusBar"; 134 public static final boolean DEBUG = BaseStatusBar.DEBUG; 135 public static final boolean SPEW = false; 136 public static final boolean DUMPTRUCK = true; // extra dumpsys info 137 public static final boolean DEBUG_GESTURES = false; 138 139 public static final boolean DEBUG_WINDOW_STATE = false; 140 141 public static final boolean SETTINGS_DRAG_SHORTCUT = true; 142 143 // additional instrumentation for testing purposes; intended to be left on during development 144 public static final boolean CHATTY = DEBUG; 145 146 public static final String ACTION_STATUSBAR_START 147 = "com.android.internal.policy.statusbar.START"; 148 149 private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000; 150 private static final int MSG_CLOSE_PANELS = 1001; 151 private static final int MSG_OPEN_SETTINGS_PANEL = 1002; 152 // 1020-1030 reserved for BaseStatusBar 153 154 private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true; 155 156 private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; // see NotificationManagerService 157 private static final int HIDE_ICONS_BELOW_SCORE = Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER; 158 159 /** 160 * Default value of {@link android.provider.Settings.Global#LOCK_SCREEN_SHOW_NOTIFICATIONS}. 161 */ 162 private static final boolean ALLOW_NOTIFICATIONS_DEFAULT = false; 163 164 private static final int STATUS_OR_NAV_TRANSIENT = 165 View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT; 166 private static final long AUTOHIDE_TIMEOUT_MS = 3000; 167 168 /** The minimum delay in ms between reports of notification visibility. */ 169 private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500; 170 171 // fling gesture tuning parameters, scaled to display density 172 private float mSelfExpandVelocityPx; // classic value: 2000px/s 173 private float mSelfCollapseVelocityPx; // classic value: 2000px/s (will be negated to collapse "up") 174 private float mFlingExpandMinVelocityPx; // classic value: 200px/s 175 private float mFlingCollapseMinVelocityPx; // classic value: 200px/s 176 private float mCollapseMinDisplayFraction; // classic value: 0.08 (25px/min(320px,480px) on G1) 177 private float mExpandMinDisplayFraction; // classic value: 0.5 (drag open halfway to expand) 178 private float mFlingGestureMaxXVelocityPx; // classic value: 150px/s 179 180 private float mExpandAccelPx; // classic value: 2000px/s/s 181 private float mCollapseAccelPx; // classic value: 2000px/s/s (will be negated to collapse "up") 182 183 private float mFlingGestureMaxOutputVelocityPx; // how fast can it really go? (should be a little 184 // faster than mSelfCollapseVelocityPx) 185 186 PhoneStatusBarPolicy mIconPolicy; 187 188 // These are no longer handled by the policy, because we need custom strategies for them 189 BluetoothControllerImpl mBluetoothController; 190 BatteryController mBatteryController; 191 LocationControllerImpl mLocationController; 192 NetworkControllerImpl mNetworkController; 193 RotationLockControllerImpl mRotationLockController; 194 UserInfoController mUserInfoController; 195 ZenModeControllerImpl mZenModeController; 196 CastControllerImpl mCastController; 197 198 int mNaturalBarHeight = -1; 199 int mIconSize = -1; 200 int mIconHPadding = -1; 201 Display mDisplay; 202 Point mCurrentDisplaySize = new Point(); 203 204 StatusBarWindowView mStatusBarWindow; 205 PhoneStatusBarView mStatusBarView; 206 private int mStatusBarWindowState = WINDOW_STATE_SHOWING; 207 private StatusBarWindowManager mStatusBarWindowManager; 208 209 int mPixelFormat; 210 Object mQueueLock = new Object(); 211 212 // viewgroup containing the normal contents of the statusbar 213 LinearLayout mStatusBarContents; 214 215 // right-hand icons 216 LinearLayout mSystemIconArea; 217 LinearLayout mSystemIcons; 218 219 // left-hand icons 220 LinearLayout mStatusIcons; 221 // the icons themselves 222 IconMerger mNotificationIcons; 223 // [+> 224 View mMoreIcon; 225 // mode indicator icon 226 ImageView mModeIcon; 227 228 // expanded notifications 229 NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window 230 View mExpandedContents; 231 int mNotificationPanelGravity; 232 int mNotificationPanelMarginBottomPx; 233 float mNotificationPanelMinHeightFrac; 234 TextView mNotificationPanelDebugText; 235 236 // settings 237 View mFlipSettingsView; 238 private QSPanel mQSPanel; 239 240 // top bar 241 StatusBarHeaderView mHeader; 242 View mKeyguardStatusView; 243 KeyguardBottomAreaView mKeyguardBottomArea; 244 boolean mLeaveOpenOnKeyguardHide; 245 KeyguardIndicationTextView mKeyguardIndicationTextView; 246 247 // TODO: Fetch phrase from search/hotword provider. 248 String mKeyguardHotwordPhrase = ""; 249 int mKeyguardMaxNotificationCount; 250 View mDateTimeView; 251 252 // carrier/wifi label 253 private TextView mCarrierLabel; 254 private boolean mCarrierLabelVisible = false; 255 private int mCarrierLabelHeight; 256 private TextView mEmergencyCallLabel; 257 private int mStatusBarHeaderHeight; 258 259 private boolean mShowCarrierInPanel = false; 260 261 // position 262 int[] mPositionTmp = new int[2]; 263 boolean mExpandedVisible; 264 265 // the date view 266 DateView mDateView; 267 268 // for heads up notifications 269 private HeadsUpNotificationView mHeadsUpNotificationView; 270 private int mHeadsUpNotificationDecay; 271 272 // on-screen navigation buttons 273 private NavigationBarView mNavigationBarView = null; 274 private int mNavigationBarWindowState = WINDOW_STATE_SHOWING; 275 276 // the tracker view 277 int mTrackingPosition; // the position of the top of the tracking view. 278 279 // ticker 280 private Ticker mTicker; 281 private View mTickerView; 282 private boolean mTicking; 283 284 // Tracking finger for opening/closing. 285 int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore 286 boolean mTracking; 287 VelocityTracker mVelocityTracker; 288 289 int[] mAbsPos = new int[2]; 290 Runnable mPostCollapseCleanup = null; 291 292 // for disabling the status bar 293 int mDisabled = 0; 294 295 // tracking calls to View.setSystemUiVisibility() 296 int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE; 297 298 DisplayMetrics mDisplayMetrics = new DisplayMetrics(); 299 300 // XXX: gesture research 301 private final GestureRecorder mGestureRec = DEBUG_GESTURES 302 ? new GestureRecorder("/sdcard/statusbar_gestures.dat") 303 : null; 304 305 private int mNavigationIconHints = 0; 306 private final Animator.AnimatorListener mMakeIconsInvisible = new AnimatorListenerAdapter() { 307 @Override 308 public void onAnimationEnd(Animator animation) { 309 // double-check to avoid races 310 if (mStatusBarContents.getAlpha() == 0) { 311 if (DEBUG) Log.d(TAG, "makeIconsInvisible"); 312 mStatusBarContents.setVisibility(View.INVISIBLE); 313 } 314 } 315 }; 316 317 // ensure quick settings is disabled until the current user makes it through the setup wizard 318 private boolean mUserSetup = false; 319 private ContentObserver mUserSetupObserver = new ContentObserver(new Handler()) { 320 @Override 321 public void onChange(boolean selfChange) { 322 final boolean userSetup = 0 != Settings.Secure.getIntForUser( 323 mContext.getContentResolver(), 324 Settings.Secure.USER_SETUP_COMPLETE, 325 0 /*default */, 326 mCurrentUserId); 327 if (MULTIUSER_DEBUG) Log.d(TAG, String.format("User setup changed: " + 328 "selfChange=%s userSetup=%s mUserSetup=%s", 329 selfChange, userSetup, mUserSetup)); 330 331 if (userSetup != mUserSetup) { 332 mUserSetup = userSetup; 333 if (mNotificationPanel != null) { 334 mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned() && userSetup); 335 } 336 if (!mUserSetup && mStatusBarView != null) 337 animateCollapseQuickSettings(); 338 } 339 } 340 }; 341 342 final private ContentObserver mHeadsUpObserver = new ContentObserver(mHandler) { 343 @Override 344 public void onChange(boolean selfChange) { 345 boolean wasUsing = mUseHeadsUp; 346 mUseHeadsUp = ENABLE_HEADS_UP && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt( 347 mContext.getContentResolver(), Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, 348 Settings.Global.HEADS_UP_OFF); 349 mHeadsUpTicker = mUseHeadsUp && 0 != Settings.Global.getInt( 350 mContext.getContentResolver(), SETTING_HEADS_UP_TICKER, 0); 351 Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled")); 352 if (wasUsing != mUseHeadsUp) { 353 if (!mUseHeadsUp) { 354 Log.d(TAG, "dismissing any existing heads up notification on disable event"); 355 setHeadsUpVisibility(false); 356 mHeadsUpNotificationView.setNotification(null); 357 removeHeadsUpView(); 358 } else { 359 addHeadsUpView(); 360 } 361 } 362 } 363 }; 364 365 private int mInteractingWindows; 366 private boolean mAutohideSuspended; 367 private int mStatusBarMode; 368 private int mNavigationBarMode; 369 private Boolean mScreenOn; 370 371 private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; 372 private ViewMediatorCallback mKeyguardViewMediatorCallback; 373 374 private final Runnable mAutohide = new Runnable() { 375 @Override 376 public void run() { 377 int requested = mSystemUiVisibility & ~STATUS_OR_NAV_TRANSIENT; 378 if (mSystemUiVisibility != requested) { 379 notifyUiVisibilityChanged(requested); 380 } 381 }}; 382 383 private Runnable mOnFlipRunnable; 384 private InterceptedNotifications mIntercepted; 385 private VelocityTracker mSettingsTracker; 386 private float mSettingsDownY; 387 private boolean mSettingsStarted; 388 private boolean mSettingsCancelled; 389 private boolean mSettingsClosing; 390 private boolean mVisible; 391 392 private final OnChildLocationsChangedListener mOnChildLocationsChangedListener = 393 new OnChildLocationsChangedListener() { 394 @Override 395 public void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout) { 396 userActivity(); 397 } 398 }; 399 400 public void setOnFlipRunnable(Runnable onFlipRunnable) { 401 mOnFlipRunnable = onFlipRunnable; 402 } 403 404 /** Keys of notifications currently visible to the user. */ 405 private final ArraySet<String> mCurrentlyVisibleNotifications = new ArraySet<String>(); 406 private long mLastVisibilityReportUptimeMs; 407 408 private static final int VISIBLE_LOCATIONS = ViewState.LOCATION_FIRST_CARD 409 | ViewState.LOCATION_TOP_STACK_PEEKING 410 | ViewState.LOCATION_MAIN_AREA 411 | ViewState.LOCATION_BOTTOM_STACK_PEEKING; 412 413 private final OnChildLocationsChangedListener mNotificationLocationsChangedListener = 414 new OnChildLocationsChangedListener() { 415 @Override 416 public void onChildLocationsChanged( 417 NotificationStackScrollLayout stackScrollLayout) { 418 if (mHandler.hasCallbacks(mVisibilityReporter)) { 419 // Visibilities will be reported when the existing 420 // callback is executed. 421 return; 422 } 423 // Calculate when we're allowed to run the visibility 424 // reporter. Note that this timestamp might already have 425 // passed. That's OK, the callback will just be executed 426 // ASAP. 427 long nextReportUptimeMs = 428 mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS; 429 mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs); 430 } 431 }; 432 433 // Tracks notifications currently visible in mNotificationStackScroller and 434 // emits visibility events via NoMan on changes. 435 private final Runnable mVisibilityReporter = new Runnable() { 436 private final ArrayList<String> mTmpNewlyVisibleNotifications = new ArrayList<String>(); 437 private final ArrayList<String> mTmpCurrentlyVisibleNotifications = new ArrayList<String>(); 438 439 @Override 440 public void run() { 441 mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis(); 442 443 // 1. Loop over mNotificationData entries: 444 // A. Keep list of visible notifications. 445 // B. Keep list of previously hidden, now visible notifications. 446 // 2. Compute no-longer visible notifications by removing currently 447 // visible notifications from the set of previously visible 448 // notifications. 449 // 3. Report newly visible and no-longer visible notifications. 450 // 4. Keep currently visible notifications for next report. 451 int N = mNotificationData.size(); 452 for (int i = 0; i < N; i++) { 453 Entry entry = mNotificationData.get(i); 454 String key = entry.notification.getKey(); 455 boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(key); 456 boolean currentlyVisible = 457 (mStackScroller.getChildLocation(entry.row) & VISIBLE_LOCATIONS) != 0; 458 if (currentlyVisible) { 459 // Build new set of visible notifications. 460 mTmpCurrentlyVisibleNotifications.add(key); 461 } 462 if (!previouslyVisible && currentlyVisible) { 463 mTmpNewlyVisibleNotifications.add(key); 464 } 465 } 466 ArraySet<String> noLongerVisibleNotifications = mCurrentlyVisibleNotifications; 467 noLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications); 468 469 logNotificationVisibilityChanges( 470 mTmpNewlyVisibleNotifications, noLongerVisibleNotifications); 471 472 mCurrentlyVisibleNotifications.clear(); 473 mCurrentlyVisibleNotifications.addAll(mTmpCurrentlyVisibleNotifications); 474 475 mTmpNewlyVisibleNotifications.clear(); 476 mTmpCurrentlyVisibleNotifications.clear(); 477 } 478 }; 479 480 private final View.OnClickListener mOverflowClickListener = new View.OnClickListener() { 481 @Override 482 public void onClick(View v) { 483 goToLockedShade(null); 484 } 485 }; 486 487 @Override 488 public void setZenMode(int mode) { 489 super.setZenMode(mode); 490 if (mModeIcon == null) return; 491 if (!isDeviceProvisioned()) return; 492 final boolean zen = mode != Settings.Global.ZEN_MODE_OFF; 493 mModeIcon.setVisibility(zen ? View.VISIBLE : View.GONE); 494 if (!zen) { 495 mIntercepted.releaseIntercepted(); 496 } 497 } 498 499 @Override 500 public void start() { 501 mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE)) 502 .getDefaultDisplay(); 503 updateDisplaySize(); 504 mIntercepted = new InterceptedNotifications(mContext, this); 505 super.start(); // calls createAndAddWindows() 506 507 addNavigationBar(); 508 509 // Lastly, call to the icon policy to install/update all the icons. 510 mIconPolicy = new PhoneStatusBarPolicy(mContext); 511 mSettingsObserver.onChange(false); // set up 512 513 mHeadsUpObserver.onChange(true); // set up 514 if (ENABLE_HEADS_UP) { 515 mContext.getContentResolver().registerContentObserver( 516 Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED), true, 517 mHeadsUpObserver); 518 mContext.getContentResolver().registerContentObserver( 519 Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true, 520 mHeadsUpObserver); 521 } 522 startKeyguard(); 523 } 524 525 // ================================================================================ 526 // Constructing the view 527 // ================================================================================ 528 protected PhoneStatusBarView makeStatusBarView() { 529 final Context context = mContext; 530 531 Resources res = context.getResources(); 532 533 updateDisplaySize(); // populates mDisplayMetrics 534 loadDimens(); 535 536 mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size); 537 538 mStatusBarWindow = (StatusBarWindowView) View.inflate(context, 539 R.layout.super_status_bar, null); 540 mStatusBarWindow.mService = this; 541 mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() { 542 @Override 543 public boolean onTouch(View v, MotionEvent event) { 544 checkUserAutohide(v, event); 545 if (event.getAction() == MotionEvent.ACTION_DOWN) { 546 if (mExpandedVisible) { 547 animateCollapsePanels(); 548 } 549 } 550 return mStatusBarWindow.onTouchEvent(event); 551 }}); 552 553 mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar); 554 mStatusBarView.setBar(this); 555 556 PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder); 557 mStatusBarView.setPanelHolder(holder); 558 559 mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById( 560 R.id.notification_panel); 561 mNotificationPanel.setStatusBar(this); 562 563 // make the header non-responsive to clicks 564 mNotificationPanel.findViewById(R.id.header).setOnTouchListener( 565 new View.OnTouchListener() { 566 @Override 567 public boolean onTouch(View v, MotionEvent event) { 568 return true; // e eats everything 569 } 570 }); 571 572 if (!ActivityManager.isHighEndGfx()) { 573 mStatusBarWindow.setBackground(null); 574 mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor( 575 R.color.notification_panel_solid_background))); 576 } 577 if (ENABLE_HEADS_UP) { 578 mHeadsUpNotificationView = 579 (HeadsUpNotificationView) View.inflate(context, R.layout.heads_up, null); 580 mHeadsUpNotificationView.setVisibility(View.GONE); 581 mHeadsUpNotificationView.setBar(this); 582 } 583 if (MULTIUSER_DEBUG) { 584 mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById( 585 R.id.header_debug_info); 586 mNotificationPanelDebugText.setVisibility(View.VISIBLE); 587 } 588 589 updateShowSearchHoldoff(); 590 591 try { 592 boolean showNav = mWindowManagerService.hasNavigationBar(); 593 if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav); 594 if (showNav) { 595 mNavigationBarView = 596 (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null); 597 598 mNavigationBarView.setDisabledFlags(mDisabled); 599 mNavigationBarView.setBar(this); 600 mNavigationBarView.setOnTouchListener(new View.OnTouchListener() { 601 @Override 602 public boolean onTouch(View v, MotionEvent event) { 603 checkUserAutohide(v, event); 604 return false; 605 }}); 606 } 607 } catch (RemoteException ex) { 608 // no window manager? good luck with that 609 } 610 611 // figure out which pixel-format to use for the status bar. 612 mPixelFormat = PixelFormat.OPAQUE; 613 614 mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area); 615 mSystemIcons = (LinearLayout) mStatusBarView.findViewById(R.id.system_icons); 616 mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons); 617 mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons); 618 mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon); 619 mNotificationIcons.setOverflowIndicator(mMoreIcon); 620 mModeIcon = (ImageView)mStatusBarView.findViewById(R.id.modeIcon); 621 mModeIcon.setImageResource(R.drawable.stat_sys_zen_limited); 622 mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents); 623 mTickerView = mStatusBarView.findViewById(R.id.ticker); 624 625 mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById( 626 R.id.notification_stack_scroller); 627 mStackScroller.setLongPressListener(getNotificationLongClicker()); 628 mStackScroller.setChildLocationsChangedListener(mOnChildLocationsChangedListener); 629 630 mKeyguardIconOverflowContainer = 631 (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate( 632 R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false); 633 mKeyguardIconOverflowContainer.setOnActivatedListener(this); 634 mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener); 635 mStackScroller.addView(mKeyguardIconOverflowContainer); 636 637 mExpandedContents = mStackScroller; 638 639 mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header); 640 mHeader.setActivityStarter(this); 641 mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view); 642 mKeyguardBottomArea = 643 (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area); 644 mKeyguardBottomArea.setActivityStarter(this); 645 mKeyguardIndicationTextView = (KeyguardIndicationTextView) mStatusBarWindow.findViewById( 646 R.id.keyguard_indication_text); 647 mDateView = (DateView)mStatusBarWindow.findViewById(R.id.date); 648 649 mDateTimeView = mHeader.findViewById(R.id.datetime); 650 if (mDateTimeView != null) { 651 mDateTimeView.setOnClickListener(mClockClickListener); 652 mDateTimeView.setEnabled(true); 653 } 654 655 mNotificationPanel.setSystemUiVisibility( 656 View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS | 657 View.STATUS_BAR_DISABLE_CLOCK); 658 659 mTicker = new MyTicker(context, mStatusBarView); 660 661 TickerView tickerView = (TickerView)mStatusBarView.findViewById(R.id.tickerText); 662 tickerView.mTicker = mTicker; 663 664 mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); 665 666 // set the inital view visibility 667 setAreThereNotifications(); 668 669 // Other icons 670 mLocationController = new LocationControllerImpl(mContext); // will post a notification 671 mBatteryController = new BatteryController(mContext); 672 mNetworkController = new NetworkControllerImpl(mContext); 673 mBluetoothController = new BluetoothControllerImpl(mContext); 674 if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)) { 675 mRotationLockController = new RotationLockControllerImpl(mContext); 676 } 677 mUserInfoController = new UserInfoController(mContext); 678 mZenModeController = new ZenModeControllerImpl(mContext, mHandler); 679 mCastController = new CastControllerImpl(mContext); 680 final SignalClusterView signalCluster = 681 (SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster); 682 683 684 mNetworkController.addSignalCluster(signalCluster); 685 signalCluster.setNetworkController(mNetworkController); 686 687 final boolean isAPhone = mNetworkController.hasVoiceCallingFeature(); 688 if (isAPhone) { 689 mEmergencyCallLabel = 690 (TextView) mStatusBarWindow.findViewById(R.id.emergency_calls_only); 691 // TODO: Uncomment when correctly positioned 692// if (mEmergencyCallLabel != null) { 693// mNetworkController.addEmergencyLabelView(mEmergencyCallLabel); 694// mEmergencyCallLabel.setOnClickListener(new View.OnClickListener() { 695// public void onClick(View v) { }}); 696// mEmergencyCallLabel.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { 697// @Override 698// public void onLayoutChange(View v, int left, int top, int right, int bottom, 699// int oldLeft, int oldTop, int oldRight, int oldBottom) { 700// updateCarrierLabelVisibility(false); 701// }}); 702// } 703 } 704 705 mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label); 706 mShowCarrierInPanel = (mCarrierLabel != null); 707 if (DEBUG) Log.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" + mShowCarrierInPanel); 708 if (mShowCarrierInPanel) { 709 mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE); 710 711 // for mobile devices, we always show mobile connection info here (SPN/PLMN) 712 // for other devices, we show whatever network is connected 713 if (mNetworkController.hasMobileDataFeature()) { 714 mNetworkController.addMobileLabelView(mCarrierLabel); 715 } else { 716 mNetworkController.addCombinedLabelView(mCarrierLabel); 717 } 718 719 // set up the dynamic hide/show of the label 720 // TODO: uncomment, handle this for the Stack scroller aswell 721// ((NotificationRowLayout) mStackScroller) 722// .setOnSizeChangedListener(new OnSizeChangedListener() { 723// @Override 724// public void onSizeChanged(View view, int w, int h, int oldw, int oldh) { 725// updateCarrierLabelVisibility(false); 726 } 727 728 // Set up the quick settings tile panel 729 mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel); 730 if (mQSPanel != null) { 731 final QSTileHost qsh = new QSTileHost(mContext, this, 732 mBluetoothController, mLocationController, mRotationLockController, 733 mNetworkController, mZenModeController, null /*tethering*/, 734 mCastController); 735 for (QSTile<?> tile : qsh.getTiles()) { 736 mQSPanel.addTile(tile); 737 } 738 mHeader.setQSPanel(mQSPanel); 739 } 740 741 // User info. Trigger first load. 742 mHeader.setUserInfoController(mUserInfoController); 743 mUserInfoController.reloadUserInfo(); 744 745 PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 746 mBroadcastReceiver.onReceive(mContext, 747 new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF)); 748 749 // receive broadcasts 750 IntentFilter filter = new IntentFilter(); 751 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 752 filter.addAction(Intent.ACTION_SCREEN_OFF); 753 filter.addAction(Intent.ACTION_SCREEN_ON); 754 filter.addAction(ACTION_DEMO); 755 context.registerReceiver(mBroadcastReceiver, filter); 756 757 // listen for USER_SETUP_COMPLETE setting (per-user) 758 resetUserSetupObserver(); 759 760 return mStatusBarView; 761 } 762 763 private void startKeyguard() { 764 KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class); 765 mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this, 766 mStatusBarWindow, mStatusBarWindowManager); 767 mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback(); 768 } 769 770 @Override 771 protected void onShowSearchPanel() { 772 if (mNavigationBarView != null) { 773 mNavigationBarView.getBarTransitions().setContentVisible(false); 774 } 775 } 776 777 @Override 778 protected void onHideSearchPanel() { 779 if (mNavigationBarView != null) { 780 mNavigationBarView.getBarTransitions().setContentVisible(true); 781 } 782 } 783 784 @Override 785 protected View getStatusBarView() { 786 return mStatusBarView; 787 } 788 789 @Override 790 protected WindowManager.LayoutParams getSearchLayoutParams(LayoutParams layoutParams) { 791 boolean opaque = false; 792 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 793 LayoutParams.MATCH_PARENT, 794 LayoutParams.MATCH_PARENT, 795 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, 796 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 797 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM 798 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, 799 (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT)); 800 if (ActivityManager.isHighEndGfx()) { 801 lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 802 } 803 lp.gravity = Gravity.BOTTOM | Gravity.START; 804 lp.setTitle("SearchPanel"); 805 // TODO: Define custom animation for Search panel 806 lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications; 807 lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED 808 | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; 809 return lp; 810 } 811 812 @Override 813 protected void updateSearchPanel() { 814 super.updateSearchPanel(); 815 if (mNavigationBarView != null) { 816 mNavigationBarView.setDelegateView(mSearchPanelView); 817 } 818 } 819 820 @Override 821 public void showSearchPanel() { 822 super.showSearchPanel(); 823 mHandler.removeCallbacks(mShowSearchPanel); 824 825 // we want to freeze the sysui state wherever it is 826 mSearchPanelView.setSystemUiVisibility(mSystemUiVisibility); 827 828 if (mNavigationBarView != null) { 829 WindowManager.LayoutParams lp = 830 (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams(); 831 lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; 832 mWindowManager.updateViewLayout(mNavigationBarView, lp); 833 } 834 } 835 836 @Override 837 public void hideSearchPanel() { 838 super.hideSearchPanel(); 839 if (mNavigationBarView != null) { 840 WindowManager.LayoutParams lp = 841 (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams(); 842 lp.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; 843 mWindowManager.updateViewLayout(mNavigationBarView, lp); 844 } 845 } 846 847 public int getStatusBarHeight() { 848 if (mNaturalBarHeight < 0) { 849 final Resources res = mContext.getResources(); 850 mNaturalBarHeight = 851 res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); 852 } 853 return mNaturalBarHeight; 854 } 855 856 private View.OnClickListener mRecentsClickListener = new View.OnClickListener() { 857 public void onClick(View v) { 858 awakenDreams(); 859 toggleRecentApps(); 860 } 861 }; 862 863 private int mShowSearchHoldoff = 0; 864 private Runnable mShowSearchPanel = new Runnable() { 865 public void run() { 866 showSearchPanel(); 867 awakenDreams(); 868 } 869 }; 870 871 View.OnTouchListener mHomeSearchActionListener = new View.OnTouchListener() { 872 public boolean onTouch(View v, MotionEvent event) { 873 switch(event.getAction()) { 874 case MotionEvent.ACTION_DOWN: 875 if (!shouldDisableNavbarGestures()) { 876 mHandler.removeCallbacks(mShowSearchPanel); 877 mHandler.postDelayed(mShowSearchPanel, mShowSearchHoldoff); 878 } 879 break; 880 881 case MotionEvent.ACTION_UP: 882 case MotionEvent.ACTION_CANCEL: 883 mHandler.removeCallbacks(mShowSearchPanel); 884 awakenDreams(); 885 break; 886 } 887 return false; 888 } 889 }; 890 891 private void awakenDreams() { 892 if (mDreamManager != null) { 893 try { 894 mDreamManager.awaken(); 895 } catch (RemoteException e) { 896 // fine, stay asleep then 897 } 898 } 899 } 900 901 private void prepareNavigationBarView() { 902 mNavigationBarView.reorient(); 903 904 mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener); 905 mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener); 906 mNavigationBarView.getHomeButton().setOnTouchListener(mHomeSearchActionListener); 907 mNavigationBarView.getSearchLight().setOnTouchListener(mHomeSearchActionListener); 908 updateSearchPanel(); 909 } 910 911 // For small-screen devices (read: phones) that lack hardware navigation buttons 912 private void addNavigationBar() { 913 if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView); 914 if (mNavigationBarView == null) return; 915 916 prepareNavigationBarView(); 917 918 mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams()); 919 } 920 921 private void repositionNavigationBar() { 922 if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return; 923 924 prepareNavigationBarView(); 925 926 mWindowManager.updateViewLayout(mNavigationBarView, getNavigationBarLayoutParams()); 927 } 928 929 private void notifyNavigationBarScreenOn(boolean screenOn) { 930 if (mNavigationBarView == null) return; 931 mNavigationBarView.notifyScreenOn(screenOn); 932 } 933 934 private WindowManager.LayoutParams getNavigationBarLayoutParams() { 935 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 936 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 937 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, 938 0 939 | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING 940 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 941 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 942 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH 943 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, 944 PixelFormat.TRANSLUCENT); 945 // this will allow the navbar to run in an overlay on devices that support this 946 if (ActivityManager.isHighEndGfx()) { 947 lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 948 } 949 950 lp.setTitle("NavigationBar"); 951 lp.windowAnimations = 0; 952 return lp; 953 } 954 955 private void addHeadsUpView() { 956 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 957 LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, 958 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar! 959 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 960 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 961 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 962 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 963 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM 964 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, 965 PixelFormat.TRANSLUCENT); 966 lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 967 lp.gravity = Gravity.TOP; 968 lp.setTitle("Heads Up"); 969 lp.packageName = mContext.getPackageName(); 970 lp.windowAnimations = R.style.Animation_StatusBar_HeadsUp; 971 972 mWindowManager.addView(mHeadsUpNotificationView, lp); 973 } 974 975 private void removeHeadsUpView() { 976 mWindowManager.removeView(mHeadsUpNotificationView); 977 } 978 979 public void refreshAllStatusBarIcons() { 980 refreshAllIconsForLayout(mStatusIcons); 981 refreshAllIconsForLayout(mNotificationIcons); 982 } 983 984 private void refreshAllIconsForLayout(LinearLayout ll) { 985 final int count = ll.getChildCount(); 986 for (int n = 0; n < count; n++) { 987 View child = ll.getChildAt(n); 988 if (child instanceof StatusBarIconView) { 989 ((StatusBarIconView) child).updateDrawable(); 990 } 991 } 992 } 993 994 public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) { 995 if (SPEW) Log.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex 996 + " icon=" + icon); 997 StatusBarIconView view = new StatusBarIconView(mContext, slot, null); 998 view.set(icon); 999 mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize)); 1000 } 1001 1002 public void updateIcon(String slot, int index, int viewIndex, 1003 StatusBarIcon old, StatusBarIcon icon) { 1004 if (SPEW) Log.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex 1005 + " old=" + old + " icon=" + icon); 1006 StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex); 1007 view.set(icon); 1008 } 1009 1010 public void removeIcon(String slot, int index, int viewIndex) { 1011 if (SPEW) Log.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex); 1012 mStatusIcons.removeViewAt(viewIndex); 1013 } 1014 1015 public UserHandle getCurrentUserHandle() { 1016 return new UserHandle(mCurrentUserId); 1017 } 1018 1019 public void addNotification(IBinder key, StatusBarNotification notification) { 1020 if (DEBUG) Log.d(TAG, "addNotification score=" + notification.getScore()); 1021 Entry shadeEntry = createNotificationViews(key, notification); 1022 if (shadeEntry == null) { 1023 return; 1024 } 1025 if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(key, notification)) { 1026 return; 1027 } 1028 if (mUseHeadsUp && shouldInterrupt(notification)) { 1029 if (DEBUG) Log.d(TAG, "launching notification in heads up mode"); 1030 Entry interruptionCandidate = new Entry(key, notification, null); 1031 ViewGroup holder = mHeadsUpNotificationView.getHolder(); 1032 if (inflateViewsForHeadsUp(interruptionCandidate, holder)) { 1033 mInterruptingNotificationTime = System.currentTimeMillis(); 1034 mInterruptingNotificationEntry = interruptionCandidate; 1035 shadeEntry.setInterruption(); 1036 1037 // 1. Populate mHeadsUpNotificationView 1038 mHeadsUpNotificationView.setNotification(mInterruptingNotificationEntry); 1039 1040 // 2. Animate mHeadsUpNotificationView in 1041 mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP); 1042 1043 // 3. Set alarm to age the notification off 1044 resetHeadsUpDecayTimer(); 1045 } 1046 } else if (notification.getNotification().fullScreenIntent != null) { 1047 // Stop screensaver if the notification has a full-screen intent. 1048 // (like an incoming phone call) 1049 awakenDreams(); 1050 1051 // not immersive & a full-screen alert should be shown 1052 if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent"); 1053 try { 1054 notification.getNotification().fullScreenIntent.send(); 1055 } catch (PendingIntent.CanceledException e) { 1056 } 1057 } else { 1058 // usual case: status bar visible & not immersive 1059 1060 // show the ticker if there isn't already a heads up 1061 if (mInterruptingNotificationEntry == null) { 1062 tick(null, notification, true); 1063 } 1064 } 1065 addNotificationViews(shadeEntry); 1066 // Recalculate the position of the sliding windows and the titles. 1067 setAreThereNotifications(); 1068 updateExpandedViewPos(EXPANDED_LEAVE_ALONE); 1069 } 1070 1071 @Override 1072 public void resetHeadsUpDecayTimer() { 1073 mHandler.removeMessages(MSG_HIDE_HEADS_UP); 1074 if (mUseHeadsUp && mHeadsUpNotificationDecay > 0 1075 && mHeadsUpNotificationView.isClearable()) { 1076 mHandler.sendEmptyMessageDelayed(MSG_HIDE_HEADS_UP, mHeadsUpNotificationDecay); 1077 } 1078 } 1079 1080 @Override 1081 public void updateNotification(IBinder key, StatusBarNotification notification) { 1082 super.updateNotification(key, notification); 1083 mIntercepted.update(key, notification); 1084 } 1085 1086 public void removeNotification(IBinder key) { 1087 StatusBarNotification old = removeNotificationViews(key); 1088 if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old); 1089 1090 if (old != null) { 1091 // Cancel the ticker if it's still running 1092 mTicker.removeEntry(old); 1093 1094 // Recalculate the position of the sliding windows and the titles. 1095 updateExpandedViewPos(EXPANDED_LEAVE_ALONE); 1096 1097 if (ENABLE_HEADS_UP && mInterruptingNotificationEntry != null 1098 && old == mInterruptingNotificationEntry.notification) { 1099 mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP); 1100 } 1101 1102 if (CLOSE_PANEL_WHEN_EMPTIED && mNotificationData.size() == 0 1103 && !mNotificationPanel.isTracking() && mState != StatusBarState.KEYGUARD) { 1104 animateCollapsePanels(); 1105 } 1106 } 1107 mIntercepted.remove(key); 1108 setAreThereNotifications(); 1109 } 1110 1111 @Override 1112 protected void refreshLayout(int layoutDirection) { 1113 if (mNavigationBarView != null) { 1114 mNavigationBarView.setLayoutDirection(layoutDirection); 1115 } 1116 refreshAllStatusBarIcons(); 1117 } 1118 1119 private void updateShowSearchHoldoff() { 1120 mShowSearchHoldoff = mContext.getResources().getInteger( 1121 R.integer.config_show_search_delay); 1122 } 1123 1124 private void loadNotificationShade() { 1125 if (mStackScroller == null) return; 1126 1127 int N = mNotificationData.size(); 1128 1129 ArrayList<View> toShow = new ArrayList<View>(); 1130 1131 final boolean provisioned = isDeviceProvisioned(); 1132 // If the device hasn't been through Setup, we only show system notifications 1133 for (int i=0; i<N; i++) { 1134 Entry ent = mNotificationData.get(N-i-1); 1135 if (!(provisioned || showNotificationEvenIfUnprovisioned(ent.notification))) continue; 1136 1137 // TODO How do we want to badge notifcations from profiles. 1138 if (!notificationIsForCurrentProfiles(ent.notification)) continue; 1139 1140 final int vis = ent.notification.getNotification().visibility; 1141 if (vis != Notification.VISIBILITY_SECRET) { 1142 // when isLockscreenPublicMode() we show the public form of VISIBILITY_PRIVATE notifications 1143 ent.row.setShowingPublic(isLockscreenPublicMode() 1144 && vis == Notification.VISIBILITY_PRIVATE 1145 && !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId())); 1146 toShow.add(ent.row); 1147 } 1148 } 1149 1150 ArrayList<View> toRemove = new ArrayList<View>(); 1151 for (int i=0; i< mStackScroller.getChildCount(); i++) { 1152 View child = mStackScroller.getChildAt(i); 1153 if (!toShow.contains(child) && child != mKeyguardIconOverflowContainer) { 1154 toRemove.add(child); 1155 } 1156 } 1157 1158 for (View remove : toRemove) { 1159 mStackScroller.removeView(remove); 1160 } 1161 1162 for (int i=0; i<toShow.size(); i++) { 1163 View v = toShow.get(i); 1164 if (v.getParent() == null) { 1165 mStackScroller.addView(v, i); 1166 } 1167 } 1168 1169 mNotificationPanel.setQsExpansionEnabled(provisioned && mUserSetup); 1170 } 1171 1172 @Override 1173 protected void updateNotificationIcons() { 1174 if (mNotificationIcons == null) return; 1175 1176 loadNotificationShade(); 1177 1178 final LinearLayout.LayoutParams params 1179 = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight); 1180 1181 int N = mNotificationData.size(); 1182 1183 if (DEBUG) { 1184 Log.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" + mNotificationIcons); 1185 } 1186 1187 ArrayList<View> toShow = new ArrayList<View>(); 1188 1189 final boolean provisioned = isDeviceProvisioned(); 1190 // If the device hasn't been through Setup, we only show system notifications 1191 for (int i=0; i<N; i++) { 1192 Entry ent = mNotificationData.get(N-i-1); 1193 if (!((provisioned && ent.notification.getScore() >= HIDE_ICONS_BELOW_SCORE) 1194 || showNotificationEvenIfUnprovisioned(ent.notification))) continue; 1195 if (!notificationIsForCurrentProfiles(ent.notification)) continue; 1196 if (isLockscreenPublicMode() 1197 && ent.notification.getNotification().visibility 1198 == Notification.VISIBILITY_SECRET 1199 && !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId())) { 1200 // in "public" mode (atop a secure keyguard), secret notifs are totally hidden 1201 continue; 1202 } 1203 if (mIntercepted.isSyntheticEntry(ent)) { 1204 continue; 1205 } 1206 toShow.add(ent.icon); 1207 } 1208 1209 ArrayList<View> toRemove = new ArrayList<View>(); 1210 for (int i=0; i<mNotificationIcons.getChildCount(); i++) { 1211 View child = mNotificationIcons.getChildAt(i); 1212 if (!toShow.contains(child)) { 1213 toRemove.add(child); 1214 } 1215 } 1216 1217 for (View remove : toRemove) { 1218 mNotificationIcons.removeView(remove); 1219 } 1220 1221 for (int i=0; i<toShow.size(); i++) { 1222 View v = toShow.get(i); 1223 if (v.getParent() == null) { 1224 mNotificationIcons.addView(v, i, params); 1225 } 1226 } 1227 } 1228 1229 protected void updateCarrierLabelVisibility(boolean force) { 1230 // TODO: Handle this for the notification stack scroller as well 1231 if (!mShowCarrierInPanel) return; 1232 // The idea here is to only show the carrier label when there is enough room to see it, 1233 // i.e. when there aren't enough notifications to fill the panel. 1234 if (SPEW) { 1235 Log.d(TAG, String.format("stackScrollerh=%d scrollh=%d carrierh=%d", 1236 mStackScroller.getHeight(), mStackScroller.getHeight(), 1237 mCarrierLabelHeight)); 1238 } 1239 1240 final boolean emergencyCallsShownElsewhere = mEmergencyCallLabel != null; 1241 final boolean makeVisible = 1242 !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly()) 1243 && mStackScroller.getHeight() < (mNotificationPanel.getHeight() 1244 - mCarrierLabelHeight - mStatusBarHeaderHeight) 1245 && mStackScroller.getVisibility() == View.VISIBLE 1246 && mState != StatusBarState.KEYGUARD; 1247 1248 if (force || mCarrierLabelVisible != makeVisible) { 1249 mCarrierLabelVisible = makeVisible; 1250 if (DEBUG) { 1251 Log.d(TAG, "making carrier label " + (makeVisible?"visible":"invisible")); 1252 } 1253 mCarrierLabel.animate().cancel(); 1254 if (makeVisible) { 1255 mCarrierLabel.setVisibility(View.VISIBLE); 1256 } 1257 mCarrierLabel.animate() 1258 .alpha(makeVisible ? 1f : 0f) 1259 //.setStartDelay(makeVisible ? 500 : 0) 1260 //.setDuration(makeVisible ? 750 : 100) 1261 .setDuration(150) 1262 .setListener(makeVisible ? null : new AnimatorListenerAdapter() { 1263 @Override 1264 public void onAnimationEnd(Animator animation) { 1265 if (!mCarrierLabelVisible) { // race 1266 mCarrierLabel.setVisibility(View.INVISIBLE); 1267 mCarrierLabel.setAlpha(0f); 1268 } 1269 } 1270 }) 1271 .start(); 1272 } 1273 } 1274 1275 @Override 1276 protected void setAreThereNotifications() { 1277 final boolean any = mNotificationData.size() > 0; 1278 1279 final boolean clearable = any && mNotificationData.hasClearableItems(); 1280 1281 if (SPEW) { 1282 Log.d(TAG, "setAreThereNotifications: N=" + mNotificationData.size() 1283 + " any=" + any + " clearable=" + clearable); 1284 } 1285 1286 final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out); 1287 final boolean showDot = (any&&!areLightsOn()); 1288 if (showDot != (nlo.getAlpha() == 1.0f)) { 1289 if (showDot) { 1290 nlo.setAlpha(0f); 1291 nlo.setVisibility(View.VISIBLE); 1292 } 1293 nlo.animate() 1294 .alpha(showDot?1:0) 1295 .setDuration(showDot?750:250) 1296 .setInterpolator(new AccelerateInterpolator(2.0f)) 1297 .setListener(showDot ? null : new AnimatorListenerAdapter() { 1298 @Override 1299 public void onAnimationEnd(Animator _a) { 1300 nlo.setVisibility(View.GONE); 1301 } 1302 }) 1303 .start(); 1304 } 1305 1306 updateCarrierLabelVisibility(false); 1307 } 1308 1309 public void showClock(boolean show) { 1310 if (mStatusBarView == null) return; 1311 View clock = mStatusBarView.findViewById(R.id.clock); 1312 if (clock != null) { 1313 clock.setVisibility(show ? View.VISIBLE : View.GONE); 1314 } 1315 } 1316 1317 /** 1318 * State is one or more of the DISABLE constants from StatusBarManager. 1319 */ 1320 public void disable(int state) { 1321 final int old = mDisabled; 1322 final int diff = state ^ old; 1323 mDisabled = state; 1324 1325 if (DEBUG) { 1326 Log.d(TAG, String.format("disable: 0x%08x -> 0x%08x (diff: 0x%08x)", 1327 old, state, diff)); 1328 } 1329 1330 StringBuilder flagdbg = new StringBuilder(); 1331 flagdbg.append("disable: < "); 1332 flagdbg.append(((state & StatusBarManager.DISABLE_EXPAND) != 0) ? "EXPAND" : "expand"); 1333 flagdbg.append(((diff & StatusBarManager.DISABLE_EXPAND) != 0) ? "* " : " "); 1334 flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "ICONS" : "icons"); 1335 flagdbg.append(((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "* " : " "); 1336 flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "ALERTS" : "alerts"); 1337 flagdbg.append(((diff & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "* " : " "); 1338 flagdbg.append(((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info"); 1339 flagdbg.append(((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " "); 1340 flagdbg.append(((state & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back"); 1341 flagdbg.append(((diff & StatusBarManager.DISABLE_BACK) != 0) ? "* " : " "); 1342 flagdbg.append(((state & StatusBarManager.DISABLE_HOME) != 0) ? "HOME" : "home"); 1343 flagdbg.append(((diff & StatusBarManager.DISABLE_HOME) != 0) ? "* " : " "); 1344 flagdbg.append(((state & StatusBarManager.DISABLE_RECENT) != 0) ? "RECENT" : "recent"); 1345 flagdbg.append(((diff & StatusBarManager.DISABLE_RECENT) != 0) ? "* " : " "); 1346 flagdbg.append(((state & StatusBarManager.DISABLE_CLOCK) != 0) ? "CLOCK" : "clock"); 1347 flagdbg.append(((diff & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " "); 1348 flagdbg.append(((state & StatusBarManager.DISABLE_SEARCH) != 0) ? "SEARCH" : "search"); 1349 flagdbg.append(((diff & StatusBarManager.DISABLE_SEARCH) != 0) ? "* " : " "); 1350 flagdbg.append(">"); 1351 Log.d(TAG, flagdbg.toString()); 1352 1353 if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) { 1354 mSystemIconArea.animate().cancel(); 1355 if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) { 1356 mSystemIconArea.animate() 1357 .alpha(0f) 1358 .translationY(mNaturalBarHeight*0.5f) 1359 .setDuration(175) 1360 .setInterpolator(new DecelerateInterpolator(1.5f)) 1361 .setListener(mMakeIconsInvisible) 1362 .start(); 1363 } else { 1364 mSystemIconArea.setVisibility(View.VISIBLE); 1365 mSystemIconArea.animate() 1366 .alpha(1f) 1367 .translationY(0) 1368 .setStartDelay(0) 1369 .setInterpolator(new DecelerateInterpolator(1.5f)) 1370 .setDuration(175) 1371 .start(); 1372 } 1373 } 1374 1375 if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) { 1376 boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0; 1377 showClock(show); 1378 } 1379 if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) { 1380 if ((state & StatusBarManager.DISABLE_EXPAND) != 0) { 1381 animateCollapsePanels(); 1382 } 1383 } 1384 1385 if ((diff & (StatusBarManager.DISABLE_HOME 1386 | StatusBarManager.DISABLE_RECENT 1387 | StatusBarManager.DISABLE_BACK 1388 | StatusBarManager.DISABLE_SEARCH)) != 0) { 1389 // the nav bar will take care of these 1390 if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state); 1391 1392 if ((state & StatusBarManager.DISABLE_RECENT) != 0) { 1393 // close recents if it's visible 1394 mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL); 1395 mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL); 1396 } 1397 } 1398 1399 if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { 1400 if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { 1401 if (mTicking) { 1402 haltTicker(); 1403 } 1404 1405 mNotificationIcons.animate() 1406 .alpha(0f) 1407 .translationY(mNaturalBarHeight*0.5f) 1408 .setDuration(175) 1409 .setInterpolator(new DecelerateInterpolator(1.5f)) 1410 .setListener(mMakeIconsInvisible) 1411 .start(); 1412 } else { 1413 mNotificationIcons.setVisibility(View.VISIBLE); 1414 mNotificationIcons.animate() 1415 .alpha(1f) 1416 .translationY(0) 1417 .setStartDelay(0) 1418 .setInterpolator(new DecelerateInterpolator(1.5f)) 1419 .setDuration(175) 1420 .start(); 1421 } 1422 } 1423 } 1424 1425 @Override 1426 protected BaseStatusBar.H createHandler() { 1427 return new PhoneStatusBar.H(); 1428 } 1429 1430 @Override 1431 public void startActivity(Intent intent) { 1432 startActivityDismissingKeyguard(intent, false); 1433 } 1434 1435 /** 1436 * All changes to the status bar and notifications funnel through here and are batched. 1437 */ 1438 private class H extends BaseStatusBar.H { 1439 public void handleMessage(Message m) { 1440 super.handleMessage(m); 1441 switch (m.what) { 1442 case MSG_OPEN_NOTIFICATION_PANEL: 1443 animateExpandNotificationsPanel(); 1444 break; 1445 case MSG_OPEN_SETTINGS_PANEL: 1446 animateExpandSettingsPanel(); 1447 break; 1448 case MSG_CLOSE_PANELS: 1449 animateCollapsePanels(); 1450 break; 1451 case MSG_SHOW_HEADS_UP: 1452 setHeadsUpVisibility(true); 1453 break; 1454 case MSG_HIDE_HEADS_UP: 1455 setHeadsUpVisibility(false); 1456 break; 1457 case MSG_ESCALATE_HEADS_UP: 1458 escalateHeadsUp(); 1459 setHeadsUpVisibility(false); 1460 break; 1461 } 1462 } 1463 } 1464 1465 /** if the interrupting notification had a fullscreen intent, fire it now. */ 1466 private void escalateHeadsUp() { 1467 if (mInterruptingNotificationEntry != null) { 1468 final StatusBarNotification sbn = mInterruptingNotificationEntry.notification; 1469 final Notification notification = sbn.getNotification(); 1470 if (notification.fullScreenIntent != null) { 1471 if (DEBUG) 1472 Log.d(TAG, "converting a heads up to fullScreen"); 1473 try { 1474 notification.fullScreenIntent.send(); 1475 } catch (PendingIntent.CanceledException e) { 1476 } 1477 } 1478 } 1479 } 1480 1481 private Handler getHandler() { 1482 return mHandler; 1483 } 1484 1485 View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() { 1486 public void onFocusChange(View v, boolean hasFocus) { 1487 // Because 'v' is a ViewGroup, all its children will be (un)selected 1488 // too, which allows marqueeing to work. 1489 v.setSelected(hasFocus); 1490 } 1491 }; 1492 1493 boolean panelsEnabled() { 1494 return (mDisabled & StatusBarManager.DISABLE_EXPAND) == 0; 1495 } 1496 1497 void makeExpandedVisible(boolean force) { 1498 if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible); 1499 if (!force && (mExpandedVisible || !panelsEnabled())) { 1500 return; 1501 } 1502 1503 mExpandedVisible = true; 1504 if (mNavigationBarView != null) 1505 mNavigationBarView.setSlippery(true); 1506 1507 updateCarrierLabelVisibility(true); 1508 1509 updateExpandedViewPos(EXPANDED_LEAVE_ALONE); 1510 1511 // Expand the window to encompass the full screen in anticipation of the drag. 1512 // This is only possible to do atomically because the status bar is at the top of the screen! 1513 mStatusBarWindowManager.setStatusBarExpanded(true); 1514 1515 visibilityChanged(true); 1516 1517 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true); 1518 } 1519 1520 public void animateCollapsePanels() { 1521 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); 1522 } 1523 1524 private final Runnable mAnimateCollapsePanels = new Runnable() { 1525 @Override 1526 public void run() { 1527 animateCollapsePanels(); 1528 } 1529 }; 1530 1531 public void postAnimateCollapsePanels() { 1532 mHandler.post(mAnimateCollapsePanels); 1533 } 1534 1535 public void animateCollapsePanels(int flags) { 1536 if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { 1537 return; 1538 } 1539 if (SPEW) { 1540 Log.d(TAG, "animateCollapse():" 1541 + " mExpandedVisible=" + mExpandedVisible 1542 + " flags=" + flags); 1543 } 1544 1545 if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) { 1546 mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL); 1547 mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL); 1548 } 1549 1550 if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) { 1551 mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL); 1552 mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL); 1553 } 1554 1555 if (mStatusBarWindow != null) { 1556 1557 // release focus immediately to kick off focus change transition 1558 mStatusBarWindowManager.setStatusBarFocusable(false); 1559 1560 mStatusBarWindow.cancelExpandHelper(); 1561 mStatusBarView.collapseAllPanels(true); 1562 if (isFlippedToSettings()) { 1563 flipToNotifications(true /*animate*/); 1564 } 1565 } 1566 } 1567 1568 public ViewPropertyAnimator setVisibilityWhenDone( 1569 final ViewPropertyAnimator a, final View v, final int vis) { 1570 a.setListener(new AnimatorListenerAdapter() { 1571 @Override 1572 public void onAnimationEnd(Animator animation) { 1573 v.setVisibility(vis); 1574 a.setListener(null); // oneshot 1575 } 1576 }); 1577 return a; 1578 } 1579 1580 public Animator setVisibilityWhenDone( 1581 final Animator a, final View v, final int vis) { 1582 a.addListener(new AnimatorListenerAdapter() { 1583 @Override 1584 public void onAnimationEnd(Animator animation) { 1585 v.setVisibility(vis); 1586 } 1587 }); 1588 return a; 1589 } 1590 1591 public Animator interpolator(TimeInterpolator ti, Animator a) { 1592 a.setInterpolator(ti); 1593 return a; 1594 } 1595 1596 public Animator startDelay(int d, Animator a) { 1597 a.setStartDelay(d); 1598 return a; 1599 } 1600 1601 public Animator start(Animator a) { 1602 a.start(); 1603 return a; 1604 } 1605 1606 final TimeInterpolator mAccelerateInterpolator = new AccelerateInterpolator(); 1607 final TimeInterpolator mDecelerateInterpolator = new DecelerateInterpolator(); 1608 final int FLIP_DURATION_OUT = 125; 1609 final int FLIP_DURATION_IN = 225; 1610 final int FLIP_DURATION = (FLIP_DURATION_IN + FLIP_DURATION_OUT); 1611 1612 Animator mScrollViewAnim, mClearButtonAnim; 1613 1614 @Override 1615 public void animateExpandNotificationsPanel() { 1616 if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible); 1617 if (!panelsEnabled()) { 1618 return ; 1619 } 1620 1621 mNotificationPanel.expand(); 1622 if (mStackScroller.getVisibility() != View.VISIBLE) { 1623 flipToNotifications(true /*animate*/); 1624 } 1625 1626 if (false) postStartTracing(); 1627 } 1628 1629 private static void cancelAnim(Animator anim) { 1630 if (anim != null) { 1631 anim.cancel(); 1632 } 1633 } 1634 1635 public void flipToNotifications(boolean animate) { 1636 // TODO: Animation 1637 mNotificationPanel.closeQs(); 1638 } 1639 1640 @Override 1641 public void animateExpandSettingsPanel() { 1642 if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible); 1643 if (!panelsEnabled()) { 1644 return; 1645 } 1646 1647 // Settings are not available in setup 1648 if (!mUserSetup) return; 1649 1650 mNotificationPanel.expand(); 1651 mNotificationPanel.openQs(); 1652 1653 if (false) postStartTracing(); 1654 } 1655 1656 public boolean isFlippedToSettings() { 1657 if (mNotificationPanel != null) { 1658 return mNotificationPanel.isQsExpanded(); 1659 } 1660 return false; 1661 } 1662 1663 public void animateCollapseQuickSettings() { 1664 mStatusBarView.collapseAllPanels(true); 1665 } 1666 1667 void makeExpandedInvisibleSoon() { 1668 mHandler.postDelayed(new Runnable() { public void run() { makeExpandedInvisible(); }}, 50); 1669 } 1670 1671 void makeExpandedInvisible() { 1672 if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible 1673 + " mExpandedVisible=" + mExpandedVisible); 1674 1675 if (!mExpandedVisible || mStatusBarWindow == null) { 1676 return; 1677 } 1678 1679 // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868) 1680 mStatusBarView.collapseAllPanels(/*animate=*/ false); 1681 1682 // reset things to their proper state 1683 if (mScrollViewAnim != null) mScrollViewAnim.cancel(); 1684 if (mClearButtonAnim != null) mClearButtonAnim.cancel(); 1685 1686 mStackScroller.setVisibility(View.VISIBLE); 1687 mNotificationPanel.setVisibility(View.GONE); 1688 1689 setAreThereNotifications(); // show the clear button 1690 1691 mNotificationPanel.closeQs(); 1692 1693 mExpandedVisible = false; 1694 if (mNavigationBarView != null) 1695 mNavigationBarView.setSlippery(false); 1696 visibilityChanged(false); 1697 1698 // Shrink the window to the size of the status bar only 1699 mStatusBarWindowManager.setStatusBarExpanded(false); 1700 1701 if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) { 1702 setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in); 1703 } 1704 1705 // Close any "App info" popups that might have snuck on-screen 1706 dismissPopups(); 1707 1708 if (mPostCollapseCleanup != null) { 1709 mPostCollapseCleanup.run(); 1710 mPostCollapseCleanup = null; 1711 } 1712 1713 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); 1714 1715 showBouncer(); 1716 } 1717 1718 public boolean interceptTouchEvent(MotionEvent event) { 1719 if (DEBUG_GESTURES) { 1720 if (event.getActionMasked() != MotionEvent.ACTION_MOVE) { 1721 EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH, 1722 event.getActionMasked(), (int) event.getX(), (int) event.getY(), mDisabled); 1723 } 1724 1725 } 1726 1727 if (SPEW) { 1728 Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled=" 1729 + mDisabled + " mTracking=" + mTracking); 1730 } else if (CHATTY) { 1731 if (event.getAction() != MotionEvent.ACTION_MOVE) { 1732 Log.d(TAG, String.format( 1733 "panel: %s at (%f, %f) mDisabled=0x%08x", 1734 MotionEvent.actionToString(event.getAction()), 1735 event.getRawX(), event.getRawY(), mDisabled)); 1736 } 1737 } 1738 1739 if (DEBUG_GESTURES) { 1740 mGestureRec.add(event); 1741 } 1742 1743 if (mStatusBarWindowState == WINDOW_STATE_SHOWING) { 1744 final boolean upOrCancel = 1745 event.getAction() == MotionEvent.ACTION_UP || 1746 event.getAction() == MotionEvent.ACTION_CANCEL; 1747 if (upOrCancel && !mExpandedVisible) { 1748 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); 1749 } else { 1750 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true); 1751 } 1752 } 1753 return false; 1754 } 1755 1756 public GestureRecorder getGestureRecorder() { 1757 return mGestureRec; 1758 } 1759 1760 private void setNavigationIconHints(int hints) { 1761 if (hints == mNavigationIconHints) return; 1762 1763 mNavigationIconHints = hints; 1764 1765 if (mNavigationBarView != null) { 1766 mNavigationBarView.setNavigationIconHints(hints); 1767 } 1768 checkBarModes(); 1769 } 1770 1771 @Override // CommandQueue 1772 public void setWindowState(int window, int state) { 1773 boolean showing = state == WINDOW_STATE_SHOWING; 1774 if (mStatusBarWindow != null 1775 && window == StatusBarManager.WINDOW_STATUS_BAR 1776 && mStatusBarWindowState != state) { 1777 mStatusBarWindowState = state; 1778 if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state)); 1779 if (!showing) { 1780 mStatusBarView.collapseAllPanels(false); 1781 } 1782 } 1783 if (mNavigationBarView != null 1784 && window == StatusBarManager.WINDOW_NAVIGATION_BAR 1785 && mNavigationBarWindowState != state) { 1786 mNavigationBarWindowState = state; 1787 if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state)); 1788 } 1789 } 1790 1791 @Override // CommandQueue 1792 public void setSystemUiVisibility(int vis, int mask) { 1793 final int oldVal = mSystemUiVisibility; 1794 final int newVal = (oldVal&~mask) | (vis&mask); 1795 final int diff = newVal ^ oldVal; 1796 if (DEBUG) Log.d(TAG, String.format( 1797 "setSystemUiVisibility vis=%s mask=%s oldVal=%s newVal=%s diff=%s", 1798 Integer.toHexString(vis), Integer.toHexString(mask), 1799 Integer.toHexString(oldVal), Integer.toHexString(newVal), 1800 Integer.toHexString(diff))); 1801 if (diff != 0) { 1802 mSystemUiVisibility = newVal; 1803 1804 // update low profile 1805 if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) { 1806 final boolean lightsOut = (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0; 1807 if (lightsOut) { 1808 animateCollapsePanels(); 1809 if (mTicking) { 1810 haltTicker(); 1811 } 1812 } 1813 1814 setAreThereNotifications(); 1815 } 1816 1817 // update status bar mode 1818 final int sbMode = computeBarMode(oldVal, newVal, mStatusBarView.getBarTransitions(), 1819 View.STATUS_BAR_TRANSIENT, View.STATUS_BAR_TRANSLUCENT); 1820 1821 // update navigation bar mode 1822 final int nbMode = mNavigationBarView == null ? -1 : computeBarMode( 1823 oldVal, newVal, mNavigationBarView.getBarTransitions(), 1824 View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT); 1825 final boolean sbModeChanged = sbMode != -1; 1826 final boolean nbModeChanged = nbMode != -1; 1827 boolean checkBarModes = false; 1828 if (sbModeChanged && sbMode != mStatusBarMode) { 1829 mStatusBarMode = sbMode; 1830 checkBarModes = true; 1831 } 1832 if (nbModeChanged && nbMode != mNavigationBarMode) { 1833 mNavigationBarMode = nbMode; 1834 checkBarModes = true; 1835 } 1836 if (checkBarModes) { 1837 checkBarModes(); 1838 } 1839 if (sbModeChanged || nbModeChanged) { 1840 // update transient bar autohide 1841 if (mStatusBarMode == MODE_SEMI_TRANSPARENT || mNavigationBarMode == MODE_SEMI_TRANSPARENT) { 1842 scheduleAutohide(); 1843 } else { 1844 cancelAutohide(); 1845 } 1846 } 1847 1848 // ready to unhide 1849 if ((vis & View.STATUS_BAR_UNHIDE) != 0) { 1850 mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE; 1851 } 1852 if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) { 1853 mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE; 1854 } 1855 1856 // send updated sysui visibility to window manager 1857 notifyUiVisibilityChanged(mSystemUiVisibility); 1858 } 1859 } 1860 1861 private int computeBarMode(int oldVis, int newVis, BarTransitions transitions, 1862 int transientFlag, int translucentFlag) { 1863 final int oldMode = barMode(oldVis, transientFlag, translucentFlag); 1864 final int newMode = barMode(newVis, transientFlag, translucentFlag); 1865 if (oldMode == newMode) { 1866 return -1; // no mode change 1867 } 1868 return newMode; 1869 } 1870 1871 private int barMode(int vis, int transientFlag, int translucentFlag) { 1872 return (vis & transientFlag) != 0 ? MODE_SEMI_TRANSPARENT 1873 : (vis & translucentFlag) != 0 ? MODE_TRANSLUCENT 1874 : (vis & View.SYSTEM_UI_TRANSPARENT) != 0 ? MODE_TRANSPARENT 1875 : (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0 ? MODE_LIGHTS_OUT 1876 : MODE_OPAQUE; 1877 } 1878 1879 private void checkBarModes() { 1880 if (mDemoMode) return; 1881 checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarView.getBarTransitions()); 1882 if (mNavigationBarView != null) { 1883 checkBarMode(mNavigationBarMode, 1884 mNavigationBarWindowState, mNavigationBarView.getBarTransitions()); 1885 } 1886 } 1887 1888 private void checkBarMode(int mode, int windowState, BarTransitions transitions) { 1889 final boolean anim = (mScreenOn == null || mScreenOn) && windowState != WINDOW_STATE_HIDDEN; 1890 transitions.transitionTo(mode, anim); 1891 } 1892 1893 private void finishBarAnimations() { 1894 mStatusBarView.getBarTransitions().finishAnimations(); 1895 if (mNavigationBarView != null) { 1896 mNavigationBarView.getBarTransitions().finishAnimations(); 1897 } 1898 } 1899 1900 private final Runnable mCheckBarModes = new Runnable() { 1901 @Override 1902 public void run() { 1903 checkBarModes(); 1904 }}; 1905 1906 @Override 1907 public void setInteracting(int barWindow, boolean interacting) { 1908 mInteractingWindows = interacting 1909 ? (mInteractingWindows | barWindow) 1910 : (mInteractingWindows & ~barWindow); 1911 if (mInteractingWindows != 0) { 1912 suspendAutohide(); 1913 } else { 1914 resumeSuspendedAutohide(); 1915 } 1916 checkBarModes(); 1917 } 1918 1919 private void resumeSuspendedAutohide() { 1920 if (mAutohideSuspended) { 1921 scheduleAutohide(); 1922 mHandler.postDelayed(mCheckBarModes, 500); // longer than home -> launcher 1923 } 1924 } 1925 1926 private void suspendAutohide() { 1927 mHandler.removeCallbacks(mAutohide); 1928 mHandler.removeCallbacks(mCheckBarModes); 1929 mAutohideSuspended = (mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0; 1930 } 1931 1932 private void cancelAutohide() { 1933 mAutohideSuspended = false; 1934 mHandler.removeCallbacks(mAutohide); 1935 } 1936 1937 private void scheduleAutohide() { 1938 cancelAutohide(); 1939 mHandler.postDelayed(mAutohide, AUTOHIDE_TIMEOUT_MS); 1940 } 1941 1942 private void checkUserAutohide(View v, MotionEvent event) { 1943 if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0 // a transient bar is revealed 1944 && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar 1945 && event.getX() == 0 && event.getY() == 0 // a touch outside both bars 1946 ) { 1947 userAutohide(); 1948 } 1949 } 1950 1951 private void userAutohide() { 1952 cancelAutohide(); 1953 mHandler.postDelayed(mAutohide, 350); // longer than app gesture -> flag clear 1954 } 1955 1956 private boolean areLightsOn() { 1957 return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE); 1958 } 1959 1960 public void setLightsOn(boolean on) { 1961 Log.v(TAG, "setLightsOn(" + on + ")"); 1962 if (on) { 1963 setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE); 1964 } else { 1965 setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE); 1966 } 1967 } 1968 1969 private void notifyUiVisibilityChanged(int vis) { 1970 try { 1971 mWindowManagerService.statusBarVisibilityChanged(vis); 1972 } catch (RemoteException ex) { 1973 } 1974 } 1975 1976 public void topAppWindowChanged(boolean showMenu) { 1977 if (DEBUG) { 1978 Log.d(TAG, (showMenu?"showing":"hiding") + " the MENU button"); 1979 } 1980 if (mNavigationBarView != null) { 1981 mNavigationBarView.setMenuVisibility(showMenu); 1982 } 1983 1984 // See above re: lights-out policy for legacy apps. 1985 if (showMenu) setLightsOn(true); 1986 } 1987 1988 @Override 1989 public void setImeWindowStatus(IBinder token, int vis, int backDisposition, 1990 boolean showImeSwitcher) { 1991 boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0; 1992 int flags = mNavigationIconHints; 1993 if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) { 1994 flags |= NAVIGATION_HINT_BACK_ALT; 1995 } else { 1996 flags &= ~NAVIGATION_HINT_BACK_ALT; 1997 } 1998 if (showImeSwitcher) { 1999 flags |= NAVIGATION_HINT_IME_SHOWN; 2000 } else { 2001 flags &= ~NAVIGATION_HINT_IME_SHOWN; 2002 } 2003 2004 setNavigationIconHints(flags); 2005 } 2006 2007 @Override 2008 public void setHardKeyboardStatus(boolean available, boolean enabled) {} 2009 2010 @Override 2011 protected void tick(IBinder key, StatusBarNotification n, boolean firstTime) { 2012 // no ticking in lights-out mode 2013 if (!areLightsOn()) return; 2014 2015 // no ticking in Setup 2016 if (!isDeviceProvisioned()) return; 2017 2018 // not for you 2019 if (!notificationIsForCurrentProfiles(n)) return; 2020 2021 // Show the ticker if one is requested. Also don't do this 2022 // until status bar window is attached to the window manager, 2023 // because... well, what's the point otherwise? And trying to 2024 // run a ticker without being attached will crash! 2025 if (n.getNotification().tickerText != null && mStatusBarWindow != null 2026 && mStatusBarWindow.getWindowToken() != null) { 2027 if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS 2028 | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) { 2029 mTicker.addEntry(n); 2030 } 2031 } 2032 } 2033 2034 private class MyTicker extends Ticker { 2035 MyTicker(Context context, View sb) { 2036 super(context, sb); 2037 } 2038 2039 @Override 2040 public void tickerStarting() { 2041 mTicking = true; 2042 mStatusBarContents.setVisibility(View.GONE); 2043 mTickerView.setVisibility(View.VISIBLE); 2044 mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null)); 2045 mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null)); 2046 } 2047 2048 @Override 2049 public void tickerDone() { 2050 mStatusBarContents.setVisibility(View.VISIBLE); 2051 mTickerView.setVisibility(View.GONE); 2052 mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null)); 2053 mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out, 2054 mTickingDoneListener)); 2055 } 2056 2057 public void tickerHalting() { 2058 if (mStatusBarContents.getVisibility() != View.VISIBLE) { 2059 mStatusBarContents.setVisibility(View.VISIBLE); 2060 mStatusBarContents 2061 .startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null)); 2062 } 2063 mTickerView.setVisibility(View.GONE); 2064 // we do not animate the ticker away at this point, just get rid of it (b/6992707) 2065 } 2066 } 2067 2068 Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {; 2069 public void onAnimationEnd(Animation animation) { 2070 mTicking = false; 2071 } 2072 public void onAnimationRepeat(Animation animation) { 2073 } 2074 public void onAnimationStart(Animation animation) { 2075 } 2076 }; 2077 2078 private Animation loadAnim(int id, Animation.AnimationListener listener) { 2079 Animation anim = AnimationUtils.loadAnimation(mContext, id); 2080 if (listener != null) { 2081 anim.setAnimationListener(listener); 2082 } 2083 return anim; 2084 } 2085 2086 public static String viewInfo(View v) { 2087 return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom() 2088 + ") " + v.getWidth() + "x" + v.getHeight() + "]"; 2089 } 2090 2091 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2092 synchronized (mQueueLock) { 2093 pw.println("Current Status Bar state:"); 2094 pw.println(" mExpandedVisible=" + mExpandedVisible 2095 + ", mTrackingPosition=" + mTrackingPosition); 2096 pw.println(" mTicking=" + mTicking); 2097 pw.println(" mTracking=" + mTracking); 2098 pw.println(" mDisplayMetrics=" + mDisplayMetrics); 2099 pw.println(" mStackScroller: " + viewInfo(mStackScroller)); 2100 pw.println(" mTickerView: " + viewInfo(mTickerView)); 2101 pw.println(" mStackScroller: " + viewInfo(mStackScroller) 2102 + " scroll " + mStackScroller.getScrollX() 2103 + "," + mStackScroller.getScrollY()); 2104 } 2105 2106 pw.print(" mInteractingWindows="); pw.println(mInteractingWindows); 2107 pw.print(" mStatusBarWindowState="); 2108 pw.println(windowStateToString(mStatusBarWindowState)); 2109 pw.print(" mStatusBarMode="); 2110 pw.println(BarTransitions.modeToString(mStatusBarMode)); 2111 pw.print(" mZenMode="); 2112 pw.println(Settings.Global.zenModeToString(mZenMode)); 2113 pw.print(" mUseHeadsUp="); 2114 pw.println(mUseHeadsUp); 2115 pw.print(" interrupting package: "); 2116 pw.println(hunStateToString(mInterruptingNotificationEntry)); 2117 dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions()); 2118 if (mNavigationBarView != null) { 2119 pw.print(" mNavigationBarWindowState="); 2120 pw.println(windowStateToString(mNavigationBarWindowState)); 2121 pw.print(" mNavigationBarMode="); 2122 pw.println(BarTransitions.modeToString(mNavigationBarMode)); 2123 dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions()); 2124 } 2125 2126 pw.print(" mNavigationBarView="); 2127 if (mNavigationBarView == null) { 2128 pw.println("null"); 2129 } else { 2130 mNavigationBarView.dump(fd, pw, args); 2131 } 2132 2133 pw.println(" Panels: "); 2134 if (mNotificationPanel != null) { 2135 pw.println(" mNotificationPanel=" + 2136 mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug("")); 2137 pw.print (" "); 2138 mNotificationPanel.dump(fd, pw, args); 2139 } 2140 2141 if (DUMPTRUCK) { 2142 synchronized (mNotificationData) { 2143 int N = mNotificationData.size(); 2144 pw.println(" notification icons: " + N); 2145 for (int i=0; i<N; i++) { 2146 NotificationData.Entry e = mNotificationData.get(i); 2147 pw.println(" [" + i + "] key=" + e.key + " icon=" + e.icon); 2148 StatusBarNotification n = e.notification; 2149 pw.println(" pkg=" + n.getPackageName() + " id=" + n.getId() + " score=" + n.getScore()); 2150 pw.println(" notification=" + n.getNotification()); 2151 pw.println(" tickerText=\"" + n.getNotification().tickerText + "\""); 2152 } 2153 } 2154 2155 int N = mStatusIcons.getChildCount(); 2156 pw.println(" system icons: " + N); 2157 for (int i=0; i<N; i++) { 2158 StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i); 2159 pw.println(" [" + i + "] icon=" + ic); 2160 } 2161 2162 if (false) { 2163 pw.println("see the logcat for a dump of the views we have created."); 2164 // must happen on ui thread 2165 mHandler.post(new Runnable() { 2166 public void run() { 2167 mStatusBarView.getLocationOnScreen(mAbsPos); 2168 Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] 2169 + ") " + mStatusBarView.getWidth() + "x" 2170 + getStatusBarHeight()); 2171 mStatusBarView.debug(); 2172 } 2173 }); 2174 } 2175 } 2176 2177 if (DEBUG_GESTURES) { 2178 pw.print(" status bar gestures: "); 2179 mGestureRec.dump(fd, pw, args); 2180 } 2181 2182 mNetworkController.dump(fd, pw, args); 2183 } 2184 2185 private String hunStateToString(Entry entry) { 2186 if (entry == null) return "null"; 2187 if (entry.notification == null) return "corrupt"; 2188 return entry.notification.getPackageName(); 2189 } 2190 2191 private static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) { 2192 pw.print(" "); pw.print(var); pw.print(".BarTransitions.mMode="); 2193 pw.println(BarTransitions.modeToString(transitions.getMode())); 2194 } 2195 2196 @Override 2197 public void createAndAddWindows() { 2198 addStatusBarWindow(); 2199 } 2200 2201 private void addStatusBarWindow() { 2202 makeStatusBarView(); 2203 mStatusBarWindowManager = new StatusBarWindowManager(mContext); 2204 mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight()); 2205 } 2206 2207 void setNotificationIconVisibility(boolean visible, int anim) { 2208 int old = mNotificationIcons.getVisibility(); 2209 int v = visible ? View.VISIBLE : View.INVISIBLE; 2210 if (old != v) { 2211 mNotificationIcons.setVisibility(v); 2212 mNotificationIcons.startAnimation(loadAnim(anim, null)); 2213 } 2214 } 2215 2216 static final float saturate(float a) { 2217 return a < 0f ? 0f : (a > 1f ? 1f : a); 2218 } 2219 2220 @Override 2221 public void updateExpandedViewPos(int thingy) { 2222 if (SPEW) Log.v(TAG, "updateExpandedViewPos"); 2223 2224 // on larger devices, the notification panel is propped open a bit 2225 mNotificationPanel.setMinimumHeight( 2226 (int)(mNotificationPanelMinHeightFrac * mCurrentDisplaySize.y)); 2227 2228 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mNotificationPanel.getLayoutParams(); 2229 lp.gravity = mNotificationPanelGravity; 2230 mNotificationPanel.setLayoutParams(lp); 2231 2232 updateCarrierLabelVisibility(false); 2233 } 2234 2235 // called by makeStatusbar and also by PhoneStatusBarView 2236 void updateDisplaySize() { 2237 mDisplay.getMetrics(mDisplayMetrics); 2238 mDisplay.getSize(mCurrentDisplaySize); 2239 if (DEBUG_GESTURES) { 2240 mGestureRec.tag("display", 2241 String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels)); 2242 } 2243 } 2244 2245 private View.OnClickListener mClearButtonListener = new View.OnClickListener() { 2246 public void onClick(View v) { 2247 synchronized (mNotificationData) { 2248 mPostCollapseCleanup = new Runnable() { 2249 @Override 2250 public void run() { 2251 if (DEBUG) { 2252 Log.v(TAG, "running post-collapse cleanup"); 2253 } 2254 try { 2255 mBarService.onClearAllNotifications(mCurrentUserId); 2256 } catch (Exception ex) { } 2257 } 2258 }; 2259 2260 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); 2261 return; 2262 // TODO: Handle this better with notification stack scroller 2263 } 2264 } 2265 }; 2266 2267 public void startActivityDismissingKeyguard(Intent intent, boolean onlyProvisioned) { 2268 if (onlyProvisioned && !isDeviceProvisioned()) return; 2269 try { 2270 // Dismiss the lock screen when Settings starts. 2271 ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity(); 2272 } catch (RemoteException e) { 2273 } 2274 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); 2275 mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); 2276 animateCollapsePanels(); 2277 } 2278 2279 private View.OnClickListener mClockClickListener = new View.OnClickListener() { 2280 public void onClick(View v) { 2281 startActivityDismissingKeyguard( 2282 new Intent(Intent.ACTION_QUICK_CLOCK), true); // have fun, everyone 2283 } 2284 }; 2285 2286 private View.OnClickListener mNotificationButtonListener = new View.OnClickListener() { 2287 public void onClick(View v) { 2288 animateExpandNotificationsPanel(); 2289 } 2290 }; 2291 2292 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 2293 public void onReceive(Context context, Intent intent) { 2294 if (DEBUG) Log.v(TAG, "onReceive: " + intent); 2295 String action = intent.getAction(); 2296 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { 2297 int flags = CommandQueue.FLAG_EXCLUDE_NONE; 2298 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { 2299 String reason = intent.getStringExtra("reason"); 2300 if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) { 2301 flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL; 2302 } 2303 } 2304 animateCollapsePanels(flags); 2305 } 2306 else if (Intent.ACTION_SCREEN_OFF.equals(action)) { 2307 mScreenOn = false; 2308 notifyNavigationBarScreenOn(false); 2309 notifyHeadsUpScreenOn(false); 2310 finishBarAnimations(); 2311 stopNotificationLogging(); 2312 } 2313 else if (Intent.ACTION_SCREEN_ON.equals(action)) { 2314 mScreenOn = true; 2315 // work around problem where mDisplay.getRotation() is not stable while screen is off (bug 7086018) 2316 repositionNavigationBar(); 2317 notifyNavigationBarScreenOn(true); 2318 startNotificationLoggingIfScreenOnAndVisible(); 2319 } 2320 else if (ACTION_DEMO.equals(action)) { 2321 Bundle bundle = intent.getExtras(); 2322 if (bundle != null) { 2323 String command = bundle.getString("command", "").trim().toLowerCase(); 2324 if (command.length() > 0) { 2325 try { 2326 dispatchDemoCommand(command, bundle); 2327 } catch (Throwable t) { 2328 Log.w(TAG, "Error running demo command, intent=" + intent, t); 2329 } 2330 } 2331 } 2332 } 2333 } 2334 }; 2335 2336 // SystemUIService notifies SystemBars of configuration changes, which then calls down here 2337 @Override 2338 protected void onConfigurationChanged(Configuration newConfig) { 2339 super.onConfigurationChanged(newConfig); // calls refreshLayout 2340 2341 if (DEBUG) { 2342 Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration()); 2343 } 2344 updateDisplaySize(); // populates mDisplayMetrics 2345 2346 updateResources(); 2347 repositionNavigationBar(); 2348 updateExpandedViewPos(EXPANDED_LEAVE_ALONE); 2349 updateShowSearchHoldoff(); 2350 } 2351 2352 @Override 2353 public void userSwitched(int newUserId) { 2354 if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId); 2355 animateCollapsePanels(); 2356 updateNotificationIcons(); 2357 resetUserSetupObserver(); 2358 } 2359 2360 private void resetUserSetupObserver() { 2361 mContext.getContentResolver().unregisterContentObserver(mUserSetupObserver); 2362 mUserSetupObserver.onChange(false); 2363 mContext.getContentResolver().registerContentObserver( 2364 Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), true, 2365 mUserSetupObserver, 2366 mCurrentUserId); 2367 } 2368 2369 private void setHeadsUpVisibility(boolean vis) { 2370 if (!ENABLE_HEADS_UP) return; 2371 if (DEBUG) Log.v(TAG, (vis ? "showing" : "hiding") + " heads up window"); 2372 mHeadsUpNotificationView.setVisibility(vis ? View.VISIBLE : View.GONE); 2373 if (!vis) { 2374 if (DEBUG) Log.d(TAG, "setting heads up entry to null"); 2375 mInterruptingNotificationEntry = null; 2376 } 2377 } 2378 2379 public void onHeadsUpDismissed() { 2380 if (mInterruptingNotificationEntry == null) return; 2381 mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP); 2382 if (mHeadsUpNotificationView.isClearable()) { 2383 try { 2384 mBarService.onNotificationClear( 2385 mInterruptingNotificationEntry.notification.getPackageName(), 2386 mInterruptingNotificationEntry.notification.getTag(), 2387 mInterruptingNotificationEntry.notification.getId(), 2388 mInterruptingNotificationEntry.notification.getUserId()); 2389 } catch (android.os.RemoteException ex) { 2390 // oh well 2391 } 2392 } 2393 } 2394 2395 /** 2396 * Reload some of our resources when the configuration changes. 2397 * 2398 * We don't reload everything when the configuration changes -- we probably 2399 * should, but getting that smooth is tough. Someday we'll fix that. In the 2400 * meantime, just update the things that we know change. 2401 */ 2402 void updateResources() { 2403 // Update the quick setting tiles 2404 if (mQSPanel != null) mQSPanel.updateResources(); 2405 2406 loadDimens(); 2407 } 2408 2409 protected void loadDimens() { 2410 final Resources res = mContext.getResources(); 2411 2412 mNaturalBarHeight = res.getDimensionPixelSize( 2413 com.android.internal.R.dimen.status_bar_height); 2414 2415 int newIconSize = res.getDimensionPixelSize( 2416 com.android.internal.R.dimen.status_bar_icon_size); 2417 int newIconHPadding = res.getDimensionPixelSize( 2418 R.dimen.status_bar_icon_padding); 2419 2420 if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) { 2421// Log.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding); 2422 mIconHPadding = newIconHPadding; 2423 mIconSize = newIconSize; 2424 //reloadAllNotificationIcons(); // reload the tray 2425 } 2426 2427 mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); 2428 2429 mSelfExpandVelocityPx = res.getDimension(R.dimen.self_expand_velocity); 2430 mSelfCollapseVelocityPx = res.getDimension(R.dimen.self_collapse_velocity); 2431 mFlingExpandMinVelocityPx = res.getDimension(R.dimen.fling_expand_min_velocity); 2432 mFlingCollapseMinVelocityPx = res.getDimension(R.dimen.fling_collapse_min_velocity); 2433 2434 mCollapseMinDisplayFraction = res.getFraction(R.dimen.collapse_min_display_fraction, 1, 1); 2435 mExpandMinDisplayFraction = res.getFraction(R.dimen.expand_min_display_fraction, 1, 1); 2436 2437 mExpandAccelPx = res.getDimension(R.dimen.expand_accel); 2438 mCollapseAccelPx = res.getDimension(R.dimen.collapse_accel); 2439 2440 mFlingGestureMaxXVelocityPx = res.getDimension(R.dimen.fling_gesture_max_x_velocity); 2441 2442 mFlingGestureMaxOutputVelocityPx = res.getDimension(R.dimen.fling_gesture_max_output_velocity); 2443 2444 mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity); 2445 if (mNotificationPanelGravity <= 0) { 2446 mNotificationPanelGravity = Gravity.START | Gravity.TOP; 2447 } 2448 2449 mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height); 2450 mStatusBarHeaderHeight = res.getDimensionPixelSize(R.dimen.status_bar_header_height); 2451 2452 mNotificationPanelMinHeightFrac = res.getFraction(R.dimen.notification_panel_min_height_frac, 1, 1); 2453 if (mNotificationPanelMinHeightFrac < 0f || mNotificationPanelMinHeightFrac > 1f) { 2454 mNotificationPanelMinHeightFrac = 0f; 2455 } 2456 2457 mHeadsUpNotificationDecay = res.getInteger(R.integer.heads_up_notification_decay); 2458 mRowMinHeight = res.getDimensionPixelSize(R.dimen.notification_min_height); 2459 mRowMaxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height); 2460 2461 mKeyguardMaxNotificationCount = res.getInteger(R.integer.keyguard_max_notification_count); 2462 2463 if (false) Log.v(TAG, "updateResources"); 2464 } 2465 2466 // Visibility reporting 2467 2468 @Override 2469 protected void visibilityChanged(boolean visible) { 2470 mVisible = visible; 2471 if (visible) { 2472 startNotificationLoggingIfScreenOnAndVisible(); 2473 } else { 2474 stopNotificationLogging(); 2475 } 2476 super.visibilityChanged(visible); 2477 } 2478 2479 private void stopNotificationLogging() { 2480 // Report all notifications as invisible and turn down the 2481 // reporter. 2482 if (!mCurrentlyVisibleNotifications.isEmpty()) { 2483 logNotificationVisibilityChanges( 2484 Collections.<String>emptyList(), mCurrentlyVisibleNotifications); 2485 mCurrentlyVisibleNotifications.clear(); 2486 } 2487 mHandler.removeCallbacks(mVisibilityReporter); 2488 mStackScroller.setChildLocationsChangedListener(null); 2489 } 2490 2491 private void startNotificationLoggingIfScreenOnAndVisible() { 2492 if (mVisible && mScreenOn) { 2493 mStackScroller.setChildLocationsChangedListener(mNotificationLocationsChangedListener); 2494 // Some transitions like mScreenOn=false -> mScreenOn=true don't 2495 // cause the scroller to emit child location events. Hence generate 2496 // one ourselves to guarantee that we're reporting visible 2497 // notifications. 2498 // (Note that in cases where the scroller does emit events, this 2499 // additional event doesn't break anything.) 2500 mNotificationLocationsChangedListener.onChildLocationsChanged(mStackScroller); 2501 } 2502 } 2503 2504 private void logNotificationVisibilityChanges( 2505 Collection<String> newlyVisible, Collection<String> noLongerVisible) { 2506 if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) { 2507 return; 2508 } 2509 2510 String[] newlyVisibleAr = newlyVisible.toArray(new String[newlyVisible.size()]); 2511 String[] noLongerVisibleAr = noLongerVisible.toArray(new String[noLongerVisible.size()]); 2512 try { 2513 mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr); 2514 } catch (RemoteException e) { 2515 // Ignore. 2516 } 2517 } 2518 2519 // 2520 // tracing 2521 // 2522 2523 void postStartTracing() { 2524 mHandler.postDelayed(mStartTracing, 3000); 2525 } 2526 2527 void vibrate() { 2528 android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService( 2529 Context.VIBRATOR_SERVICE); 2530 vib.vibrate(250, AudioManager.STREAM_SYSTEM); 2531 } 2532 2533 Runnable mStartTracing = new Runnable() { 2534 public void run() { 2535 vibrate(); 2536 SystemClock.sleep(250); 2537 Log.d(TAG, "startTracing"); 2538 android.os.Debug.startMethodTracing("/data/statusbar-traces/trace"); 2539 mHandler.postDelayed(mStopTracing, 10000); 2540 } 2541 }; 2542 2543 Runnable mStopTracing = new Runnable() { 2544 public void run() { 2545 android.os.Debug.stopMethodTracing(); 2546 Log.d(TAG, "stopTracing"); 2547 vibrate(); 2548 } 2549 }; 2550 2551 @Override 2552 protected void haltTicker() { 2553 mTicker.halt(); 2554 } 2555 2556 @Override 2557 protected boolean shouldDisableNavbarGestures() { 2558 return !isDeviceProvisioned() 2559 || mExpandedVisible 2560 || (mDisabled & StatusBarManager.DISABLE_SEARCH) != 0; 2561 } 2562 2563 public void postStartSettingsActivity(final Intent intent) { 2564 mHandler.post(new Runnable() { 2565 @Override 2566 public void run() { 2567 handleStartSettingsActivity(intent, true /*onlyProvisioned*/); 2568 } 2569 }); 2570 } 2571 2572 private void handleStartSettingsActivity(Intent intent, boolean onlyProvisioned) { 2573 if (onlyProvisioned && !isDeviceProvisioned()) return; 2574 try { 2575 // Dismiss the lock screen when Settings starts. 2576 ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity(); 2577 } catch (RemoteException e) { 2578 } 2579 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); 2580 mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); 2581 animateCollapsePanels(); 2582 } 2583 2584 public void startSettingsActivity(String action) { 2585 postStartSettingsActivity(new Intent(action)); 2586 } 2587 2588 private static class FastColorDrawable extends Drawable { 2589 private final int mColor; 2590 2591 public FastColorDrawable(int color) { 2592 mColor = 0xff000000 | color; 2593 } 2594 2595 @Override 2596 public void draw(Canvas canvas) { 2597 canvas.drawColor(mColor, PorterDuff.Mode.SRC); 2598 } 2599 2600 @Override 2601 public void setAlpha(int alpha) { 2602 } 2603 2604 @Override 2605 public void setColorFilter(ColorFilter cf) { 2606 } 2607 2608 @Override 2609 public int getOpacity() { 2610 return PixelFormat.OPAQUE; 2611 } 2612 2613 @Override 2614 public void setBounds(int left, int top, int right, int bottom) { 2615 } 2616 2617 @Override 2618 public void setBounds(Rect bounds) { 2619 } 2620 } 2621 2622 @Override 2623 public void destroy() { 2624 super.destroy(); 2625 if (mStatusBarWindow != null) { 2626 mWindowManager.removeViewImmediate(mStatusBarWindow); 2627 mStatusBarWindow = null; 2628 } 2629 if (mNavigationBarView != null) { 2630 mWindowManager.removeViewImmediate(mNavigationBarView); 2631 mNavigationBarView = null; 2632 } 2633 mContext.unregisterReceiver(mBroadcastReceiver); 2634 } 2635 2636 private boolean mDemoModeAllowed; 2637 private boolean mDemoMode; 2638 private DemoStatusIcons mDemoStatusIcons; 2639 2640 @Override 2641 public void dispatchDemoCommand(String command, Bundle args) { 2642 if (!mDemoModeAllowed) { 2643 mDemoModeAllowed = Settings.Global.getInt(mContext.getContentResolver(), 2644 "sysui_demo_allowed", 0) != 0; 2645 } 2646 if (!mDemoModeAllowed) return; 2647 if (command.equals(COMMAND_ENTER)) { 2648 mDemoMode = true; 2649 } else if (command.equals(COMMAND_EXIT)) { 2650 mDemoMode = false; 2651 checkBarModes(); 2652 } else if (!mDemoMode) { 2653 // automatically enter demo mode on first demo command 2654 dispatchDemoCommand(COMMAND_ENTER, new Bundle()); 2655 } 2656 boolean modeChange = command.equals(COMMAND_ENTER) || command.equals(COMMAND_EXIT); 2657 if (modeChange || command.equals(COMMAND_CLOCK)) { 2658 dispatchDemoCommandToView(command, args, R.id.clock); 2659 } 2660 if (modeChange || command.equals(COMMAND_BATTERY)) { 2661 dispatchDemoCommandToView(command, args, R.id.battery); 2662 } 2663 if (modeChange || command.equals(COMMAND_STATUS)) { 2664 if (mDemoStatusIcons == null) { 2665 mDemoStatusIcons = new DemoStatusIcons(mStatusIcons, mIconSize); 2666 } 2667 mDemoStatusIcons.dispatchDemoCommand(command, args); 2668 } 2669 if (mNetworkController != null && (modeChange || command.equals(COMMAND_NETWORK))) { 2670 mNetworkController.dispatchDemoCommand(command, args); 2671 } 2672 if (modeChange || command.equals(COMMAND_NOTIFICATIONS)) { 2673 View notifications = mStatusBarView == null ? null 2674 : mStatusBarView.findViewById(R.id.notification_icon_area); 2675 if (notifications != null) { 2676 String visible = args.getString("visible"); 2677 int vis = mDemoMode && "false".equals(visible) ? View.INVISIBLE : View.VISIBLE; 2678 notifications.setVisibility(vis); 2679 } 2680 } 2681 if (command.equals(COMMAND_BARS)) { 2682 String mode = args.getString("mode"); 2683 int barMode = "opaque".equals(mode) ? MODE_OPAQUE : 2684 "translucent".equals(mode) ? MODE_TRANSLUCENT : 2685 "semi-transparent".equals(mode) ? MODE_SEMI_TRANSPARENT : 2686 -1; 2687 if (barMode != -1) { 2688 boolean animate = true; 2689 if (mStatusBarView != null) { 2690 mStatusBarView.getBarTransitions().transitionTo(barMode, animate); 2691 } 2692 if (mNavigationBarView != null) { 2693 mNavigationBarView.getBarTransitions().transitionTo(barMode, animate); 2694 } 2695 } 2696 } 2697 } 2698 2699 private void dispatchDemoCommandToView(String command, Bundle args, int id) { 2700 if (mStatusBarView == null) return; 2701 View v = mStatusBarView.findViewById(id); 2702 if (v instanceof DemoMode) { 2703 ((DemoMode)v).dispatchDemoCommand(command, args); 2704 } 2705 } 2706 2707 /** 2708 * @return The {@link StatusBarState} the status bar is in. 2709 */ 2710 public int getBarState() { 2711 return mState; 2712 } 2713 2714 public void showKeyguard() { 2715 setBarState(StatusBarState.KEYGUARD); 2716 updateKeyguardState(); 2717 instantExpandNotificationsPanel(); 2718 mLeaveOpenOnKeyguardHide = false; 2719 } 2720 2721 public void hideKeyguard() { 2722 setBarState(StatusBarState.SHADE); 2723 if (mLeaveOpenOnKeyguardHide) { 2724 mLeaveOpenOnKeyguardHide = false; 2725 mNotificationPanel.animateNextTopPaddingChange(); 2726 } else { 2727 instantCollapseNotificationPanel(); 2728 } 2729 updateKeyguardState(); 2730 } 2731 2732 private void updatePublicMode() { 2733 setLockscreenPublicMode(mState == StatusBarState.KEYGUARD 2734 && mStatusBarKeyguardViewManager.isSecure()); 2735 } 2736 2737 private void updateKeyguardState() { 2738 if (mState == StatusBarState.KEYGUARD) { 2739 mKeyguardStatusView.setVisibility(View.VISIBLE); 2740 mKeyguardIndicationTextView.setVisibility(View.VISIBLE); 2741 mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase); 2742 mNotificationPanel.closeQs(); 2743 } else { 2744 mKeyguardStatusView.setVisibility(View.GONE); 2745 mKeyguardIndicationTextView.setVisibility(View.GONE); 2746 } 2747 if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { 2748 mKeyguardBottomArea.setVisibility(View.VISIBLE); 2749 mHeader.setKeyguardShowing(true); 2750 } else { 2751 mKeyguardBottomArea.setVisibility(View.GONE); 2752 mHeader.setKeyguardShowing(false); 2753 } 2754 2755 updateStackScrollerState(); 2756 updatePublicMode(); 2757 updateRowStates(); 2758 checkBarModes(); 2759 updateNotificationIcons(); 2760 updateCarrierLabelVisibility(false); 2761 } 2762 2763 public void updateStackScrollerState() { 2764 mStackScroller.setDimmed(mState == StatusBarState.KEYGUARD, false /* animate */); 2765 } 2766 2767 public void userActivity() { 2768 if (mState == StatusBarState.KEYGUARD) { 2769 mKeyguardViewMediatorCallback.userActivity(); 2770 } 2771 } 2772 2773 public boolean interceptMediaKey(KeyEvent event) { 2774 return mState == StatusBarState.KEYGUARD 2775 && mStatusBarKeyguardViewManager.interceptMediaKey(event); 2776 } 2777 2778 public boolean onMenuPressed() { 2779 return mState == StatusBarState.KEYGUARD && mStatusBarKeyguardViewManager.onMenuPressed(); 2780 } 2781 2782 public boolean onBackPressed() { 2783 if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { 2784 return mStatusBarKeyguardViewManager.onBackPressed(); 2785 } else { 2786 animateCollapsePanels(); 2787 return true; 2788 } 2789 } 2790 2791 private void showBouncer() { 2792 if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { 2793 mStatusBarKeyguardViewManager.dismiss(); 2794 } 2795 } 2796 2797 private void instantExpandNotificationsPanel() { 2798 2799 // Make our window larger and the panel visible. 2800 makeExpandedVisible(true); 2801 mNotificationPanel.setVisibility(View.VISIBLE); 2802 2803 // Wait for window manager to pickup the change, so we know the maximum height of the panel 2804 // then. 2805 mNotificationPanel.getViewTreeObserver().addOnGlobalLayoutListener( 2806 new ViewTreeObserver.OnGlobalLayoutListener() { 2807 @Override 2808 public void onGlobalLayout() { 2809 if (mStatusBarWindow.getHeight() != getStatusBarHeight()) { 2810 mNotificationPanel.getViewTreeObserver().removeOnGlobalLayoutListener(this); 2811 mNotificationPanel.setExpandedFraction(1); 2812 } 2813 } 2814 }); 2815 } 2816 2817 private void instantCollapseNotificationPanel() { 2818 mNotificationPanel.setExpandedFraction(0); 2819 } 2820 2821 @Override 2822 public void onActivated(View view) { 2823 userActivity(); 2824 mKeyguardIndicationTextView.switchIndication(R.string.notification_tap_again); 2825 mStackScroller.setActivatedChild(view); 2826 } 2827 2828 /** 2829 * @param state The {@link StatusBarState} to set. 2830 */ 2831 public void setBarState(int state) { 2832 mState = state; 2833 mStatusBarWindowManager.setStatusBarState(state); 2834 } 2835 2836 @Override 2837 public void onActivationReset(View view) { 2838 if (view == mStackScroller.getActivatedChild()) { 2839 mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase); 2840 mStackScroller.setActivatedChild(null); 2841 } 2842 } 2843 2844 public void onTrackingStarted() { 2845 if (mState == StatusBarState.KEYGUARD) { 2846 mKeyguardIndicationTextView.switchIndication(R.string.keyguard_unlock); 2847 } 2848 } 2849 2850 public void onTrackingStopped() { 2851 if (mState == StatusBarState.KEYGUARD) { 2852 mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase); 2853 } 2854 } 2855 2856 @Override 2857 protected int getMaxKeyguardNotifications() { 2858 return mKeyguardMaxNotificationCount; 2859 } 2860 2861 public NavigationBarView getNavigationBarView() { 2862 return mNavigationBarView; 2863 } 2864 2865 // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------ 2866 2867 @Override 2868 public void onDraggedDown(View startingChild) { 2869 goToLockedShade(startingChild); 2870 } 2871 2872 @Override 2873 public void onDragDownReset() { 2874 mStackScroller.setDimmed(true /* dimmed */, true /* animated */); 2875 } 2876 2877 public void onThresholdReached() { 2878 mStackScroller.setDimmed(false /* dimmed */, true /* animate */); 2879 } 2880 2881 /** 2882 * If secure with redaction: Show bouncer, go to unlocked shade. 2883 * 2884 * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p> 2885 * 2886 * @param expandView The view to expand after going to the shade. 2887 */ 2888 public void goToLockedShade(View expandView) { 2889 if (expandView instanceof ExpandableNotificationRow) { 2890 ExpandableNotificationRow row = (ExpandableNotificationRow) expandView; 2891 row.setUserExpanded(true); 2892 } 2893 if (isLockscreenPublicMode() && !userAllowsPrivateNotificationsInPublic(mCurrentUserId)) { 2894 mLeaveOpenOnKeyguardHide = true; 2895 showBouncer(); 2896 } else { 2897 mNotificationPanel.animateNextTopPaddingChange(); 2898 setBarState(StatusBarState.SHADE_LOCKED); 2899 updateKeyguardState(); 2900 } 2901 } 2902 2903 /** 2904 * @return a ViewGroup that spans the entire panel which contains the quick settings 2905 */ 2906 public ViewGroup getQuickSettingsOverlayParent() { 2907 return mNotificationPanel; 2908 } 2909 2910 public LinearLayout getSystemIcons() { 2911 return mSystemIcons; 2912 } 2913 2914 /** 2915 * Reattaches the system icons to its normal parent in collapsed status bar. 2916 */ 2917 public void reattachSystemIcons() { 2918 mSystemIconArea.addView(mSystemIcons, 0); 2919 } 2920} 2921