diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index f73dac77ae003..7284429ca4859 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -43358,6 +43358,7 @@ ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/android/Flu ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterSurfaceView.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterTextureView.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterView.java + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterViewDelegate.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/android/KeyChannelResponder.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/android/KeyData.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/embedding/android/KeyEmbedderResponder.java + ../../../flutter/LICENSE @@ -46250,6 +46251,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/Flutt FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterSurfaceView.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterTextureView.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterView.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterViewDelegate.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/KeyChannelResponder.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/KeyData.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/KeyEmbedderResponder.java diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index 433af900f8cbf..65797086ff0fe 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -228,6 +228,7 @@ android_java_sources = [ "io/flutter/embedding/android/FlutterSurfaceView.java", "io/flutter/embedding/android/FlutterTextureView.java", "io/flutter/embedding/android/FlutterView.java", + "io/flutter/embedding/android/FlutterViewDelegate.java", "io/flutter/embedding/android/KeyChannelResponder.java", "io/flutter/embedding/android/KeyData.java", "io/flutter/embedding/android/KeyEmbedderResponder.java", diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterView.java b/shell/platform/android/io/flutter/embedding/android/FlutterView.java index 7272b5125ba05..37d9621c6b945 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterView.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterView.java @@ -772,6 +772,15 @@ navigationBarVisible && guessBottomKeyboardInset(insets) == 0 viewportMetrics.viewInsetLeft = 0; } + // The caption bar inset is a new addition, and the APIs called to query it utilize a list of + // bounding Rects instead of an Insets object, which is a newer API method, as compared to the + // existing Insets-based method calls above. + if (Build.VERSION.SDK_INT >= API_LEVELS.API_35) { + delegate.growViewportMetricsToCaptionBar(getContext(), viewportMetrics); + } else { + Log.w(TAG, "API level " + Build.VERSION.SDK_INT + " is too low to query bounding rects."); + } + Log.v( TAG, "Updating window insets (onApplyWindowInsets()):\n" @@ -1449,6 +1458,13 @@ public void removeFlutterEngineAttachmentListener( .send(); } + private FlutterViewDelegate delegate = new FlutterViewDelegate(); + + @VisibleForTesting + public void setDelegate(@NonNull FlutterViewDelegate delegate) { + this.delegate = delegate; + } + private void sendViewportMetricsToFlutter() { if (!isAttachedToFlutterEngine()) { Log.w( @@ -1460,6 +1476,7 @@ private void sendViewportMetricsToFlutter() { viewportMetrics.devicePixelRatio = getResources().getDisplayMetrics().density; viewportMetrics.physicalTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); + flutterEngine.getRenderer().setViewportMetrics(viewportMetrics); } @@ -1485,6 +1502,15 @@ public void setVisibility(int visibility) { } } + /** + * Allow access to the viewport metrics so that tests can set them to be valid with nonzero + * dimensions. + */ + @VisibleForTesting + public FlutterRenderer.ViewportMetrics getViewportMetrics() { + return viewportMetrics; + } + /** * Listener that is notified when a {@link io.flutter.embedding.engine.FlutterEngine} is attached * to/detached from a given {@code FlutterView}. diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterViewDelegate.java b/shell/platform/android/io/flutter/embedding/android/FlutterViewDelegate.java new file mode 100644 index 0000000000000..ea773ffaf0ca7 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/android/FlutterViewDelegate.java @@ -0,0 +1,71 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.embedding.android; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Rect; +import android.view.Window; +import android.view.WindowInsets; +import androidx.annotation.RequiresApi; +import androidx.annotation.VisibleForTesting; +import io.flutter.Build; +import io.flutter.embedding.engine.renderer.FlutterRenderer; +import io.flutter.util.ViewUtils; +import java.util.Collections; +import java.util.List; + +/** + * A delegate class that performs the task of retrieving the bounding rect values. Logic that is + * independent of the engine, or that tests must access in the absence of an engine, shall reside + * within this class. + */ +public class FlutterViewDelegate { + /** + * Return the WindowInsets object for the provided Context. A Context will only have a window if + * it is an instance of Activity. If context does not have a window, or it is not an activity, + * this method will return null. Otherwise, this method will return the WindowInsets for the + * provided activity's window. + */ + @RequiresApi(api = Build.API_LEVELS.API_23) + @VisibleForTesting + public WindowInsets getWindowInsets(Context context) { + Activity activity = ViewUtils.getActivity(context); + if (activity == null) { + return null; + } + Window window = activity.getWindow(); + if (window == null) { + return null; + } + return window.getDecorView().getRootWindowInsets(); + } + + @RequiresApi(api = Build.API_LEVELS.API_35) + public List getCaptionBarInsets(Context context) { + WindowInsets insets = getWindowInsets(context); + if (insets == null) { + return Collections.emptyList(); + } + return insets.getBoundingRects(WindowInsets.Type.captionBar()); + } + + @RequiresApi(api = Build.API_LEVELS.API_35) + public void growViewportMetricsToCaptionBar( + Context context, FlutterRenderer.ViewportMetrics viewportMetrics) { + List boundingRects = getCaptionBarInsets(context); + int viewPaddingTop = viewportMetrics.viewPaddingTop; + for (Rect rect : boundingRects) { + viewPaddingTop = Math.max(viewPaddingTop, rect.bottom); + } + // The value getCaptionBarInset returns is only the bounding rects of the caption bar. + // When assigning the new value of viewPaddingTop, the maximum is taken with its old value + // to ensure that any previous top padding that is greater than that from the caption bar + // is not destroyed by this operation. + // Any potential update that will allow the caption bar to be positioned somewhere other than + // the top of the app window will require that this method be rewritten. + viewportMetrics.viewPaddingTop = viewPaddingTop; + } +} \ No newline at end of file diff --git a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java index 445e331845c13..c4e3df02c1cd7 100644 --- a/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java +++ b/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java @@ -1271,6 +1271,9 @@ public static final class ViewportMetrics { public float devicePixelRatio = 1.0f; public int width = 0; public int height = 0; + // The fields prefixed with viewPadding and viewInset are used to calculate the padding, + // viewPadding, and viewInsets of ViewConfiguration in Dart. This calculation is performed at + // https://github.com/flutter/engine/blob/main/lib/ui/hooks.dart#L139-L155. public int viewPaddingTop = 0; public int viewPaddingRight = 0; public int viewPaddingBottom = 0;