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

Commit e922da6

Browse files
Reverts "Remove WindowManager reflection in SingleViewPresentation.java (#49996)" (#50873)
Reverts #49996 Initiated by: gmackall Reason for reverting: b/326363243 Original PR Author: gmackall Reviewed By: {johnmccutchan, reidbaker} This change reverts the following previous change: Original Description: Fixes flutter/flutter#106449. Changes it to a static proxy, as the comment recommended. This does mean we will have to update it to override new methods as they are added to the interface when updating the version of the Android sdk we use. [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
1 parent 05ffcb1 commit e922da6

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)