Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit ca48828

Browse files
author
auto-submit[bot]
committed
Revert "Remove WindowManager reflection in SingleViewPresentation.java (#49996)"
This reverts commit a023d98.
1 parent 05ffcb1 commit ca48828

File tree

2 files changed

+56
-184
lines changed

2 files changed

+56
-184
lines changed

shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java

Lines changed: 56 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,17 @@
2222
import android.view.View;
2323
import android.view.ViewGroup;
2424
import android.view.WindowManager;
25-
import android.view.WindowMetrics;
2625
import android.view.accessibility.AccessibilityEvent;
2726
import android.view.inputmethod.InputMethodManager;
2827
import android.widget.FrameLayout;
2928
import androidx.annotation.Keep;
3029
import androidx.annotation.NonNull;
3130
import androidx.annotation.Nullable;
32-
import androidx.annotation.RequiresApi;
33-
import androidx.annotation.VisibleForTesting;
3431
import io.flutter.Log;
35-
import java.util.concurrent.Executor;
36-
import java.util.function.Consumer;
32+
import java.lang.reflect.InvocationHandler;
33+
import java.lang.reflect.InvocationTargetException;
34+
import java.lang.reflect.Method;
35+
import java.lang.reflect.Proxy;
3736

3837
/*
3938
* A presentation used for hosting a single Android view in a virtual display.
@@ -360,7 +359,7 @@ public Object getSystemService(String name) {
360359

361360
private WindowManager getWindowManager() {
362361
if (windowManager == null) {
363-
windowManager = windowManagerHandler;
362+
windowManager = windowManagerHandler.getWindowManager();
364363
}
365364
return windowManager;
366365
}
@@ -378,18 +377,21 @@ private boolean isCalledFromAlertDialog() {
378377
}
379378

380379
/*
381-
* A static proxy handler for a WindowManager with custom overrides.
380+
* A dynamic proxy handler for a WindowManager with custom overrides.
382381
*
383382
* The presentation's window manager delegates all calls to the default window manager.
384383
* WindowManager#addView calls triggered by views that are attached to the virtual display are crashing
385384
* (see: https://github.com/flutter/flutter/issues/20714). This was triggered when selecting text in an embedded
386385
* WebView (as the selection handles are implemented as popup windows).
387386
*
388-
* This static proxy overrides the addView, removeView, removeViewImmediate, and updateViewLayout methods
389-
* to prevent these crashes, and forwards all other calls to the delegate.
387+
* This dynamic proxy overrides the addView, removeView, removeViewImmediate, and updateViewLayout methods
388+
* to prevent these crashes.
389+
*
390+
* This will be more efficient as a static proxy that's not using reflection, but as the engine is currently
391+
* not being built against the latest Android SDK we cannot override all relevant method.
392+
* Tracking issue for upgrading the engine's Android sdk: https://github.com/flutter/flutter/issues/20717
390393
*/
391-
@VisibleForTesting
392-
static class WindowManagerHandler implements WindowManager {
394+
static class WindowManagerHandler implements InvocationHandler {
393395
private static final String TAG = "PlatformViewsController";
394396

395397
private final WindowManager delegate;
@@ -400,86 +402,72 @@ static class WindowManagerHandler implements WindowManager {
400402
fakeWindowRootView = fakeWindowViewGroup;
401403
}
402404

403-
@Override
404-
@Deprecated
405-
public Display getDefaultDisplay() {
406-
return delegate.getDefaultDisplay();
405+
public WindowManager getWindowManager() {
406+
return (WindowManager)
407+
Proxy.newProxyInstance(
408+
WindowManager.class.getClassLoader(), new Class<?>[] {WindowManager.class}, this);
407409
}
408410

409411
@Override
410-
public void removeViewImmediate(View view) {
411-
if (fakeWindowRootView == null) {
412-
Log.w(TAG, "Embedded view called removeViewImmediate while detached from presentation");
413-
return;
412+
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
413+
switch (method.getName()) {
414+
case "addView":
415+
addView(args);
416+
return null;
417+
case "removeView":
418+
removeView(args);
419+
return null;
420+
case "removeViewImmediate":
421+
removeViewImmediate(args);
422+
return null;
423+
case "updateViewLayout":
424+
updateViewLayout(args);
425+
return null;
426+
}
427+
try {
428+
return method.invoke(delegate, args);
429+
} catch (InvocationTargetException e) {
430+
throw e.getCause();
414431
}
415-
view.clearAnimation();
416-
fakeWindowRootView.removeView(view);
417432
}
418433

419-
@Override
420-
public void addView(View view, ViewGroup.LayoutParams params) {
434+
private void addView(Object[] args) {
421435
if (fakeWindowRootView == null) {
422436
Log.w(TAG, "Embedded view called addView while detached from presentation");
423437
return;
424438
}
425-
fakeWindowRootView.addView(view, params);
439+
View view = (View) args[0];
440+
WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) args[1];
441+
fakeWindowRootView.addView(view, layoutParams);
426442
}
427443

428-
@Override
429-
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
444+
private void removeView(Object[] args) {
430445
if (fakeWindowRootView == null) {
431-
Log.w(TAG, "Embedded view called updateViewLayout while detached from presentation");
446+
Log.w(TAG, "Embedded view called removeView while detached from presentation");
432447
return;
433448
}
434-
fakeWindowRootView.updateViewLayout(view, params);
449+
View view = (View) args[0];
450+
fakeWindowRootView.removeView(view);
435451
}
436452

437-
@Override
438-
public void removeView(View view) {
453+
private void removeViewImmediate(Object[] args) {
439454
if (fakeWindowRootView == null) {
440-
Log.w(TAG, "Embedded view called removeView while detached from presentation");
455+
Log.w(TAG, "Embedded view called removeViewImmediate while detached from presentation");
441456
return;
442457
}
458+
View view = (View) args[0];
459+
view.clearAnimation();
443460
fakeWindowRootView.removeView(view);
444461
}
445462

446-
@RequiresApi(api = Build.VERSION_CODES.R)
447-
@NonNull
448-
@Override
449-
public WindowMetrics getCurrentWindowMetrics() {
450-
return delegate.getCurrentWindowMetrics();
451-
}
452-
453-
@RequiresApi(api = Build.VERSION_CODES.R)
454-
@NonNull
455-
@Override
456-
public WindowMetrics getMaximumWindowMetrics() {
457-
return delegate.getMaximumWindowMetrics();
458-
}
459-
460-
@RequiresApi(api = Build.VERSION_CODES.S)
461-
@Override
462-
public boolean isCrossWindowBlurEnabled() {
463-
return delegate.isCrossWindowBlurEnabled();
464-
}
465-
466-
@RequiresApi(api = Build.VERSION_CODES.S)
467-
@Override
468-
public void addCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
469-
delegate.addCrossWindowBlurEnabledListener(listener);
470-
}
471-
472-
@RequiresApi(api = Build.VERSION_CODES.S)
473-
@Override
474-
public void addCrossWindowBlurEnabledListener(
475-
@NonNull Executor executor, @NonNull Consumer<Boolean> listener) {
476-
delegate.addCrossWindowBlurEnabledListener(executor, listener);
477-
}
478-
479-
@RequiresApi(api = Build.VERSION_CODES.S)
480-
@Override
481-
public void removeCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
482-
delegate.removeCrossWindowBlurEnabledListener(listener);
463+
private void updateViewLayout(Object[] args) {
464+
if (fakeWindowRootView == null) {
465+
Log.w(TAG, "Embedded view called updateViewLayout while detached from presentation");
466+
return;
467+
}
468+
View view = (View) args[0];
469+
WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) args[1];
470+
fakeWindowRootView.updateViewLayout(view, layoutParams);
483471
}
484472
}
485473

shell/platform/android/test/io/flutter/plugin/platform/SingleViewPresentationTest.java

Lines changed: 0 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,18 @@
77
import static android.os.Build.VERSION_CODES.KITKAT;
88
import static android.os.Build.VERSION_CODES.P;
99
import static android.os.Build.VERSION_CODES.R;
10-
import static android.os.Build.VERSION_CODES.S;
1110
import static org.junit.Assert.assertEquals;
1211
import static org.mockito.Mockito.mock;
1312
import static org.mockito.Mockito.spy;
14-
import static org.mockito.Mockito.verify;
15-
import static org.mockito.Mockito.verifyNoInteractions;
1613
import static org.mockito.Mockito.when;
1714

1815
import android.annotation.TargetApi;
1916
import android.content.Context;
2017
import android.hardware.display.DisplayManager;
2118
import android.view.Display;
22-
import android.view.View;
23-
import android.view.ViewGroup;
24-
import android.view.WindowManager;
2519
import android.view.inputmethod.InputMethodManager;
2620
import androidx.test.core.app.ApplicationProvider;
2721
import androidx.test.ext.junit.runners.AndroidJUnit4;
28-
import java.util.concurrent.Executor;
29-
import java.util.function.Consumer;
3022
import org.junit.Test;
3123
import org.junit.runner.RunWith;
3224
import org.robolectric.annotation.Config;
@@ -91,112 +83,4 @@ public void returnsOuterContextInputMethodManager_createDisplayContext() {
9183
// Android OS (or Robolectric's shadow, in this case).
9284
assertEquals(expected, actual);
9385
}
94-
95-
@Test
96-
@Config(minSdk = R)
97-
public void windowManagerHandler_passesCorrectlyToFakeWindowViewGroup() {
98-
// Mock the WindowManager and FakeWindowViewGroup that get used by the WindowManagerHandler.
99-
WindowManager mockWindowManager = mock(WindowManager.class);
100-
SingleViewPresentation.FakeWindowViewGroup mockFakeWindowViewGroup =
101-
mock(SingleViewPresentation.FakeWindowViewGroup.class);
102-
103-
View mockView = mock(View.class);
104-
ViewGroup.LayoutParams mockLayoutParams = mock(ViewGroup.LayoutParams.class);
105-
106-
SingleViewPresentation.WindowManagerHandler windowManagerHandler =
107-
new SingleViewPresentation.WindowManagerHandler(mockWindowManager, mockFakeWindowViewGroup);
108-
109-
// removeViewImmediate
110-
windowManagerHandler.removeViewImmediate(mockView);
111-
verify(mockView).clearAnimation();
112-
verify(mockFakeWindowViewGroup).removeView(mockView);
113-
verifyNoInteractions(mockWindowManager);
114-
115-
// addView
116-
windowManagerHandler.addView(mockView, mockLayoutParams);
117-
verify(mockFakeWindowViewGroup).addView(mockView, mockLayoutParams);
118-
verifyNoInteractions(mockWindowManager);
119-
120-
// updateViewLayout
121-
windowManagerHandler.updateViewLayout(mockView, mockLayoutParams);
122-
verify(mockFakeWindowViewGroup).updateViewLayout(mockView, mockLayoutParams);
123-
verifyNoInteractions(mockWindowManager);
124-
125-
// removeView
126-
windowManagerHandler.updateViewLayout(mockView, mockLayoutParams);
127-
verify(mockFakeWindowViewGroup).removeView(mockView);
128-
verifyNoInteractions(mockWindowManager);
129-
}
130-
131-
@Test
132-
@Config(minSdk = R)
133-
public void windowManagerHandler_logAndReturnEarly_whenFakeWindowViewGroupIsNull() {
134-
// Mock the WindowManager and FakeWindowViewGroup that get used by the WindowManagerHandler.
135-
WindowManager mockWindowManager = mock(WindowManager.class);
136-
137-
View mockView = mock(View.class);
138-
ViewGroup.LayoutParams mockLayoutParams = mock(ViewGroup.LayoutParams.class);
139-
140-
SingleViewPresentation.WindowManagerHandler windowManagerHandler =
141-
new SingleViewPresentation.WindowManagerHandler(mockWindowManager, null);
142-
143-
// removeViewImmediate
144-
windowManagerHandler.removeViewImmediate(mockView);
145-
verifyNoInteractions(mockView);
146-
verifyNoInteractions(mockWindowManager);
147-
148-
// addView
149-
windowManagerHandler.addView(mockView, mockLayoutParams);
150-
verifyNoInteractions(mockWindowManager);
151-
152-
// updateViewLayout
153-
windowManagerHandler.updateViewLayout(mockView, mockLayoutParams);
154-
verifyNoInteractions(mockWindowManager);
155-
156-
// removeView
157-
windowManagerHandler.updateViewLayout(mockView, mockLayoutParams);
158-
verifyNoInteractions(mockWindowManager);
159-
}
160-
161-
// This section tests that WindowManagerHandler forwards all of the non-special case calls to the
162-
// delegate WindowManager. Because this must include some deprecated WindowManager method calls
163-
// (because the proxy overrides every method), we suppress deprecation warnings here.
164-
@Test
165-
@Config(minSdk = S)
166-
@SuppressWarnings("deprecation")
167-
public void windowManagerHandler_forwardsAllOtherCallsToDelegate() {
168-
// Mock the WindowManager and FakeWindowViewGroup that get used by the WindowManagerHandler.
169-
WindowManager mockWindowManager = mock(WindowManager.class);
170-
SingleViewPresentation.FakeWindowViewGroup mockFakeWindowViewGroup =
171-
mock(SingleViewPresentation.FakeWindowViewGroup.class);
172-
173-
SingleViewPresentation.WindowManagerHandler windowManagerHandler =
174-
new SingleViewPresentation.WindowManagerHandler(mockWindowManager, mockFakeWindowViewGroup);
175-
176-
// Verify that all other calls get forwarded to the delegate.
177-
Executor mockExecutor = mock(Executor.class);
178-
@SuppressWarnings("Unchecked cast")
179-
Consumer<Boolean> mockListener = (Consumer<Boolean>) mock(Consumer.class);
180-
181-
windowManagerHandler.getDefaultDisplay();
182-
verify(mockWindowManager).getDefaultDisplay();
183-
184-
windowManagerHandler.getCurrentWindowMetrics();
185-
verify(mockWindowManager).getCurrentWindowMetrics();
186-
187-
windowManagerHandler.getMaximumWindowMetrics();
188-
verify(mockWindowManager).getMaximumWindowMetrics();
189-
190-
windowManagerHandler.isCrossWindowBlurEnabled();
191-
verify(mockWindowManager).isCrossWindowBlurEnabled();
192-
193-
windowManagerHandler.addCrossWindowBlurEnabledListener(mockListener);
194-
verify(mockWindowManager).addCrossWindowBlurEnabledListener(mockListener);
195-
196-
windowManagerHandler.addCrossWindowBlurEnabledListener(mockExecutor, mockListener);
197-
verify(mockWindowManager).addCrossWindowBlurEnabledListener(mockExecutor, mockListener);
198-
199-
windowManagerHandler.removeCrossWindowBlurEnabledListener(mockListener);
200-
verify(mockWindowManager).removeCrossWindowBlurEnabledListener(mockListener);
201-
}
20286
}

0 commit comments

Comments
 (0)