2222import android .view .View ;
2323import android .view .ViewGroup ;
2424import android .view .WindowManager ;
25- import android .view .WindowMetrics ;
2625import android .view .accessibility .AccessibilityEvent ;
2726import android .view .inputmethod .InputMethodManager ;
2827import android .widget .FrameLayout ;
2928import androidx .annotation .Keep ;
3029import androidx .annotation .NonNull ;
3130import androidx .annotation .Nullable ;
32- import androidx .annotation .RequiresApi ;
33- import androidx .annotation .VisibleForTesting ;
3431import 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
0 commit comments