1 /* 2 * Copyright (C) 2012 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 17 package com.android.systemui.statusbar.phone; 18 19 import android.annotation.ColorInt; 20 import android.annotation.DrawableRes; 21 import android.annotation.LayoutRes; 22 import android.app.StatusBarManager; 23 import android.content.Context; 24 import android.content.res.Configuration; 25 import android.content.res.TypedArray; 26 import android.graphics.Canvas; 27 import android.graphics.Paint; 28 import android.graphics.PorterDuff; 29 import android.graphics.PorterDuffXfermode; 30 import android.graphics.Rect; 31 import android.graphics.drawable.Drawable; 32 import android.hardware.display.AmbientDisplayConfiguration; 33 import android.media.AudioManager; 34 import android.media.session.MediaSessionLegacyHelper; 35 import android.net.Uri; 36 import android.os.Bundle; 37 import android.os.SystemClock; 38 import android.os.UserHandle; 39 import android.provider.Settings; 40 import android.util.AttributeSet; 41 import android.view.ActionMode; 42 import android.view.DisplayCutout; 43 import android.view.GestureDetector; 44 import android.view.InputDevice; 45 import android.view.InputQueue; 46 import android.view.KeyEvent; 47 import android.view.LayoutInflater; 48 import android.view.Menu; 49 import android.view.MenuItem; 50 import android.view.MotionEvent; 51 import android.view.SurfaceHolder; 52 import android.view.View; 53 import android.view.ViewGroup; 54 import android.view.ViewTreeObserver; 55 import android.view.Window; 56 import android.view.WindowInsetsController; 57 import android.widget.FrameLayout; 58 59 import com.android.internal.annotations.VisibleForTesting; 60 import com.android.internal.view.FloatingActionMode; 61 import com.android.internal.widget.FloatingToolbar; 62 import com.android.systemui.Dependency; 63 import com.android.systemui.ExpandHelper; 64 import com.android.systemui.R; 65 import com.android.systemui.classifier.FalsingManagerFactory; 66 import com.android.systemui.plugins.FalsingManager; 67 import com.android.systemui.plugins.statusbar.StatusBarStateController; 68 import com.android.systemui.statusbar.DragDownHelper; 69 import com.android.systemui.statusbar.StatusBarState; 70 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; 71 import com.android.systemui.statusbar.phone.ScrimController.ScrimVisibility; 72 import com.android.systemui.tuner.TunerService; 73 74 import java.io.FileDescriptor; 75 import java.io.PrintWriter; 76 77 /** 78 * Combined status bar and notification panel view. Also holding backdrop and scrims. 79 */ 80 public class StatusBarWindowView extends FrameLayout { 81 public static final String TAG = "StatusBarWindowView"; 82 public static final boolean DEBUG = StatusBar.DEBUG; 83 84 private final GestureDetector mGestureDetector; 85 private final StatusBarStateController mStatusBarStateController; 86 private boolean mDoubleTapEnabled; 87 private boolean mSingleTapEnabled; 88 private DragDownHelper mDragDownHelper; 89 private NotificationStackScrollLayout mStackScrollLayout; 90 private NotificationPanelView mNotificationPanel; 91 private View mBrightnessMirror; 92 private LockIcon mLockIcon; 93 private PhoneStatusBarView mStatusBarView; 94 95 private int mRightInset = 0; 96 private int mLeftInset = 0; 97 98 private StatusBar mService; 99 private final Paint mTransparentSrcPaint = new Paint(); 100 private FalsingManager mFalsingManager; 101 102 // Implements the floating action mode for TextView's Cut/Copy/Past menu. Normally provided by 103 // DecorView, but since this is a special window we have to roll our own. 104 private View mFloatingActionModeOriginatingView; 105 private ActionMode mFloatingActionMode; 106 private FloatingToolbar mFloatingToolbar; 107 private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener; 108 private boolean mTouchCancelled; 109 private boolean mTouchActive; 110 private boolean mExpandAnimationRunning; 111 private boolean mExpandAnimationPending; 112 private boolean mSuppressingWakeUpGesture; 113 114 private final GestureDetector.SimpleOnGestureListener mGestureListener = 115 new GestureDetector.SimpleOnGestureListener() { 116 @Override 117 public boolean onSingleTapConfirmed(MotionEvent e) { 118 if (mSingleTapEnabled && !mSuppressingWakeUpGesture) { 119 mService.wakeUpIfDozing(SystemClock.uptimeMillis(), StatusBarWindowView.this, 120 "SINGLE_TAP"); 121 return true; 122 } 123 return false; 124 } 125 126 @Override 127 public boolean onDoubleTap(MotionEvent e) { 128 if (mDoubleTapEnabled || mSingleTapEnabled) { 129 mService.wakeUpIfDozing(SystemClock.uptimeMillis(), StatusBarWindowView.this, 130 "DOUBLE_TAP"); 131 return true; 132 } 133 return false; 134 } 135 }; 136 private final TunerService.Tunable mTunable = (key, newValue) -> { 137 AmbientDisplayConfiguration configuration = new AmbientDisplayConfiguration(mContext); 138 switch (key) { 139 case Settings.Secure.DOZE_DOUBLE_TAP_GESTURE: 140 mDoubleTapEnabled = configuration.doubleTapGestureEnabled(UserHandle.USER_CURRENT); 141 break; 142 case Settings.Secure.DOZE_TAP_SCREEN_GESTURE: 143 mSingleTapEnabled = configuration.tapGestureEnabled(UserHandle.USER_CURRENT); 144 } 145 }; 146 147 /** 148 * If set to true, the current gesture started below the notch and we need to dispatch touch 149 * events manually as it's outside of the regular view bounds. 150 */ 151 private boolean mExpandingBelowNotch; 152 StatusBarWindowView(Context context, AttributeSet attrs)153 public StatusBarWindowView(Context context, AttributeSet attrs) { 154 super(context, attrs); 155 setMotionEventSplittingEnabled(false); 156 mTransparentSrcPaint.setColor(0); 157 mTransparentSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); 158 mFalsingManager = FalsingManagerFactory.getInstance(context); 159 mGestureDetector = new GestureDetector(context, mGestureListener); 160 mStatusBarStateController = Dependency.get(StatusBarStateController.class); 161 Dependency.get(TunerService.class).addTunable(mTunable, 162 Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, 163 Settings.Secure.DOZE_TAP_SCREEN_GESTURE); 164 } 165 166 @Override fitSystemWindows(Rect insets)167 protected boolean fitSystemWindows(Rect insets) { 168 if (getFitsSystemWindows()) { 169 boolean paddingChanged = insets.top != getPaddingTop() 170 || insets.bottom != getPaddingBottom(); 171 172 int rightCutout = 0; 173 int leftCutout = 0; 174 DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout(); 175 if (displayCutout != null) { 176 leftCutout = displayCutout.getSafeInsetLeft(); 177 rightCutout = displayCutout.getSafeInsetRight(); 178 } 179 180 int targetLeft = Math.max(insets.left, leftCutout); 181 int targetRight = Math.max(insets.right, rightCutout); 182 183 // Super-special right inset handling, because scrims and backdrop need to ignore it. 184 if (targetRight != mRightInset || targetLeft != mLeftInset) { 185 mRightInset = targetRight; 186 mLeftInset = targetLeft; 187 applyMargins(); 188 } 189 // Drop top inset, and pass through bottom inset. 190 if (paddingChanged) { 191 setPadding(0, 0, 0, 0); 192 } 193 insets.left = 0; 194 insets.top = 0; 195 insets.right = 0; 196 } else { 197 if (mRightInset != 0 || mLeftInset != 0) { 198 mRightInset = 0; 199 mLeftInset = 0; 200 applyMargins(); 201 } 202 boolean changed = getPaddingLeft() != 0 203 || getPaddingRight() != 0 204 || getPaddingTop() != 0 205 || getPaddingBottom() != 0; 206 if (changed) { 207 setPadding(0, 0, 0, 0); 208 } 209 insets.top = 0; 210 } 211 return false; 212 } 213 applyMargins()214 private void applyMargins() { 215 final int N = getChildCount(); 216 for (int i = 0; i < N; i++) { 217 View child = getChildAt(i); 218 if (child.getLayoutParams() instanceof LayoutParams) { 219 LayoutParams lp = (LayoutParams) child.getLayoutParams(); 220 if (!lp.ignoreRightInset 221 && (lp.rightMargin != mRightInset || lp.leftMargin != mLeftInset)) { 222 lp.rightMargin = mRightInset; 223 lp.leftMargin = mLeftInset; 224 child.requestLayout(); 225 } 226 } 227 } 228 } 229 230 @VisibleForTesting getStackScrollLayout()231 protected NotificationStackScrollLayout getStackScrollLayout() { 232 return mStackScrollLayout; 233 } 234 235 @Override generateLayoutParams(AttributeSet attrs)236 public FrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) { 237 return new LayoutParams(getContext(), attrs); 238 } 239 240 @Override generateDefaultLayoutParams()241 protected FrameLayout.LayoutParams generateDefaultLayoutParams() { 242 return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 243 } 244 245 @Override onFinishInflate()246 protected void onFinishInflate() { 247 super.onFinishInflate(); 248 mStackScrollLayout = findViewById(R.id.notification_stack_scroller); 249 mNotificationPanel = findViewById(R.id.notification_panel); 250 mBrightnessMirror = findViewById(R.id.brightness_mirror); 251 mLockIcon = findViewById(R.id.lock_icon); 252 } 253 254 @Override onViewAdded(View child)255 public void onViewAdded(View child) { 256 super.onViewAdded(child); 257 if (child.getId() == R.id.brightness_mirror) { 258 mBrightnessMirror = child; 259 } 260 } 261 262 /** 263 * Propagate {@link StatusBar} pulsing state. 264 */ setPulsing(boolean pulsing)265 public void setPulsing(boolean pulsing) { 266 if (mLockIcon != null) { 267 mLockIcon.setPulsing(pulsing); 268 } 269 } 270 271 /** 272 * Called when the biometric authentication mode changes. 273 * @param wakeAndUnlock If the type is {@link BiometricUnlockController#isWakeAndUnlock()} 274 */ onBiometricAuthModeChanged(boolean wakeAndUnlock)275 public void onBiometricAuthModeChanged(boolean wakeAndUnlock) { 276 if (mLockIcon != null) { 277 mLockIcon.onBiometricAuthModeChanged(wakeAndUnlock); 278 } 279 } 280 setStatusBarView(PhoneStatusBarView statusBarView)281 public void setStatusBarView(PhoneStatusBarView statusBarView) { 282 mStatusBarView = statusBarView; 283 } 284 setService(StatusBar service)285 public void setService(StatusBar service) { 286 mService = service; 287 NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout(); 288 ExpandHelper.Callback expandHelperCallback = stackScrollLayout.getExpandHelperCallback(); 289 DragDownHelper.DragDownCallback dragDownCallback = stackScrollLayout.getDragDownCallback(); 290 setDragDownHelper(new DragDownHelper(getContext(), this, expandHelperCallback, 291 dragDownCallback)); 292 } 293 294 @VisibleForTesting setDragDownHelper(DragDownHelper dragDownHelper)295 void setDragDownHelper(DragDownHelper dragDownHelper) { 296 mDragDownHelper = dragDownHelper; 297 } 298 299 @Override onAttachedToWindow()300 protected void onAttachedToWindow () { 301 super.onAttachedToWindow(); 302 setWillNotDraw(!DEBUG); 303 } 304 305 @Override dispatchKeyEvent(KeyEvent event)306 public boolean dispatchKeyEvent(KeyEvent event) { 307 if (mService.interceptMediaKey(event)) { 308 return true; 309 } 310 if (super.dispatchKeyEvent(event)) { 311 return true; 312 } 313 boolean down = event.getAction() == KeyEvent.ACTION_DOWN; 314 switch (event.getKeyCode()) { 315 case KeyEvent.KEYCODE_BACK: 316 if (!down) { 317 mService.onBackPressed(); 318 } 319 return true; 320 case KeyEvent.KEYCODE_MENU: 321 if (!down) { 322 return mService.onMenuPressed(); 323 } 324 case KeyEvent.KEYCODE_SPACE: 325 if (!down) { 326 return mService.onSpacePressed(); 327 } 328 break; 329 case KeyEvent.KEYCODE_VOLUME_DOWN: 330 case KeyEvent.KEYCODE_VOLUME_UP: 331 if (mService.isDozing()) { 332 MediaSessionLegacyHelper.getHelper(mContext).sendVolumeKeyEvent( 333 event, AudioManager.USE_DEFAULT_STREAM_TYPE, true); 334 return true; 335 } 336 break; 337 } 338 return false; 339 } 340 setTouchActive(boolean touchActive)341 public void setTouchActive(boolean touchActive) { 342 mTouchActive = touchActive; 343 } 344 suppressWakeUpGesture(boolean suppress)345 void suppressWakeUpGesture(boolean suppress) { 346 mSuppressingWakeUpGesture = suppress; 347 } 348 349 @Override dispatchTouchEvent(MotionEvent ev)350 public boolean dispatchTouchEvent(MotionEvent ev) { 351 boolean isDown = ev.getActionMasked() == MotionEvent.ACTION_DOWN; 352 boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP; 353 boolean isCancel = ev.getActionMasked() == MotionEvent.ACTION_CANCEL; 354 355 // Reset manual touch dispatch state here but make sure the UP/CANCEL event still gets 356 // delivered. 357 boolean expandingBelowNotch = mExpandingBelowNotch; 358 if (isUp || isCancel) { 359 mExpandingBelowNotch = false; 360 } 361 362 if (!isCancel && mService.shouldIgnoreTouch()) { 363 return false; 364 } 365 if (isDown && mNotificationPanel.isFullyCollapsed()) { 366 mNotificationPanel.startExpandLatencyTracking(); 367 } 368 if (isDown) { 369 setTouchActive(true); 370 mTouchCancelled = false; 371 } else if (ev.getActionMasked() == MotionEvent.ACTION_UP 372 || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) { 373 setTouchActive(false); 374 } 375 if (mTouchCancelled || mExpandAnimationRunning || mExpandAnimationPending) { 376 return false; 377 } 378 mFalsingManager.onTouchEvent(ev, getWidth(), getHeight()); 379 mGestureDetector.onTouchEvent(ev); 380 if (mBrightnessMirror != null && mBrightnessMirror.getVisibility() == VISIBLE) { 381 // Disallow new pointers while the brightness mirror is visible. This is so that you 382 // can't touch anything other than the brightness slider while the mirror is showing 383 // and the rest of the panel is transparent. 384 if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) { 385 return false; 386 } 387 } 388 if (isDown) { 389 getStackScrollLayout().closeControlsIfOutsideTouch(ev); 390 } 391 if (mService.isDozing()) { 392 mService.mDozeScrimController.extendPulse(); 393 } 394 395 // In case we start outside of the view bounds (below the status bar), we need to dispatch 396 // the touch manually as the view system can't accomodate for touches outside of the 397 // regular view bounds. 398 if (isDown && ev.getY() >= mBottom) { 399 mExpandingBelowNotch = true; 400 expandingBelowNotch = true; 401 } 402 if (expandingBelowNotch) { 403 return mStatusBarView.dispatchTouchEvent(ev); 404 } 405 406 return super.dispatchTouchEvent(ev); 407 } 408 409 @Override onInterceptTouchEvent(MotionEvent ev)410 public boolean onInterceptTouchEvent(MotionEvent ev) { 411 NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout(); 412 if (mService.isDozing() && !mService.isPulsing()) { 413 // Capture all touch events in always-on. 414 return true; 415 } 416 boolean intercept = false; 417 if (mNotificationPanel.isFullyExpanded() 418 && stackScrollLayout.getVisibility() == View.VISIBLE 419 && mStatusBarStateController.getState() == StatusBarState.KEYGUARD 420 && !mService.isBouncerShowing() 421 && !mService.isDozing()) { 422 intercept = mDragDownHelper.onInterceptTouchEvent(ev); 423 } 424 if (!intercept) { 425 super.onInterceptTouchEvent(ev); 426 } 427 if (intercept) { 428 MotionEvent cancellation = MotionEvent.obtain(ev); 429 cancellation.setAction(MotionEvent.ACTION_CANCEL); 430 stackScrollLayout.onInterceptTouchEvent(cancellation); 431 mNotificationPanel.onInterceptTouchEvent(cancellation); 432 cancellation.recycle(); 433 } 434 return intercept; 435 } 436 437 @Override onTouchEvent(MotionEvent ev)438 public boolean onTouchEvent(MotionEvent ev) { 439 boolean handled = false; 440 if (mService.isDozing()) { 441 handled = !mService.isPulsing(); 442 } 443 if ((mStatusBarStateController.getState() == StatusBarState.KEYGUARD && !handled) 444 || mDragDownHelper.isDraggingDown()) { 445 // we still want to finish our drag down gesture when locking the screen 446 handled = mDragDownHelper.onTouchEvent(ev); 447 } 448 if (!handled) { 449 handled = super.onTouchEvent(ev); 450 } 451 final int action = ev.getAction(); 452 if (!handled && (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL)) { 453 mService.setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); 454 } 455 return handled; 456 } 457 458 @Override onDraw(Canvas canvas)459 public void onDraw(Canvas canvas) { 460 super.onDraw(canvas); 461 if (DEBUG) { 462 Paint pt = new Paint(); 463 pt.setColor(0x80FFFF00); 464 pt.setStrokeWidth(12.0f); 465 pt.setStyle(Paint.Style.STROKE); 466 canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), pt); 467 } 468 } 469 cancelExpandHelper()470 public void cancelExpandHelper() { 471 NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout(); 472 if (stackScrollLayout != null) { 473 stackScrollLayout.cancelExpandHelper(); 474 } 475 } 476 cancelCurrentTouch()477 public void cancelCurrentTouch() { 478 if (mTouchActive) { 479 final long now = SystemClock.uptimeMillis(); 480 MotionEvent event = MotionEvent.obtain(now, now, 481 MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0); 482 event.setSource(InputDevice.SOURCE_TOUCHSCREEN); 483 dispatchTouchEvent(event); 484 event.recycle(); 485 mTouchCancelled = true; 486 } 487 } 488 setExpandAnimationRunning(boolean expandAnimationRunning)489 public void setExpandAnimationRunning(boolean expandAnimationRunning) { 490 mExpandAnimationRunning = expandAnimationRunning; 491 } 492 setExpandAnimationPending(boolean pending)493 public void setExpandAnimationPending(boolean pending) { 494 mExpandAnimationPending = pending; 495 } 496 dump(FileDescriptor fd, PrintWriter pw, String[] args)497 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 498 pw.print(" mExpandAnimationPending="); pw.println(mExpandAnimationPending); 499 pw.print(" mExpandAnimationRunning="); pw.println(mExpandAnimationRunning); 500 pw.print(" mTouchCancelled="); pw.println(mTouchCancelled); 501 pw.print(" mTouchActive="); pw.println(mTouchActive); 502 } 503 504 /** 505 * Called whenever the scrims become opaque, transparent or semi-transparent. 506 */ onScrimVisibilityChanged(@crimVisibility int scrimsVisible)507 public void onScrimVisibilityChanged(@ScrimVisibility int scrimsVisible) { 508 if (mLockIcon != null) { 509 mLockIcon.onScrimVisibilityChanged(scrimsVisible); 510 } 511 } 512 513 /** 514 * When we're launching an affordance, like double pressing power to open camera. 515 */ onShowingLaunchAffordanceChanged(boolean showing)516 public void onShowingLaunchAffordanceChanged(boolean showing) { 517 if (mLockIcon != null) { 518 mLockIcon.onShowingLaunchAffordanceChanged(showing); 519 } 520 } 521 522 public class LayoutParams extends FrameLayout.LayoutParams { 523 524 public boolean ignoreRightInset; 525 LayoutParams(int width, int height)526 public LayoutParams(int width, int height) { 527 super(width, height); 528 } 529 LayoutParams(Context c, AttributeSet attrs)530 public LayoutParams(Context c, AttributeSet attrs) { 531 super(c, attrs); 532 533 TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.StatusBarWindowView_Layout); 534 ignoreRightInset = a.getBoolean( 535 R.styleable.StatusBarWindowView_Layout_ignoreRightInset, false); 536 a.recycle(); 537 } 538 } 539 540 @Override startActionModeForChild(View originalView, ActionMode.Callback callback, int type)541 public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback, 542 int type) { 543 if (type == ActionMode.TYPE_FLOATING) { 544 return startActionMode(originalView, callback, type); 545 } 546 return super.startActionModeForChild(originalView, callback, type); 547 } 548 createFloatingActionMode( View originatingView, ActionMode.Callback2 callback)549 private ActionMode createFloatingActionMode( 550 View originatingView, ActionMode.Callback2 callback) { 551 if (mFloatingActionMode != null) { 552 mFloatingActionMode.finish(); 553 } 554 cleanupFloatingActionModeViews(); 555 mFloatingToolbar = new FloatingToolbar(mFakeWindow); 556 final FloatingActionMode mode = 557 new FloatingActionMode(mContext, callback, originatingView, mFloatingToolbar); 558 mFloatingActionModeOriginatingView = originatingView; 559 mFloatingToolbarPreDrawListener = 560 new ViewTreeObserver.OnPreDrawListener() { 561 @Override 562 public boolean onPreDraw() { 563 mode.updateViewLocationInWindow(); 564 return true; 565 } 566 }; 567 return mode; 568 } 569 setHandledFloatingActionMode(ActionMode mode)570 private void setHandledFloatingActionMode(ActionMode mode) { 571 mFloatingActionMode = mode; 572 mFloatingActionMode.invalidate(); // Will show the floating toolbar if necessary. 573 mFloatingActionModeOriginatingView.getViewTreeObserver() 574 .addOnPreDrawListener(mFloatingToolbarPreDrawListener); 575 } 576 cleanupFloatingActionModeViews()577 private void cleanupFloatingActionModeViews() { 578 if (mFloatingToolbar != null) { 579 mFloatingToolbar.dismiss(); 580 mFloatingToolbar = null; 581 } 582 if (mFloatingActionModeOriginatingView != null) { 583 if (mFloatingToolbarPreDrawListener != null) { 584 mFloatingActionModeOriginatingView.getViewTreeObserver() 585 .removeOnPreDrawListener(mFloatingToolbarPreDrawListener); 586 mFloatingToolbarPreDrawListener = null; 587 } 588 mFloatingActionModeOriginatingView = null; 589 } 590 } 591 startActionMode( View originatingView, ActionMode.Callback callback, int type)592 private ActionMode startActionMode( 593 View originatingView, ActionMode.Callback callback, int type) { 594 ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback); 595 ActionMode mode = createFloatingActionMode(originatingView, wrappedCallback); 596 if (mode != null && wrappedCallback.onCreateActionMode(mode, mode.getMenu())) { 597 setHandledFloatingActionMode(mode); 598 } else { 599 mode = null; 600 } 601 return mode; 602 } 603 604 private class ActionModeCallback2Wrapper extends ActionMode.Callback2 { 605 private final ActionMode.Callback mWrapped; 606 ActionModeCallback2Wrapper(ActionMode.Callback wrapped)607 public ActionModeCallback2Wrapper(ActionMode.Callback wrapped) { 608 mWrapped = wrapped; 609 } 610 onCreateActionMode(ActionMode mode, Menu menu)611 public boolean onCreateActionMode(ActionMode mode, Menu menu) { 612 return mWrapped.onCreateActionMode(mode, menu); 613 } 614 onPrepareActionMode(ActionMode mode, Menu menu)615 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 616 requestFitSystemWindows(); 617 return mWrapped.onPrepareActionMode(mode, menu); 618 } 619 onActionItemClicked(ActionMode mode, MenuItem item)620 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 621 return mWrapped.onActionItemClicked(mode, item); 622 } 623 onDestroyActionMode(ActionMode mode)624 public void onDestroyActionMode(ActionMode mode) { 625 mWrapped.onDestroyActionMode(mode); 626 if (mode == mFloatingActionMode) { 627 cleanupFloatingActionModeViews(); 628 mFloatingActionMode = null; 629 } 630 requestFitSystemWindows(); 631 } 632 633 @Override onGetContentRect(ActionMode mode, View view, Rect outRect)634 public void onGetContentRect(ActionMode mode, View view, Rect outRect) { 635 if (mWrapped instanceof ActionMode.Callback2) { 636 ((ActionMode.Callback2) mWrapped).onGetContentRect(mode, view, outRect); 637 } else { 638 super.onGetContentRect(mode, view, outRect); 639 } 640 } 641 } 642 643 /** 644 * Minimal window to satisfy FloatingToolbar. 645 */ 646 private Window mFakeWindow = new Window(mContext) { 647 @Override 648 public void takeSurface(SurfaceHolder.Callback2 callback) { 649 } 650 651 @Override 652 public void takeInputQueue(InputQueue.Callback callback) { 653 } 654 655 @Override 656 public boolean isFloating() { 657 return false; 658 } 659 660 @Override 661 public void alwaysReadCloseOnTouchAttr() { 662 } 663 664 @Override 665 public void setContentView(@LayoutRes int layoutResID) { 666 } 667 668 @Override 669 public void setContentView(View view) { 670 } 671 672 @Override 673 public void setContentView(View view, ViewGroup.LayoutParams params) { 674 } 675 676 @Override 677 public void addContentView(View view, ViewGroup.LayoutParams params) { 678 } 679 680 @Override 681 public void clearContentView() { 682 } 683 684 @Override 685 public View getCurrentFocus() { 686 return null; 687 } 688 689 @Override 690 public LayoutInflater getLayoutInflater() { 691 return null; 692 } 693 694 @Override 695 public void setTitle(CharSequence title) { 696 } 697 698 @Override 699 public void setTitleColor(@ColorInt int textColor) { 700 } 701 702 @Override 703 public void openPanel(int featureId, KeyEvent event) { 704 } 705 706 @Override 707 public void closePanel(int featureId) { 708 } 709 710 @Override 711 public void togglePanel(int featureId, KeyEvent event) { 712 } 713 714 @Override 715 public void invalidatePanelMenu(int featureId) { 716 } 717 718 @Override 719 public boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) { 720 return false; 721 } 722 723 @Override 724 public boolean performPanelIdentifierAction(int featureId, int id, int flags) { 725 return false; 726 } 727 728 @Override 729 public void closeAllPanels() { 730 } 731 732 @Override 733 public boolean performContextMenuIdentifierAction(int id, int flags) { 734 return false; 735 } 736 737 @Override 738 public void onConfigurationChanged(Configuration newConfig) { 739 } 740 741 @Override 742 public void setBackgroundDrawable(Drawable drawable) { 743 } 744 745 @Override 746 public void setFeatureDrawableResource(int featureId, @DrawableRes int resId) { 747 } 748 749 @Override 750 public void setFeatureDrawableUri(int featureId, Uri uri) { 751 } 752 753 @Override 754 public void setFeatureDrawable(int featureId, Drawable drawable) { 755 } 756 757 @Override 758 public void setFeatureDrawableAlpha(int featureId, int alpha) { 759 } 760 761 @Override 762 public void setFeatureInt(int featureId, int value) { 763 } 764 765 @Override 766 public void takeKeyEvents(boolean get) { 767 } 768 769 @Override 770 public boolean superDispatchKeyEvent(KeyEvent event) { 771 return false; 772 } 773 774 @Override 775 public boolean superDispatchKeyShortcutEvent(KeyEvent event) { 776 return false; 777 } 778 779 @Override 780 public boolean superDispatchTouchEvent(MotionEvent event) { 781 return false; 782 } 783 784 @Override 785 public boolean superDispatchTrackballEvent(MotionEvent event) { 786 return false; 787 } 788 789 @Override 790 public boolean superDispatchGenericMotionEvent(MotionEvent event) { 791 return false; 792 } 793 794 @Override 795 public View getDecorView() { 796 return StatusBarWindowView.this; 797 } 798 799 @Override 800 public View peekDecorView() { 801 return null; 802 } 803 804 @Override 805 public Bundle saveHierarchyState() { 806 return null; 807 } 808 809 @Override 810 public void restoreHierarchyState(Bundle savedInstanceState) { 811 } 812 813 @Override 814 protected void onActive() { 815 } 816 817 @Override 818 public void setChildDrawable(int featureId, Drawable drawable) { 819 } 820 821 @Override 822 public void setChildInt(int featureId, int value) { 823 } 824 825 @Override 826 public boolean isShortcutKey(int keyCode, KeyEvent event) { 827 return false; 828 } 829 830 @Override 831 public void setVolumeControlStream(int streamType) { 832 } 833 834 @Override 835 public int getVolumeControlStream() { 836 return 0; 837 } 838 839 @Override 840 public int getStatusBarColor() { 841 return 0; 842 } 843 844 @Override 845 public void setStatusBarColor(@ColorInt int color) { 846 } 847 848 @Override 849 public int getNavigationBarColor() { 850 return 0; 851 } 852 853 @Override 854 public void setNavigationBarColor(@ColorInt int color) { 855 } 856 857 @Override 858 public void setDecorCaptionShade(int decorCaptionShade) { 859 } 860 861 @Override 862 public void setResizingCaptionDrawable(Drawable drawable) { 863 } 864 865 @Override 866 public void onMultiWindowModeChanged() { 867 } 868 869 @Override 870 public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) { 871 } 872 873 @Override 874 public void reportActivityRelaunched() { 875 } 876 877 @Override 878 public WindowInsetsController getInsetsController() { 879 return null; 880 } 881 }; 882 883 } 884 885