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