PhoneStatusBar.java revision 657c62c31ecdade22f67f13102c2fb19765cbe13
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.keyguard.KeyguardHostView.OnDismissAction; 26import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT; 27import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; 28import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT; 29import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT; 30import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT; 31 32import android.animation.Animator; 33import android.animation.AnimatorListenerAdapter; 34import android.animation.TimeInterpolator; 35import android.app.ActivityManager; 36import android.app.ActivityManagerNative; 37import android.app.IActivityManager; 38import android.app.Notification; 39import android.app.PendingIntent; 40import android.app.StatusBarManager; 41import android.content.BroadcastReceiver; 42import android.content.Context; 43import android.content.Intent; 44import android.content.IntentFilter; 45import android.content.res.Configuration; 46import android.content.res.Resources; 47import android.database.ContentObserver; 48import android.graphics.Bitmap; 49import android.graphics.Canvas; 50import android.graphics.ColorFilter; 51import android.graphics.PixelFormat; 52import android.graphics.Point; 53import android.graphics.PorterDuff; 54import android.graphics.Rect; 55import android.graphics.drawable.BitmapDrawable; 56import android.graphics.drawable.ColorDrawable; 57import android.graphics.drawable.Drawable; 58import android.inputmethodservice.InputMethodService; 59import android.media.AudioAttributes; 60import android.media.MediaMetadata; 61import android.media.session.MediaController; 62import android.media.session.MediaSession; 63import android.media.session.MediaSessionManager; 64import android.media.session.PlaybackState; 65import android.os.Bundle; 66import android.os.Handler; 67import android.os.IBinder; 68import android.os.Message; 69import android.os.PowerManager; 70import android.os.RemoteException; 71import android.os.SystemClock; 72import android.os.UserHandle; 73import android.provider.Settings; 74import android.service.notification.NotificationListenerService.RankingMap; 75import android.service.notification.StatusBarNotification; 76import android.util.ArraySet; 77import android.util.DisplayMetrics; 78import android.util.EventLog; 79import android.util.Log; 80import android.view.Display; 81import android.view.Gravity; 82import android.view.HardwareCanvas; 83import android.view.KeyEvent; 84import android.view.LayoutInflater; 85import android.view.MotionEvent; 86import android.view.VelocityTracker; 87import android.view.View; 88import android.view.ViewGroup; 89import android.view.ViewGroup.LayoutParams; 90import android.view.ViewPropertyAnimator; 91import android.view.ViewStub; 92import android.view.ViewTreeObserver; 93import android.view.WindowManager; 94import android.view.animation.AccelerateInterpolator; 95import android.view.animation.Animation; 96import android.view.animation.AnimationUtils; 97import android.view.animation.DecelerateInterpolator; 98import android.view.animation.Interpolator; 99import android.view.animation.PathInterpolator; 100import android.widget.FrameLayout; 101import android.widget.ImageView; 102import android.widget.LinearLayout; 103import android.widget.TextView; 104 105import com.android.internal.statusbar.StatusBarIcon; 106import com.android.keyguard.ViewMediatorCallback; 107import com.android.systemui.DemoMode; 108import com.android.systemui.EventLogTags; 109import com.android.systemui.R; 110import com.android.systemui.doze.DozeService; 111import com.android.systemui.keyguard.KeyguardViewMediator; 112import com.android.systemui.qs.QSPanel; 113import com.android.systemui.qs.QSTile; 114import com.android.systemui.statusbar.ActivatableNotificationView; 115import com.android.systemui.statusbar.BaseStatusBar; 116import com.android.systemui.statusbar.CommandQueue; 117import com.android.systemui.statusbar.DragDownHelper; 118import com.android.systemui.statusbar.ExpandableNotificationRow; 119import com.android.systemui.statusbar.GestureRecorder; 120import com.android.systemui.statusbar.KeyguardIndicationController; 121import com.android.systemui.statusbar.NotificationData; 122import com.android.systemui.statusbar.NotificationData.Entry; 123import com.android.systemui.statusbar.NotificationOverflowContainer; 124import com.android.systemui.statusbar.SignalClusterView; 125import com.android.systemui.statusbar.SpeedBumpView; 126import com.android.systemui.statusbar.StatusBarIconView; 127import com.android.systemui.statusbar.StatusBarState; 128import com.android.systemui.statusbar.policy.BatteryController; 129import com.android.systemui.statusbar.policy.BluetoothControllerImpl; 130import com.android.systemui.statusbar.policy.CastControllerImpl; 131import com.android.systemui.statusbar.policy.FlashlightController; 132import com.android.systemui.statusbar.policy.HeadsUpNotificationView; 133import com.android.systemui.statusbar.policy.KeyguardMonitor; 134import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; 135import com.android.systemui.statusbar.policy.UserInfoController; 136import com.android.systemui.statusbar.policy.LocationControllerImpl; 137import com.android.systemui.statusbar.policy.NetworkControllerImpl; 138import com.android.systemui.statusbar.policy.RotationLockControllerImpl; 139import com.android.systemui.statusbar.policy.UserSwitcherController; 140import com.android.systemui.statusbar.policy.ZenModeController; 141import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; 142import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener; 143import com.android.systemui.statusbar.stack.StackScrollAlgorithm; 144import com.android.systemui.statusbar.stack.StackScrollState.ViewState; 145import com.android.systemui.volume.VolumeComponent; 146 147import java.io.FileDescriptor; 148import java.io.PrintWriter; 149import java.util.ArrayList; 150import java.util.Collection; 151import java.util.Collections; 152import java.util.List; 153 154public class PhoneStatusBar extends BaseStatusBar implements DemoMode, 155 DragDownHelper.OnDragDownListener, ActivityStarter { 156 static final String TAG = "PhoneStatusBar"; 157 public static final boolean DEBUG = BaseStatusBar.DEBUG; 158 public static final boolean SPEW = false; 159 public static final boolean DUMPTRUCK = true; // extra dumpsys info 160 public static final boolean DEBUG_GESTURES = false; 161 public static final boolean DEBUG_MEDIA = false; 162 public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false; 163 164 public static final boolean DEBUG_WINDOW_STATE = false; 165 166 // additional instrumentation for testing purposes; intended to be left on during development 167 public static final boolean CHATTY = DEBUG; 168 169 public static final String ACTION_STATUSBAR_START 170 = "com.android.internal.policy.statusbar.START"; 171 172 public static final boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = true; 173 174 private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000; 175 private static final int MSG_CLOSE_PANELS = 1001; 176 private static final int MSG_OPEN_SETTINGS_PANEL = 1002; 177 // 1020-1030 reserved for BaseStatusBar 178 179 private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true; 180 181 private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; // see NotificationManagerService 182 private static final int HIDE_ICONS_BELOW_SCORE = Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER; 183 184 private static final int STATUS_OR_NAV_TRANSIENT = 185 View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT; 186 private static final long AUTOHIDE_TIMEOUT_MS = 3000; 187 188 /** The minimum delay in ms between reports of notification visibility. */ 189 private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500; 190 191 /** 192 * The delay to reset the hint text when the hint animation is finished running. 193 */ 194 private static final int HINT_RESET_DELAY_MS = 1200; 195 196 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() 197 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) 198 .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) 199 .build(); 200 201 PhoneStatusBarPolicy mIconPolicy; 202 203 // These are no longer handled by the policy, because we need custom strategies for them 204 BluetoothControllerImpl mBluetoothController; 205 BatteryController mBatteryController; 206 LocationControllerImpl mLocationController; 207 NetworkControllerImpl mNetworkController; 208 RotationLockControllerImpl mRotationLockController; 209 UserInfoController mUserInfoController; 210 ZenModeController mZenModeController; 211 CastControllerImpl mCastController; 212 VolumeComponent mVolumeComponent; 213 KeyguardUserSwitcher mKeyguardUserSwitcher; 214 FlashlightController mFlashlightController; 215 UserSwitcherController mUserSwitcherController; 216 KeyguardMonitor mKeyguardMonitor; 217 218 int mNaturalBarHeight = -1; 219 int mIconSize = -1; 220 int mIconHPadding = -1; 221 Display mDisplay; 222 Point mCurrentDisplaySize = new Point(); 223 224 StatusBarWindowView mStatusBarWindow; 225 PhoneStatusBarView mStatusBarView; 226 private int mStatusBarWindowState = WINDOW_STATE_SHOWING; 227 private StatusBarWindowManager mStatusBarWindowManager; 228 private UnlockMethodCache mUnlockMethodCache; 229 private DozeServiceHost mDozeServiceHost; 230 231 int mPixelFormat; 232 Object mQueueLock = new Object(); 233 234 // viewgroup containing the normal contents of the statusbar 235 LinearLayout mStatusBarContents; 236 237 // right-hand icons 238 LinearLayout mSystemIconArea; 239 LinearLayout mSystemIcons; 240 241 // left-hand icons 242 LinearLayout mStatusIcons; 243 // the icons themselves 244 IconMerger mNotificationIcons; 245 View mNotificationIconArea; 246 247 // [+> 248 View mMoreIcon; 249 250 // expanded notifications 251 NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window 252 View mExpandedContents; 253 int mNotificationPanelGravity; 254 int mNotificationPanelMarginBottomPx; 255 float mNotificationPanelMinHeightFrac; 256 TextView mNotificationPanelDebugText; 257 258 // settings 259 View mFlipSettingsView; 260 private QSPanel mQSPanel; 261 262 // top bar 263 StatusBarHeaderView mHeader; 264 View mKeyguardStatusView; 265 KeyguardBottomAreaView mKeyguardBottomArea; 266 boolean mLeaveOpenOnKeyguardHide; 267 KeyguardIndicationController mKeyguardIndicationController; 268 269 private boolean mKeyguardFadingAway; 270 private long mKeyguardFadingAwayDelay; 271 private long mKeyguardFadingAwayDuration; 272 273 int mKeyguardMaxNotificationCount; 274 View mDateTimeView; 275 276 // carrier/wifi label 277 private TextView mCarrierLabel; 278 private boolean mCarrierLabelVisible = false; 279 private int mCarrierLabelHeight; 280 private int mStatusBarHeaderHeight; 281 282 private boolean mShowCarrierInPanel = false; 283 284 // position 285 int[] mPositionTmp = new int[2]; 286 boolean mExpandedVisible; 287 288 // on-screen navigation buttons 289 private NavigationBarView mNavigationBarView = null; 290 private int mNavigationBarWindowState = WINDOW_STATE_SHOWING; 291 292 // the tracker view 293 int mTrackingPosition; // the position of the top of the tracking view. 294 295 // ticker 296 private boolean mTickerEnabled; 297 private Ticker mTicker; 298 private View mTickerView; 299 private boolean mTicking; 300 301 // Tracking finger for opening/closing. 302 int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore 303 boolean mTracking; 304 VelocityTracker mVelocityTracker; 305 306 int[] mAbsPos = new int[2]; 307 Runnable mPostCollapseCleanup = null; 308 309 // for disabling the status bar 310 int mDisabled = 0; 311 312 // tracking calls to View.setSystemUiVisibility() 313 int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE; 314 315 DisplayMetrics mDisplayMetrics = new DisplayMetrics(); 316 317 // XXX: gesture research 318 private final GestureRecorder mGestureRec = DEBUG_GESTURES 319 ? new GestureRecorder("/sdcard/statusbar_gestures.dat") 320 : null; 321 322 private int mNavigationIconHints = 0; 323 324 // ensure quick settings is disabled until the current user makes it through the setup wizard 325 private boolean mUserSetup = false; 326 private ContentObserver mUserSetupObserver = new ContentObserver(new Handler()) { 327 @Override 328 public void onChange(boolean selfChange) { 329 final boolean userSetup = 0 != Settings.Secure.getIntForUser( 330 mContext.getContentResolver(), 331 Settings.Secure.USER_SETUP_COMPLETE, 332 0 /*default */, 333 mCurrentUserId); 334 if (MULTIUSER_DEBUG) Log.d(TAG, String.format("User setup changed: " + 335 "selfChange=%s userSetup=%s mUserSetup=%s", 336 selfChange, userSetup, mUserSetup)); 337 338 if (userSetup != mUserSetup) { 339 mUserSetup = userSetup; 340 if (mNotificationPanel != null) { 341 mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned() && userSetup); 342 } 343 if (!mUserSetup && mStatusBarView != null) 344 animateCollapseQuickSettings(); 345 } 346 } 347 }; 348 349 final private ContentObserver mHeadsUpObserver = new ContentObserver(mHandler) { 350 @Override 351 public void onChange(boolean selfChange) { 352 boolean wasUsing = mUseHeadsUp; 353 mUseHeadsUp = ENABLE_HEADS_UP && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt( 354 mContext.getContentResolver(), Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, 355 Settings.Global.HEADS_UP_OFF); 356 mHeadsUpTicker = mUseHeadsUp && 0 != Settings.Global.getInt( 357 mContext.getContentResolver(), SETTING_HEADS_UP_TICKER, 0); 358 Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled")); 359 if (wasUsing != mUseHeadsUp) { 360 if (!mUseHeadsUp) { 361 Log.d(TAG, "dismissing any existing heads up notification on disable event"); 362 setHeadsUpVisibility(false); 363 mHeadsUpNotificationView.release(); 364 removeHeadsUpView(); 365 } else { 366 addHeadsUpView(); 367 } 368 } 369 } 370 }; 371 372 private int mInteractingWindows; 373 private boolean mAutohideSuspended; 374 private int mStatusBarMode; 375 private int mNavigationBarMode; 376 private Boolean mScreenOn; 377 378 private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; 379 private ViewMediatorCallback mKeyguardViewMediatorCallback; 380 private ScrimController mScrimController; 381 382 private final Runnable mAutohide = new Runnable() { 383 @Override 384 public void run() { 385 int requested = mSystemUiVisibility & ~STATUS_OR_NAV_TRANSIENT; 386 if (mSystemUiVisibility != requested) { 387 notifyUiVisibilityChanged(requested); 388 } 389 }}; 390 391 private boolean mVisible; 392 private boolean mWaitingForKeyguardExit; 393 private boolean mDozing; 394 395 private Interpolator mLinearOutSlowIn; 396 private Interpolator mAlphaIn = new PathInterpolator(0f, 0.2f, 1f, 1f); 397 private Interpolator mAlphaOut = new PathInterpolator(0f, 0f, 0.8f, 1f); 398 399 private FrameLayout mBackdrop; 400 private ImageView mBackdropFront, mBackdropBack; 401 402 private MediaSessionManager mMediaSessionManager; 403 private MediaController mMediaController; 404 private String mMediaNotificationKey; 405 private MediaMetadata mMediaMetadata; 406 private MediaController.Callback mMediaListener 407 = new MediaController.Callback() { 408 @Override 409 public void onPlaybackStateChanged(PlaybackState state) { 410 super.onPlaybackStateChanged(state); 411 if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onPlaybackStateChanged: " + state); 412 } 413 414 @Override 415 public void onMetadataChanged(MediaMetadata metadata) { 416 super.onMetadataChanged(metadata); 417 if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata); 418 mMediaMetadata = metadata; 419 updateMediaMetaData(true); 420 } 421 }; 422 423 private final OnChildLocationsChangedListener mOnChildLocationsChangedListener = 424 new OnChildLocationsChangedListener() { 425 @Override 426 public void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout) { 427 userActivity(); 428 } 429 }; 430 431 private int mDisabledUnmodified; 432 433 /** Keys of notifications currently visible to the user. */ 434 private final ArraySet<String> mCurrentlyVisibleNotifications = new ArraySet<String>(); 435 private long mLastVisibilityReportUptimeMs; 436 437 private final ShadeUpdates mShadeUpdates = new ShadeUpdates(); 438 439 private int mDrawCount; 440 441 private static final int VISIBLE_LOCATIONS = ViewState.LOCATION_FIRST_CARD 442 | ViewState.LOCATION_TOP_STACK_PEEKING 443 | ViewState.LOCATION_MAIN_AREA 444 | ViewState.LOCATION_BOTTOM_STACK_PEEKING; 445 446 private final OnChildLocationsChangedListener mNotificationLocationsChangedListener = 447 new OnChildLocationsChangedListener() { 448 @Override 449 public void onChildLocationsChanged( 450 NotificationStackScrollLayout stackScrollLayout) { 451 if (mHandler.hasCallbacks(mVisibilityReporter)) { 452 // Visibilities will be reported when the existing 453 // callback is executed. 454 return; 455 } 456 // Calculate when we're allowed to run the visibility 457 // reporter. Note that this timestamp might already have 458 // passed. That's OK, the callback will just be executed 459 // ASAP. 460 long nextReportUptimeMs = 461 mLastVisibilityReportUptimeMs + VISIBILITY_REPORT_MIN_DELAY_MS; 462 mHandler.postAtTime(mVisibilityReporter, nextReportUptimeMs); 463 } 464 }; 465 466 // Tracks notifications currently visible in mNotificationStackScroller and 467 // emits visibility events via NoMan on changes. 468 private final Runnable mVisibilityReporter = new Runnable() { 469 private final ArrayList<String> mTmpNewlyVisibleNotifications = new ArrayList<String>(); 470 private final ArrayList<String> mTmpCurrentlyVisibleNotifications = new ArrayList<String>(); 471 472 @Override 473 public void run() { 474 mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis(); 475 476 // 1. Loop over mNotificationData entries: 477 // A. Keep list of visible notifications. 478 // B. Keep list of previously hidden, now visible notifications. 479 // 2. Compute no-longer visible notifications by removing currently 480 // visible notifications from the set of previously visible 481 // notifications. 482 // 3. Report newly visible and no-longer visible notifications. 483 // 4. Keep currently visible notifications for next report. 484 int N = mNotificationData.size(); 485 for (int i = 0; i < N; i++) { 486 Entry entry = mNotificationData.get(i); 487 String key = entry.notification.getKey(); 488 boolean previouslyVisible = mCurrentlyVisibleNotifications.contains(key); 489 boolean currentlyVisible = 490 (mStackScroller.getChildLocation(entry.row) & VISIBLE_LOCATIONS) != 0; 491 if (currentlyVisible) { 492 // Build new set of visible notifications. 493 mTmpCurrentlyVisibleNotifications.add(key); 494 } 495 if (!previouslyVisible && currentlyVisible) { 496 mTmpNewlyVisibleNotifications.add(key); 497 } 498 } 499 ArraySet<String> noLongerVisibleNotifications = mCurrentlyVisibleNotifications; 500 noLongerVisibleNotifications.removeAll(mTmpCurrentlyVisibleNotifications); 501 502 logNotificationVisibilityChanges( 503 mTmpNewlyVisibleNotifications, noLongerVisibleNotifications); 504 505 mCurrentlyVisibleNotifications.clear(); 506 mCurrentlyVisibleNotifications.addAll(mTmpCurrentlyVisibleNotifications); 507 508 mTmpNewlyVisibleNotifications.clear(); 509 mTmpCurrentlyVisibleNotifications.clear(); 510 } 511 }; 512 513 private final View.OnClickListener mOverflowClickListener = new View.OnClickListener() { 514 @Override 515 public void onClick(View v) { 516 goToLockedShade(null); 517 } 518 }; 519 520 @Override 521 protected void setShowLockscreenNotifications(boolean show) { 522 super.setShowLockscreenNotifications(show); 523 updateStackScrollerState(); 524 } 525 526 @Override 527 public void start() { 528 mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE)) 529 .getDefaultDisplay(); 530 updateDisplaySize(); 531 super.start(); // calls createAndAddWindows() 532 533 mMediaSessionManager 534 = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE); 535 // TODO: use MediaSessionManager.SessionListener to hook us up to future updates 536 // in session state 537 538 addNavigationBar(); 539 540 // Lastly, call to the icon policy to install/update all the icons. 541 mIconPolicy = new PhoneStatusBarPolicy(mContext); 542 mSettingsObserver.onChange(false); // set up 543 544 mHeadsUpObserver.onChange(true); // set up 545 if (ENABLE_HEADS_UP) { 546 mContext.getContentResolver().registerContentObserver( 547 Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED), true, 548 mHeadsUpObserver); 549 mContext.getContentResolver().registerContentObserver( 550 Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true, 551 mHeadsUpObserver); 552 } 553 mUnlockMethodCache = UnlockMethodCache.getInstance(mContext); 554 startKeyguard(); 555 556 mDozeServiceHost = new DozeServiceHost(); 557 putComponent(DozeService.Host.class, mDozeServiceHost); 558 559 setControllerUsers(); 560 } 561 562 // ================================================================================ 563 // Constructing the view 564 // ================================================================================ 565 protected PhoneStatusBarView makeStatusBarView() { 566 final Context context = mContext; 567 568 Resources res = context.getResources(); 569 570 updateDisplaySize(); // populates mDisplayMetrics 571 updateResources(); 572 573 mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size); 574 575 mStatusBarWindow = (StatusBarWindowView) View.inflate(context, 576 R.layout.super_status_bar, null); 577 mStatusBarWindow.mService = this; 578 mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() { 579 @Override 580 public boolean onTouch(View v, MotionEvent event) { 581 checkUserAutohide(v, event); 582 if (event.getAction() == MotionEvent.ACTION_DOWN) { 583 if (mExpandedVisible) { 584 animateCollapsePanels(); 585 } 586 } 587 return mStatusBarWindow.onTouchEvent(event); 588 }}); 589 590 mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar); 591 mStatusBarView.setBar(this); 592 593 PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder); 594 mStatusBarView.setPanelHolder(holder); 595 596 mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById( 597 R.id.notification_panel); 598 mNotificationPanel.setStatusBar(this); 599 600 if (!ActivityManager.isHighEndGfx()) { 601 mStatusBarWindow.setBackground(null); 602 mNotificationPanel.setBackground(new FastColorDrawable(context.getResources().getColor( 603 R.color.notification_panel_solid_background))); 604 } 605 if (ENABLE_HEADS_UP) { 606 mHeadsUpNotificationView = 607 (HeadsUpNotificationView) View.inflate(context, R.layout.heads_up, null); 608 mHeadsUpNotificationView.setVisibility(View.GONE); 609 mHeadsUpNotificationView.setBar(this); 610 } 611 if (MULTIUSER_DEBUG) { 612 mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById( 613 R.id.header_debug_info); 614 mNotificationPanelDebugText.setVisibility(View.VISIBLE); 615 } 616 617 updateShowSearchHoldoff(); 618 619 try { 620 boolean showNav = mWindowManagerService.hasNavigationBar(); 621 if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav); 622 if (showNav) { 623 mNavigationBarView = 624 (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null); 625 626 mNavigationBarView.setDisabledFlags(mDisabled); 627 mNavigationBarView.setBar(this); 628 mNavigationBarView.setOnTouchListener(new View.OnTouchListener() { 629 @Override 630 public boolean onTouch(View v, MotionEvent event) { 631 checkUserAutohide(v, event); 632 return false; 633 }}); 634 } 635 } catch (RemoteException ex) { 636 // no window manager? good luck with that 637 } 638 639 // figure out which pixel-format to use for the status bar. 640 mPixelFormat = PixelFormat.OPAQUE; 641 642 mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area); 643 mSystemIcons = (LinearLayout) mStatusBarView.findViewById(R.id.system_icons); 644 mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons); 645 mNotificationIconArea = mStatusBarView.findViewById(R.id.notification_icon_area_inner); 646 mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons); 647 mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon); 648 mNotificationIcons.setOverflowIndicator(mMoreIcon); 649 mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents); 650 651 mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById( 652 R.id.notification_stack_scroller); 653 mStackScroller.setLongPressListener(getNotificationLongClicker()); 654 655 mKeyguardIconOverflowContainer = 656 (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate( 657 R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false); 658 mKeyguardIconOverflowContainer.setOnActivatedListener(this); 659 mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener); 660 mStackScroller.addView(mKeyguardIconOverflowContainer); 661 662 SpeedBumpView speedBump = (SpeedBumpView) LayoutInflater.from(mContext).inflate( 663 R.layout.status_bar_notification_speed_bump, mStackScroller, false); 664 mStackScroller.setSpeedBumpView(speedBump); 665 mExpandedContents = mStackScroller; 666 667 mScrimController = new ScrimController(mStatusBarWindow.findViewById(R.id.scrim_behind), 668 mStatusBarWindow.findViewById(R.id.scrim_in_front)); 669 mStatusBarView.setScrimController(mScrimController); 670 671 mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header); 672 mHeader.setActivityStarter(this); 673 mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view); 674 mKeyguardBottomArea = 675 (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area); 676 mKeyguardBottomArea.setActivityStarter(this); 677 mKeyguardIndicationController = new KeyguardIndicationController(mContext, 678 (KeyguardIndicationTextView) mStatusBarWindow.findViewById( 679 R.id.keyguard_indication_text)); 680 681 mDateTimeView = mHeader.findViewById(R.id.datetime); 682 if (mDateTimeView != null) { 683 mDateTimeView.setOnClickListener(mClockClickListener); 684 mDateTimeView.setEnabled(true); 685 } 686 687 mTickerEnabled = res.getBoolean(R.bool.enable_ticker); 688 if (mTickerEnabled) { 689 final ViewStub tickerStub = (ViewStub) mStatusBarView.findViewById(R.id.ticker_stub); 690 if (tickerStub != null) { 691 mTickerView = tickerStub.inflate(); 692 mTicker = new MyTicker(context, mStatusBarView); 693 694 TickerView tickerView = (TickerView) mStatusBarView.findViewById(R.id.tickerText); 695 tickerView.mTicker = mTicker; 696 } 697 } 698 699 mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); 700 701 // set the inital view visibility 702 setAreThereNotifications(); 703 704 // Other icons 705 mLocationController = new LocationControllerImpl(mContext); // will post a notification 706 mBatteryController = new BatteryController(mContext); 707 mNetworkController = new NetworkControllerImpl(mContext); 708 mBluetoothController = new BluetoothControllerImpl(mContext); 709 if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)) { 710 mRotationLockController = new RotationLockControllerImpl(mContext); 711 } 712 mUserInfoController = new UserInfoController(mContext); 713 mVolumeComponent = getComponent(VolumeComponent.class); 714 mZenModeController = mVolumeComponent.getZenController(); 715 mCastController = new CastControllerImpl(mContext); 716 final SignalClusterView signalCluster = 717 (SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster); 718 719 mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext, 720 (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher), mHeader); 721 722 mNetworkController.addSignalCluster(signalCluster); 723 signalCluster.setNetworkController(mNetworkController); 724 final boolean isAPhone = mNetworkController.hasVoiceCallingFeature(); 725 if (isAPhone) { 726 mNetworkController.addEmergencyLabelView(mHeader); 727 } 728 729 mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label); 730 mShowCarrierInPanel = (mCarrierLabel != null); 731 if (DEBUG) Log.v(TAG, "carrierlabel=" + mCarrierLabel + " show=" + mShowCarrierInPanel); 732 if (mShowCarrierInPanel) { 733 mCarrierLabel.setVisibility(mCarrierLabelVisible ? View.VISIBLE : View.INVISIBLE); 734 735 // for mobile devices, we always show mobile connection info here (SPN/PLMN) 736 // for other devices, we show whatever network is connected 737 if (mNetworkController.hasMobileDataFeature()) { 738 mNetworkController.addMobileLabelView(mCarrierLabel); 739 } else { 740 mNetworkController.addCombinedLabelView(mCarrierLabel); 741 } 742 743 // set up the dynamic hide/show of the label 744 // TODO: uncomment, handle this for the Stack scroller aswell 745// ((NotificationRowLayout) mStackScroller) 746// .setOnSizeChangedListener(new OnSizeChangedListener() { 747// @Override 748// public void onSizeChanged(View view, int w, int h, int oldw, int oldh) { 749// updateCarrierLabelVisibility(false); 750 } 751 752 mFlashlightController = new FlashlightController(mContext); 753 mUserSwitcherController = new UserSwitcherController(mContext); 754 mKeyguardMonitor = new KeyguardMonitor(); 755 756 // Set up the quick settings tile panel 757 mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel); 758 if (mQSPanel != null) { 759 final QSTileHost qsh = new QSTileHost(mContext, this, 760 mBluetoothController, mLocationController, mRotationLockController, 761 mNetworkController, mZenModeController, null /*tethering*/, 762 mCastController, mFlashlightController, 763 mUserSwitcherController, mKeyguardMonitor); 764 mQSPanel.setHost(qsh); 765 for (QSTile<?> tile : qsh.getTiles()) { 766 mQSPanel.addTile(tile); 767 } 768 mHeader.setQSPanel(mQSPanel); 769 } 770 771 mBackdrop = (FrameLayout) mStatusBarWindow.findViewById(R.id.backdrop); 772 mBackdropFront = (ImageView) mBackdrop.findViewById(R.id.backdrop_front); 773 mBackdropBack = (ImageView) mBackdrop.findViewById(R.id.backdrop_back); 774 775 // User info. Trigger first load. 776 mHeader.setUserInfoController(mUserInfoController); 777 mUserInfoController.reloadUserInfo(); 778 779 mHeader.setBatteryController(mBatteryController); 780 781 PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 782 mBroadcastReceiver.onReceive(mContext, 783 new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF)); 784 785 // receive broadcasts 786 IntentFilter filter = new IntentFilter(); 787 filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 788 filter.addAction(Intent.ACTION_SCREEN_OFF); 789 filter.addAction(Intent.ACTION_SCREEN_ON); 790 if (DEBUG_MEDIA_FAKE_ARTWORK) { 791 filter.addAction("fake_artwork"); 792 } 793 filter.addAction(ACTION_DEMO); 794 context.registerReceiver(mBroadcastReceiver, filter); 795 796 // listen for USER_SETUP_COMPLETE setting (per-user) 797 resetUserSetupObserver(); 798 799 startGlyphRasterizeHack(); 800 return mStatusBarView; 801 } 802 803 /** 804 * Hack to improve glyph rasterization for scaled text views. 805 */ 806 private void startGlyphRasterizeHack() { 807 mStatusBarView.getViewTreeObserver().addOnPreDrawListener( 808 new ViewTreeObserver.OnPreDrawListener() { 809 @Override 810 public boolean onPreDraw() { 811 if (mDrawCount == 1) { 812 mStatusBarView.getViewTreeObserver().removeOnPreDrawListener(this); 813 HardwareCanvas.setProperty("extraRasterBucket", 814 Float.toString(StackScrollAlgorithm.DIMMED_SCALE)); 815 HardwareCanvas.setProperty("extraRasterBucket", Float.toString( 816 mContext.getResources().getDimensionPixelSize( 817 R.dimen.qs_time_collapsed_size) 818 / mContext.getResources().getDimensionPixelSize( 819 R.dimen.qs_time_expanded_size))); 820 } 821 mDrawCount++; 822 return true; 823 } 824 }); 825 } 826 827 @Override 828 protected void setZenMode(int mode) { 829 super.setZenMode(mode); 830 if (mIconPolicy != null) { 831 mIconPolicy.setZenMode(mode); 832 } 833 } 834 835 private void startKeyguard() { 836 KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class); 837 mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this, 838 mStatusBarWindow, mStatusBarWindowManager, mScrimController); 839 mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback(); 840 } 841 842 @Override 843 protected void onShowSearchPanel() { 844 if (mNavigationBarView != null) { 845 mNavigationBarView.getBarTransitions().setContentVisible(false); 846 } 847 } 848 849 @Override 850 protected void onHideSearchPanel() { 851 if (mNavigationBarView != null) { 852 mNavigationBarView.getBarTransitions().setContentVisible(true); 853 } 854 } 855 856 @Override 857 protected View getStatusBarView() { 858 return mStatusBarView; 859 } 860 861 public StatusBarWindowView getStatusBarWindow() { 862 return mStatusBarWindow; 863 } 864 865 @Override 866 protected WindowManager.LayoutParams getSearchLayoutParams(LayoutParams layoutParams) { 867 boolean opaque = false; 868 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 869 LayoutParams.MATCH_PARENT, 870 LayoutParams.MATCH_PARENT, 871 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, 872 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 873 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM 874 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, 875 (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT)); 876 if (ActivityManager.isHighEndGfx()) { 877 lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 878 } 879 lp.gravity = Gravity.BOTTOM | Gravity.START; 880 lp.setTitle("SearchPanel"); 881 // TODO: Define custom animation for Search panel 882 lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications; 883 lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED 884 | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; 885 return lp; 886 } 887 888 @Override 889 protected void updateSearchPanel() { 890 super.updateSearchPanel(); 891 if (mNavigationBarView != null) { 892 mNavigationBarView.setDelegateView(mSearchPanelView); 893 } 894 } 895 896 @Override 897 public void showSearchPanel() { 898 super.showSearchPanel(); 899 mHandler.removeCallbacks(mShowSearchPanel); 900 901 // we want to freeze the sysui state wherever it is 902 mSearchPanelView.setSystemUiVisibility(mSystemUiVisibility); 903 904 if (mNavigationBarView != null) { 905 WindowManager.LayoutParams lp = 906 (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams(); 907 lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; 908 mWindowManager.updateViewLayout(mNavigationBarView, lp); 909 } 910 } 911 912 @Override 913 public void hideSearchPanel() { 914 super.hideSearchPanel(); 915 if (mNavigationBarView != null) { 916 WindowManager.LayoutParams lp = 917 (android.view.WindowManager.LayoutParams) mNavigationBarView.getLayoutParams(); 918 lp.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; 919 mWindowManager.updateViewLayout(mNavigationBarView, lp); 920 } 921 } 922 923 public int getStatusBarHeight() { 924 if (mNaturalBarHeight < 0) { 925 final Resources res = mContext.getResources(); 926 mNaturalBarHeight = 927 res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); 928 } 929 return mNaturalBarHeight; 930 } 931 932 private View.OnClickListener mRecentsClickListener = new View.OnClickListener() { 933 public void onClick(View v) { 934 awakenDreams(); 935 toggleRecentApps(); 936 } 937 }; 938 939 private View.OnLongClickListener mLockToAppClickListener = new View.OnLongClickListener() { 940 @Override 941 public boolean onLongClick(View v) { 942 toggleLockedApp(); 943 return true; 944 } 945 }; 946 947 private int mShowSearchHoldoff = 0; 948 private Runnable mShowSearchPanel = new Runnable() { 949 public void run() { 950 showSearchPanel(); 951 awakenDreams(); 952 } 953 }; 954 955 View.OnTouchListener mHomeActionListener = new View.OnTouchListener() { 956 public boolean onTouch(View v, MotionEvent event) { 957 switch(event.getAction()) { 958 case MotionEvent.ACTION_DOWN: 959 if (!shouldDisableNavbarGestures()) { 960 mHandler.removeCallbacks(mShowSearchPanel); 961 mHandler.postDelayed(mShowSearchPanel, mShowSearchHoldoff); 962 } 963 break; 964 965 case MotionEvent.ACTION_UP: 966 case MotionEvent.ACTION_CANCEL: 967 mHandler.removeCallbacks(mShowSearchPanel); 968 awakenDreams(); 969 break; 970 } 971 return false; 972 } 973 }; 974 975 private void awakenDreams() { 976 if (mDreamManager != null) { 977 try { 978 mDreamManager.awaken(); 979 } catch (RemoteException e) { 980 // fine, stay asleep then 981 } 982 } 983 } 984 985 private void prepareNavigationBarView() { 986 mNavigationBarView.reorient(); 987 988 mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener); 989 mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener); 990 mNavigationBarView.getRecentsButton().setLongClickable(true); 991 mNavigationBarView.getRecentsButton().setOnLongClickListener(mLockToAppClickListener); 992 mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener); 993 updateSearchPanel(); 994 } 995 996 // For small-screen devices (read: phones) that lack hardware navigation buttons 997 private void addNavigationBar() { 998 if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView); 999 if (mNavigationBarView == null) return; 1000 1001 prepareNavigationBarView(); 1002 1003 mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams()); 1004 } 1005 1006 private void repositionNavigationBar() { 1007 if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return; 1008 1009 prepareNavigationBarView(); 1010 1011 mWindowManager.updateViewLayout(mNavigationBarView, getNavigationBarLayoutParams()); 1012 } 1013 1014 private void notifyNavigationBarScreenOn(boolean screenOn) { 1015 if (mNavigationBarView == null) return; 1016 mNavigationBarView.notifyScreenOn(screenOn); 1017 } 1018 1019 private WindowManager.LayoutParams getNavigationBarLayoutParams() { 1020 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 1021 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 1022 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, 1023 0 1024 | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING 1025 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 1026 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 1027 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH 1028 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, 1029 PixelFormat.TRANSLUCENT); 1030 // this will allow the navbar to run in an overlay on devices that support this 1031 if (ActivityManager.isHighEndGfx()) { 1032 lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 1033 } 1034 1035 lp.setTitle("NavigationBar"); 1036 lp.windowAnimations = 0; 1037 return lp; 1038 } 1039 1040 private void addHeadsUpView() { 1041 int headsUpHeight = mContext.getResources() 1042 .getDimensionPixelSize(R.dimen.heads_up_window_height); 1043 WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 1044 LayoutParams.MATCH_PARENT, headsUpHeight, 1045 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, // above the status bar! 1046 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 1047 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS 1048 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 1049 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 1050 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM 1051 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, 1052 PixelFormat.TRANSLUCENT); 1053 lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 1054 lp.gravity = Gravity.TOP; 1055 lp.setTitle("Heads Up"); 1056 lp.packageName = mContext.getPackageName(); 1057 lp.windowAnimations = R.style.Animation_StatusBar_HeadsUp; 1058 1059 mWindowManager.addView(mHeadsUpNotificationView, lp); 1060 } 1061 1062 private void removeHeadsUpView() { 1063 mWindowManager.removeView(mHeadsUpNotificationView); 1064 } 1065 1066 public void refreshAllStatusBarIcons() { 1067 refreshAllIconsForLayout(mStatusIcons); 1068 refreshAllIconsForLayout(mNotificationIcons); 1069 } 1070 1071 private void refreshAllIconsForLayout(LinearLayout ll) { 1072 final int count = ll.getChildCount(); 1073 for (int n = 0; n < count; n++) { 1074 View child = ll.getChildAt(n); 1075 if (child instanceof StatusBarIconView) { 1076 ((StatusBarIconView) child).updateDrawable(); 1077 } 1078 } 1079 } 1080 1081 public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) { 1082 if (SPEW) Log.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex 1083 + " icon=" + icon); 1084 StatusBarIconView view = new StatusBarIconView(mContext, slot, null); 1085 view.set(icon); 1086 mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize)); 1087 } 1088 1089 public void updateIcon(String slot, int index, int viewIndex, 1090 StatusBarIcon old, StatusBarIcon icon) { 1091 if (SPEW) Log.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex 1092 + " old=" + old + " icon=" + icon); 1093 StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex); 1094 view.set(icon); 1095 } 1096 1097 public void removeIcon(String slot, int index, int viewIndex) { 1098 if (SPEW) Log.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex); 1099 mStatusIcons.removeViewAt(viewIndex); 1100 } 1101 1102 public UserHandle getCurrentUserHandle() { 1103 return new UserHandle(mCurrentUserId); 1104 } 1105 1106 @Override 1107 public void addNotification(StatusBarNotification notification, RankingMap ranking) { 1108 if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey()); 1109 if (mUseHeadsUp && shouldInterrupt(notification)) { 1110 if (DEBUG) Log.d(TAG, "launching notification in heads up mode"); 1111 Entry interruptionCandidate = new Entry(notification, null); 1112 ViewGroup holder = mHeadsUpNotificationView.getHolder(); 1113 if (inflateViewsForHeadsUp(interruptionCandidate, holder)) { 1114 // 1. Populate mHeadsUpNotificationView 1115 mHeadsUpNotificationView.showNotification(interruptionCandidate); 1116 1117 // do not show the notification in the shade, yet. 1118 return; 1119 } 1120 } 1121 1122 Entry shadeEntry = createNotificationViews(notification); 1123 if (shadeEntry == null) { 1124 return; 1125 } 1126 1127 if (notification.getNotification().fullScreenIntent != null) { 1128 // Stop screensaver if the notification has a full-screen intent. 1129 // (like an incoming phone call) 1130 awakenDreams(); 1131 1132 // not immersive & a full-screen alert should be shown 1133 if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent"); 1134 try { 1135 notification.getNotification().fullScreenIntent.send(); 1136 } catch (PendingIntent.CanceledException e) { 1137 } 1138 } else { 1139 // usual case: status bar visible & not immersive 1140 1141 // show the ticker if there isn't already a heads up 1142 if (mHeadsUpNotificationView.getEntry() == null) { 1143 tick(notification, true); 1144 } 1145 } 1146 addNotificationViews(shadeEntry, ranking); 1147 // Recalculate the position of the sliding windows and the titles. 1148 setAreThereNotifications(); 1149 updateExpandedViewPos(EXPANDED_LEAVE_ALONE); 1150 } 1151 1152 public void displayNotificationFromHeadsUp(StatusBarNotification notification) { 1153 NotificationData.Entry shadeEntry = createNotificationViews(notification); 1154 if (shadeEntry == null) { 1155 return; 1156 } 1157 shadeEntry.setInterruption(); 1158 1159 addNotificationViews(shadeEntry, null); 1160 // Recalculate the position of the sliding windows and the titles. 1161 setAreThereNotifications(); 1162 updateExpandedViewPos(EXPANDED_LEAVE_ALONE); 1163 } 1164 1165 @Override 1166 public void resetHeadsUpDecayTimer() { 1167 mHandler.removeMessages(MSG_DECAY_HEADS_UP); 1168 if (mUseHeadsUp && mHeadsUpNotificationDecay > 0 1169 && mHeadsUpNotificationView.isClearable()) { 1170 mHandler.sendEmptyMessageDelayed(MSG_DECAY_HEADS_UP, mHeadsUpNotificationDecay); 1171 } 1172 } 1173 1174 @Override 1175 public void scheduleHeadsUpOpen() { 1176 mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP); 1177 } 1178 1179 @Override 1180 public void scheduleHeadsUpClose() { 1181 mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP); 1182 } 1183 1184 @Override 1185 public void scheduleHeadsUpEscalation() { 1186 mHandler.sendEmptyMessage(MSG_ESCALATE_HEADS_UP); 1187 } 1188 1189 @Override 1190 protected void updateNotificationRanking(RankingMap ranking) { 1191 mNotificationData.updateRanking(ranking); 1192 updateNotifications(); 1193 } 1194 1195 @Override 1196 public void removeNotification(String key, RankingMap ranking) { 1197 if (ENABLE_HEADS_UP && mHeadsUpNotificationView.getEntry() != null 1198 && key.equals(mHeadsUpNotificationView.getEntry().notification.getKey())) { 1199 mHeadsUpNotificationView.clear(); 1200 } 1201 1202 StatusBarNotification old = removeNotificationViews(key, ranking); 1203 if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old); 1204 1205 if (old != null) { 1206 // Cancel the ticker if it's still running 1207 if (mTickerEnabled) { 1208 mTicker.removeEntry(old); 1209 } 1210 1211 // Recalculate the position of the sliding windows and the titles. 1212 updateExpandedViewPos(EXPANDED_LEAVE_ALONE); 1213 1214 if (CLOSE_PANEL_WHEN_EMPTIED && mNotificationData.size() == 0 1215 && !mNotificationPanel.isTracking() && mState != StatusBarState.KEYGUARD) { 1216 animateCollapsePanels(); 1217 } 1218 } 1219 setAreThereNotifications(); 1220 } 1221 1222 @Override 1223 protected void refreshLayout(int layoutDirection) { 1224 if (mNavigationBarView != null) { 1225 mNavigationBarView.setLayoutDirection(layoutDirection); 1226 } 1227 refreshAllStatusBarIcons(); 1228 } 1229 1230 private void updateShowSearchHoldoff() { 1231 mShowSearchHoldoff = mContext.getResources().getInteger( 1232 R.integer.config_show_search_delay); 1233 } 1234 1235 private void updateNotificationShade() { 1236 if (mStackScroller == null) return; 1237 1238 int N = mNotificationData.size(); 1239 1240 ArrayList<View> toShow = new ArrayList<View>(); 1241 1242 final boolean provisioned = isDeviceProvisioned(); 1243 // If the device hasn't been through Setup, we only show system notifications 1244 for (int i=0; i<N; i++) { 1245 Entry ent = mNotificationData.get(i); 1246 if (!(provisioned || showNotificationEvenIfUnprovisioned(ent.notification))) continue; 1247 1248 if (!notificationIsForCurrentProfiles(ent.notification)) continue; 1249 1250 final boolean hideSensitive = shouldHideSensitiveContents(ent.notification.getUserId()); 1251 final int vis = ent.notification.getNotification().visibility; 1252 1253 // when isLockscreenPublicMode() we suppress VISIBILITY_SECRET notifications 1254 if (vis == Notification.VISIBILITY_SECRET && hideSensitive) { 1255 continue; 1256 } 1257 1258 // when isLockscreenPublicMode() we show the public form of VISIBILITY_PRIVATE notifications 1259 boolean showingPublic = vis == Notification.VISIBILITY_PRIVATE && hideSensitive; 1260 ent.row.setShowingPublic(showingPublic); 1261 if (ent.autoRedacted && ent.legacy) { 1262 if (showingPublic) { 1263 ent.row.setShowingLegacyBackground(false); 1264 } else { 1265 ent.row.setShowingLegacyBackground(true); 1266 } 1267 } 1268 toShow.add(ent.row); 1269 } 1270 1271 ArrayList<View> toRemove = new ArrayList<View>(); 1272 for (int i=0; i< mStackScroller.getChildCount(); i++) { 1273 View child = mStackScroller.getChildAt(i); 1274 if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) { 1275 toRemove.add(child); 1276 } 1277 } 1278 1279 for (View remove : toRemove) { 1280 mStackScroller.removeView(remove); 1281 } 1282 for (int i=0; i<toShow.size(); i++) { 1283 View v = toShow.get(i); 1284 if (v.getParent() == null) { 1285 mStackScroller.addView(v); 1286 } 1287 } 1288 1289 // So after all this work notifications still aren't sorted correctly. 1290 // Let's do that now by advancing through toShow and mStackScroller in 1291 // lock-step, making sure mStackScroller matches what we see in toShow. 1292 int j = 0; 1293 for (int i = 0; i < mStackScroller.getChildCount(); i++) { 1294 View child = mStackScroller.getChildAt(i); 1295 if (!(child instanceof ExpandableNotificationRow)) { 1296 // We don't care about non-notification views. 1297 continue; 1298 } 1299 1300 if (child == toShow.get(j)) { 1301 // Everything is well, advance both lists. 1302 j++; 1303 continue; 1304 } 1305 1306 // Oops, wrong notification at this position. Put the right one 1307 // here and advance both lists. 1308 mStackScroller.changeViewPosition(toShow.get(j), i); 1309 j++; 1310 } 1311 updateRowStates(); 1312 updateSpeedbump(); 1313 mNotificationPanel.setQsExpansionEnabled(provisioned && mUserSetup); 1314 mShadeUpdates.check(); 1315 } 1316 1317 private void updateSpeedbump() { 1318 int speedbumpIndex = -1; 1319 int currentIndex = 0; 1320 for (int i = 0; i < mNotificationData.size(); i++) { 1321 Entry entry = mNotificationData.get(i); 1322 if (entry.row.getParent() == null) { 1323 // This view isn't even added, so the stack scroller doesn't 1324 // know about it. Ignore completely. 1325 continue; 1326 } 1327 if (entry.row.getVisibility() != View.GONE && 1328 mNotificationData.isAmbient(entry.key)) { 1329 speedbumpIndex = currentIndex; 1330 break; 1331 } 1332 currentIndex++; 1333 } 1334 mStackScroller.updateSpeedBumpIndex(speedbumpIndex); 1335 } 1336 1337 @Override 1338 protected void updateNotifications() { 1339 // TODO: Move this into updateNotificationIcons()? 1340 if (mNotificationIcons == null) return; 1341 1342 updateNotificationShade(); 1343 updateNotificationIcons(); 1344 } 1345 1346 /** 1347 * Returns true if we're on a secure lockscreen and the user wants to hide "sensitive" 1348 * notification data. If so, private notifications should show their (possibly 1349 * auto-generated) publicVersion, and secret notifications should be totally invisible. 1350 */ 1351 private boolean shouldHideSensitiveContents(int userid) { 1352 return isLockscreenPublicMode() && !userAllowsPrivateNotificationsInPublic(userid); 1353 } 1354 1355 private void updateNotificationIcons() { 1356 final LinearLayout.LayoutParams params 1357 = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight); 1358 1359 int N = mNotificationData.size(); 1360 1361 if (DEBUG) { 1362 Log.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" + 1363 mNotificationIcons); 1364 } 1365 1366 ArrayList<View> toShow = new ArrayList<View>(); 1367 1368 final boolean provisioned = isDeviceProvisioned(); 1369 // If the device hasn't been through Setup, we only show system notifications 1370 for (int i=0; i<N; i++) { 1371 Entry ent = mNotificationData.get(i); 1372 if (!((provisioned && ent.notification.getScore() >= HIDE_ICONS_BELOW_SCORE) 1373 || showNotificationEvenIfUnprovisioned(ent.notification))) continue; 1374 if (!notificationIsForCurrentProfiles(ent.notification)) continue; 1375 if (ent.notification.getNotification().visibility == Notification.VISIBILITY_SECRET 1376 && shouldHideSensitiveContents(ent.notification.getUserId())) { 1377 // in "public" mode (atop a secure keyguard), secret notifs are totally hidden 1378 continue; 1379 } 1380 toShow.add(ent.icon); 1381 } 1382 1383 ArrayList<View> toRemove = new ArrayList<View>(); 1384 for (int i=0; i<mNotificationIcons.getChildCount(); i++) { 1385 View child = mNotificationIcons.getChildAt(i); 1386 if (!toShow.contains(child)) { 1387 toRemove.add(child); 1388 } 1389 } 1390 1391 for (View remove : toRemove) { 1392 mNotificationIcons.removeView(remove); 1393 } 1394 1395 for (int i=0; i<toShow.size(); i++) { 1396 View v = toShow.get(i); 1397 if (v.getParent() == null) { 1398 mNotificationIcons.addView(v, i, params); 1399 } 1400 } 1401 } 1402 1403 @Override 1404 protected void updateRowStates() { 1405 super.updateRowStates(); 1406 mNotificationPanel.notifyVisibleChildrenChanged(); 1407 } 1408 1409 protected void updateCarrierLabelVisibility(boolean force) { 1410 // TODO: Handle this for the notification stack scroller as well 1411 if (!mShowCarrierInPanel) return; 1412 // The idea here is to only show the carrier label when there is enough room to see it, 1413 // i.e. when there aren't enough notifications to fill the panel. 1414 if (SPEW) { 1415 Log.d(TAG, String.format("stackScrollerh=%d scrollh=%d carrierh=%d", 1416 mStackScroller.getHeight(), mStackScroller.getHeight(), 1417 mCarrierLabelHeight)); 1418 } 1419 1420 // Emergency calls only is shown in the expanded header now. 1421 final boolean emergencyCallsShownElsewhere = true; 1422 final boolean makeVisible = 1423 !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly()) 1424 && mStackScroller.getHeight() < (mNotificationPanel.getHeight() 1425 - mCarrierLabelHeight - mStatusBarHeaderHeight) 1426 && mStackScroller.getVisibility() == View.VISIBLE 1427 && mState != StatusBarState.KEYGUARD; 1428 1429 if (force || mCarrierLabelVisible != makeVisible) { 1430 mCarrierLabelVisible = makeVisible; 1431 if (DEBUG) { 1432 Log.d(TAG, "making carrier label " + (makeVisible?"visible":"invisible")); 1433 } 1434 mCarrierLabel.animate().cancel(); 1435 if (makeVisible) { 1436 mCarrierLabel.setVisibility(View.VISIBLE); 1437 } 1438 mCarrierLabel.animate() 1439 .alpha(makeVisible ? 1f : 0f) 1440 //.setStartDelay(makeVisible ? 500 : 0) 1441 //.setDuration(makeVisible ? 750 : 100) 1442 .setDuration(150) 1443 .setListener(makeVisible ? null : new AnimatorListenerAdapter() { 1444 @Override 1445 public void onAnimationEnd(Animator animation) { 1446 if (!mCarrierLabelVisible) { // race 1447 mCarrierLabel.setVisibility(View.INVISIBLE); 1448 mCarrierLabel.setAlpha(0f); 1449 } 1450 } 1451 }) 1452 .start(); 1453 } 1454 } 1455 1456 @Override 1457 protected void setAreThereNotifications() { 1458 final boolean any = mNotificationData.size() > 0; 1459 1460 final boolean clearable = any && mNotificationData.hasClearableItems(); 1461 1462 if (SPEW) { 1463 Log.d(TAG, "setAreThereNotifications: N=" + mNotificationData.size() 1464 + " any=" + any + " clearable=" + clearable); 1465 } 1466 1467 final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out); 1468 final boolean showDot = (any&&!areLightsOn()); 1469 if (showDot != (nlo.getAlpha() == 1.0f)) { 1470 if (showDot) { 1471 nlo.setAlpha(0f); 1472 nlo.setVisibility(View.VISIBLE); 1473 } 1474 nlo.animate() 1475 .alpha(showDot?1:0) 1476 .setDuration(showDot?750:250) 1477 .setInterpolator(new AccelerateInterpolator(2.0f)) 1478 .setListener(showDot ? null : new AnimatorListenerAdapter() { 1479 @Override 1480 public void onAnimationEnd(Animator _a) { 1481 nlo.setVisibility(View.GONE); 1482 } 1483 }) 1484 .start(); 1485 } 1486 1487 findAndUpdateMediaNotifications(); 1488 1489 updateCarrierLabelVisibility(false); 1490 } 1491 1492 public void findAndUpdateMediaNotifications() { 1493 boolean metaDataChanged = false; 1494 1495 synchronized (mNotificationData) { 1496 final int N = mNotificationData.size(); 1497 Entry mediaNotification = null; 1498 MediaController controller = null; 1499 for (int i=0; i<N; i++) { 1500 final Entry entry = mNotificationData.get(i); 1501 if (isMediaNotification(entry)) { 1502 final MediaSession.Token token = entry.notification.getNotification().extras 1503 .getParcelable(Notification.EXTRA_MEDIA_SESSION); 1504 if (token != null) { 1505 controller = new MediaController(token); 1506 if (controller != null) { 1507 // we've got a live one, here 1508 mediaNotification = entry; 1509 } 1510 } 1511 } 1512 } 1513 1514 if (mediaNotification == null) { 1515 // Still nothing? OK, let's just look for live media sessions and see if they match 1516 // one of our notifications. This will catch apps that aren't (yet!) using media 1517 // notifications. 1518 1519 if (mMediaSessionManager != null) { 1520 final List<MediaController> sessions 1521 = mMediaSessionManager.getActiveSessionsForUser( 1522 null, 1523 UserHandle.USER_ALL); 1524 1525 for (MediaController aController : sessions) { 1526 if (aController == null) continue; 1527 final PlaybackState state = aController.getPlaybackState(); 1528 if (state == null) continue; 1529 switch (state.getState()) { 1530 case PlaybackState.STATE_STOPPED: 1531 case PlaybackState.STATE_ERROR: 1532 continue; 1533 default: 1534 // now to see if we have one like this 1535 final String pkg = aController.getSessionInfo().getPackageName(); 1536 1537 for (int i = 0; i < N; i++) { 1538 final Entry entry = mNotificationData.get(i); 1539 if (entry.notification.getPackageName().equals(pkg)) { 1540 if (DEBUG_MEDIA) { 1541 Log.v(TAG, "DEBUG_MEDIA: found controller matching " 1542 + entry.notification.getKey()); 1543 } 1544 controller = aController; 1545 mediaNotification = entry; 1546 break; 1547 } 1548 } 1549 } 1550 } 1551 } 1552 } 1553 1554 if (controller != mMediaController) { 1555 // We have a new media session 1556 1557 if (mMediaController != null) { 1558 // something old was playing 1559 Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: " 1560 + mMediaController); 1561 mMediaController.removeCallback(mMediaListener); 1562 } 1563 mMediaController = controller; 1564 1565 if (mMediaController != null) { 1566 mMediaController.addCallback(mMediaListener); 1567 mMediaMetadata = mMediaController.getMetadata(); 1568 if (DEBUG_MEDIA) { 1569 Log.v(TAG, "DEBUG_MEDIA: insert listener, receive metadata: " 1570 + mMediaMetadata); 1571 } 1572 1573 final String notificationKey = mediaNotification == null 1574 ? null 1575 : mediaNotification.notification.getKey(); 1576 1577 if (notificationKey == null || !notificationKey.equals(mMediaNotificationKey)) { 1578 // we have a new notification! 1579 if (DEBUG_MEDIA) { 1580 Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key=" 1581 + notificationKey + " controller=" + controller); 1582 } 1583 mMediaNotificationKey = notificationKey; 1584 } 1585 } else { 1586 mMediaMetadata = null; 1587 mMediaNotificationKey = null; 1588 } 1589 1590 metaDataChanged = true; 1591 } else { 1592 // Media session unchanged 1593 1594 if (DEBUG_MEDIA) { 1595 Log.v(TAG, "DEBUG_MEDIA: Continuing media notification: key=" + mMediaNotificationKey); 1596 } 1597 } 1598 } 1599 1600 updateMediaMetaData(metaDataChanged); 1601 } 1602 1603 /** 1604 * Hide the album artwork that is fading out and release its bitmap. 1605 */ 1606 private Runnable mHideBackdropFront = new Runnable() { 1607 @Override 1608 public void run() { 1609 if (DEBUG_MEDIA) { 1610 Log.v(TAG, "DEBUG_MEDIA: removing fade layer"); 1611 } 1612 mBackdropFront.setVisibility(View.INVISIBLE); 1613 mBackdropFront.animate().cancel(); 1614 mBackdropFront.setImageDrawable(null); 1615 } 1616 }; 1617 1618 /** 1619 * Refresh or remove lockscreen artwork from media metadata. 1620 */ 1621 public void updateMediaMetaData(boolean metaDataChanged) { 1622 if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) return; 1623 1624 if (mBackdrop == null) return; // called too early 1625 1626 if (DEBUG_MEDIA) { 1627 Log.v(TAG, "DEBUG_MEDIA: updating album art for notification " + mMediaNotificationKey 1628 + " metadata=" + mMediaMetadata 1629 + " metaDataChanged=" + metaDataChanged 1630 + " state=" + mState); 1631 } 1632 1633 Bitmap artworkBitmap = null; 1634 if (mMediaMetadata != null) { 1635 artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART); 1636 if (artworkBitmap == null) { 1637 artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART); 1638 // might still be null 1639 } 1640 } 1641 1642 final boolean hasArtwork = artworkBitmap != null; 1643 1644 if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK) 1645 && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) { 1646 // time to show some art! 1647 if (mBackdrop.getVisibility() != View.VISIBLE) { 1648 mBackdrop.setVisibility(View.VISIBLE); 1649 mBackdrop.animate().alpha(1f); 1650 metaDataChanged = true; 1651 if (DEBUG_MEDIA) { 1652 Log.v(TAG, "DEBUG_MEDIA: Fading in album artwork"); 1653 } 1654 } 1655 if (metaDataChanged) { 1656 if (mBackdropBack.getDrawable() != null) { 1657 mBackdropFront.setImageDrawable(mBackdropBack.getDrawable()); 1658 mBackdropFront.setAlpha(1f); 1659 mBackdropFront.setVisibility(View.VISIBLE); 1660 } else { 1661 mBackdropFront.setVisibility(View.INVISIBLE); 1662 } 1663 1664 if (DEBUG_MEDIA_FAKE_ARTWORK) { 1665 final int c = 0xFF000000 | (int)(Math.random() * 0xFFFFFF); 1666 Log.v(TAG, String.format("DEBUG_MEDIA: setting new color: 0x%08x", c)); 1667 mBackdropBack.setBackgroundColor(0xFFFFFFFF); 1668 mBackdropBack.setImageDrawable(new ColorDrawable(c)); 1669 } else { 1670 mBackdropBack.setImageBitmap(artworkBitmap); 1671 } 1672 1673 if (mBackdropFront.getVisibility() == View.VISIBLE) { 1674 if (DEBUG_MEDIA) { 1675 Log.v(TAG, "DEBUG_MEDIA: Crossfading album artwork from " 1676 + mBackdropFront.getDrawable() 1677 + " to " 1678 + mBackdropBack.getDrawable()); 1679 } 1680 mBackdropFront.animate().withLayer() 1681 .setDuration(250) 1682 .alpha(0f).withEndAction(mHideBackdropFront); 1683 } 1684 } 1685 } else { 1686 // need to hide the album art, either because we are unlocked or because 1687 // the metadata isn't there to support it 1688 if (mBackdrop.getVisibility() != View.GONE) { 1689 if (DEBUG_MEDIA) { 1690 Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork"); 1691 } 1692 mBackdrop.animate().withLayer() 1693 .alpha(0f).withEndAction(new Runnable() { 1694 @Override 1695 public void run() { 1696 mBackdrop.setVisibility(View.GONE); 1697 mBackdropFront.animate().cancel(); 1698 mBackdropBack.animate().cancel(); 1699 mHandler.post(mHideBackdropFront); 1700 } 1701 }); 1702 } 1703 } 1704 } 1705 1706 public void showClock(boolean show) { 1707 if (mStatusBarView == null) return; 1708 View clock = mStatusBarView.findViewById(R.id.clock); 1709 if (clock != null) { 1710 clock.setVisibility(show ? View.VISIBLE : View.GONE); 1711 } 1712 } 1713 1714 private int adjustDisableFlags(int state) { 1715 if (mExpandedVisible || mBouncerShowing || mWaitingForKeyguardExit) { 1716 state |= StatusBarManager.DISABLE_NOTIFICATION_ICONS; 1717 state |= StatusBarManager.DISABLE_SYSTEM_INFO; 1718 } 1719 return state; 1720 } 1721 1722 /** 1723 * State is one or more of the DISABLE constants from StatusBarManager. 1724 */ 1725 public void disable(int state, boolean animate) { 1726 mDisabledUnmodified = state; 1727 state = adjustDisableFlags(state); 1728 final int old = mDisabled; 1729 final int diff = state ^ old; 1730 mDisabled = state; 1731 1732 if (DEBUG) { 1733 Log.d(TAG, String.format("disable: 0x%08x -> 0x%08x (diff: 0x%08x)", 1734 old, state, diff)); 1735 } 1736 1737 StringBuilder flagdbg = new StringBuilder(); 1738 flagdbg.append("disable: < "); 1739 flagdbg.append(((state & StatusBarManager.DISABLE_EXPAND) != 0) ? "EXPAND" : "expand"); 1740 flagdbg.append(((diff & StatusBarManager.DISABLE_EXPAND) != 0) ? "* " : " "); 1741 flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "ICONS" : "icons"); 1742 flagdbg.append(((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "* " : " "); 1743 flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "ALERTS" : "alerts"); 1744 flagdbg.append(((diff & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "* " : " "); 1745 flagdbg.append(((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info"); 1746 flagdbg.append(((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " "); 1747 flagdbg.append(((state & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back"); 1748 flagdbg.append(((diff & StatusBarManager.DISABLE_BACK) != 0) ? "* " : " "); 1749 flagdbg.append(((state & StatusBarManager.DISABLE_HOME) != 0) ? "HOME" : "home"); 1750 flagdbg.append(((diff & StatusBarManager.DISABLE_HOME) != 0) ? "* " : " "); 1751 flagdbg.append(((state & StatusBarManager.DISABLE_RECENT) != 0) ? "RECENT" : "recent"); 1752 flagdbg.append(((diff & StatusBarManager.DISABLE_RECENT) != 0) ? "* " : " "); 1753 flagdbg.append(((state & StatusBarManager.DISABLE_CLOCK) != 0) ? "CLOCK" : "clock"); 1754 flagdbg.append(((diff & StatusBarManager.DISABLE_CLOCK) != 0) ? "* " : " "); 1755 flagdbg.append(((state & StatusBarManager.DISABLE_SEARCH) != 0) ? "SEARCH" : "search"); 1756 flagdbg.append(((diff & StatusBarManager.DISABLE_SEARCH) != 0) ? "* " : " "); 1757 flagdbg.append(">"); 1758 Log.d(TAG, flagdbg.toString()); 1759 1760 if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) { 1761 mSystemIconArea.animate().cancel(); 1762 if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) { 1763 animateStatusBarHide(mSystemIconArea, animate); 1764 } else { 1765 animateStatusBarShow(mSystemIconArea, animate); 1766 } 1767 } 1768 1769 if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) { 1770 boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0; 1771 showClock(show); 1772 } 1773 if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) { 1774 if ((state & StatusBarManager.DISABLE_EXPAND) != 0) { 1775 animateCollapsePanels(); 1776 } 1777 } 1778 1779 if ((diff & (StatusBarManager.DISABLE_HOME 1780 | StatusBarManager.DISABLE_RECENT 1781 | StatusBarManager.DISABLE_BACK 1782 | StatusBarManager.DISABLE_SEARCH)) != 0) { 1783 // the nav bar will take care of these 1784 if (mNavigationBarView != null) mNavigationBarView.setDisabledFlags(state); 1785 1786 if ((state & StatusBarManager.DISABLE_RECENT) != 0) { 1787 // close recents if it's visible 1788 mHandler.removeMessages(MSG_HIDE_RECENT_APPS); 1789 mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS); 1790 } 1791 } 1792 1793 if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { 1794 if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { 1795 if (mTicking) { 1796 haltTicker(); 1797 } 1798 animateStatusBarHide(mNotificationIconArea, animate); 1799 } else { 1800 animateStatusBarShow(mNotificationIconArea, animate); 1801 } 1802 } 1803 } 1804 1805 /** 1806 * Animates {@code v}, a view that is part of the status bar, out. 1807 */ 1808 private void animateStatusBarHide(final View v, boolean animate) { 1809 v.animate().cancel(); 1810 if (!animate) { 1811 v.setAlpha(0f); 1812 v.setVisibility(View.INVISIBLE); 1813 return; 1814 } 1815 v.animate() 1816 .alpha(0f) 1817 .setDuration(160) 1818 .setStartDelay(0) 1819 .setInterpolator(mAlphaOut) 1820 .withEndAction(new Runnable() { 1821 @Override 1822 public void run() { 1823 v.setVisibility(View.INVISIBLE); 1824 } 1825 }); 1826 } 1827 1828 /** 1829 * Animates {@code v}, a view that is part of the status bar, in. 1830 */ 1831 private void animateStatusBarShow(View v, boolean animate) { 1832 v.animate().cancel(); 1833 v.setVisibility(View.VISIBLE); 1834 if (!animate) { 1835 v.setAlpha(1f); 1836 return; 1837 } 1838 v.animate() 1839 .alpha(1f) 1840 .setDuration(320) 1841 .setInterpolator(mAlphaIn) 1842 .setStartDelay(50); 1843 1844 // Synchronize the motion with the Keyguard fading if necessary. 1845 if (mKeyguardFadingAway) { 1846 v.animate() 1847 .setDuration(mKeyguardFadingAwayDuration) 1848 .setInterpolator(mLinearOutSlowIn) 1849 .setStartDelay(mKeyguardFadingAwayDelay) 1850 .start(); 1851 } 1852 } 1853 1854 @Override 1855 protected BaseStatusBar.H createHandler() { 1856 return new PhoneStatusBar.H(); 1857 } 1858 1859 @Override 1860 public void startActivity(Intent intent) { 1861 startActivityDismissingKeyguard(intent, false); 1862 } 1863 1864 public ScrimController getScrimController() { 1865 return mScrimController; 1866 } 1867 1868 public void setQsExpanded(boolean expanded) { 1869 mStatusBarWindowManager.setQsExpanded(expanded); 1870 } 1871 1872 /** 1873 * All changes to the status bar and notifications funnel through here and are batched. 1874 */ 1875 private class H extends BaseStatusBar.H { 1876 public void handleMessage(Message m) { 1877 super.handleMessage(m); 1878 switch (m.what) { 1879 case MSG_OPEN_NOTIFICATION_PANEL: 1880 animateExpandNotificationsPanel(); 1881 break; 1882 case MSG_OPEN_SETTINGS_PANEL: 1883 animateExpandSettingsPanel(); 1884 break; 1885 case MSG_CLOSE_PANELS: 1886 animateCollapsePanels(); 1887 break; 1888 case MSG_SHOW_HEADS_UP: 1889 setHeadsUpVisibility(true); 1890 break; 1891 case MSG_DECAY_HEADS_UP: 1892 mHeadsUpNotificationView.release(); 1893 setHeadsUpVisibility(false); 1894 break; 1895 case MSG_HIDE_HEADS_UP: 1896 mHeadsUpNotificationView.release(); 1897 setHeadsUpVisibility(false); 1898 break; 1899 case MSG_ESCALATE_HEADS_UP: 1900 escalateHeadsUp(); 1901 setHeadsUpVisibility(false); 1902 break; 1903 } 1904 } 1905 } 1906 1907 /** if the interrupting notification had a fullscreen intent, fire it now. */ 1908 private void escalateHeadsUp() { 1909 if (mHeadsUpNotificationView.getEntry() != null) { 1910 final StatusBarNotification sbn = mHeadsUpNotificationView.getEntry().notification; 1911 mHeadsUpNotificationView.release(); 1912 final Notification notification = sbn.getNotification(); 1913 if (notification.fullScreenIntent != null) { 1914 if (DEBUG) 1915 Log.d(TAG, "converting a heads up to fullScreen"); 1916 try { 1917 notification.fullScreenIntent.send(); 1918 } catch (PendingIntent.CanceledException e) { 1919 } 1920 } 1921 } 1922 } 1923 1924 View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() { 1925 public void onFocusChange(View v, boolean hasFocus) { 1926 // Because 'v' is a ViewGroup, all its children will be (un)selected 1927 // too, which allows marqueeing to work. 1928 v.setSelected(hasFocus); 1929 } 1930 }; 1931 1932 boolean panelsEnabled() { 1933 return (mDisabled & StatusBarManager.DISABLE_EXPAND) == 0; 1934 } 1935 1936 void makeExpandedVisible(boolean force) { 1937 if (SPEW) Log.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible); 1938 if (!force && (mExpandedVisible || !panelsEnabled())) { 1939 return; 1940 } 1941 1942 mExpandedVisible = true; 1943 if (mNavigationBarView != null) 1944 mNavigationBarView.setSlippery(true); 1945 1946 updateCarrierLabelVisibility(true); 1947 1948 updateExpandedViewPos(EXPANDED_LEAVE_ALONE); 1949 1950 // Expand the window to encompass the full screen in anticipation of the drag. 1951 // This is only possible to do atomically because the status bar is at the top of the screen! 1952 mStatusBarWindowManager.setStatusBarExpanded(true); 1953 1954 visibilityChanged(true); 1955 mWaitingForKeyguardExit = false; 1956 disable(mDisabledUnmodified, !force /* animate */); 1957 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true); 1958 } 1959 1960 public void animateCollapsePanels() { 1961 animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); 1962 } 1963 1964 private final Runnable mAnimateCollapsePanels = new Runnable() { 1965 @Override 1966 public void run() { 1967 animateCollapsePanels(); 1968 } 1969 }; 1970 1971 public void postAnimateCollapsePanels() { 1972 mHandler.post(mAnimateCollapsePanels); 1973 } 1974 1975 public void animateCollapsePanels(int flags) { 1976 animateCollapsePanels(flags, false /* force */); 1977 } 1978 1979 public void animateCollapsePanels(int flags, boolean force) { 1980 if (!force && 1981 (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) { 1982 return; 1983 } 1984 if (SPEW) { 1985 Log.d(TAG, "animateCollapse():" 1986 + " mExpandedVisible=" + mExpandedVisible 1987 + " flags=" + flags); 1988 } 1989 1990 if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) { 1991 mHandler.removeMessages(MSG_HIDE_RECENT_APPS); 1992 mHandler.sendEmptyMessage(MSG_HIDE_RECENT_APPS); 1993 } 1994 1995 if ((flags & CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL) == 0) { 1996 mHandler.removeMessages(MSG_CLOSE_SEARCH_PANEL); 1997 mHandler.sendEmptyMessage(MSG_CLOSE_SEARCH_PANEL); 1998 } 1999 2000 if (mStatusBarWindow != null) { 2001 // release focus immediately to kick off focus change transition 2002 mStatusBarWindowManager.setStatusBarFocusable(false); 2003 2004 mStatusBarWindow.cancelExpandHelper(); 2005 mStatusBarView.collapseAllPanels(true); 2006 } 2007 } 2008 2009 public ViewPropertyAnimator setVisibilityWhenDone( 2010 final ViewPropertyAnimator a, final View v, final int vis) { 2011 a.setListener(new AnimatorListenerAdapter() { 2012 @Override 2013 public void onAnimationEnd(Animator animation) { 2014 v.setVisibility(vis); 2015 a.setListener(null); // oneshot 2016 } 2017 }); 2018 return a; 2019 } 2020 2021 public Animator setVisibilityWhenDone( 2022 final Animator a, final View v, final int vis) { 2023 a.addListener(new AnimatorListenerAdapter() { 2024 @Override 2025 public void onAnimationEnd(Animator animation) { 2026 v.setVisibility(vis); 2027 } 2028 }); 2029 return a; 2030 } 2031 2032 public Animator interpolator(TimeInterpolator ti, Animator a) { 2033 a.setInterpolator(ti); 2034 return a; 2035 } 2036 2037 public Animator startDelay(int d, Animator a) { 2038 a.setStartDelay(d); 2039 return a; 2040 } 2041 2042 public Animator start(Animator a) { 2043 a.start(); 2044 return a; 2045 } 2046 2047 final TimeInterpolator mAccelerateInterpolator = new AccelerateInterpolator(); 2048 final TimeInterpolator mDecelerateInterpolator = new DecelerateInterpolator(); 2049 final int FLIP_DURATION_OUT = 125; 2050 final int FLIP_DURATION_IN = 225; 2051 final int FLIP_DURATION = (FLIP_DURATION_IN + FLIP_DURATION_OUT); 2052 2053 Animator mScrollViewAnim, mClearButtonAnim; 2054 2055 @Override 2056 public void animateExpandNotificationsPanel() { 2057 if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible); 2058 if (!panelsEnabled()) { 2059 return ; 2060 } 2061 2062 mNotificationPanel.expand(); 2063 2064 if (false) postStartTracing(); 2065 } 2066 2067 @Override 2068 public void animateExpandSettingsPanel() { 2069 if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible); 2070 if (!panelsEnabled()) { 2071 return; 2072 } 2073 2074 // Settings are not available in setup 2075 if (!mUserSetup) return; 2076 2077 mNotificationPanel.expand(); 2078 mNotificationPanel.openQs(); 2079 2080 if (false) postStartTracing(); 2081 } 2082 2083 public void animateCollapseQuickSettings() { 2084 mStatusBarView.collapseAllPanels(true); 2085 } 2086 2087 void makeExpandedInvisible() { 2088 if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible 2089 + " mExpandedVisible=" + mExpandedVisible); 2090 2091 if (!mExpandedVisible || mStatusBarWindow == null) { 2092 return; 2093 } 2094 2095 // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868) 2096 mStatusBarView.collapseAllPanels(/*animate=*/ false); 2097 2098 // reset things to their proper state 2099 if (mScrollViewAnim != null) mScrollViewAnim.cancel(); 2100 if (mClearButtonAnim != null) mClearButtonAnim.cancel(); 2101 2102 mStackScroller.setVisibility(View.VISIBLE); 2103 mNotificationPanel.setVisibility(View.GONE); 2104 2105 setAreThereNotifications(); // show the clear button 2106 2107 mNotificationPanel.closeQs(); 2108 2109 mExpandedVisible = false; 2110 if (mNavigationBarView != null) 2111 mNavigationBarView.setSlippery(false); 2112 visibilityChanged(false); 2113 2114 // Shrink the window to the size of the status bar only 2115 mStatusBarWindowManager.setStatusBarExpanded(false); 2116 2117 // Close any "App info" popups that might have snuck on-screen 2118 dismissPopups(); 2119 2120 if (mPostCollapseCleanup != null) { 2121 mPostCollapseCleanup.run(); 2122 mPostCollapseCleanup = null; 2123 } 2124 2125 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); 2126 showBouncer(); 2127 disable(mDisabledUnmodified, true /* animate */); 2128 } 2129 2130 public boolean interceptTouchEvent(MotionEvent event) { 2131 if (DEBUG_GESTURES) { 2132 if (event.getActionMasked() != MotionEvent.ACTION_MOVE) { 2133 EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH, 2134 event.getActionMasked(), (int) event.getX(), (int) event.getY(), mDisabled); 2135 } 2136 2137 } 2138 2139 if (SPEW) { 2140 Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled=" 2141 + mDisabled + " mTracking=" + mTracking); 2142 } else if (CHATTY) { 2143 if (event.getAction() != MotionEvent.ACTION_MOVE) { 2144 Log.d(TAG, String.format( 2145 "panel: %s at (%f, %f) mDisabled=0x%08x", 2146 MotionEvent.actionToString(event.getAction()), 2147 event.getRawX(), event.getRawY(), mDisabled)); 2148 } 2149 } 2150 2151 if (DEBUG_GESTURES) { 2152 mGestureRec.add(event); 2153 } 2154 2155 if (mStatusBarWindowState == WINDOW_STATE_SHOWING) { 2156 final boolean upOrCancel = 2157 event.getAction() == MotionEvent.ACTION_UP || 2158 event.getAction() == MotionEvent.ACTION_CANCEL; 2159 if (upOrCancel && !mExpandedVisible) { 2160 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); 2161 } else { 2162 setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true); 2163 } 2164 } 2165 return false; 2166 } 2167 2168 public GestureRecorder getGestureRecorder() { 2169 return mGestureRec; 2170 } 2171 2172 private void setNavigationIconHints(int hints) { 2173 if (hints == mNavigationIconHints) return; 2174 2175 mNavigationIconHints = hints; 2176 2177 if (mNavigationBarView != null) { 2178 mNavigationBarView.setNavigationIconHints(hints); 2179 } 2180 checkBarModes(); 2181 } 2182 2183 @Override // CommandQueue 2184 public void setWindowState(int window, int state) { 2185 boolean showing = state == WINDOW_STATE_SHOWING; 2186 if (mStatusBarWindow != null 2187 && window == StatusBarManager.WINDOW_STATUS_BAR 2188 && mStatusBarWindowState != state) { 2189 mStatusBarWindowState = state; 2190 if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state)); 2191 if (!showing) { 2192 mStatusBarView.collapseAllPanels(false); 2193 } 2194 } 2195 if (mNavigationBarView != null 2196 && window == StatusBarManager.WINDOW_NAVIGATION_BAR 2197 && mNavigationBarWindowState != state) { 2198 mNavigationBarWindowState = state; 2199 if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state)); 2200 } 2201 } 2202 2203 @Override // CommandQueue 2204 public void setSystemUiVisibility(int vis, int mask) { 2205 final int oldVal = mSystemUiVisibility; 2206 final int newVal = (oldVal&~mask) | (vis&mask); 2207 final int diff = newVal ^ oldVal; 2208 if (DEBUG) Log.d(TAG, String.format( 2209 "setSystemUiVisibility vis=%s mask=%s oldVal=%s newVal=%s diff=%s", 2210 Integer.toHexString(vis), Integer.toHexString(mask), 2211 Integer.toHexString(oldVal), Integer.toHexString(newVal), 2212 Integer.toHexString(diff))); 2213 if (diff != 0) { 2214 // we never set the recents bit via this method, so save the prior state to prevent 2215 // clobbering the bit below 2216 final boolean wasRecentsVisible = (mSystemUiVisibility & View.RECENT_APPS_VISIBLE) > 0; 2217 2218 mSystemUiVisibility = newVal; 2219 2220 // update low profile 2221 if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) { 2222 final boolean lightsOut = (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0; 2223 if (lightsOut) { 2224 animateCollapsePanels(); 2225 if (mTicking) { 2226 haltTicker(); 2227 } 2228 } 2229 2230 setAreThereNotifications(); 2231 } 2232 2233 // update status bar mode 2234 final int sbMode = computeBarMode(oldVal, newVal, mStatusBarView.getBarTransitions(), 2235 View.STATUS_BAR_TRANSIENT, View.STATUS_BAR_TRANSLUCENT); 2236 2237 // update navigation bar mode 2238 final int nbMode = mNavigationBarView == null ? -1 : computeBarMode( 2239 oldVal, newVal, mNavigationBarView.getBarTransitions(), 2240 View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT); 2241 final boolean sbModeChanged = sbMode != -1; 2242 final boolean nbModeChanged = nbMode != -1; 2243 boolean checkBarModes = false; 2244 if (sbModeChanged && sbMode != mStatusBarMode) { 2245 mStatusBarMode = sbMode; 2246 checkBarModes = true; 2247 } 2248 if (nbModeChanged && nbMode != mNavigationBarMode) { 2249 mNavigationBarMode = nbMode; 2250 checkBarModes = true; 2251 } 2252 if (checkBarModes) { 2253 checkBarModes(); 2254 } 2255 if (sbModeChanged || nbModeChanged) { 2256 // update transient bar autohide 2257 if (mStatusBarMode == MODE_SEMI_TRANSPARENT || mNavigationBarMode == MODE_SEMI_TRANSPARENT) { 2258 scheduleAutohide(); 2259 } else { 2260 cancelAutohide(); 2261 } 2262 } 2263 2264 // ready to unhide 2265 if ((vis & View.STATUS_BAR_UNHIDE) != 0) { 2266 mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE; 2267 } 2268 if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) { 2269 mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE; 2270 } 2271 2272 // restore the recents bit 2273 if (wasRecentsVisible) { 2274 mSystemUiVisibility |= View.RECENT_APPS_VISIBLE; 2275 } 2276 2277 // send updated sysui visibility to window manager 2278 notifyUiVisibilityChanged(mSystemUiVisibility); 2279 } 2280 } 2281 2282 private int computeBarMode(int oldVis, int newVis, BarTransitions transitions, 2283 int transientFlag, int translucentFlag) { 2284 final int oldMode = barMode(oldVis, transientFlag, translucentFlag); 2285 final int newMode = barMode(newVis, transientFlag, translucentFlag); 2286 if (oldMode == newMode) { 2287 return -1; // no mode change 2288 } 2289 return newMode; 2290 } 2291 2292 private int barMode(int vis, int transientFlag, int translucentFlag) { 2293 return (vis & transientFlag) != 0 ? MODE_SEMI_TRANSPARENT 2294 : (vis & translucentFlag) != 0 ? MODE_TRANSLUCENT 2295 : (vis & View.SYSTEM_UI_TRANSPARENT) != 0 ? MODE_TRANSPARENT 2296 : (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0 ? MODE_LIGHTS_OUT 2297 : MODE_OPAQUE; 2298 } 2299 2300 private void checkBarModes() { 2301 if (mDemoMode) return; 2302 checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarView.getBarTransitions()); 2303 if (mNavigationBarView != null) { 2304 checkBarMode(mNavigationBarMode, 2305 mNavigationBarWindowState, mNavigationBarView.getBarTransitions()); 2306 } 2307 } 2308 2309 private void checkBarMode(int mode, int windowState, BarTransitions transitions) { 2310 final boolean anim = (mScreenOn == null || mScreenOn) && windowState != WINDOW_STATE_HIDDEN; 2311 transitions.transitionTo(mode, anim); 2312 } 2313 2314 private void finishBarAnimations() { 2315 mStatusBarView.getBarTransitions().finishAnimations(); 2316 if (mNavigationBarView != null) { 2317 mNavigationBarView.getBarTransitions().finishAnimations(); 2318 } 2319 } 2320 2321 private final Runnable mCheckBarModes = new Runnable() { 2322 @Override 2323 public void run() { 2324 checkBarModes(); 2325 }}; 2326 2327 @Override 2328 public void setInteracting(int barWindow, boolean interacting) { 2329 mInteractingWindows = interacting 2330 ? (mInteractingWindows | barWindow) 2331 : (mInteractingWindows & ~barWindow); 2332 if (mInteractingWindows != 0) { 2333 suspendAutohide(); 2334 } else { 2335 resumeSuspendedAutohide(); 2336 } 2337 checkBarModes(); 2338 } 2339 2340 private void resumeSuspendedAutohide() { 2341 if (mAutohideSuspended) { 2342 scheduleAutohide(); 2343 mHandler.postDelayed(mCheckBarModes, 500); // longer than home -> launcher 2344 } 2345 } 2346 2347 private void suspendAutohide() { 2348 mHandler.removeCallbacks(mAutohide); 2349 mHandler.removeCallbacks(mCheckBarModes); 2350 mAutohideSuspended = (mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0; 2351 } 2352 2353 private void cancelAutohide() { 2354 mAutohideSuspended = false; 2355 mHandler.removeCallbacks(mAutohide); 2356 } 2357 2358 private void scheduleAutohide() { 2359 cancelAutohide(); 2360 mHandler.postDelayed(mAutohide, AUTOHIDE_TIMEOUT_MS); 2361 } 2362 2363 private void checkUserAutohide(View v, MotionEvent event) { 2364 if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0 // a transient bar is revealed 2365 && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar 2366 && event.getX() == 0 && event.getY() == 0 // a touch outside both bars 2367 ) { 2368 userAutohide(); 2369 } 2370 } 2371 2372 private void userAutohide() { 2373 cancelAutohide(); 2374 mHandler.postDelayed(mAutohide, 350); // longer than app gesture -> flag clear 2375 } 2376 2377 private boolean areLightsOn() { 2378 return 0 == (mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE); 2379 } 2380 2381 public void setLightsOn(boolean on) { 2382 Log.v(TAG, "setLightsOn(" + on + ")"); 2383 if (on) { 2384 setSystemUiVisibility(0, View.SYSTEM_UI_FLAG_LOW_PROFILE); 2385 } else { 2386 setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE, View.SYSTEM_UI_FLAG_LOW_PROFILE); 2387 } 2388 } 2389 2390 private void notifyUiVisibilityChanged(int vis) { 2391 try { 2392 mWindowManagerService.statusBarVisibilityChanged(vis); 2393 } catch (RemoteException ex) { 2394 } 2395 } 2396 2397 public void topAppWindowChanged(boolean showMenu) { 2398 if (DEBUG) { 2399 Log.d(TAG, (showMenu?"showing":"hiding") + " the MENU button"); 2400 } 2401 if (mNavigationBarView != null) { 2402 mNavigationBarView.setMenuVisibility(showMenu); 2403 } 2404 2405 // See above re: lights-out policy for legacy apps. 2406 if (showMenu) setLightsOn(true); 2407 } 2408 2409 @Override 2410 public void setImeWindowStatus(IBinder token, int vis, int backDisposition, 2411 boolean showImeSwitcher) { 2412 boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0; 2413 int flags = mNavigationIconHints; 2414 if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) { 2415 flags |= NAVIGATION_HINT_BACK_ALT; 2416 } else { 2417 flags &= ~NAVIGATION_HINT_BACK_ALT; 2418 } 2419 if (showImeSwitcher) { 2420 flags |= NAVIGATION_HINT_IME_SHOWN; 2421 } else { 2422 flags &= ~NAVIGATION_HINT_IME_SHOWN; 2423 } 2424 2425 setNavigationIconHints(flags); 2426 } 2427 2428 @Override 2429 public void setHardKeyboardStatus(boolean available, boolean enabled) {} 2430 2431 @Override 2432 protected void tick(StatusBarNotification n, boolean firstTime) { 2433 if (!mTickerEnabled) return; 2434 2435 // no ticking in lights-out mode 2436 if (!areLightsOn()) return; 2437 2438 // no ticking in Setup 2439 if (!isDeviceProvisioned()) return; 2440 2441 // not for you 2442 if (!notificationIsForCurrentProfiles(n)) return; 2443 2444 // Show the ticker if one is requested. Also don't do this 2445 // until status bar window is attached to the window manager, 2446 // because... well, what's the point otherwise? And trying to 2447 // run a ticker without being attached will crash! 2448 if (n.getNotification().tickerText != null && mStatusBarWindow != null 2449 && mStatusBarWindow.getWindowToken() != null) { 2450 if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS 2451 | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) { 2452 mTicker.addEntry(n); 2453 } 2454 } 2455 } 2456 2457 private class MyTicker extends Ticker { 2458 MyTicker(Context context, View sb) { 2459 super(context, sb); 2460 if (!mTickerEnabled) { 2461 Log.w(TAG, "MyTicker instantiated with mTickerEnabled=false", new Throwable()); 2462 } 2463 } 2464 2465 @Override 2466 public void tickerStarting() { 2467 if (!mTickerEnabled) return; 2468 mTicking = true; 2469 mStatusBarContents.setVisibility(View.GONE); 2470 mTickerView.setVisibility(View.VISIBLE); 2471 mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null)); 2472 mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null)); 2473 } 2474 2475 @Override 2476 public void tickerDone() { 2477 if (!mTickerEnabled) return; 2478 mStatusBarContents.setVisibility(View.VISIBLE); 2479 mTickerView.setVisibility(View.GONE); 2480 mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null)); 2481 mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out, 2482 mTickingDoneListener)); 2483 } 2484 2485 public void tickerHalting() { 2486 if (!mTickerEnabled) return; 2487 if (mStatusBarContents.getVisibility() != View.VISIBLE) { 2488 mStatusBarContents.setVisibility(View.VISIBLE); 2489 mStatusBarContents 2490 .startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null)); 2491 } 2492 mTickerView.setVisibility(View.GONE); 2493 // we do not animate the ticker away at this point, just get rid of it (b/6992707) 2494 } 2495 } 2496 2497 Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {; 2498 public void onAnimationEnd(Animation animation) { 2499 mTicking = false; 2500 } 2501 public void onAnimationRepeat(Animation animation) { 2502 } 2503 public void onAnimationStart(Animation animation) { 2504 } 2505 }; 2506 2507 private Animation loadAnim(int id, Animation.AnimationListener listener) { 2508 Animation anim = AnimationUtils.loadAnimation(mContext, id); 2509 if (listener != null) { 2510 anim.setAnimationListener(listener); 2511 } 2512 return anim; 2513 } 2514 2515 public static String viewInfo(View v) { 2516 return "[(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom() 2517 + ") " + v.getWidth() + "x" + v.getHeight() + "]"; 2518 } 2519 2520 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2521 synchronized (mQueueLock) { 2522 pw.println("Current Status Bar state:"); 2523 pw.println(" mExpandedVisible=" + mExpandedVisible 2524 + ", mTrackingPosition=" + mTrackingPosition); 2525 pw.println(" mTickerEnabled=" + mTickerEnabled); 2526 if (mTickerEnabled) { 2527 pw.println(" mTicking=" + mTicking); 2528 pw.println(" mTickerView: " + viewInfo(mTickerView)); 2529 } 2530 pw.println(" mTracking=" + mTracking); 2531 pw.println(" mDisplayMetrics=" + mDisplayMetrics); 2532 pw.println(" mStackScroller: " + viewInfo(mStackScroller)); 2533 pw.println(" mStackScroller: " + viewInfo(mStackScroller) 2534 + " scroll " + mStackScroller.getScrollX() 2535 + "," + mStackScroller.getScrollY()); 2536 } 2537 2538 pw.print(" mInteractingWindows="); pw.println(mInteractingWindows); 2539 pw.print(" mStatusBarWindowState="); 2540 pw.println(windowStateToString(mStatusBarWindowState)); 2541 pw.print(" mStatusBarMode="); 2542 pw.println(BarTransitions.modeToString(mStatusBarMode)); 2543 pw.print(" mDozing="); pw.println(mDozing); 2544 pw.print(" mZenMode="); 2545 pw.println(Settings.Global.zenModeToString(mZenMode)); 2546 pw.print(" mUseHeadsUp="); 2547 pw.println(mUseHeadsUp); 2548 pw.print(" interrupting package: "); 2549 pw.println(hunStateToString(mHeadsUpNotificationView.getEntry())); 2550 dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions()); 2551 if (mNavigationBarView != null) { 2552 pw.print(" mNavigationBarWindowState="); 2553 pw.println(windowStateToString(mNavigationBarWindowState)); 2554 pw.print(" mNavigationBarMode="); 2555 pw.println(BarTransitions.modeToString(mNavigationBarMode)); 2556 dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions()); 2557 } 2558 2559 pw.print(" mNavigationBarView="); 2560 if (mNavigationBarView == null) { 2561 pw.println("null"); 2562 } else { 2563 mNavigationBarView.dump(fd, pw, args); 2564 } 2565 2566 pw.print(" mMediaSessionManager="); 2567 pw.println(mMediaSessionManager); 2568 pw.print(" mMediaNotificationKey="); 2569 pw.println(mMediaNotificationKey); 2570 pw.print(" mMediaController="); 2571 pw.print(mMediaController); 2572 if (mMediaController != null) { 2573 pw.print(" state=" + mMediaController.getPlaybackState()); 2574 } 2575 pw.println(); 2576 pw.print(" mMediaMetadata="); 2577 pw.print(mMediaMetadata); 2578 if (mMediaMetadata != null) { 2579 pw.print(" title=" + mMediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE)); 2580 } 2581 pw.println(); 2582 2583 pw.println(" Panels: "); 2584 if (mNotificationPanel != null) { 2585 pw.println(" mNotificationPanel=" + 2586 mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug("")); 2587 pw.print (" "); 2588 mNotificationPanel.dump(fd, pw, args); 2589 } 2590 2591 if (DUMPTRUCK) { 2592 synchronized (mNotificationData) { 2593 int N = mNotificationData.size(); 2594 pw.println(" notification icons: " + N); 2595 for (int i=0; i<N; i++) { 2596 NotificationData.Entry e = mNotificationData.get(i); 2597 pw.println(" [" + i + "] key=" + e.key + " icon=" + e.icon); 2598 StatusBarNotification n = e.notification; 2599 pw.println(" pkg=" + n.getPackageName() + " id=" + n.getId() + " score=" + n.getScore()); 2600 pw.println(" notification=" + n.getNotification()); 2601 pw.println(" tickerText=\"" + n.getNotification().tickerText + "\""); 2602 } 2603 } 2604 2605 int N = mStatusIcons.getChildCount(); 2606 pw.println(" system icons: " + N); 2607 for (int i=0; i<N; i++) { 2608 StatusBarIconView ic = (StatusBarIconView) mStatusIcons.getChildAt(i); 2609 pw.println(" [" + i + "] icon=" + ic); 2610 } 2611 2612 if (false) { 2613 pw.println("see the logcat for a dump of the views we have created."); 2614 // must happen on ui thread 2615 mHandler.post(new Runnable() { 2616 public void run() { 2617 mStatusBarView.getLocationOnScreen(mAbsPos); 2618 Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] 2619 + ") " + mStatusBarView.getWidth() + "x" 2620 + getStatusBarHeight()); 2621 mStatusBarView.debug(); 2622 } 2623 }); 2624 } 2625 } 2626 2627 if (DEBUG_GESTURES) { 2628 pw.print(" status bar gestures: "); 2629 mGestureRec.dump(fd, pw, args); 2630 } 2631 2632 if (mNetworkController != null) { 2633 mNetworkController.dump(fd, pw, args); 2634 } 2635 if (mBluetoothController != null) { 2636 mBluetoothController.dump(fd, pw, args); 2637 } 2638 if (mCastController != null) { 2639 mCastController.dump(fd, pw, args); 2640 } 2641 if (mUserSwitcherController != null) { 2642 mUserSwitcherController.dump(fd, pw, args); 2643 } 2644 } 2645 2646 private String hunStateToString(Entry entry) { 2647 if (entry == null) return "null"; 2648 if (entry.notification == null) return "corrupt"; 2649 return entry.notification.getPackageName(); 2650 } 2651 2652 private static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) { 2653 pw.print(" "); pw.print(var); pw.print(".BarTransitions.mMode="); 2654 pw.println(BarTransitions.modeToString(transitions.getMode())); 2655 } 2656 2657 @Override 2658 public void createAndAddWindows() { 2659 addStatusBarWindow(); 2660 } 2661 2662 private void addStatusBarWindow() { 2663 makeStatusBarView(); 2664 mStatusBarWindowManager = new StatusBarWindowManager(mContext); 2665 mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight()); 2666 } 2667 2668 static final float saturate(float a) { 2669 return a < 0f ? 0f : (a > 1f ? 1f : a); 2670 } 2671 2672 @Override 2673 public void updateExpandedViewPos(int thingy) { 2674 if (SPEW) Log.v(TAG, "updateExpandedViewPos"); 2675 2676 // on larger devices, the notification panel is propped open a bit 2677 mNotificationPanel.setMinimumHeight( 2678 (int)(mNotificationPanelMinHeightFrac * mCurrentDisplaySize.y)); 2679 2680 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mNotificationPanel.getLayoutParams(); 2681 lp.gravity = mNotificationPanelGravity; 2682 mNotificationPanel.setLayoutParams(lp); 2683 2684 updateCarrierLabelVisibility(false); 2685 } 2686 2687 // called by makeStatusbar and also by PhoneStatusBarView 2688 void updateDisplaySize() { 2689 mDisplay.getMetrics(mDisplayMetrics); 2690 mDisplay.getSize(mCurrentDisplaySize); 2691 if (DEBUG_GESTURES) { 2692 mGestureRec.tag("display", 2693 String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels)); 2694 } 2695 } 2696 2697 public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned) { 2698 if (onlyProvisioned && !isDeviceProvisioned()) return; 2699 2700 dismissKeyguardThenExecute(new OnDismissAction() { 2701 @Override 2702 public boolean onDismiss() { 2703 try { 2704 // Dismiss the lock screen when Settings starts. 2705 ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity(); 2706 } catch (RemoteException e) { 2707 } 2708 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); 2709 mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); 2710 animateCollapsePanels(); 2711 2712 return DELAY_DISMISS_TO_ACTIVITY_LAUNCH; 2713 } 2714 }); 2715 } 2716 2717 private View.OnClickListener mClockClickListener = new View.OnClickListener() { 2718 public void onClick(View v) { 2719 startActivityDismissingKeyguard( 2720 new Intent(Intent.ACTION_QUICK_CLOCK), true); // have fun, everyone 2721 } 2722 }; 2723 2724 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 2725 public void onReceive(Context context, Intent intent) { 2726 if (DEBUG) Log.v(TAG, "onReceive: " + intent); 2727 String action = intent.getAction(); 2728 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { 2729 int flags = CommandQueue.FLAG_EXCLUDE_NONE; 2730 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { 2731 String reason = intent.getStringExtra("reason"); 2732 if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) { 2733 flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL; 2734 } 2735 } 2736 animateCollapsePanels(flags); 2737 } 2738 else if (Intent.ACTION_SCREEN_OFF.equals(action)) { 2739 mScreenOn = false; 2740 notifyNavigationBarScreenOn(false); 2741 notifyHeadsUpScreenOn(false); 2742 finishBarAnimations(); 2743 stopNotificationLogging(); 2744 } 2745 else if (Intent.ACTION_SCREEN_ON.equals(action)) { 2746 mScreenOn = true; 2747 // work around problem where mDisplay.getRotation() is not stable while screen is off (bug 7086018) 2748 repositionNavigationBar(); 2749 notifyNavigationBarScreenOn(true); 2750 startNotificationLoggingIfScreenOnAndVisible(); 2751 } 2752 else if (ACTION_DEMO.equals(action)) { 2753 Bundle bundle = intent.getExtras(); 2754 if (bundle != null) { 2755 String command = bundle.getString("command", "").trim().toLowerCase(); 2756 if (command.length() > 0) { 2757 try { 2758 dispatchDemoCommand(command, bundle); 2759 } catch (Throwable t) { 2760 Log.w(TAG, "Error running demo command, intent=" + intent, t); 2761 } 2762 } 2763 } 2764 } else if ("fake_artwork".equals(action)) { 2765 if (DEBUG_MEDIA_FAKE_ARTWORK) { 2766 updateMediaMetaData(true); 2767 } 2768 } 2769 } 2770 }; 2771 2772 @Override 2773 protected void dismissKeyguardThenExecute(OnDismissAction action) { 2774 if (mStatusBarKeyguardViewManager.isShowing()) { 2775 mStatusBarKeyguardViewManager.dismissWithAction(action); 2776 } else { 2777 action.onDismiss(); 2778 } 2779 } 2780 2781 // SystemUIService notifies SystemBars of configuration changes, which then calls down here 2782 @Override 2783 protected void onConfigurationChanged(Configuration newConfig) { 2784 super.onConfigurationChanged(newConfig); // calls refreshLayout 2785 2786 if (DEBUG) { 2787 Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration()); 2788 } 2789 updateDisplaySize(); // populates mDisplayMetrics 2790 2791 updateResources(); 2792 repositionNavigationBar(); 2793 updateExpandedViewPos(EXPANDED_LEAVE_ALONE); 2794 updateShowSearchHoldoff(); 2795 updateRowStates(); 2796 } 2797 2798 @Override 2799 public void userSwitched(int newUserId) { 2800 if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId); 2801 animateCollapsePanels(); 2802 updateNotifications(); 2803 resetUserSetupObserver(); 2804 setControllerUsers(); 2805 } 2806 2807 private void setControllerUsers() { 2808 if (mZenModeController != null) { 2809 mZenModeController.setUserId(mCurrentUserId); 2810 } 2811 } 2812 2813 private void resetUserSetupObserver() { 2814 mContext.getContentResolver().unregisterContentObserver(mUserSetupObserver); 2815 mUserSetupObserver.onChange(false); 2816 mContext.getContentResolver().registerContentObserver( 2817 Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), true, 2818 mUserSetupObserver, 2819 mCurrentUserId); 2820 } 2821 2822 private void setHeadsUpVisibility(boolean vis) { 2823 if (!ENABLE_HEADS_UP) return; 2824 if (DEBUG) Log.v(TAG, (vis ? "showing" : "hiding") + " heads up window"); 2825 mHeadsUpNotificationView.setVisibility(vis ? View.VISIBLE : View.GONE); 2826 } 2827 2828 public void onHeadsUpDismissed() { 2829 mHeadsUpNotificationView.dismiss(); 2830 } 2831 2832 /** 2833 * Reload some of our resources when the configuration changes. 2834 * 2835 * We don't reload everything when the configuration changes -- we probably 2836 * should, but getting that smooth is tough. Someday we'll fix that. In the 2837 * meantime, just update the things that we know change. 2838 */ 2839 void updateResources() { 2840 // Update the quick setting tiles 2841 if (mQSPanel != null) mQSPanel.updateResources(); 2842 2843 loadDimens(); 2844 mLinearOutSlowIn = AnimationUtils.loadInterpolator( 2845 mContext, android.R.interpolator.linear_out_slow_in); 2846 } 2847 2848 protected void loadDimens() { 2849 final Resources res = mContext.getResources(); 2850 2851 mNaturalBarHeight = res.getDimensionPixelSize( 2852 com.android.internal.R.dimen.status_bar_height); 2853 2854 int newIconSize = res.getDimensionPixelSize( 2855 com.android.internal.R.dimen.status_bar_icon_size); 2856 int newIconHPadding = res.getDimensionPixelSize( 2857 R.dimen.status_bar_icon_padding); 2858 2859 if (newIconHPadding != mIconHPadding || newIconSize != mIconSize) { 2860// Log.d(TAG, "size=" + newIconSize + " padding=" + newIconHPadding); 2861 mIconHPadding = newIconHPadding; 2862 mIconSize = newIconSize; 2863 //reloadAllNotificationIcons(); // reload the tray 2864 } 2865 2866 mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); 2867 2868 mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity); 2869 if (mNotificationPanelGravity <= 0) { 2870 mNotificationPanelGravity = Gravity.START | Gravity.TOP; 2871 } 2872 2873 mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height); 2874 mStatusBarHeaderHeight = res.getDimensionPixelSize(R.dimen.status_bar_header_height); 2875 2876 mNotificationPanelMinHeightFrac = res.getFraction(R.dimen.notification_panel_min_height_frac, 1, 1); 2877 if (mNotificationPanelMinHeightFrac < 0f || mNotificationPanelMinHeightFrac > 1f) { 2878 mNotificationPanelMinHeightFrac = 0f; 2879 } 2880 2881 mHeadsUpNotificationDecay = res.getInteger(R.integer.heads_up_notification_decay); 2882 mRowMinHeight = res.getDimensionPixelSize(R.dimen.notification_min_height); 2883 mRowMaxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height); 2884 2885 mKeyguardMaxNotificationCount = res.getInteger(R.integer.keyguard_max_notification_count); 2886 2887 if (false) Log.v(TAG, "updateResources"); 2888 } 2889 2890 // Visibility reporting 2891 2892 @Override 2893 protected void visibilityChanged(boolean visible) { 2894 mVisible = visible; 2895 if (visible) { 2896 startNotificationLoggingIfScreenOnAndVisible(); 2897 } else { 2898 stopNotificationLogging(); 2899 } 2900 super.visibilityChanged(visible); 2901 } 2902 2903 private void stopNotificationLogging() { 2904 // Report all notifications as invisible and turn down the 2905 // reporter. 2906 if (!mCurrentlyVisibleNotifications.isEmpty()) { 2907 logNotificationVisibilityChanges( 2908 Collections.<String>emptyList(), mCurrentlyVisibleNotifications); 2909 mCurrentlyVisibleNotifications.clear(); 2910 } 2911 mHandler.removeCallbacks(mVisibilityReporter); 2912 mStackScroller.setChildLocationsChangedListener(null); 2913 } 2914 2915 private void startNotificationLoggingIfScreenOnAndVisible() { 2916 if (mVisible && mScreenOn) { 2917 mStackScroller.setChildLocationsChangedListener(mNotificationLocationsChangedListener); 2918 // Some transitions like mScreenOn=false -> mScreenOn=true don't 2919 // cause the scroller to emit child location events. Hence generate 2920 // one ourselves to guarantee that we're reporting visible 2921 // notifications. 2922 // (Note that in cases where the scroller does emit events, this 2923 // additional event doesn't break anything.) 2924 mNotificationLocationsChangedListener.onChildLocationsChanged(mStackScroller); 2925 } 2926 } 2927 2928 private void logNotificationVisibilityChanges( 2929 Collection<String> newlyVisible, Collection<String> noLongerVisible) { 2930 if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) { 2931 return; 2932 } 2933 String[] newlyVisibleAr = newlyVisible.toArray(new String[newlyVisible.size()]); 2934 String[] noLongerVisibleAr = noLongerVisible.toArray(new String[noLongerVisible.size()]); 2935 try { 2936 mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr); 2937 } catch (RemoteException e) { 2938 // Ignore. 2939 } 2940 } 2941 2942 // 2943 // tracing 2944 // 2945 2946 void postStartTracing() { 2947 mHandler.postDelayed(mStartTracing, 3000); 2948 } 2949 2950 void vibrate() { 2951 android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService( 2952 Context.VIBRATOR_SERVICE); 2953 vib.vibrate(250, VIBRATION_ATTRIBUTES); 2954 } 2955 2956 Runnable mStartTracing = new Runnable() { 2957 public void run() { 2958 vibrate(); 2959 SystemClock.sleep(250); 2960 Log.d(TAG, "startTracing"); 2961 android.os.Debug.startMethodTracing("/data/statusbar-traces/trace"); 2962 mHandler.postDelayed(mStopTracing, 10000); 2963 } 2964 }; 2965 2966 Runnable mStopTracing = new Runnable() { 2967 public void run() { 2968 android.os.Debug.stopMethodTracing(); 2969 Log.d(TAG, "stopTracing"); 2970 vibrate(); 2971 } 2972 }; 2973 2974 @Override 2975 protected void haltTicker() { 2976 if (mTickerEnabled) { 2977 mTicker.halt(); 2978 } 2979 } 2980 2981 @Override 2982 protected boolean shouldDisableNavbarGestures() { 2983 return !isDeviceProvisioned() 2984 || mExpandedVisible 2985 || (mDisabled & StatusBarManager.DISABLE_SEARCH) != 0; 2986 } 2987 2988 public void postStartSettingsActivity(final Intent intent, int delay) { 2989 mHandler.postDelayed(new Runnable() { 2990 @Override 2991 public void run() { 2992 handleStartSettingsActivity(intent, true /*onlyProvisioned*/); 2993 } 2994 }, delay); 2995 } 2996 2997 private void handleStartSettingsActivity(Intent intent, boolean onlyProvisioned) { 2998 if (onlyProvisioned && !isDeviceProvisioned()) return; 2999 try { 3000 // Dismiss the lock screen when Settings starts. 3001 ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity(); 3002 } catch (RemoteException e) { 3003 } 3004 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); 3005 mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); 3006 animateCollapsePanels(); 3007 } 3008 3009 public void startSettingsActivity(String action) { 3010 postStartSettingsActivity(new Intent(action), 0); 3011 } 3012 3013 private static class FastColorDrawable extends Drawable { 3014 private final int mColor; 3015 3016 public FastColorDrawable(int color) { 3017 mColor = 0xff000000 | color; 3018 } 3019 3020 @Override 3021 public void draw(Canvas canvas) { 3022 canvas.drawColor(mColor, PorterDuff.Mode.SRC); 3023 } 3024 3025 @Override 3026 public void setAlpha(int alpha) { 3027 } 3028 3029 @Override 3030 public void setColorFilter(ColorFilter cf) { 3031 } 3032 3033 @Override 3034 public int getOpacity() { 3035 return PixelFormat.OPAQUE; 3036 } 3037 3038 @Override 3039 public void setBounds(int left, int top, int right, int bottom) { 3040 } 3041 3042 @Override 3043 public void setBounds(Rect bounds) { 3044 } 3045 } 3046 3047 @Override 3048 public void destroy() { 3049 super.destroy(); 3050 if (mStatusBarWindow != null) { 3051 mWindowManager.removeViewImmediate(mStatusBarWindow); 3052 mStatusBarWindow = null; 3053 } 3054 if (mNavigationBarView != null) { 3055 mWindowManager.removeViewImmediate(mNavigationBarView); 3056 mNavigationBarView = null; 3057 } 3058 mContext.unregisterReceiver(mBroadcastReceiver); 3059 } 3060 3061 private boolean mDemoModeAllowed; 3062 private boolean mDemoMode; 3063 private DemoStatusIcons mDemoStatusIcons; 3064 3065 @Override 3066 public void dispatchDemoCommand(String command, Bundle args) { 3067 if (!mDemoModeAllowed) { 3068 mDemoModeAllowed = Settings.Global.getInt(mContext.getContentResolver(), 3069 "sysui_demo_allowed", 0) != 0; 3070 } 3071 if (!mDemoModeAllowed) return; 3072 if (command.equals(COMMAND_ENTER)) { 3073 mDemoMode = true; 3074 } else if (command.equals(COMMAND_EXIT)) { 3075 mDemoMode = false; 3076 checkBarModes(); 3077 } else if (!mDemoMode) { 3078 // automatically enter demo mode on first demo command 3079 dispatchDemoCommand(COMMAND_ENTER, new Bundle()); 3080 } 3081 boolean modeChange = command.equals(COMMAND_ENTER) || command.equals(COMMAND_EXIT); 3082 if (modeChange || command.equals(COMMAND_CLOCK)) { 3083 dispatchDemoCommandToView(command, args, R.id.clock); 3084 } 3085 if (modeChange || command.equals(COMMAND_BATTERY)) { 3086 dispatchDemoCommandToView(command, args, R.id.battery); 3087 } 3088 if (modeChange || command.equals(COMMAND_STATUS)) { 3089 if (mDemoStatusIcons == null) { 3090 mDemoStatusIcons = new DemoStatusIcons(mStatusIcons, mIconSize); 3091 } 3092 mDemoStatusIcons.dispatchDemoCommand(command, args); 3093 } 3094 if (mNetworkController != null && (modeChange || command.equals(COMMAND_NETWORK))) { 3095 mNetworkController.dispatchDemoCommand(command, args); 3096 } 3097 if (modeChange || command.equals(COMMAND_NOTIFICATIONS)) { 3098 View notifications = mStatusBarView == null ? null 3099 : mStatusBarView.findViewById(R.id.notification_icon_area); 3100 if (notifications != null) { 3101 String visible = args.getString("visible"); 3102 int vis = mDemoMode && "false".equals(visible) ? View.INVISIBLE : View.VISIBLE; 3103 notifications.setVisibility(vis); 3104 } 3105 } 3106 if (command.equals(COMMAND_BARS)) { 3107 String mode = args.getString("mode"); 3108 int barMode = "opaque".equals(mode) ? MODE_OPAQUE : 3109 "translucent".equals(mode) ? MODE_TRANSLUCENT : 3110 "semi-transparent".equals(mode) ? MODE_SEMI_TRANSPARENT : 3111 -1; 3112 if (barMode != -1) { 3113 boolean animate = true; 3114 if (mStatusBarView != null) { 3115 mStatusBarView.getBarTransitions().transitionTo(barMode, animate); 3116 } 3117 if (mNavigationBarView != null) { 3118 mNavigationBarView.getBarTransitions().transitionTo(barMode, animate); 3119 } 3120 } 3121 } 3122 } 3123 3124 private void dispatchDemoCommandToView(String command, Bundle args, int id) { 3125 if (mStatusBarView == null) return; 3126 View v = mStatusBarView.findViewById(id); 3127 if (v instanceof DemoMode) { 3128 ((DemoMode)v).dispatchDemoCommand(command, args); 3129 } 3130 } 3131 3132 /** 3133 * @return The {@link StatusBarState} the status bar is in. 3134 */ 3135 public int getBarState() { 3136 return mState; 3137 } 3138 3139 public void showKeyguard() { 3140 setBarState(StatusBarState.KEYGUARD); 3141 updateKeyguardState(); 3142 instantExpandNotificationsPanel(); 3143 mLeaveOpenOnKeyguardHide = false; 3144 } 3145 3146 public void hideKeyguard() { 3147 setBarState(StatusBarState.SHADE); 3148 if (mLeaveOpenOnKeyguardHide) { 3149 mLeaveOpenOnKeyguardHide = false; 3150 mNotificationPanel.animateToFullShade(); 3151 } else { 3152 instantCollapseNotificationPanel(); 3153 } 3154 updateKeyguardState(); 3155 } 3156 3157 /** 3158 * Notifies the status bar the Keyguard is fading away with the specified timings. 3159 * 3160 * @param delay the animation delay in miliseconds 3161 * @param fadeoutDuration the duration of the exit animation, in milliseconds 3162 */ 3163 public void setKeyguardFadingAway(long delay, long fadeoutDuration) { 3164 mKeyguardFadingAway = true; 3165 mKeyguardFadingAwayDelay = delay; 3166 mKeyguardFadingAwayDuration = fadeoutDuration; 3167 mWaitingForKeyguardExit = false; 3168 disable(mDisabledUnmodified, true /* animate */); 3169 } 3170 3171 /** 3172 * Notifies that the Keyguard fading away animation is done. 3173 */ 3174 public void finishKeyguardFadingAway() { 3175 mKeyguardFadingAway = false; 3176 } 3177 3178 private void updatePublicMode() { 3179 setLockscreenPublicMode( 3180 (mStatusBarKeyguardViewManager.isShowing() || 3181 mStatusBarKeyguardViewManager.isOccluded()) 3182 && mStatusBarKeyguardViewManager.isSecure()); 3183 } 3184 3185 private void updateKeyguardState() { 3186 if (mState == StatusBarState.KEYGUARD) { 3187 mKeyguardStatusView.setVisibility(View.VISIBLE); 3188 mKeyguardIndicationController.setVisible(true); 3189 mNotificationPanel.resetViews(); 3190 mKeyguardUserSwitcher.setKeyguard(true); 3191 } else { 3192 mKeyguardStatusView.setVisibility(View.GONE); 3193 mKeyguardIndicationController.setVisible(false); 3194 mKeyguardUserSwitcher.setKeyguard(false); 3195 } 3196 if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { 3197 mKeyguardBottomArea.setVisibility(View.VISIBLE); 3198 mHeader.setKeyguardShowing(true); 3199 mScrimController.setKeyguardShowing(true); 3200 } else { 3201 mKeyguardBottomArea.setVisibility(View.GONE); 3202 mHeader.setKeyguardShowing(false); 3203 mScrimController.setKeyguardShowing(false); 3204 } 3205 mNotificationPanel.setBarState(mState); 3206 updateDozingState(); 3207 updateStackScrollerState(); 3208 updatePublicMode(); 3209 updateNotifications(); 3210 checkBarModes(); 3211 updateCarrierLabelVisibility(false); 3212 updateMediaMetaData(false); 3213 mKeyguardMonitor.notifyKeyguardState(mStatusBarKeyguardViewManager.isShowing(), 3214 mStatusBarKeyguardViewManager.isSecure()); 3215 } 3216 3217 private void updateDozingState() { 3218 final boolean bottomGone = mKeyguardBottomArea.getVisibility() == View.GONE; 3219 if (mDozing) { 3220 mNotificationPanel.setBackgroundColor(0xff000000); 3221 mHeader.setVisibility(View.INVISIBLE); 3222 if (!bottomGone) { 3223 mKeyguardBottomArea.setVisibility(View.INVISIBLE); 3224 } 3225 mStackScroller.setDark(true, false /*animate*/); 3226 } else { 3227 mNotificationPanel.setBackground(null); 3228 mHeader.setVisibility(View.VISIBLE); 3229 if (!bottomGone) { 3230 mKeyguardBottomArea.setVisibility(View.VISIBLE); 3231 } 3232 mStackScroller.setDark(false, false /*animate*/); 3233 } 3234 mScrimController.setDozing(mDozing); 3235 } 3236 3237 public void updateStackScrollerState() { 3238 if (mStackScroller == null) return; 3239 boolean onKeyguard = mState == StatusBarState.KEYGUARD; 3240 mStackScroller.setDimmed(onKeyguard, false /* animate */); 3241 mStackScroller.setVisibility(!mShowLockscreenNotifications && onKeyguard 3242 ? View.INVISIBLE : View.VISIBLE); 3243 mStackScroller.setExpandingEnabled(!onKeyguard); 3244 ActivatableNotificationView activatedChild = mStackScroller.getActivatedChild(); 3245 mStackScroller.setActivatedChild(null); 3246 if (activatedChild != null) { 3247 activatedChild.makeInactive(false /* animate */); 3248 } 3249 } 3250 3251 public void userActivity() { 3252 if (mState == StatusBarState.KEYGUARD) { 3253 mKeyguardViewMediatorCallback.userActivity(); 3254 } 3255 } 3256 3257 public boolean interceptMediaKey(KeyEvent event) { 3258 return mState == StatusBarState.KEYGUARD 3259 && mStatusBarKeyguardViewManager.interceptMediaKey(event); 3260 } 3261 3262 public boolean onMenuPressed() { 3263 return mState == StatusBarState.KEYGUARD && mStatusBarKeyguardViewManager.onMenuPressed(); 3264 } 3265 3266 public boolean onBackPressed() { 3267 if (mStatusBarKeyguardViewManager.onBackPressed()) { 3268 return true; 3269 } 3270 if (mNotificationPanel.isQsExpanded()) { 3271 mNotificationPanel.animateCloseQs(); 3272 return true; 3273 } 3274 if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) { 3275 animateCollapsePanels(); 3276 return true; 3277 } 3278 return false; 3279 } 3280 3281 public boolean onSpacePressed() { 3282 if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { 3283 animateCollapsePanels(0 /* flags */, true /* force */); 3284 return true; 3285 } 3286 return false; 3287 } 3288 3289 private void showBouncer() { 3290 if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { 3291 mWaitingForKeyguardExit = true; 3292 mStatusBarKeyguardViewManager.dismiss(); 3293 } 3294 } 3295 3296 private void instantExpandNotificationsPanel() { 3297 3298 // Make our window larger and the panel expanded. 3299 makeExpandedVisible(true); 3300 mNotificationPanel.instantExpand(); 3301 } 3302 3303 private void instantCollapseNotificationPanel() { 3304 mNotificationPanel.setExpandedFraction(0); 3305 } 3306 3307 @Override 3308 public void onActivated(ActivatableNotificationView view) { 3309 mKeyguardIndicationController.showTransientIndication(R.string.notification_tap_again); 3310 ActivatableNotificationView previousView = mStackScroller.getActivatedChild(); 3311 if (previousView != null) { 3312 previousView.makeInactive(true /* animate */); 3313 } 3314 mStackScroller.setActivatedChild(view); 3315 } 3316 3317 /** 3318 * @param state The {@link StatusBarState} to set. 3319 */ 3320 public void setBarState(int state) { 3321 mState = state; 3322 mStatusBarWindowManager.setStatusBarState(state); 3323 } 3324 3325 @Override 3326 public void onActivationReset(ActivatableNotificationView view) { 3327 if (view == mStackScroller.getActivatedChild()) { 3328 mKeyguardIndicationController.hideTransientIndication(); 3329 mStackScroller.setActivatedChild(null); 3330 } 3331 } 3332 3333 public void onTrackingStarted() { 3334 } 3335 3336 public void onUnlockHintStarted() { 3337 mKeyguardIndicationController.showTransientIndication(R.string.keyguard_unlock); 3338 } 3339 3340 public void onHintFinished() { 3341 // Delay the reset a bit so the user can read the text. 3342 mKeyguardIndicationController.hideTransientIndicationDelayed(HINT_RESET_DELAY_MS); 3343 } 3344 3345 public void onCameraHintStarted() { 3346 mKeyguardIndicationController.showTransientIndication(R.string.camera_hint); 3347 } 3348 3349 public void onPhoneHintStarted() { 3350 mKeyguardIndicationController.showTransientIndication(R.string.phone_hint); 3351 } 3352 3353 public void onTrackingStopped(boolean expand) { 3354 if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { 3355 if (!expand && !mUnlockMethodCache.isMethodInsecure()) { 3356 showBouncer(); 3357 } 3358 } 3359 } 3360 3361 @Override 3362 protected int getMaxKeyguardNotifications() { 3363 return mKeyguardMaxNotificationCount; 3364 } 3365 3366 public NavigationBarView getNavigationBarView() { 3367 return mNavigationBarView; 3368 } 3369 3370 // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------ 3371 3372 @Override 3373 public void onDraggedDown(View startingChild) { 3374 goToLockedShade(startingChild); 3375 } 3376 3377 @Override 3378 public void onDragDownReset() { 3379 mStackScroller.setDimmed(true /* dimmed */, true /* animated */); 3380 } 3381 3382 public void onThresholdReached() { 3383 mStackScroller.setDimmed(false /* dimmed */, true /* animate */); 3384 } 3385 3386 @Override 3387 public void onTouchSlopExceeded() { 3388 mStackScroller.removeLongPressCallback(); 3389 } 3390 3391 /** 3392 * If secure with redaction: Show bouncer, go to unlocked shade. 3393 * 3394 * <p>If secure without redaction or no security: Go to {@link StatusBarState#SHADE_LOCKED}.</p> 3395 * 3396 * @param expandView The view to expand after going to the shade. 3397 */ 3398 public void goToLockedShade(View expandView) { 3399 if (expandView instanceof ExpandableNotificationRow) { 3400 ExpandableNotificationRow row = (ExpandableNotificationRow) expandView; 3401 row.setUserExpanded(true); 3402 } 3403 if (isLockscreenPublicMode() && !userAllowsPrivateNotificationsInPublic(mCurrentUserId)) { 3404 mLeaveOpenOnKeyguardHide = true; 3405 showBouncer(); 3406 } else { 3407 mNotificationPanel.animateToFullShade(); 3408 setBarState(StatusBarState.SHADE_LOCKED); 3409 updateKeyguardState(); 3410 } 3411 } 3412 3413 /** 3414 * Goes back to the keyguard after hanging around in {@link StatusBarState#SHADE_LOCKED}. 3415 */ 3416 public void goToKeyguard() { 3417 if (mState == StatusBarState.SHADE_LOCKED) { 3418 setBarState(StatusBarState.KEYGUARD); 3419 updateKeyguardState(); 3420 } 3421 } 3422 3423 /** 3424 * @return a ViewGroup that spans the entire panel which contains the quick settings 3425 */ 3426 public ViewGroup getQuickSettingsOverlayParent() { 3427 return mNotificationPanel; 3428 } 3429 3430 public LinearLayout getSystemIcons() { 3431 return mSystemIcons; 3432 } 3433 3434 public LinearLayout getSystemIconArea() { 3435 return mSystemIconArea; 3436 } 3437 3438 @Override 3439 public void setBouncerShowing(boolean bouncerShowing) { 3440 super.setBouncerShowing(bouncerShowing); 3441 disable(mDisabledUnmodified, true /* animate */); 3442 } 3443 3444 public void onScreenTurnedOff() { 3445 mStackScroller.setAnimationsEnabled(false); 3446 } 3447 3448 public void onScreenTurnedOn() { 3449 mStackScroller.setAnimationsEnabled(true); 3450 } 3451 3452 public void toggleLockedApp() { 3453 Log.d(TAG, "Trying to toggle lock-to-app"); 3454 try { 3455 IActivityManager activityManager = ActivityManagerNative.getDefault(); 3456 if (activityManager.isInLockTaskMode()) { 3457 activityManager.stopLockTaskModeOnCurrent(); 3458 } 3459 } catch (RemoteException e) { 3460 Log.d(TAG, "Unable to toggle Lock-to-app", e); 3461 } 3462 } 3463 3464 // Recents 3465 3466 @Override 3467 protected void showRecents(boolean triggeredFromAltTab) { 3468 // Set the recents visibility flag 3469 mSystemUiVisibility |= View.RECENT_APPS_VISIBLE; 3470 notifyUiVisibilityChanged(mSystemUiVisibility); 3471 super.showRecents(triggeredFromAltTab); 3472 } 3473 3474 @Override 3475 protected void hideRecents(boolean triggeredFromAltTab) { 3476 // Unset the recents visibility flag 3477 mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE; 3478 notifyUiVisibilityChanged(mSystemUiVisibility); 3479 super.hideRecents(triggeredFromAltTab); 3480 } 3481 3482 @Override 3483 protected void toggleRecents() { 3484 // Toggle the recents visibility flag 3485 mSystemUiVisibility ^= View.RECENT_APPS_VISIBLE; 3486 notifyUiVisibilityChanged(mSystemUiVisibility); 3487 super.toggleRecents(); 3488 } 3489 3490 @Override 3491 public void onVisibilityChanged(boolean visible) { 3492 // Update the recents visibility flag 3493 if (visible) { 3494 mSystemUiVisibility |= View.RECENT_APPS_VISIBLE; 3495 } else { 3496 mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE; 3497 } 3498 notifyUiVisibilityChanged(mSystemUiVisibility); 3499 } 3500 3501 private final class ShadeUpdates { 3502 private final ArraySet<String> mVisibleNotifications = new ArraySet<String>(); 3503 private final ArraySet<String> mNewVisibleNotifications = new ArraySet<String>(); 3504 3505 public void check() { 3506 mNewVisibleNotifications.clear(); 3507 for (int i = 0; i < mNotificationData.size(); i++) { 3508 final Entry entry = mNotificationData.get(i); 3509 final boolean visible = entry.row != null 3510 && entry.row.getVisibility() == View.VISIBLE; 3511 if (visible) { 3512 mNewVisibleNotifications.add(entry.key + entry.notification.getPostTime()); 3513 } 3514 } 3515 final boolean updates = !mVisibleNotifications.containsAll(mNewVisibleNotifications); 3516 mVisibleNotifications.clear(); 3517 mVisibleNotifications.addAll(mNewVisibleNotifications); 3518 3519 // We have new notifications 3520 if (updates && mDozeServiceHost != null) { 3521 mDozeServiceHost.fireNewNotifications(); 3522 } 3523 } 3524 } 3525 3526 private final class DozeServiceHost implements DozeService.Host { 3527 // Amount of time to allow to update the time shown on the screen before releasing 3528 // the wakelock. This timeout is design to compensate for the fact that we don't 3529 // currently have a way to know when time display contents have actually been 3530 // refreshed once we've finished rendering a new frame. 3531 private static final long PROCESSING_TIME = 500; 3532 3533 private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); 3534 private final H mHandler = new H(); 3535 3536 private DozeService mCurrentDozeService; 3537 3538 public void fireNewNotifications() { 3539 for (Callback callback : mCallbacks) { 3540 callback.onNewNotifications(); 3541 } 3542 } 3543 3544 @Override 3545 public void addCallback(Callback callback) { 3546 mCallbacks.add(callback); 3547 } 3548 3549 @Override 3550 public void removeCallback(Callback callback) { 3551 mCallbacks.remove(callback); 3552 } 3553 3554 @Override 3555 public void requestDoze(DozeService dozeService) { 3556 if (dozeService == null) return; 3557 dozeService.stayAwake(PROCESSING_TIME); 3558 mHandler.obtainMessage(H.REQUEST_DOZE, dozeService).sendToTarget(); 3559 } 3560 3561 @Override 3562 public void requestTease(DozeService dozeService) { 3563 if (dozeService == null) return; 3564 dozeService.stayAwake(PROCESSING_TIME); 3565 mHandler.obtainMessage(H.REQUEST_TEASE, dozeService).sendToTarget(); 3566 } 3567 3568 @Override 3569 public void dozingStopped(DozeService dozeService) { 3570 if (dozeService == null) return; 3571 dozeService.stayAwake(PROCESSING_TIME); 3572 mHandler.obtainMessage(H.DOZING_STOPPED, dozeService).sendToTarget(); 3573 } 3574 3575 private void handleRequestDoze(DozeService dozeService) { 3576 mCurrentDozeService = dozeService; 3577 if (!mDozing) { 3578 mDozing = true; 3579 updateDozingState(); 3580 } 3581 mCurrentDozeService.startDozing(); 3582 } 3583 3584 private void handleRequestTease(DozeService dozeService) { 3585 if (!dozeService.equals(mCurrentDozeService)) return; 3586 final long stayAwake = mScrimController.tease(); 3587 mCurrentDozeService.stayAwake(stayAwake); 3588 } 3589 3590 private void handleDozingStopped(DozeService dozeService) { 3591 if (dozeService.equals(mCurrentDozeService)) { 3592 mCurrentDozeService = null; 3593 } 3594 if (mDozing) { 3595 mDozing = false; 3596 updateDozingState(); 3597 } 3598 } 3599 3600 private final class H extends Handler { 3601 private static final int REQUEST_DOZE = 1; 3602 private static final int REQUEST_TEASE = 2; 3603 private static final int DOZING_STOPPED = 3; 3604 3605 @Override 3606 public void handleMessage(Message msg) { 3607 if (msg.what == REQUEST_DOZE) { 3608 handleRequestDoze((DozeService) msg.obj); 3609 } else if (msg.what == REQUEST_TEASE) { 3610 handleRequestTease((DozeService) msg.obj); 3611 } else if (msg.what == DOZING_STOPPED) { 3612 handleDozingStopped((DozeService) msg.obj); 3613 } 3614 } 3615 } 3616 } 3617} 3618