2222import android .view .View ;
2323import android .view .ViewGroup ;
2424import android .view .WindowManager ;
25+ import android .view .WindowMetrics ;
2526import android .view .accessibility .AccessibilityEvent ;
2627import android .view .inputmethod .InputMethodManager ;
2728import android .widget .FrameLayout ;
2829import androidx .annotation .Keep ;
2930import androidx .annotation .NonNull ;
3031import androidx .annotation .Nullable ;
32+ import androidx .annotation .RequiresApi ;
33+ import androidx .annotation .VisibleForTesting ;
3134import io .flutter .Log ;
32- import java .lang .reflect .InvocationHandler ;
33- import java .lang .reflect .InvocationTargetException ;
34- import java .lang .reflect .Method ;
35- import java .lang .reflect .Proxy ;
35+ import java .util .concurrent .Executor ;
36+ import java .util .function .Consumer ;
3637
3738/*
3839 * A presentation used for hosting a single Android view in a virtual display.
@@ -359,7 +360,7 @@ public Object getSystemService(String name) {
359360
360361 private WindowManager getWindowManager () {
361362 if (windowManager == null ) {
362- windowManager = windowManagerHandler . getWindowManager () ;
363+ windowManager = windowManagerHandler ;
363364 }
364365 return windowManager ;
365366 }
@@ -377,21 +378,18 @@ private boolean isCalledFromAlertDialog() {
377378 }
378379
379380 /*
380- * A dynamic proxy handler for a WindowManager with custom overrides.
381+ * A static proxy handler for a WindowManager with custom overrides.
381382 *
382383 * The presentation's window manager delegates all calls to the default window manager.
383384 * WindowManager#addView calls triggered by views that are attached to the virtual display are crashing
384385 * (see: https://github.com/flutter/flutter/issues/20714). This was triggered when selecting text in an embedded
385386 * WebView (as the selection handles are implemented as popup windows).
386387 *
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
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.
393390 */
394- static class WindowManagerHandler implements InvocationHandler {
391+ @ VisibleForTesting
392+ static class WindowManagerHandler implements WindowManager {
395393 private static final String TAG = "PlatformViewsController" ;
396394
397395 private final WindowManager delegate ;
@@ -402,72 +400,86 @@ static class WindowManagerHandler implements InvocationHandler {
402400 fakeWindowRootView = fakeWindowViewGroup ;
403401 }
404402
405- public WindowManager getWindowManager () {
406- return ( WindowManager )
407- Proxy . newProxyInstance (
408- WindowManager . class . getClassLoader (), new Class <?>[] { WindowManager . class }, this );
403+ @ Override
404+ @ Deprecated
405+ public Display getDefaultDisplay () {
406+ return delegate . getDefaultDisplay ( );
409407 }
410408
411409 @ Override
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 ();
410+ public void removeViewImmediate (View view ) {
411+ if (fakeWindowRootView == null ) {
412+ Log .w (TAG , "Embedded view called removeViewImmediate while detached from presentation" );
413+ return ;
431414 }
415+ view .clearAnimation ();
416+ fakeWindowRootView .removeView (view );
432417 }
433418
434- private void addView (Object [] args ) {
419+ @ Override
420+ public void addView (View view , ViewGroup .LayoutParams params ) {
435421 if (fakeWindowRootView == null ) {
436422 Log .w (TAG , "Embedded view called addView while detached from presentation" );
437423 return ;
438424 }
439- View view = (View ) args [0 ];
440- WindowManager .LayoutParams layoutParams = (WindowManager .LayoutParams ) args [1 ];
441- fakeWindowRootView .addView (view , layoutParams );
425+ fakeWindowRootView .addView (view , params );
442426 }
443427
444- private void removeView (Object [] args ) {
428+ @ Override
429+ public void updateViewLayout (View view , ViewGroup .LayoutParams params ) {
445430 if (fakeWindowRootView == null ) {
446- Log .w (TAG , "Embedded view called removeView while detached from presentation" );
431+ Log .w (TAG , "Embedded view called updateViewLayout while detached from presentation" );
447432 return ;
448433 }
449- View view = (View ) args [0 ];
450- fakeWindowRootView .removeView (view );
434+ fakeWindowRootView .updateViewLayout (view , params );
451435 }
452436
453- private void removeViewImmediate (Object [] args ) {
437+ @ Override
438+ public void removeView (View view ) {
454439 if (fakeWindowRootView == null ) {
455- Log .w (TAG , "Embedded view called removeViewImmediate while detached from presentation" );
440+ Log .w (TAG , "Embedded view called removeView while detached from presentation" );
456441 return ;
457442 }
458- View view = (View ) args [0 ];
459- view .clearAnimation ();
460443 fakeWindowRootView .removeView (view );
461444 }
462445
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 );
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 );
471483 }
472484 }
473485
0 commit comments