From e7e46c9718b5b790ec21c1bb0fda2c8cab5752fa Mon Sep 17 00:00:00 2001 From: Gray Mackall Date: Thu, 22 Feb 2024 15:05:21 -0800 Subject: [PATCH 1/8] Revert "Reverts "Remove WindowManager reflection in SingleViewPresentation.java (#49996)" (#50873)" This reverts commit e922da61d808dd1784df84c6aa310a352177ccfd. --- .../platform/SingleViewPresentation.java | 124 ++++++++++-------- .../platform/SingleViewPresentationTest.java | 116 ++++++++++++++++ 2 files changed, 184 insertions(+), 56 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java b/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java index b7ca559ac7c64..d8dc53049a5be 100644 --- a/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java +++ b/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java @@ -22,17 +22,18 @@ import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; +import android.view.WindowMetrics; import android.view.accessibility.AccessibilityEvent; import android.view.inputmethod.InputMethodManager; import android.widget.FrameLayout; import androidx.annotation.Keep; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; +import androidx.annotation.VisibleForTesting; import io.flutter.Log; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; +import java.util.concurrent.Executor; +import java.util.function.Consumer; /* * A presentation used for hosting a single Android view in a virtual display. @@ -359,7 +360,7 @@ public Object getSystemService(String name) { private WindowManager getWindowManager() { if (windowManager == null) { - windowManager = windowManagerHandler.getWindowManager(); + windowManager = windowManagerHandler; } return windowManager; } @@ -377,21 +378,18 @@ private boolean isCalledFromAlertDialog() { } /* - * A dynamic proxy handler for a WindowManager with custom overrides. + * A static proxy handler for a WindowManager with custom overrides. * * The presentation's window manager delegates all calls to the default window manager. * WindowManager#addView calls triggered by views that are attached to the virtual display are crashing * (see: https://github.com/flutter/flutter/issues/20714). This was triggered when selecting text in an embedded * WebView (as the selection handles are implemented as popup windows). * - * This dynamic proxy overrides the addView, removeView, removeViewImmediate, and updateViewLayout methods - * to prevent these crashes. - * - * This will be more efficient as a static proxy that's not using reflection, but as the engine is currently - * not being built against the latest Android SDK we cannot override all relevant method. - * Tracking issue for upgrading the engine's Android sdk: https://github.com/flutter/flutter/issues/20717 + * This static proxy overrides the addView, removeView, removeViewImmediate, and updateViewLayout methods + * to prevent these crashes, and forwards all other calls to the delegate. */ - static class WindowManagerHandler implements InvocationHandler { + @VisibleForTesting + static class WindowManagerHandler implements WindowManager { private static final String TAG = "PlatformViewsController"; private final WindowManager delegate; @@ -402,72 +400,86 @@ static class WindowManagerHandler implements InvocationHandler { fakeWindowRootView = fakeWindowViewGroup; } - public WindowManager getWindowManager() { - return (WindowManager) - Proxy.newProxyInstance( - WindowManager.class.getClassLoader(), new Class[] {WindowManager.class}, this); + @Override + @Deprecated + public Display getDefaultDisplay() { + return delegate.getDefaultDisplay(); } @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - switch (method.getName()) { - case "addView": - addView(args); - return null; - case "removeView": - removeView(args); - return null; - case "removeViewImmediate": - removeViewImmediate(args); - return null; - case "updateViewLayout": - updateViewLayout(args); - return null; - } - try { - return method.invoke(delegate, args); - } catch (InvocationTargetException e) { - throw e.getCause(); + public void removeViewImmediate(View view) { + if (fakeWindowRootView == null) { + Log.w(TAG, "Embedded view called removeViewImmediate while detached from presentation"); + return; } + view.clearAnimation(); + fakeWindowRootView.removeView(view); } - private void addView(Object[] args) { + @Override + public void addView(View view, ViewGroup.LayoutParams params) { if (fakeWindowRootView == null) { Log.w(TAG, "Embedded view called addView while detached from presentation"); return; } - View view = (View) args[0]; - WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) args[1]; - fakeWindowRootView.addView(view, layoutParams); + fakeWindowRootView.addView(view, params); } - private void removeView(Object[] args) { + @Override + public void updateViewLayout(View view, ViewGroup.LayoutParams params) { if (fakeWindowRootView == null) { - Log.w(TAG, "Embedded view called removeView while detached from presentation"); + Log.w(TAG, "Embedded view called updateViewLayout while detached from presentation"); return; } - View view = (View) args[0]; - fakeWindowRootView.removeView(view); + fakeWindowRootView.updateViewLayout(view, params); } - private void removeViewImmediate(Object[] args) { + @Override + public void removeView(View view) { if (fakeWindowRootView == null) { - Log.w(TAG, "Embedded view called removeViewImmediate while detached from presentation"); + Log.w(TAG, "Embedded view called removeView while detached from presentation"); return; } - View view = (View) args[0]; - view.clearAnimation(); fakeWindowRootView.removeView(view); } - private void updateViewLayout(Object[] args) { - if (fakeWindowRootView == null) { - Log.w(TAG, "Embedded view called updateViewLayout while detached from presentation"); - return; - } - View view = (View) args[0]; - WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) args[1]; - fakeWindowRootView.updateViewLayout(view, layoutParams); + @RequiresApi(api = Build.VERSION_CODES.R) + @NonNull + @Override + public WindowMetrics getCurrentWindowMetrics() { + return delegate.getCurrentWindowMetrics(); + } + + @RequiresApi(api = Build.VERSION_CODES.R) + @NonNull + @Override + public WindowMetrics getMaximumWindowMetrics() { + return delegate.getMaximumWindowMetrics(); + } + + @RequiresApi(api = Build.VERSION_CODES.S) + @Override + public boolean isCrossWindowBlurEnabled() { + return delegate.isCrossWindowBlurEnabled(); + } + + @RequiresApi(api = Build.VERSION_CODES.S) + @Override + public void addCrossWindowBlurEnabledListener(@NonNull Consumer listener) { + delegate.addCrossWindowBlurEnabledListener(listener); + } + + @RequiresApi(api = Build.VERSION_CODES.S) + @Override + public void addCrossWindowBlurEnabledListener( + @NonNull Executor executor, @NonNull Consumer listener) { + delegate.addCrossWindowBlurEnabledListener(executor, listener); + } + + @RequiresApi(api = Build.VERSION_CODES.S) + @Override + public void removeCrossWindowBlurEnabledListener(@NonNull Consumer listener) { + delegate.removeCrossWindowBlurEnabledListener(listener); } } diff --git a/shell/platform/android/test/io/flutter/plugin/platform/SingleViewPresentationTest.java b/shell/platform/android/test/io/flutter/plugin/platform/SingleViewPresentationTest.java index d27e08bbbdc97..d99d344568d25 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/SingleViewPresentationTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/SingleViewPresentationTest.java @@ -7,18 +7,26 @@ import static android.os.Build.VERSION_CODES.KITKAT; import static android.os.Build.VERSION_CODES.P; import static android.os.Build.VERSION_CODES.R; +import static android.os.Build.VERSION_CODES.S; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import android.annotation.TargetApi; import android.content.Context; import android.hardware.display.DisplayManager; import android.view.Display; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; +import java.util.concurrent.Executor; +import java.util.function.Consumer; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.annotation.Config; @@ -83,4 +91,112 @@ public void returnsOuterContextInputMethodManager_createDisplayContext() { // Android OS (or Robolectric's shadow, in this case). assertEquals(expected, actual); } + + @Test + @Config(minSdk = R) + public void windowManagerHandler_passesCorrectlyToFakeWindowViewGroup() { + // Mock the WindowManager and FakeWindowViewGroup that get used by the WindowManagerHandler. + WindowManager mockWindowManager = mock(WindowManager.class); + SingleViewPresentation.FakeWindowViewGroup mockFakeWindowViewGroup = + mock(SingleViewPresentation.FakeWindowViewGroup.class); + + View mockView = mock(View.class); + ViewGroup.LayoutParams mockLayoutParams = mock(ViewGroup.LayoutParams.class); + + SingleViewPresentation.WindowManagerHandler windowManagerHandler = + new SingleViewPresentation.WindowManagerHandler(mockWindowManager, mockFakeWindowViewGroup); + + // removeViewImmediate + windowManagerHandler.removeViewImmediate(mockView); + verify(mockView).clearAnimation(); + verify(mockFakeWindowViewGroup).removeView(mockView); + verifyNoInteractions(mockWindowManager); + + // addView + windowManagerHandler.addView(mockView, mockLayoutParams); + verify(mockFakeWindowViewGroup).addView(mockView, mockLayoutParams); + verifyNoInteractions(mockWindowManager); + + // updateViewLayout + windowManagerHandler.updateViewLayout(mockView, mockLayoutParams); + verify(mockFakeWindowViewGroup).updateViewLayout(mockView, mockLayoutParams); + verifyNoInteractions(mockWindowManager); + + // removeView + windowManagerHandler.updateViewLayout(mockView, mockLayoutParams); + verify(mockFakeWindowViewGroup).removeView(mockView); + verifyNoInteractions(mockWindowManager); + } + + @Test + @Config(minSdk = R) + public void windowManagerHandler_logAndReturnEarly_whenFakeWindowViewGroupIsNull() { + // Mock the WindowManager and FakeWindowViewGroup that get used by the WindowManagerHandler. + WindowManager mockWindowManager = mock(WindowManager.class); + + View mockView = mock(View.class); + ViewGroup.LayoutParams mockLayoutParams = mock(ViewGroup.LayoutParams.class); + + SingleViewPresentation.WindowManagerHandler windowManagerHandler = + new SingleViewPresentation.WindowManagerHandler(mockWindowManager, null); + + // removeViewImmediate + windowManagerHandler.removeViewImmediate(mockView); + verifyNoInteractions(mockView); + verifyNoInteractions(mockWindowManager); + + // addView + windowManagerHandler.addView(mockView, mockLayoutParams); + verifyNoInteractions(mockWindowManager); + + // updateViewLayout + windowManagerHandler.updateViewLayout(mockView, mockLayoutParams); + verifyNoInteractions(mockWindowManager); + + // removeView + windowManagerHandler.updateViewLayout(mockView, mockLayoutParams); + verifyNoInteractions(mockWindowManager); + } + + // This section tests that WindowManagerHandler forwards all of the non-special case calls to the + // delegate WindowManager. Because this must include some deprecated WindowManager method calls + // (because the proxy overrides every method), we suppress deprecation warnings here. + @Test + @Config(minSdk = S) + @SuppressWarnings("deprecation") + public void windowManagerHandler_forwardsAllOtherCallsToDelegate() { + // Mock the WindowManager and FakeWindowViewGroup that get used by the WindowManagerHandler. + WindowManager mockWindowManager = mock(WindowManager.class); + SingleViewPresentation.FakeWindowViewGroup mockFakeWindowViewGroup = + mock(SingleViewPresentation.FakeWindowViewGroup.class); + + SingleViewPresentation.WindowManagerHandler windowManagerHandler = + new SingleViewPresentation.WindowManagerHandler(mockWindowManager, mockFakeWindowViewGroup); + + // Verify that all other calls get forwarded to the delegate. + Executor mockExecutor = mock(Executor.class); + @SuppressWarnings("Unchecked cast") + Consumer mockListener = (Consumer) mock(Consumer.class); + + windowManagerHandler.getDefaultDisplay(); + verify(mockWindowManager).getDefaultDisplay(); + + windowManagerHandler.getCurrentWindowMetrics(); + verify(mockWindowManager).getCurrentWindowMetrics(); + + windowManagerHandler.getMaximumWindowMetrics(); + verify(mockWindowManager).getMaximumWindowMetrics(); + + windowManagerHandler.isCrossWindowBlurEnabled(); + verify(mockWindowManager).isCrossWindowBlurEnabled(); + + windowManagerHandler.addCrossWindowBlurEnabledListener(mockListener); + verify(mockWindowManager).addCrossWindowBlurEnabledListener(mockListener); + + windowManagerHandler.addCrossWindowBlurEnabledListener(mockExecutor, mockListener); + verify(mockWindowManager).addCrossWindowBlurEnabledListener(mockExecutor, mockListener); + + windowManagerHandler.removeCrossWindowBlurEnabledListener(mockListener); + verify(mockWindowManager).removeCrossWindowBlurEnabledListener(mockListener); + } } From 433dbf08e43880090e4ec16d7d7872cd2025619a Mon Sep 17 00:00:00 2001 From: Gray Mackall Date: Fri, 23 Feb 2024 15:17:20 -0800 Subject: [PATCH 2/8] add suggest fixes for SystemApi interface issue --- .../SingleViewFakeWindowViewGroup.java | 61 +++++++ .../platform/SingleViewPresentation.java | 169 +----------------- .../platform/SingleViewWindowManager.java | 125 +++++++++++++ .../plugin/platform/WindowManagerHandler.java | 11 ++ .../platform/SingleViewPresentationTest.java | 28 +-- 5 files changed, 213 insertions(+), 181 deletions(-) create mode 100644 shell/platform/android/io/flutter/plugin/platform/SingleViewFakeWindowViewGroup.java create mode 100644 shell/platform/android/io/flutter/plugin/platform/SingleViewWindowManager.java create mode 100644 shell/platform/android/io/flutter/plugin/platform/WindowManagerHandler.java diff --git a/shell/platform/android/io/flutter/plugin/platform/SingleViewFakeWindowViewGroup.java b/shell/platform/android/io/flutter/plugin/platform/SingleViewFakeWindowViewGroup.java new file mode 100644 index 0000000000000..4a7d6307fb39a --- /dev/null +++ b/shell/platform/android/io/flutter/plugin/platform/SingleViewFakeWindowViewGroup.java @@ -0,0 +1,61 @@ +package io.flutter.plugin.platform; + +import android.content.Context; +import android.graphics.Rect; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; + +/* + * A view group that implements the same layout protocol that exist between the WindowManager and its direct + * children. + * + * Currently only a subset of the protocol is supported (gravity, x, and y). + */ +class SingleViewFakeWindowViewGroup extends ViewGroup { + // Used in onLayout to keep the bounds of the current view. + // We keep it as a member to avoid object allocations during onLayout which are discouraged. + private final Rect viewBounds; + + // Used in onLayout to keep the bounds of the child views. + // We keep it as a member to avoid object allocations during onLayout which are discouraged. + private final Rect childRect; + + public SingleViewFakeWindowViewGroup(Context context) { + super(context); + viewBounds = new Rect(); + childRect = new Rect(); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + WindowManager.LayoutParams params = (WindowManager.LayoutParams) child.getLayoutParams(); + viewBounds.set(l, t, r, b); + Gravity.apply( + params.gravity, + child.getMeasuredWidth(), + child.getMeasuredHeight(), + viewBounds, + params.x, + params.y, + childRect); + child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + child.measure(atMost(widthMeasureSpec), atMost(heightMeasureSpec)); + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + private static int atMost(int measureSpec) { + return MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(measureSpec), MeasureSpec.AT_MOST); + } +} diff --git a/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java b/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java index d8dc53049a5be..0157c8daba155 100644 --- a/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java +++ b/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java @@ -13,27 +13,19 @@ import android.content.Context; import android.content.ContextWrapper; import android.content.MutableContextWrapper; -import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.os.Build; import android.os.Bundle; import android.view.Display; -import android.view.Gravity; import android.view.View; -import android.view.ViewGroup; import android.view.WindowManager; -import android.view.WindowMetrics; import android.view.accessibility.AccessibilityEvent; import android.view.inputmethod.InputMethodManager; import android.widget.FrameLayout; import androidx.annotation.Keep; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; -import androidx.annotation.VisibleForTesting; import io.flutter.Log; -import java.util.concurrent.Executor; -import java.util.function.Consumer; /* * A presentation used for hosting a single Android view in a virtual display. @@ -72,7 +64,7 @@ static class PresentationState { // Contains views that were added directly to the window manager (e.g // android.widget.PopupWindow). - private FakeWindowViewGroup fakeWindowViewGroup; + private SingleViewFakeWindowViewGroup fakeWindowViewGroup; } // A reference to the current accessibility bridge to which accessibility events will be @@ -159,7 +151,7 @@ protected void onCreate(Bundle savedInstanceState) { // This makes sure we preserve alpha for the VD's content. getWindow().setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT)); if (state.fakeWindowViewGroup == null) { - state.fakeWindowViewGroup = new FakeWindowViewGroup(getContext()); + state.fakeWindowViewGroup = new SingleViewFakeWindowViewGroup(getContext()); } if (state.windowManagerHandler == null) { WindowManager windowManagerDelegate = @@ -229,58 +221,6 @@ public PlatformView getView() { return state.platformView; } - /* - * A view group that implements the same layout protocol that exist between the WindowManager and its direct - * children. - * - * Currently only a subset of the protocol is supported (gravity, x, and y). - */ - static class FakeWindowViewGroup extends ViewGroup { - // Used in onLayout to keep the bounds of the current view. - // We keep it as a member to avoid object allocations during onLayout which are discouraged. - private final Rect viewBounds; - - // Used in onLayout to keep the bounds of the child views. - // We keep it as a member to avoid object allocations during onLayout which are discouraged. - private final Rect childRect; - - public FakeWindowViewGroup(Context context) { - super(context); - viewBounds = new Rect(); - childRect = new Rect(); - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - WindowManager.LayoutParams params = (WindowManager.LayoutParams) child.getLayoutParams(); - viewBounds.set(l, t, r, b); - Gravity.apply( - params.gravity, - child.getMeasuredWidth(), - child.getMeasuredHeight(), - viewBounds, - params.x, - params.y, - childRect); - child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom); - } - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - child.measure(atMost(widthMeasureSpec), atMost(heightMeasureSpec)); - } - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - - private static int atMost(int measureSpec) { - return MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(measureSpec), MeasureSpec.AT_MOST); - } - } /** Answers calls for {@link InputMethodManager} with an instance cached at creation time. */ // TODO(mklim): This caches the IMM at construction time and won't pick up any changes. In rare @@ -377,111 +317,6 @@ private boolean isCalledFromAlertDialog() { } } - /* - * A static proxy handler for a WindowManager with custom overrides. - * - * The presentation's window manager delegates all calls to the default window manager. - * WindowManager#addView calls triggered by views that are attached to the virtual display are crashing - * (see: https://github.com/flutter/flutter/issues/20714). This was triggered when selecting text in an embedded - * WebView (as the selection handles are implemented as popup windows). - * - * This static proxy overrides the addView, removeView, removeViewImmediate, and updateViewLayout methods - * to prevent these crashes, and forwards all other calls to the delegate. - */ - @VisibleForTesting - static class WindowManagerHandler implements WindowManager { - private static final String TAG = "PlatformViewsController"; - - private final WindowManager delegate; - FakeWindowViewGroup fakeWindowRootView; - - WindowManagerHandler(WindowManager delegate, FakeWindowViewGroup fakeWindowViewGroup) { - this.delegate = delegate; - fakeWindowRootView = fakeWindowViewGroup; - } - - @Override - @Deprecated - public Display getDefaultDisplay() { - return delegate.getDefaultDisplay(); - } - - @Override - public void removeViewImmediate(View view) { - if (fakeWindowRootView == null) { - Log.w(TAG, "Embedded view called removeViewImmediate while detached from presentation"); - return; - } - view.clearAnimation(); - fakeWindowRootView.removeView(view); - } - - @Override - public void addView(View view, ViewGroup.LayoutParams params) { - if (fakeWindowRootView == null) { - Log.w(TAG, "Embedded view called addView while detached from presentation"); - return; - } - fakeWindowRootView.addView(view, params); - } - - @Override - public void updateViewLayout(View view, ViewGroup.LayoutParams params) { - if (fakeWindowRootView == null) { - Log.w(TAG, "Embedded view called updateViewLayout while detached from presentation"); - return; - } - fakeWindowRootView.updateViewLayout(view, params); - } - - @Override - public void removeView(View view) { - if (fakeWindowRootView == null) { - Log.w(TAG, "Embedded view called removeView while detached from presentation"); - return; - } - fakeWindowRootView.removeView(view); - } - - @RequiresApi(api = Build.VERSION_CODES.R) - @NonNull - @Override - public WindowMetrics getCurrentWindowMetrics() { - return delegate.getCurrentWindowMetrics(); - } - - @RequiresApi(api = Build.VERSION_CODES.R) - @NonNull - @Override - public WindowMetrics getMaximumWindowMetrics() { - return delegate.getMaximumWindowMetrics(); - } - - @RequiresApi(api = Build.VERSION_CODES.S) - @Override - public boolean isCrossWindowBlurEnabled() { - return delegate.isCrossWindowBlurEnabled(); - } - - @RequiresApi(api = Build.VERSION_CODES.S) - @Override - public void addCrossWindowBlurEnabledListener(@NonNull Consumer listener) { - delegate.addCrossWindowBlurEnabledListener(listener); - } - - @RequiresApi(api = Build.VERSION_CODES.S) - @Override - public void addCrossWindowBlurEnabledListener( - @NonNull Executor executor, @NonNull Consumer listener) { - delegate.addCrossWindowBlurEnabledListener(executor, listener); - } - - @RequiresApi(api = Build.VERSION_CODES.S) - @Override - public void removeCrossWindowBlurEnabledListener(@NonNull Consumer listener) { - delegate.removeCrossWindowBlurEnabledListener(listener); - } - } private static class AccessibilityDelegatingFrameLayout extends FrameLayout { private final AccessibilityEventsDelegate accessibilityEventsDelegate; diff --git a/shell/platform/android/io/flutter/plugin/platform/SingleViewWindowManager.java b/shell/platform/android/io/flutter/plugin/platform/SingleViewWindowManager.java new file mode 100644 index 0000000000000..a63bf95580318 --- /dev/null +++ b/shell/platform/android/io/flutter/plugin/platform/SingleViewWindowManager.java @@ -0,0 +1,125 @@ +package io.flutter.plugin.platform; + +import android.os.Build; +import android.view.Display; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.WindowMetrics; +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; +import androidx.annotation.VisibleForTesting; +import io.flutter.Log; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +/** + * A static proxy handler for a WindowManager with custom overrides. + * + *

The presentation's window manager delegates all calls to the default window manager. + * WindowManager#addView calls triggered by views that are attached to the virtual display are + * crashing (see: https://github.com/flutter/flutter/issues/20714). This was triggered when + * selecting text in an embedded WebView (as the selection handles are implemented as popup + * windows). + * + *

This static proxy overrides the addView, removeView, removeViewImmediate, and updateViewLayout + * methods to prevent these crashes, and forwards all other calls to the delegate. + * + *

This is an abstract class because some clients of Flutter compile the Android embedding with + * the Android System SDK, which has additional abstract methods that need to be overriden. + */ +@VisibleForTesting +abstract class SingleViewWindowManager implements WindowManager { + private static final String TAG = "PlatformViewsController"; + + final WindowManager delegate; + SingleViewFakeWindowViewGroup fakeWindowRootView; + + SingleViewWindowManager( + WindowManager delegate, SingleViewFakeWindowViewGroup fakeWindowViewGroup) { + this.delegate = delegate; + fakeWindowRootView = fakeWindowViewGroup; + } + + @Override + @Deprecated + public Display getDefaultDisplay() { + return delegate.getDefaultDisplay(); + } + + @Override + public void removeViewImmediate(View view) { + if (fakeWindowRootView == null) { + Log.w(TAG, "Embedded view called removeViewImmediate while detached from presentation"); + return; + } + view.clearAnimation(); + fakeWindowRootView.removeView(view); + } + + @Override + public void addView(View view, ViewGroup.LayoutParams params) { + if (fakeWindowRootView == null) { + Log.w(TAG, "Embedded view called addView while detached from presentation"); + return; + } + fakeWindowRootView.addView(view, params); + } + + @Override + public void updateViewLayout(View view, ViewGroup.LayoutParams params) { + if (fakeWindowRootView == null) { + Log.w(TAG, "Embedded view called updateViewLayout while detached from presentation"); + return; + } + fakeWindowRootView.updateViewLayout(view, params); + } + + @Override + public void removeView(View view) { + if (fakeWindowRootView == null) { + Log.w(TAG, "Embedded view called removeView while detached from presentation"); + return; + } + fakeWindowRootView.removeView(view); + } + + @RequiresApi(api = Build.VERSION_CODES.R) + @NonNull + @Override + public WindowMetrics getCurrentWindowMetrics() { + return delegate.getCurrentWindowMetrics(); + } + + @RequiresApi(api = Build.VERSION_CODES.R) + @NonNull + @Override + public WindowMetrics getMaximumWindowMetrics() { + return delegate.getMaximumWindowMetrics(); + } + + @RequiresApi(api = Build.VERSION_CODES.S) + @Override + public boolean isCrossWindowBlurEnabled() { + return delegate.isCrossWindowBlurEnabled(); + } + + @RequiresApi(api = Build.VERSION_CODES.S) + @Override + public void addCrossWindowBlurEnabledListener(@NonNull Consumer listener) { + delegate.addCrossWindowBlurEnabledListener(listener); + } + + @RequiresApi(api = Build.VERSION_CODES.S) + @Override + public void addCrossWindowBlurEnabledListener( + @NonNull Executor executor, @NonNull Consumer listener) { + delegate.addCrossWindowBlurEnabledListener(executor, listener); + } + + @RequiresApi(api = Build.VERSION_CODES.S) + @Override + public void removeCrossWindowBlurEnabledListener(@NonNull Consumer listener) { + delegate.removeCrossWindowBlurEnabledListener(listener); + } +} diff --git a/shell/platform/android/io/flutter/plugin/platform/WindowManagerHandler.java b/shell/platform/android/io/flutter/plugin/platform/WindowManagerHandler.java new file mode 100644 index 0000000000000..42709edadf8d2 --- /dev/null +++ b/shell/platform/android/io/flutter/plugin/platform/WindowManagerHandler.java @@ -0,0 +1,11 @@ +package io.flutter.plugin.platform; + +import android.view.WindowManager; + +/** Default implementation when using the regular Android SDK. */ +final class WindowManagerHandler extends SingleViewWindowManager { + + WindowManagerHandler(WindowManager delegate, SingleViewFakeWindowViewGroup fakeWindowViewGroup) { + super(delegate, fakeWindowViewGroup); + } +} diff --git a/shell/platform/android/test/io/flutter/plugin/platform/SingleViewPresentationTest.java b/shell/platform/android/test/io/flutter/plugin/platform/SingleViewPresentationTest.java index d99d344568d25..fd066297cad83 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/SingleViewPresentationTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/SingleViewPresentationTest.java @@ -97,34 +97,34 @@ public void returnsOuterContextInputMethodManager_createDisplayContext() { public void windowManagerHandler_passesCorrectlyToFakeWindowViewGroup() { // Mock the WindowManager and FakeWindowViewGroup that get used by the WindowManagerHandler. WindowManager mockWindowManager = mock(WindowManager.class); - SingleViewPresentation.FakeWindowViewGroup mockFakeWindowViewGroup = - mock(SingleViewPresentation.FakeWindowViewGroup.class); + SingleViewFakeWindowViewGroup mockSingleViewFakeWindowViewGroup = + mock(SingleViewFakeWindowViewGroup.class); View mockView = mock(View.class); ViewGroup.LayoutParams mockLayoutParams = mock(ViewGroup.LayoutParams.class); - SingleViewPresentation.WindowManagerHandler windowManagerHandler = - new SingleViewPresentation.WindowManagerHandler(mockWindowManager, mockFakeWindowViewGroup); + WindowManagerHandler windowManagerHandler = + new WindowManagerHandler(mockWindowManager, mockSingleViewFakeWindowViewGroup); // removeViewImmediate windowManagerHandler.removeViewImmediate(mockView); verify(mockView).clearAnimation(); - verify(mockFakeWindowViewGroup).removeView(mockView); + verify(mockSingleViewFakeWindowViewGroup).removeView(mockView); verifyNoInteractions(mockWindowManager); // addView windowManagerHandler.addView(mockView, mockLayoutParams); - verify(mockFakeWindowViewGroup).addView(mockView, mockLayoutParams); + verify(mockSingleViewFakeWindowViewGroup).addView(mockView, mockLayoutParams); verifyNoInteractions(mockWindowManager); // updateViewLayout windowManagerHandler.updateViewLayout(mockView, mockLayoutParams); - verify(mockFakeWindowViewGroup).updateViewLayout(mockView, mockLayoutParams); + verify(mockSingleViewFakeWindowViewGroup).updateViewLayout(mockView, mockLayoutParams); verifyNoInteractions(mockWindowManager); // removeView windowManagerHandler.updateViewLayout(mockView, mockLayoutParams); - verify(mockFakeWindowViewGroup).removeView(mockView); + verify(mockSingleViewFakeWindowViewGroup).removeView(mockView); verifyNoInteractions(mockWindowManager); } @@ -137,8 +137,8 @@ public void windowManagerHandler_logAndReturnEarly_whenFakeWindowViewGroupIsNull View mockView = mock(View.class); ViewGroup.LayoutParams mockLayoutParams = mock(ViewGroup.LayoutParams.class); - SingleViewPresentation.WindowManagerHandler windowManagerHandler = - new SingleViewPresentation.WindowManagerHandler(mockWindowManager, null); + WindowManagerHandler windowManagerHandler = + new WindowManagerHandler(mockWindowManager, null); // removeViewImmediate windowManagerHandler.removeViewImmediate(mockView); @@ -167,11 +167,11 @@ public void windowManagerHandler_logAndReturnEarly_whenFakeWindowViewGroupIsNull public void windowManagerHandler_forwardsAllOtherCallsToDelegate() { // Mock the WindowManager and FakeWindowViewGroup that get used by the WindowManagerHandler. WindowManager mockWindowManager = mock(WindowManager.class); - SingleViewPresentation.FakeWindowViewGroup mockFakeWindowViewGroup = - mock(SingleViewPresentation.FakeWindowViewGroup.class); + SingleViewFakeWindowViewGroup mockSingleViewFakeWindowViewGroup = + mock(SingleViewFakeWindowViewGroup.class); - SingleViewPresentation.WindowManagerHandler windowManagerHandler = - new SingleViewPresentation.WindowManagerHandler(mockWindowManager, mockFakeWindowViewGroup); + WindowManagerHandler windowManagerHandler = + new WindowManagerHandler(mockWindowManager, mockSingleViewFakeWindowViewGroup); // Verify that all other calls get forwarded to the delegate. Executor mockExecutor = mock(Executor.class); From 0b23f65275ff5055e9657bcc8e70270394931de9 Mon Sep 17 00:00:00 2001 From: Gray Mackall Date: Fri, 23 Feb 2024 15:18:12 -0800 Subject: [PATCH 3/8] format --- .../SingleViewFakeWindowViewGroup.java | 74 ++++----- .../platform/SingleViewPresentation.java | 2 - .../platform/SingleViewWindowManager.java | 150 +++++++++--------- .../plugin/platform/WindowManagerHandler.java | 6 +- .../platform/SingleViewPresentationTest.java | 3 +- 5 files changed, 116 insertions(+), 119 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/platform/SingleViewFakeWindowViewGroup.java b/shell/platform/android/io/flutter/plugin/platform/SingleViewFakeWindowViewGroup.java index 4a7d6307fb39a..2dbcb7c29a6f7 100644 --- a/shell/platform/android/io/flutter/plugin/platform/SingleViewFakeWindowViewGroup.java +++ b/shell/platform/android/io/flutter/plugin/platform/SingleViewFakeWindowViewGroup.java @@ -14,48 +14,48 @@ * Currently only a subset of the protocol is supported (gravity, x, and y). */ class SingleViewFakeWindowViewGroup extends ViewGroup { - // Used in onLayout to keep the bounds of the current view. - // We keep it as a member to avoid object allocations during onLayout which are discouraged. - private final Rect viewBounds; + // Used in onLayout to keep the bounds of the current view. + // We keep it as a member to avoid object allocations during onLayout which are discouraged. + private final Rect viewBounds; - // Used in onLayout to keep the bounds of the child views. - // We keep it as a member to avoid object allocations during onLayout which are discouraged. - private final Rect childRect; + // Used in onLayout to keep the bounds of the child views. + // We keep it as a member to avoid object allocations during onLayout which are discouraged. + private final Rect childRect; - public SingleViewFakeWindowViewGroup(Context context) { - super(context); - viewBounds = new Rect(); - childRect = new Rect(); - } + public SingleViewFakeWindowViewGroup(Context context) { + super(context); + viewBounds = new Rect(); + childRect = new Rect(); + } - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - WindowManager.LayoutParams params = (WindowManager.LayoutParams) child.getLayoutParams(); - viewBounds.set(l, t, r, b); - Gravity.apply( - params.gravity, - child.getMeasuredWidth(), - child.getMeasuredHeight(), - viewBounds, - params.x, - params.y, - childRect); - child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom); - } + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + WindowManager.LayoutParams params = (WindowManager.LayoutParams) child.getLayoutParams(); + viewBounds.set(l, t, r, b); + Gravity.apply( + params.gravity, + child.getMeasuredWidth(), + child.getMeasuredHeight(), + viewBounds, + params.x, + params.y, + childRect); + child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom); } + } - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - child.measure(atMost(widthMeasureSpec), atMost(heightMeasureSpec)); - } - super.onMeasure(widthMeasureSpec, heightMeasureSpec); + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + child.measure(atMost(widthMeasureSpec), atMost(heightMeasureSpec)); } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } - private static int atMost(int measureSpec) { - return MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(measureSpec), MeasureSpec.AT_MOST); - } + private static int atMost(int measureSpec) { + return MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(measureSpec), MeasureSpec.AT_MOST); + } } diff --git a/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java b/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java index 0157c8daba155..1d0b326aaaf9b 100644 --- a/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java +++ b/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java @@ -221,7 +221,6 @@ public PlatformView getView() { return state.platformView; } - /** Answers calls for {@link InputMethodManager} with an instance cached at creation time. */ // TODO(mklim): This caches the IMM at construction time and won't pick up any changes. In rare // cases where the FlutterView changes windows this will return an outdated instance. This @@ -317,7 +316,6 @@ private boolean isCalledFromAlertDialog() { } } - private static class AccessibilityDelegatingFrameLayout extends FrameLayout { private final AccessibilityEventsDelegate accessibilityEventsDelegate; private final View embeddedView; diff --git a/shell/platform/android/io/flutter/plugin/platform/SingleViewWindowManager.java b/shell/platform/android/io/flutter/plugin/platform/SingleViewWindowManager.java index a63bf95580318..ec30dbf06bc52 100644 --- a/shell/platform/android/io/flutter/plugin/platform/SingleViewWindowManager.java +++ b/shell/platform/android/io/flutter/plugin/platform/SingleViewWindowManager.java @@ -30,96 +30,96 @@ */ @VisibleForTesting abstract class SingleViewWindowManager implements WindowManager { - private static final String TAG = "PlatformViewsController"; + private static final String TAG = "PlatformViewsController"; - final WindowManager delegate; - SingleViewFakeWindowViewGroup fakeWindowRootView; + final WindowManager delegate; + SingleViewFakeWindowViewGroup fakeWindowRootView; - SingleViewWindowManager( - WindowManager delegate, SingleViewFakeWindowViewGroup fakeWindowViewGroup) { - this.delegate = delegate; - fakeWindowRootView = fakeWindowViewGroup; - } + SingleViewWindowManager( + WindowManager delegate, SingleViewFakeWindowViewGroup fakeWindowViewGroup) { + this.delegate = delegate; + fakeWindowRootView = fakeWindowViewGroup; + } - @Override - @Deprecated - public Display getDefaultDisplay() { - return delegate.getDefaultDisplay(); - } + @Override + @Deprecated + public Display getDefaultDisplay() { + return delegate.getDefaultDisplay(); + } - @Override - public void removeViewImmediate(View view) { - if (fakeWindowRootView == null) { - Log.w(TAG, "Embedded view called removeViewImmediate while detached from presentation"); - return; - } - view.clearAnimation(); - fakeWindowRootView.removeView(view); + @Override + public void removeViewImmediate(View view) { + if (fakeWindowRootView == null) { + Log.w(TAG, "Embedded view called removeViewImmediate while detached from presentation"); + return; } + view.clearAnimation(); + fakeWindowRootView.removeView(view); + } - @Override - public void addView(View view, ViewGroup.LayoutParams params) { - if (fakeWindowRootView == null) { - Log.w(TAG, "Embedded view called addView while detached from presentation"); - return; - } - fakeWindowRootView.addView(view, params); + @Override + public void addView(View view, ViewGroup.LayoutParams params) { + if (fakeWindowRootView == null) { + Log.w(TAG, "Embedded view called addView while detached from presentation"); + return; } + fakeWindowRootView.addView(view, params); + } - @Override - public void updateViewLayout(View view, ViewGroup.LayoutParams params) { - if (fakeWindowRootView == null) { - Log.w(TAG, "Embedded view called updateViewLayout while detached from presentation"); - return; - } - fakeWindowRootView.updateViewLayout(view, params); + @Override + public void updateViewLayout(View view, ViewGroup.LayoutParams params) { + if (fakeWindowRootView == null) { + Log.w(TAG, "Embedded view called updateViewLayout while detached from presentation"); + return; } + fakeWindowRootView.updateViewLayout(view, params); + } - @Override - public void removeView(View view) { - if (fakeWindowRootView == null) { - Log.w(TAG, "Embedded view called removeView while detached from presentation"); - return; - } - fakeWindowRootView.removeView(view); + @Override + public void removeView(View view) { + if (fakeWindowRootView == null) { + Log.w(TAG, "Embedded view called removeView while detached from presentation"); + return; } + fakeWindowRootView.removeView(view); + } - @RequiresApi(api = Build.VERSION_CODES.R) - @NonNull - @Override - public WindowMetrics getCurrentWindowMetrics() { - return delegate.getCurrentWindowMetrics(); - } + @RequiresApi(api = Build.VERSION_CODES.R) + @NonNull + @Override + public WindowMetrics getCurrentWindowMetrics() { + return delegate.getCurrentWindowMetrics(); + } - @RequiresApi(api = Build.VERSION_CODES.R) - @NonNull - @Override - public WindowMetrics getMaximumWindowMetrics() { - return delegate.getMaximumWindowMetrics(); - } + @RequiresApi(api = Build.VERSION_CODES.R) + @NonNull + @Override + public WindowMetrics getMaximumWindowMetrics() { + return delegate.getMaximumWindowMetrics(); + } - @RequiresApi(api = Build.VERSION_CODES.S) - @Override - public boolean isCrossWindowBlurEnabled() { - return delegate.isCrossWindowBlurEnabled(); - } + @RequiresApi(api = Build.VERSION_CODES.S) + @Override + public boolean isCrossWindowBlurEnabled() { + return delegate.isCrossWindowBlurEnabled(); + } - @RequiresApi(api = Build.VERSION_CODES.S) - @Override - public void addCrossWindowBlurEnabledListener(@NonNull Consumer listener) { - delegate.addCrossWindowBlurEnabledListener(listener); - } + @RequiresApi(api = Build.VERSION_CODES.S) + @Override + public void addCrossWindowBlurEnabledListener(@NonNull Consumer listener) { + delegate.addCrossWindowBlurEnabledListener(listener); + } - @RequiresApi(api = Build.VERSION_CODES.S) - @Override - public void addCrossWindowBlurEnabledListener( - @NonNull Executor executor, @NonNull Consumer listener) { - delegate.addCrossWindowBlurEnabledListener(executor, listener); - } + @RequiresApi(api = Build.VERSION_CODES.S) + @Override + public void addCrossWindowBlurEnabledListener( + @NonNull Executor executor, @NonNull Consumer listener) { + delegate.addCrossWindowBlurEnabledListener(executor, listener); + } - @RequiresApi(api = Build.VERSION_CODES.S) - @Override - public void removeCrossWindowBlurEnabledListener(@NonNull Consumer listener) { - delegate.removeCrossWindowBlurEnabledListener(listener); - } + @RequiresApi(api = Build.VERSION_CODES.S) + @Override + public void removeCrossWindowBlurEnabledListener(@NonNull Consumer listener) { + delegate.removeCrossWindowBlurEnabledListener(listener); + } } diff --git a/shell/platform/android/io/flutter/plugin/platform/WindowManagerHandler.java b/shell/platform/android/io/flutter/plugin/platform/WindowManagerHandler.java index 42709edadf8d2..aa95a52ed5d79 100644 --- a/shell/platform/android/io/flutter/plugin/platform/WindowManagerHandler.java +++ b/shell/platform/android/io/flutter/plugin/platform/WindowManagerHandler.java @@ -5,7 +5,7 @@ /** Default implementation when using the regular Android SDK. */ final class WindowManagerHandler extends SingleViewWindowManager { - WindowManagerHandler(WindowManager delegate, SingleViewFakeWindowViewGroup fakeWindowViewGroup) { - super(delegate, fakeWindowViewGroup); - } + WindowManagerHandler(WindowManager delegate, SingleViewFakeWindowViewGroup fakeWindowViewGroup) { + super(delegate, fakeWindowViewGroup); + } } diff --git a/shell/platform/android/test/io/flutter/plugin/platform/SingleViewPresentationTest.java b/shell/platform/android/test/io/flutter/plugin/platform/SingleViewPresentationTest.java index fd066297cad83..3093d970061a7 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/SingleViewPresentationTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/SingleViewPresentationTest.java @@ -137,8 +137,7 @@ public void windowManagerHandler_logAndReturnEarly_whenFakeWindowViewGroupIsNull View mockView = mock(View.class); ViewGroup.LayoutParams mockLayoutParams = mock(ViewGroup.LayoutParams.class); - WindowManagerHandler windowManagerHandler = - new WindowManagerHandler(mockWindowManager, null); + WindowManagerHandler windowManagerHandler = new WindowManagerHandler(mockWindowManager, null); // removeViewImmediate windowManagerHandler.removeViewImmediate(mockView); From c92c58ac56849b2846895c9539fbc018c3f7765a Mon Sep 17 00:00:00 2001 From: Gray Mackall Date: Fri, 23 Feb 2024 15:44:58 -0800 Subject: [PATCH 4/8] fix BUILD.gn and move tests to appropriate location --- shell/platform/android/BUILD.gn | 3 + .../platform/SingleViewPresentationTest.java | 115 --------------- .../platform/WindowManagerHandlerTest.java | 131 ++++++++++++++++++ 3 files changed, 134 insertions(+), 115 deletions(-) create mode 100644 shell/platform/android/test/io/flutter/plugin/platform/WindowManagerHandlerTest.java diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index ac495887e0934..ca876b802afe9 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -324,7 +324,10 @@ android_java_sources = [ "io/flutter/plugin/platform/PlatformViewWrapper.java", "io/flutter/plugin/platform/PlatformViewsAccessibilityDelegate.java", "io/flutter/plugin/platform/PlatformViewsController.java", + "io/flutter/plugin/platform/SingleViewFakeWindowViewGroup.java", "io/flutter/plugin/platform/SingleViewPresentation.java", + "io/flutter/plugin/platform/SingleViewWindowManager.java", + "io/flutter/plugin/platform/WindowManagerHandler.java", "io/flutter/plugin/platform/SurfaceProducerPlatformViewRenderTarget.java", "io/flutter/plugin/platform/SurfaceTexturePlatformViewRenderTarget.java", "io/flutter/plugin/platform/VirtualDisplayController.java", diff --git a/shell/platform/android/test/io/flutter/plugin/platform/SingleViewPresentationTest.java b/shell/platform/android/test/io/flutter/plugin/platform/SingleViewPresentationTest.java index 3093d970061a7..d27e08bbbdc97 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/SingleViewPresentationTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/SingleViewPresentationTest.java @@ -7,26 +7,18 @@ import static android.os.Build.VERSION_CODES.KITKAT; import static android.os.Build.VERSION_CODES.P; import static android.os.Build.VERSION_CODES.R; -import static android.os.Build.VERSION_CODES.S; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import android.annotation.TargetApi; import android.content.Context; import android.hardware.display.DisplayManager; import android.view.Display; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; -import java.util.concurrent.Executor; -import java.util.function.Consumer; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.annotation.Config; @@ -91,111 +83,4 @@ public void returnsOuterContextInputMethodManager_createDisplayContext() { // Android OS (or Robolectric's shadow, in this case). assertEquals(expected, actual); } - - @Test - @Config(minSdk = R) - public void windowManagerHandler_passesCorrectlyToFakeWindowViewGroup() { - // Mock the WindowManager and FakeWindowViewGroup that get used by the WindowManagerHandler. - WindowManager mockWindowManager = mock(WindowManager.class); - SingleViewFakeWindowViewGroup mockSingleViewFakeWindowViewGroup = - mock(SingleViewFakeWindowViewGroup.class); - - View mockView = mock(View.class); - ViewGroup.LayoutParams mockLayoutParams = mock(ViewGroup.LayoutParams.class); - - WindowManagerHandler windowManagerHandler = - new WindowManagerHandler(mockWindowManager, mockSingleViewFakeWindowViewGroup); - - // removeViewImmediate - windowManagerHandler.removeViewImmediate(mockView); - verify(mockView).clearAnimation(); - verify(mockSingleViewFakeWindowViewGroup).removeView(mockView); - verifyNoInteractions(mockWindowManager); - - // addView - windowManagerHandler.addView(mockView, mockLayoutParams); - verify(mockSingleViewFakeWindowViewGroup).addView(mockView, mockLayoutParams); - verifyNoInteractions(mockWindowManager); - - // updateViewLayout - windowManagerHandler.updateViewLayout(mockView, mockLayoutParams); - verify(mockSingleViewFakeWindowViewGroup).updateViewLayout(mockView, mockLayoutParams); - verifyNoInteractions(mockWindowManager); - - // removeView - windowManagerHandler.updateViewLayout(mockView, mockLayoutParams); - verify(mockSingleViewFakeWindowViewGroup).removeView(mockView); - verifyNoInteractions(mockWindowManager); - } - - @Test - @Config(minSdk = R) - public void windowManagerHandler_logAndReturnEarly_whenFakeWindowViewGroupIsNull() { - // Mock the WindowManager and FakeWindowViewGroup that get used by the WindowManagerHandler. - WindowManager mockWindowManager = mock(WindowManager.class); - - View mockView = mock(View.class); - ViewGroup.LayoutParams mockLayoutParams = mock(ViewGroup.LayoutParams.class); - - WindowManagerHandler windowManagerHandler = new WindowManagerHandler(mockWindowManager, null); - - // removeViewImmediate - windowManagerHandler.removeViewImmediate(mockView); - verifyNoInteractions(mockView); - verifyNoInteractions(mockWindowManager); - - // addView - windowManagerHandler.addView(mockView, mockLayoutParams); - verifyNoInteractions(mockWindowManager); - - // updateViewLayout - windowManagerHandler.updateViewLayout(mockView, mockLayoutParams); - verifyNoInteractions(mockWindowManager); - - // removeView - windowManagerHandler.updateViewLayout(mockView, mockLayoutParams); - verifyNoInteractions(mockWindowManager); - } - - // This section tests that WindowManagerHandler forwards all of the non-special case calls to the - // delegate WindowManager. Because this must include some deprecated WindowManager method calls - // (because the proxy overrides every method), we suppress deprecation warnings here. - @Test - @Config(minSdk = S) - @SuppressWarnings("deprecation") - public void windowManagerHandler_forwardsAllOtherCallsToDelegate() { - // Mock the WindowManager and FakeWindowViewGroup that get used by the WindowManagerHandler. - WindowManager mockWindowManager = mock(WindowManager.class); - SingleViewFakeWindowViewGroup mockSingleViewFakeWindowViewGroup = - mock(SingleViewFakeWindowViewGroup.class); - - WindowManagerHandler windowManagerHandler = - new WindowManagerHandler(mockWindowManager, mockSingleViewFakeWindowViewGroup); - - // Verify that all other calls get forwarded to the delegate. - Executor mockExecutor = mock(Executor.class); - @SuppressWarnings("Unchecked cast") - Consumer mockListener = (Consumer) mock(Consumer.class); - - windowManagerHandler.getDefaultDisplay(); - verify(mockWindowManager).getDefaultDisplay(); - - windowManagerHandler.getCurrentWindowMetrics(); - verify(mockWindowManager).getCurrentWindowMetrics(); - - windowManagerHandler.getMaximumWindowMetrics(); - verify(mockWindowManager).getMaximumWindowMetrics(); - - windowManagerHandler.isCrossWindowBlurEnabled(); - verify(mockWindowManager).isCrossWindowBlurEnabled(); - - windowManagerHandler.addCrossWindowBlurEnabledListener(mockListener); - verify(mockWindowManager).addCrossWindowBlurEnabledListener(mockListener); - - windowManagerHandler.addCrossWindowBlurEnabledListener(mockExecutor, mockListener); - verify(mockWindowManager).addCrossWindowBlurEnabledListener(mockExecutor, mockListener); - - windowManagerHandler.removeCrossWindowBlurEnabledListener(mockListener); - verify(mockWindowManager).removeCrossWindowBlurEnabledListener(mockListener); - } } diff --git a/shell/platform/android/test/io/flutter/plugin/platform/WindowManagerHandlerTest.java b/shell/platform/android/test/io/flutter/plugin/platform/WindowManagerHandlerTest.java new file mode 100644 index 0000000000000..90fc5340407b8 --- /dev/null +++ b/shell/platform/android/test/io/flutter/plugin/platform/WindowManagerHandlerTest.java @@ -0,0 +1,131 @@ +package io.flutter.plugin.platform; + +import static android.os.Build.VERSION_CODES.P; +import static android.os.Build.VERSION_CODES.R; +import static android.os.Build.VERSION_CODES.S; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; + +import android.annotation.TargetApi; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +@Config(manifest = Config.NONE) +@RunWith(AndroidJUnit4.class) +@TargetApi(P) +public class WindowManagerHandlerTest { + @Test + @Config(minSdk = R) + public void windowManagerHandler_passesCorrectlyToFakeWindowViewGroup() { + // Mock the WindowManager and FakeWindowViewGroup that get used by the WindowManagerHandler. + WindowManager mockWindowManager = mock(WindowManager.class); + SingleViewFakeWindowViewGroup mockSingleViewFakeWindowViewGroup = + mock(SingleViewFakeWindowViewGroup.class); + + View mockView = mock(View.class); + ViewGroup.LayoutParams mockLayoutParams = mock(ViewGroup.LayoutParams.class); + + WindowManagerHandler windowManagerHandler = + new WindowManagerHandler(mockWindowManager, mockSingleViewFakeWindowViewGroup); + + // removeViewImmediate + windowManagerHandler.removeViewImmediate(mockView); + verify(mockView).clearAnimation(); + verify(mockSingleViewFakeWindowViewGroup).removeView(mockView); + verifyNoInteractions(mockWindowManager); + + // addView + windowManagerHandler.addView(mockView, mockLayoutParams); + verify(mockSingleViewFakeWindowViewGroup).addView(mockView, mockLayoutParams); + verifyNoInteractions(mockWindowManager); + + // updateViewLayout + windowManagerHandler.updateViewLayout(mockView, mockLayoutParams); + verify(mockSingleViewFakeWindowViewGroup).updateViewLayout(mockView, mockLayoutParams); + verifyNoInteractions(mockWindowManager); + + // removeView + windowManagerHandler.updateViewLayout(mockView, mockLayoutParams); + verify(mockSingleViewFakeWindowViewGroup).removeView(mockView); + verifyNoInteractions(mockWindowManager); + } + + @Test + @Config(minSdk = R) + public void windowManagerHandler_logAndReturnEarly_whenFakeWindowViewGroupIsNull() { + // Mock the WindowManager and FakeWindowViewGroup that get used by the WindowManagerHandler. + WindowManager mockWindowManager = mock(WindowManager.class); + + View mockView = mock(View.class); + ViewGroup.LayoutParams mockLayoutParams = mock(ViewGroup.LayoutParams.class); + + WindowManagerHandler windowManagerHandler = new WindowManagerHandler(mockWindowManager, null); + + // removeViewImmediate + windowManagerHandler.removeViewImmediate(mockView); + verifyNoInteractions(mockView); + verifyNoInteractions(mockWindowManager); + + // addView + windowManagerHandler.addView(mockView, mockLayoutParams); + verifyNoInteractions(mockWindowManager); + + // updateViewLayout + windowManagerHandler.updateViewLayout(mockView, mockLayoutParams); + verifyNoInteractions(mockWindowManager); + + // removeView + windowManagerHandler.updateViewLayout(mockView, mockLayoutParams); + verifyNoInteractions(mockWindowManager); + } + + // This section tests that WindowManagerHandler forwards all of the non-special case calls to the + // delegate WindowManager. Because this must include some deprecated WindowManager method calls + // (because the proxy overrides every method), we suppress deprecation warnings here. + @Test + @Config(minSdk = S) + @SuppressWarnings("deprecation") + public void windowManagerHandler_forwardsAllOtherCallsToDelegate() { + // Mock the WindowManager and FakeWindowViewGroup that get used by the WindowManagerHandler. + WindowManager mockWindowManager = mock(WindowManager.class); + SingleViewFakeWindowViewGroup mockSingleViewFakeWindowViewGroup = + mock(SingleViewFakeWindowViewGroup.class); + + WindowManagerHandler windowManagerHandler = + new WindowManagerHandler(mockWindowManager, mockSingleViewFakeWindowViewGroup); + + // Verify that all other calls get forwarded to the delegate. + Executor mockExecutor = mock(Executor.class); + @SuppressWarnings("Unchecked cast") + Consumer mockListener = (Consumer) mock(Consumer.class); + + windowManagerHandler.getDefaultDisplay(); + verify(mockWindowManager).getDefaultDisplay(); + + windowManagerHandler.getCurrentWindowMetrics(); + verify(mockWindowManager).getCurrentWindowMetrics(); + + windowManagerHandler.getMaximumWindowMetrics(); + verify(mockWindowManager).getMaximumWindowMetrics(); + + windowManagerHandler.isCrossWindowBlurEnabled(); + verify(mockWindowManager).isCrossWindowBlurEnabled(); + + windowManagerHandler.addCrossWindowBlurEnabledListener(mockListener); + verify(mockWindowManager).addCrossWindowBlurEnabledListener(mockListener); + + windowManagerHandler.addCrossWindowBlurEnabledListener(mockExecutor, mockListener); + verify(mockWindowManager).addCrossWindowBlurEnabledListener(mockExecutor, mockListener); + + windowManagerHandler.removeCrossWindowBlurEnabledListener(mockListener); + verify(mockWindowManager).removeCrossWindowBlurEnabledListener(mockListener); + } +} From 19e5f0815fed1c8dccdee2d8c7146ee48e65ad8a Mon Sep 17 00:00:00 2001 From: Gray Mackall Date: Fri, 23 Feb 2024 15:48:27 -0800 Subject: [PATCH 5/8] format --- shell/platform/android/BUILD.gn | 2 +- .../platform/WindowManagerHandlerTest.java | 216 +++++++++--------- 2 files changed, 109 insertions(+), 109 deletions(-) diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index ca876b802afe9..c9e23f35dae62 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -327,10 +327,10 @@ android_java_sources = [ "io/flutter/plugin/platform/SingleViewFakeWindowViewGroup.java", "io/flutter/plugin/platform/SingleViewPresentation.java", "io/flutter/plugin/platform/SingleViewWindowManager.java", - "io/flutter/plugin/platform/WindowManagerHandler.java", "io/flutter/plugin/platform/SurfaceProducerPlatformViewRenderTarget.java", "io/flutter/plugin/platform/SurfaceTexturePlatformViewRenderTarget.java", "io/flutter/plugin/platform/VirtualDisplayController.java", + "io/flutter/plugin/platform/WindowManagerHandler.java", "io/flutter/plugin/text/ProcessTextPlugin.java", "io/flutter/util/HandlerCompat.java", "io/flutter/util/PathUtils.java", diff --git a/shell/platform/android/test/io/flutter/plugin/platform/WindowManagerHandlerTest.java b/shell/platform/android/test/io/flutter/plugin/platform/WindowManagerHandlerTest.java index 90fc5340407b8..1a1e5860e315d 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/WindowManagerHandlerTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/WindowManagerHandlerTest.java @@ -12,120 +12,120 @@ import android.view.ViewGroup; import android.view.WindowManager; import androidx.test.ext.junit.runners.AndroidJUnit4; +import java.util.concurrent.Executor; +import java.util.function.Consumer; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.annotation.Config; -import java.util.concurrent.Executor; -import java.util.function.Consumer; @Config(manifest = Config.NONE) @RunWith(AndroidJUnit4.class) @TargetApi(P) public class WindowManagerHandlerTest { - @Test - @Config(minSdk = R) - public void windowManagerHandler_passesCorrectlyToFakeWindowViewGroup() { - // Mock the WindowManager and FakeWindowViewGroup that get used by the WindowManagerHandler. - WindowManager mockWindowManager = mock(WindowManager.class); - SingleViewFakeWindowViewGroup mockSingleViewFakeWindowViewGroup = - mock(SingleViewFakeWindowViewGroup.class); - - View mockView = mock(View.class); - ViewGroup.LayoutParams mockLayoutParams = mock(ViewGroup.LayoutParams.class); - - WindowManagerHandler windowManagerHandler = - new WindowManagerHandler(mockWindowManager, mockSingleViewFakeWindowViewGroup); - - // removeViewImmediate - windowManagerHandler.removeViewImmediate(mockView); - verify(mockView).clearAnimation(); - verify(mockSingleViewFakeWindowViewGroup).removeView(mockView); - verifyNoInteractions(mockWindowManager); - - // addView - windowManagerHandler.addView(mockView, mockLayoutParams); - verify(mockSingleViewFakeWindowViewGroup).addView(mockView, mockLayoutParams); - verifyNoInteractions(mockWindowManager); - - // updateViewLayout - windowManagerHandler.updateViewLayout(mockView, mockLayoutParams); - verify(mockSingleViewFakeWindowViewGroup).updateViewLayout(mockView, mockLayoutParams); - verifyNoInteractions(mockWindowManager); - - // removeView - windowManagerHandler.updateViewLayout(mockView, mockLayoutParams); - verify(mockSingleViewFakeWindowViewGroup).removeView(mockView); - verifyNoInteractions(mockWindowManager); - } - - @Test - @Config(minSdk = R) - public void windowManagerHandler_logAndReturnEarly_whenFakeWindowViewGroupIsNull() { - // Mock the WindowManager and FakeWindowViewGroup that get used by the WindowManagerHandler. - WindowManager mockWindowManager = mock(WindowManager.class); - - View mockView = mock(View.class); - ViewGroup.LayoutParams mockLayoutParams = mock(ViewGroup.LayoutParams.class); - - WindowManagerHandler windowManagerHandler = new WindowManagerHandler(mockWindowManager, null); - - // removeViewImmediate - windowManagerHandler.removeViewImmediate(mockView); - verifyNoInteractions(mockView); - verifyNoInteractions(mockWindowManager); - - // addView - windowManagerHandler.addView(mockView, mockLayoutParams); - verifyNoInteractions(mockWindowManager); - - // updateViewLayout - windowManagerHandler.updateViewLayout(mockView, mockLayoutParams); - verifyNoInteractions(mockWindowManager); - - // removeView - windowManagerHandler.updateViewLayout(mockView, mockLayoutParams); - verifyNoInteractions(mockWindowManager); - } - - // This section tests that WindowManagerHandler forwards all of the non-special case calls to the - // delegate WindowManager. Because this must include some deprecated WindowManager method calls - // (because the proxy overrides every method), we suppress deprecation warnings here. - @Test - @Config(minSdk = S) - @SuppressWarnings("deprecation") - public void windowManagerHandler_forwardsAllOtherCallsToDelegate() { - // Mock the WindowManager and FakeWindowViewGroup that get used by the WindowManagerHandler. - WindowManager mockWindowManager = mock(WindowManager.class); - SingleViewFakeWindowViewGroup mockSingleViewFakeWindowViewGroup = - mock(SingleViewFakeWindowViewGroup.class); - - WindowManagerHandler windowManagerHandler = - new WindowManagerHandler(mockWindowManager, mockSingleViewFakeWindowViewGroup); - - // Verify that all other calls get forwarded to the delegate. - Executor mockExecutor = mock(Executor.class); - @SuppressWarnings("Unchecked cast") - Consumer mockListener = (Consumer) mock(Consumer.class); - - windowManagerHandler.getDefaultDisplay(); - verify(mockWindowManager).getDefaultDisplay(); - - windowManagerHandler.getCurrentWindowMetrics(); - verify(mockWindowManager).getCurrentWindowMetrics(); - - windowManagerHandler.getMaximumWindowMetrics(); - verify(mockWindowManager).getMaximumWindowMetrics(); - - windowManagerHandler.isCrossWindowBlurEnabled(); - verify(mockWindowManager).isCrossWindowBlurEnabled(); - - windowManagerHandler.addCrossWindowBlurEnabledListener(mockListener); - verify(mockWindowManager).addCrossWindowBlurEnabledListener(mockListener); - - windowManagerHandler.addCrossWindowBlurEnabledListener(mockExecutor, mockListener); - verify(mockWindowManager).addCrossWindowBlurEnabledListener(mockExecutor, mockListener); - - windowManagerHandler.removeCrossWindowBlurEnabledListener(mockListener); - verify(mockWindowManager).removeCrossWindowBlurEnabledListener(mockListener); - } + @Test + @Config(minSdk = R) + public void windowManagerHandler_passesCorrectlyToFakeWindowViewGroup() { + // Mock the WindowManager and FakeWindowViewGroup that get used by the WindowManagerHandler. + WindowManager mockWindowManager = mock(WindowManager.class); + SingleViewFakeWindowViewGroup mockSingleViewFakeWindowViewGroup = + mock(SingleViewFakeWindowViewGroup.class); + + View mockView = mock(View.class); + ViewGroup.LayoutParams mockLayoutParams = mock(ViewGroup.LayoutParams.class); + + WindowManagerHandler windowManagerHandler = + new WindowManagerHandler(mockWindowManager, mockSingleViewFakeWindowViewGroup); + + // removeViewImmediate + windowManagerHandler.removeViewImmediate(mockView); + verify(mockView).clearAnimation(); + verify(mockSingleViewFakeWindowViewGroup).removeView(mockView); + verifyNoInteractions(mockWindowManager); + + // addView + windowManagerHandler.addView(mockView, mockLayoutParams); + verify(mockSingleViewFakeWindowViewGroup).addView(mockView, mockLayoutParams); + verifyNoInteractions(mockWindowManager); + + // updateViewLayout + windowManagerHandler.updateViewLayout(mockView, mockLayoutParams); + verify(mockSingleViewFakeWindowViewGroup).updateViewLayout(mockView, mockLayoutParams); + verifyNoInteractions(mockWindowManager); + + // removeView + windowManagerHandler.updateViewLayout(mockView, mockLayoutParams); + verify(mockSingleViewFakeWindowViewGroup).removeView(mockView); + verifyNoInteractions(mockWindowManager); + } + + @Test + @Config(minSdk = R) + public void windowManagerHandler_logAndReturnEarly_whenFakeWindowViewGroupIsNull() { + // Mock the WindowManager and FakeWindowViewGroup that get used by the WindowManagerHandler. + WindowManager mockWindowManager = mock(WindowManager.class); + + View mockView = mock(View.class); + ViewGroup.LayoutParams mockLayoutParams = mock(ViewGroup.LayoutParams.class); + + WindowManagerHandler windowManagerHandler = new WindowManagerHandler(mockWindowManager, null); + + // removeViewImmediate + windowManagerHandler.removeViewImmediate(mockView); + verifyNoInteractions(mockView); + verifyNoInteractions(mockWindowManager); + + // addView + windowManagerHandler.addView(mockView, mockLayoutParams); + verifyNoInteractions(mockWindowManager); + + // updateViewLayout + windowManagerHandler.updateViewLayout(mockView, mockLayoutParams); + verifyNoInteractions(mockWindowManager); + + // removeView + windowManagerHandler.updateViewLayout(mockView, mockLayoutParams); + verifyNoInteractions(mockWindowManager); + } + + // This section tests that WindowManagerHandler forwards all of the non-special case calls to the + // delegate WindowManager. Because this must include some deprecated WindowManager method calls + // (because the proxy overrides every method), we suppress deprecation warnings here. + @Test + @Config(minSdk = S) + @SuppressWarnings("deprecation") + public void windowManagerHandler_forwardsAllOtherCallsToDelegate() { + // Mock the WindowManager and FakeWindowViewGroup that get used by the WindowManagerHandler. + WindowManager mockWindowManager = mock(WindowManager.class); + SingleViewFakeWindowViewGroup mockSingleViewFakeWindowViewGroup = + mock(SingleViewFakeWindowViewGroup.class); + + WindowManagerHandler windowManagerHandler = + new WindowManagerHandler(mockWindowManager, mockSingleViewFakeWindowViewGroup); + + // Verify that all other calls get forwarded to the delegate. + Executor mockExecutor = mock(Executor.class); + @SuppressWarnings("Unchecked cast") + Consumer mockListener = (Consumer) mock(Consumer.class); + + windowManagerHandler.getDefaultDisplay(); + verify(mockWindowManager).getDefaultDisplay(); + + windowManagerHandler.getCurrentWindowMetrics(); + verify(mockWindowManager).getCurrentWindowMetrics(); + + windowManagerHandler.getMaximumWindowMetrics(); + verify(mockWindowManager).getMaximumWindowMetrics(); + + windowManagerHandler.isCrossWindowBlurEnabled(); + verify(mockWindowManager).isCrossWindowBlurEnabled(); + + windowManagerHandler.addCrossWindowBlurEnabledListener(mockListener); + verify(mockWindowManager).addCrossWindowBlurEnabledListener(mockListener); + + windowManagerHandler.addCrossWindowBlurEnabledListener(mockExecutor, mockListener); + verify(mockWindowManager).addCrossWindowBlurEnabledListener(mockExecutor, mockListener); + + windowManagerHandler.removeCrossWindowBlurEnabledListener(mockListener); + verify(mockWindowManager).removeCrossWindowBlurEnabledListener(mockListener); + } } From 3b88f4b787fca1900bcf52f4e701fd6af890ab7b Mon Sep 17 00:00:00 2001 From: Gray Mackall Date: Fri, 23 Feb 2024 16:26:25 -0800 Subject: [PATCH 6/8] licenses --- .../plugin/platform/SingleViewFakeWindowViewGroup.java | 4 ++++ .../io/flutter/plugin/platform/SingleViewWindowManager.java | 4 ++++ .../io/flutter/plugin/platform/WindowManagerHandler.java | 4 ++++ .../io/flutter/plugin/platform/WindowManagerHandlerTest.java | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/shell/platform/android/io/flutter/plugin/platform/SingleViewFakeWindowViewGroup.java b/shell/platform/android/io/flutter/plugin/platform/SingleViewFakeWindowViewGroup.java index 2dbcb7c29a6f7..fa16b525435ef 100644 --- a/shell/platform/android/io/flutter/plugin/platform/SingleViewFakeWindowViewGroup.java +++ b/shell/platform/android/io/flutter/plugin/platform/SingleViewFakeWindowViewGroup.java @@ -1,3 +1,7 @@ +// 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.plugin.platform; import android.content.Context; diff --git a/shell/platform/android/io/flutter/plugin/platform/SingleViewWindowManager.java b/shell/platform/android/io/flutter/plugin/platform/SingleViewWindowManager.java index ec30dbf06bc52..baa3f4784dca3 100644 --- a/shell/platform/android/io/flutter/plugin/platform/SingleViewWindowManager.java +++ b/shell/platform/android/io/flutter/plugin/platform/SingleViewWindowManager.java @@ -1,3 +1,7 @@ +// 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.plugin.platform; import android.os.Build; diff --git a/shell/platform/android/io/flutter/plugin/platform/WindowManagerHandler.java b/shell/platform/android/io/flutter/plugin/platform/WindowManagerHandler.java index aa95a52ed5d79..5072420b6ba54 100644 --- a/shell/platform/android/io/flutter/plugin/platform/WindowManagerHandler.java +++ b/shell/platform/android/io/flutter/plugin/platform/WindowManagerHandler.java @@ -1,3 +1,7 @@ +// 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.plugin.platform; import android.view.WindowManager; diff --git a/shell/platform/android/test/io/flutter/plugin/platform/WindowManagerHandlerTest.java b/shell/platform/android/test/io/flutter/plugin/platform/WindowManagerHandlerTest.java index 1a1e5860e315d..c7fd1d353f8a8 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/WindowManagerHandlerTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/WindowManagerHandlerTest.java @@ -1,3 +1,7 @@ +// 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.plugin.platform; import static android.os.Build.VERSION_CODES.P; From 0f0a76b02db9da1c5c0652cefdacad3dfb933763 Mon Sep 17 00:00:00 2001 From: Gray Mackall Date: Mon, 26 Feb 2024 10:49:18 -0800 Subject: [PATCH 7/8] licenses --- ci/licenses_golden/licenses_flutter | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index e051154ac0e64..f4b61882fe93e 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -29220,8 +29220,11 @@ ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/Platf ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewWrapper.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsAccessibilityDelegate.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SingleViewFakeWindowViewGroup.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SingleViewWindowManager.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/VirtualDisplayController.java + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/WindowManagerHandler.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/plugin/text/ProcessTextPlugin.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/util/HandlerCompat.java + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/platform/android/io/flutter/util/PathUtils.java + ../../../flutter/LICENSE @@ -32068,10 +32071,13 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/Platfor FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewWrapper.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsAccessibilityDelegate.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SingleViewFakeWindowViewGroup.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SingleViewWindowManager.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SurfaceProducerPlatformViewRenderTarget.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/SurfaceTexturePlatformViewRenderTarget.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/VirtualDisplayController.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/platform/WindowManagerHandler.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/text/ProcessTextPlugin.java FILE: ../../../flutter/shell/platform/android/io/flutter/util/HandlerCompat.java FILE: ../../../flutter/shell/platform/android/io/flutter/util/PathUtils.java From 7076108d80d68d099d74af14da5c7c42a74212c9 Mon Sep 17 00:00:00 2001 From: Gray Mackall Date: Wed, 28 Feb 2024 08:39:56 -0800 Subject: [PATCH 8/8] remove annotation --- .../io/flutter/plugin/platform/SingleViewWindowManager.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/platform/SingleViewWindowManager.java b/shell/platform/android/io/flutter/plugin/platform/SingleViewWindowManager.java index baa3f4784dca3..ad004acbf4cad 100644 --- a/shell/platform/android/io/flutter/plugin/platform/SingleViewWindowManager.java +++ b/shell/platform/android/io/flutter/plugin/platform/SingleViewWindowManager.java @@ -12,7 +12,6 @@ import android.view.WindowMetrics; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; -import androidx.annotation.VisibleForTesting; import io.flutter.Log; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -32,7 +31,6 @@ *

This is an abstract class because some clients of Flutter compile the Android embedding with * the Android System SDK, which has additional abstract methods that need to be overriden. */ -@VisibleForTesting abstract class SingleViewWindowManager implements WindowManager { private static final String TAG = "PlatformViewsController";