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