1 /* 2 * Copyright (C) 2022 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.clipboardoverlay; 18 19 import static android.content.res.Configuration.ORIENTATION_PORTRAIT; 20 21 import android.animation.Animator; 22 import android.animation.AnimatorListenerAdapter; 23 import android.animation.AnimatorSet; 24 import android.animation.ObjectAnimator; 25 import android.animation.TimeInterpolator; 26 import android.animation.ValueAnimator; 27 import android.annotation.Nullable; 28 import android.app.RemoteAction; 29 import android.content.Context; 30 import android.content.res.Resources; 31 import android.graphics.Bitmap; 32 import android.graphics.Insets; 33 import android.graphics.Paint; 34 import android.graphics.Rect; 35 import android.graphics.Region; 36 import android.graphics.drawable.Icon; 37 import android.util.AttributeSet; 38 import android.util.DisplayMetrics; 39 import android.util.MathUtils; 40 import android.util.TypedValue; 41 import android.view.DisplayCutout; 42 import android.view.Gravity; 43 import android.view.LayoutInflater; 44 import android.view.View; 45 import android.view.WindowInsets; 46 import android.view.accessibility.AccessibilityManager; 47 import android.view.animation.LinearInterpolator; 48 import android.view.animation.PathInterpolator; 49 import android.widget.FrameLayout; 50 import android.widget.ImageView; 51 import android.widget.LinearLayout; 52 import android.widget.TextView; 53 54 import androidx.core.view.ViewCompat; 55 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; 56 57 import com.android.systemui.R; 58 import com.android.systemui.screenshot.DraggableConstraintLayout; 59 import com.android.systemui.screenshot.FloatingWindowUtil; 60 import com.android.systemui.screenshot.OverlayActionChip; 61 62 import java.util.ArrayList; 63 64 /** 65 * Handles the visual elements and animations for the clipboard overlay. 66 */ 67 public class ClipboardOverlayView extends DraggableConstraintLayout { 68 69 interface ClipboardOverlayCallbacks extends SwipeDismissCallbacks { onDismissButtonTapped()70 void onDismissButtonTapped(); 71 onRemoteCopyButtonTapped()72 void onRemoteCopyButtonTapped(); 73 onShareButtonTapped()74 void onShareButtonTapped(); 75 onPreviewTapped()76 void onPreviewTapped(); 77 onMinimizedViewTapped()78 void onMinimizedViewTapped(); 79 } 80 81 private static final String TAG = "ClipboardView"; 82 83 private static final int SWIPE_PADDING_DP = 12; // extra padding around views to allow swipe 84 private static final int FONT_SEARCH_STEP_PX = 4; 85 86 private final DisplayMetrics mDisplayMetrics; 87 private final AccessibilityManager mAccessibilityManager; 88 private final ArrayList<OverlayActionChip> mActionChips = new ArrayList<>(); 89 90 private View mClipboardPreview; 91 private ImageView mImagePreview; 92 private TextView mTextPreview; 93 private TextView mHiddenPreview; 94 private LinearLayout mMinimizedPreview; 95 private View mPreviewBorder; 96 private OverlayActionChip mShareChip; 97 private OverlayActionChip mRemoteCopyChip; 98 private View mActionContainerBackground; 99 private View mDismissButton; 100 private LinearLayout mActionContainer; 101 ClipboardOverlayView(Context context)102 public ClipboardOverlayView(Context context) { 103 this(context, null); 104 } 105 ClipboardOverlayView(Context context, AttributeSet attrs)106 public ClipboardOverlayView(Context context, AttributeSet attrs) { 107 this(context, attrs, 0); 108 } 109 ClipboardOverlayView(Context context, AttributeSet attrs, int defStyleAttr)110 public ClipboardOverlayView(Context context, AttributeSet attrs, int defStyleAttr) { 111 super(context, attrs, defStyleAttr); 112 mDisplayMetrics = new DisplayMetrics(); 113 mContext.getDisplay().getRealMetrics(mDisplayMetrics); 114 mAccessibilityManager = AccessibilityManager.getInstance(mContext); 115 } 116 117 @Override onFinishInflate()118 protected void onFinishInflate() { 119 mActionContainerBackground = requireViewById(R.id.actions_container_background); 120 mActionContainer = requireViewById(R.id.actions); 121 mClipboardPreview = requireViewById(R.id.clipboard_preview); 122 mPreviewBorder = requireViewById(R.id.preview_border); 123 mImagePreview = requireViewById(R.id.image_preview); 124 mTextPreview = requireViewById(R.id.text_preview); 125 mHiddenPreview = requireViewById(R.id.hidden_preview); 126 mMinimizedPreview = requireViewById(R.id.minimized_preview); 127 mShareChip = requireViewById(R.id.share_chip); 128 mRemoteCopyChip = requireViewById(R.id.remote_copy_chip); 129 mDismissButton = requireViewById(R.id.dismiss_button); 130 131 mShareChip.setAlpha(1); 132 mRemoteCopyChip.setAlpha(1); 133 mShareChip.setContentDescription(mContext.getString(com.android.internal.R.string.share)); 134 135 mRemoteCopyChip.setIcon( 136 Icon.createWithResource(mContext, R.drawable.ic_baseline_devices_24), true); 137 mShareChip.setIcon( 138 Icon.createWithResource(mContext, R.drawable.ic_screenshot_share), true); 139 140 mRemoteCopyChip.setContentDescription( 141 mContext.getString(R.string.clipboard_send_nearby_description)); 142 143 mTextPreview.getViewTreeObserver().addOnPreDrawListener(() -> { 144 int availableHeight = mTextPreview.getHeight() 145 - (mTextPreview.getPaddingTop() + mTextPreview.getPaddingBottom()); 146 mTextPreview.setMaxLines(availableHeight / mTextPreview.getLineHeight()); 147 return true; 148 }); 149 super.onFinishInflate(); 150 } 151 152 @Override setCallbacks(SwipeDismissCallbacks callbacks)153 public void setCallbacks(SwipeDismissCallbacks callbacks) { 154 super.setCallbacks(callbacks); 155 ClipboardOverlayCallbacks clipboardCallbacks = (ClipboardOverlayCallbacks) callbacks; 156 mShareChip.setOnClickListener(v -> clipboardCallbacks.onShareButtonTapped()); 157 mDismissButton.setOnClickListener(v -> clipboardCallbacks.onDismissButtonTapped()); 158 mRemoteCopyChip.setOnClickListener(v -> clipboardCallbacks.onRemoteCopyButtonTapped()); 159 mClipboardPreview.setOnClickListener(v -> clipboardCallbacks.onPreviewTapped()); 160 mMinimizedPreview.setOnClickListener(v -> clipboardCallbacks.onMinimizedViewTapped()); 161 } 162 setEditAccessibilityAction(boolean editable)163 void setEditAccessibilityAction(boolean editable) { 164 if (editable) { 165 ViewCompat.replaceAccessibilityAction(mClipboardPreview, 166 AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLICK, 167 mContext.getString(R.string.clipboard_edit), null); 168 } else { 169 ViewCompat.replaceAccessibilityAction(mClipboardPreview, 170 AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLICK, 171 null, null); 172 } 173 } 174 setMinimized(boolean minimized)175 void setMinimized(boolean minimized) { 176 if (minimized) { 177 mMinimizedPreview.setVisibility(View.VISIBLE); 178 mClipboardPreview.setVisibility(View.GONE); 179 mPreviewBorder.setVisibility(View.GONE); 180 mActionContainer.setVisibility(View.GONE); 181 mActionContainerBackground.setVisibility(View.GONE); 182 } else { 183 mMinimizedPreview.setVisibility(View.GONE); 184 mClipboardPreview.setVisibility(View.VISIBLE); 185 mPreviewBorder.setVisibility(View.VISIBLE); 186 mActionContainer.setVisibility(View.VISIBLE); 187 } 188 } 189 setInsets(WindowInsets insets, int orientation)190 void setInsets(WindowInsets insets, int orientation) { 191 FrameLayout.LayoutParams p = (FrameLayout.LayoutParams) getLayoutParams(); 192 if (p == null) { 193 return; 194 } 195 Rect margins = computeMargins(insets, orientation); 196 197 p.setMargins(margins.left, margins.top, margins.right, margins.bottom); 198 setLayoutParams(p); 199 requestLayout(); 200 } 201 isInTouchRegion(int x, int y)202 boolean isInTouchRegion(int x, int y) { 203 Region touchRegion = new Region(); 204 final Rect tmpRect = new Rect(); 205 206 mPreviewBorder.getBoundsOnScreen(tmpRect); 207 tmpRect.inset( 208 (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP), 209 (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP)); 210 touchRegion.op(tmpRect, Region.Op.UNION); 211 212 mActionContainerBackground.getBoundsOnScreen(tmpRect); 213 tmpRect.inset( 214 (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP), 215 (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP)); 216 touchRegion.op(tmpRect, Region.Op.UNION); 217 218 mMinimizedPreview.getBoundsOnScreen(tmpRect); 219 tmpRect.inset( 220 (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP), 221 (int) FloatingWindowUtil.dpToPx(mDisplayMetrics, -SWIPE_PADDING_DP)); 222 touchRegion.op(tmpRect, Region.Op.UNION); 223 224 mDismissButton.getBoundsOnScreen(tmpRect); 225 touchRegion.op(tmpRect, Region.Op.UNION); 226 227 return touchRegion.contains(x, y); 228 } 229 setRemoteCopyVisibility(boolean visible)230 void setRemoteCopyVisibility(boolean visible) { 231 if (visible) { 232 mRemoteCopyChip.setVisibility(View.VISIBLE); 233 mActionContainerBackground.setVisibility(View.VISIBLE); 234 } else { 235 mRemoteCopyChip.setVisibility(View.GONE); 236 } 237 } 238 showDefaultTextPreview()239 void showDefaultTextPreview() { 240 String copied = mContext.getString(R.string.clipboard_overlay_text_copied); 241 showTextPreview(copied, false); 242 } 243 showTextPreview(CharSequence text, boolean hidden)244 void showTextPreview(CharSequence text, boolean hidden) { 245 TextView textView = hidden ? mHiddenPreview : mTextPreview; 246 showSinglePreview(textView); 247 textView.setText(text.subSequence(0, Math.min(500, text.length()))); 248 updateTextSize(text, textView); 249 textView.addOnLayoutChangeListener( 250 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { 251 if (right - left != oldRight - oldLeft) { 252 updateTextSize(text, textView); 253 } 254 }); 255 } 256 getPreview()257 View getPreview() { 258 return mClipboardPreview; 259 } 260 showImagePreview(@ullable Bitmap thumbnail)261 void showImagePreview(@Nullable Bitmap thumbnail) { 262 if (thumbnail == null) { 263 mHiddenPreview.setText(mContext.getString(R.string.clipboard_text_hidden)); 264 showSinglePreview(mHiddenPreview); 265 } else { 266 mImagePreview.setImageBitmap(thumbnail); 267 showSinglePreview(mImagePreview); 268 } 269 } 270 showShareChip()271 void showShareChip() { 272 mShareChip.setVisibility(View.VISIBLE); 273 mActionContainerBackground.setVisibility(View.VISIBLE); 274 } 275 reset()276 void reset() { 277 setTranslationX(0); 278 setAlpha(0); 279 mActionContainerBackground.setVisibility(View.GONE); 280 mDismissButton.setVisibility(View.GONE); 281 mShareChip.setVisibility(View.GONE); 282 mRemoteCopyChip.setVisibility(View.GONE); 283 setEditAccessibilityAction(false); 284 resetActionChips(); 285 } 286 resetActionChips()287 void resetActionChips() { 288 for (OverlayActionChip chip : mActionChips) { 289 mActionContainer.removeView(chip); 290 } 291 mActionChips.clear(); 292 } 293 getMinimizedFadeoutAnimation()294 Animator getMinimizedFadeoutAnimation() { 295 ObjectAnimator anim = ObjectAnimator.ofFloat(mMinimizedPreview, "alpha", 1, 0); 296 anim.setDuration(66); 297 anim.addListener(new AnimatorListenerAdapter() { 298 @Override 299 public void onAnimationEnd(Animator animation) { 300 super.onAnimationEnd(animation); 301 mMinimizedPreview.setVisibility(View.GONE); 302 mMinimizedPreview.setAlpha(1); 303 } 304 }); 305 return anim; 306 } 307 getEnterAnimation()308 Animator getEnterAnimation() { 309 if (mAccessibilityManager.isEnabled()) { 310 mDismissButton.setVisibility(View.VISIBLE); 311 } 312 TimeInterpolator linearInterpolator = new LinearInterpolator(); 313 TimeInterpolator scaleInterpolator = new PathInterpolator(0, 0, 0, 1f); 314 AnimatorSet enterAnim = new AnimatorSet(); 315 316 ValueAnimator rootAnim = ValueAnimator.ofFloat(0, 1); 317 rootAnim.setInterpolator(linearInterpolator); 318 rootAnim.setDuration(66); 319 rootAnim.addUpdateListener(animation -> { 320 setAlpha(animation.getAnimatedFraction()); 321 }); 322 323 ValueAnimator scaleAnim = ValueAnimator.ofFloat(0, 1); 324 scaleAnim.setInterpolator(scaleInterpolator); 325 scaleAnim.setDuration(333); 326 scaleAnim.addUpdateListener(animation -> { 327 float previewScale = MathUtils.lerp(.9f, 1f, animation.getAnimatedFraction()); 328 mMinimizedPreview.setScaleX(previewScale); 329 mMinimizedPreview.setScaleY(previewScale); 330 mClipboardPreview.setScaleX(previewScale); 331 mClipboardPreview.setScaleY(previewScale); 332 mPreviewBorder.setScaleX(previewScale); 333 mPreviewBorder.setScaleY(previewScale); 334 335 float pivotX = mClipboardPreview.getWidth() / 2f + mClipboardPreview.getX(); 336 mActionContainerBackground.setPivotX(pivotX - mActionContainerBackground.getX()); 337 mActionContainer.setPivotX(pivotX - ((View) mActionContainer.getParent()).getX()); 338 float actionsScaleX = MathUtils.lerp(.7f, 1f, animation.getAnimatedFraction()); 339 float actionsScaleY = MathUtils.lerp(.9f, 1f, animation.getAnimatedFraction()); 340 mActionContainer.setScaleX(actionsScaleX); 341 mActionContainer.setScaleY(actionsScaleY); 342 mActionContainerBackground.setScaleX(actionsScaleX); 343 mActionContainerBackground.setScaleY(actionsScaleY); 344 }); 345 346 ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1); 347 alphaAnim.setInterpolator(linearInterpolator); 348 alphaAnim.setDuration(283); 349 alphaAnim.addUpdateListener(animation -> { 350 float alpha = animation.getAnimatedFraction(); 351 mMinimizedPreview.setAlpha(alpha); 352 mClipboardPreview.setAlpha(alpha); 353 mPreviewBorder.setAlpha(alpha); 354 mDismissButton.setAlpha(alpha); 355 mActionContainer.setAlpha(alpha); 356 }); 357 358 mMinimizedPreview.setAlpha(0); 359 mActionContainer.setAlpha(0); 360 mPreviewBorder.setAlpha(0); 361 mClipboardPreview.setAlpha(0); 362 enterAnim.play(rootAnim).with(scaleAnim); 363 enterAnim.play(alphaAnim).after(50).after(rootAnim); 364 365 enterAnim.addListener(new AnimatorListenerAdapter() { 366 @Override 367 public void onAnimationEnd(Animator animation) { 368 super.onAnimationEnd(animation); 369 setAlpha(1); 370 } 371 }); 372 return enterAnim; 373 } 374 getFadeOutAnimation()375 Animator getFadeOutAnimation() { 376 ValueAnimator alphaAnim = ValueAnimator.ofFloat(1, 0); 377 alphaAnim.addUpdateListener(animation -> { 378 float alpha = (float) animation.getAnimatedValue(); 379 mActionContainer.setAlpha(alpha); 380 mActionContainerBackground.setAlpha(alpha); 381 mPreviewBorder.setAlpha(alpha); 382 mDismissButton.setAlpha(alpha); 383 }); 384 alphaAnim.setDuration(300); 385 return alphaAnim; 386 } 387 getExitAnimation()388 Animator getExitAnimation() { 389 TimeInterpolator linearInterpolator = new LinearInterpolator(); 390 TimeInterpolator scaleInterpolator = new PathInterpolator(.3f, 0, 1f, 1f); 391 AnimatorSet exitAnim = new AnimatorSet(); 392 393 ValueAnimator rootAnim = ValueAnimator.ofFloat(0, 1); 394 rootAnim.setInterpolator(linearInterpolator); 395 rootAnim.setDuration(100); 396 rootAnim.addUpdateListener(anim -> setAlpha(1 - anim.getAnimatedFraction())); 397 398 ValueAnimator scaleAnim = ValueAnimator.ofFloat(0, 1); 399 scaleAnim.setInterpolator(scaleInterpolator); 400 scaleAnim.setDuration(250); 401 scaleAnim.addUpdateListener(animation -> { 402 float previewScale = MathUtils.lerp(1f, .9f, animation.getAnimatedFraction()); 403 mMinimizedPreview.setScaleX(previewScale); 404 mMinimizedPreview.setScaleY(previewScale); 405 mClipboardPreview.setScaleX(previewScale); 406 mClipboardPreview.setScaleY(previewScale); 407 mPreviewBorder.setScaleX(previewScale); 408 mPreviewBorder.setScaleY(previewScale); 409 410 float pivotX = mClipboardPreview.getWidth() / 2f + mClipboardPreview.getX(); 411 mActionContainerBackground.setPivotX(pivotX - mActionContainerBackground.getX()); 412 mActionContainer.setPivotX(pivotX - ((View) mActionContainer.getParent()).getX()); 413 float actionScaleX = MathUtils.lerp(1f, .8f, animation.getAnimatedFraction()); 414 float actionScaleY = MathUtils.lerp(1f, .9f, animation.getAnimatedFraction()); 415 mActionContainer.setScaleX(actionScaleX); 416 mActionContainer.setScaleY(actionScaleY); 417 mActionContainerBackground.setScaleX(actionScaleX); 418 mActionContainerBackground.setScaleY(actionScaleY); 419 }); 420 421 ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1); 422 alphaAnim.setInterpolator(linearInterpolator); 423 alphaAnim.setDuration(166); 424 alphaAnim.addUpdateListener(animation -> { 425 float alpha = 1 - animation.getAnimatedFraction(); 426 mMinimizedPreview.setAlpha(alpha); 427 mClipboardPreview.setAlpha(alpha); 428 mPreviewBorder.setAlpha(alpha); 429 mDismissButton.setAlpha(alpha); 430 mActionContainer.setAlpha(alpha); 431 }); 432 433 exitAnim.play(alphaAnim).with(scaleAnim); 434 exitAnim.play(rootAnim).after(150).after(alphaAnim); 435 return exitAnim; 436 } 437 setActionChip(RemoteAction action, Runnable onFinish)438 void setActionChip(RemoteAction action, Runnable onFinish) { 439 mActionContainerBackground.setVisibility(View.VISIBLE); 440 OverlayActionChip chip = constructActionChip(action, onFinish); 441 mActionContainer.addView(chip); 442 mActionChips.add(chip); 443 } 444 showSinglePreview(View v)445 private void showSinglePreview(View v) { 446 mTextPreview.setVisibility(View.GONE); 447 mImagePreview.setVisibility(View.GONE); 448 mHiddenPreview.setVisibility(View.GONE); 449 mMinimizedPreview.setVisibility(View.GONE); 450 v.setVisibility(View.VISIBLE); 451 } 452 constructActionChip(RemoteAction action, Runnable onFinish)453 private OverlayActionChip constructActionChip(RemoteAction action, Runnable onFinish) { 454 OverlayActionChip chip = (OverlayActionChip) LayoutInflater.from(mContext).inflate( 455 R.layout.overlay_action_chip, mActionContainer, false); 456 chip.setText(action.getTitle()); 457 chip.setContentDescription(action.getTitle()); 458 chip.setIcon(action.getIcon(), false); 459 chip.setPendingIntent(action.getActionIntent(), onFinish); 460 chip.setAlpha(1); 461 return chip; 462 } 463 updateTextSize(CharSequence text, TextView textView)464 private static void updateTextSize(CharSequence text, TextView textView) { 465 Paint paint = new Paint(textView.getPaint()); 466 Resources res = textView.getResources(); 467 float minFontSize = res.getDimensionPixelSize(R.dimen.clipboard_overlay_min_font); 468 float maxFontSize = res.getDimensionPixelSize(R.dimen.clipboard_overlay_max_font); 469 if (isOneWord(text) && fitsInView(text, textView, paint, minFontSize)) { 470 // If the text is a single word and would fit within the TextView at the min font size, 471 // find the biggest font size that will fit. 472 float fontSizePx = minFontSize; 473 while (fontSizePx + FONT_SEARCH_STEP_PX < maxFontSize 474 && fitsInView(text, textView, paint, fontSizePx + FONT_SEARCH_STEP_PX)) { 475 fontSizePx += FONT_SEARCH_STEP_PX; 476 } 477 // Need to turn off autosizing, otherwise setTextSize is a no-op. 478 textView.setAutoSizeTextTypeWithDefaults(TextView.AUTO_SIZE_TEXT_TYPE_NONE); 479 // It's possible to hit the max font size and not fill the width, so centering 480 // horizontally looks better in this case. 481 textView.setGravity(Gravity.CENTER); 482 textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, (int) fontSizePx); 483 } else { 484 // Otherwise just stick with autosize. 485 textView.setAutoSizeTextTypeUniformWithConfiguration((int) minFontSize, 486 (int) maxFontSize, FONT_SEARCH_STEP_PX, TypedValue.COMPLEX_UNIT_PX); 487 textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.START); 488 } 489 } 490 fitsInView(CharSequence text, TextView textView, Paint paint, float fontSizePx)491 private static boolean fitsInView(CharSequence text, TextView textView, Paint paint, 492 float fontSizePx) { 493 paint.setTextSize(fontSizePx); 494 float size = paint.measureText(text.toString()); 495 float availableWidth = textView.getWidth() - textView.getPaddingLeft() 496 - textView.getPaddingRight(); 497 return size < availableWidth; 498 } 499 isOneWord(CharSequence text)500 private static boolean isOneWord(CharSequence text) { 501 return text.toString().split("\\s+", 2).length == 1; 502 } 503 computeMargins(WindowInsets insets, int orientation)504 private static Rect computeMargins(WindowInsets insets, int orientation) { 505 DisplayCutout cutout = insets.getDisplayCutout(); 506 Insets navBarInsets = insets.getInsets(WindowInsets.Type.navigationBars()); 507 Insets imeInsets = insets.getInsets(WindowInsets.Type.ime()); 508 if (cutout == null) { 509 return new Rect(0, 0, 0, Math.max(imeInsets.bottom, navBarInsets.bottom)); 510 } else { 511 Insets waterfall = cutout.getWaterfallInsets(); 512 if (orientation == ORIENTATION_PORTRAIT) { 513 return new Rect( 514 waterfall.left, 515 Math.max(cutout.getSafeInsetTop(), waterfall.top), 516 waterfall.right, 517 Math.max(imeInsets.bottom, 518 Math.max(cutout.getSafeInsetBottom(), 519 Math.max(navBarInsets.bottom, waterfall.bottom)))); 520 } else { 521 return new Rect( 522 waterfall.left, 523 waterfall.top, 524 waterfall.right, 525 Math.max(imeInsets.bottom, 526 Math.max(navBarInsets.bottom, waterfall.bottom))); 527 } 528 } 529 } 530 } 531