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