22
22
import android .view .View ;
23
23
import android .view .ViewGroup ;
24
24
import android .view .WindowManager ;
25
- import android .view .WindowMetrics ;
26
25
import android .view .accessibility .AccessibilityEvent ;
27
26
import android .view .inputmethod .InputMethodManager ;
28
27
import android .widget .FrameLayout ;
29
28
import androidx .annotation .Keep ;
30
29
import androidx .annotation .NonNull ;
31
30
import androidx .annotation .Nullable ;
32
- import androidx .annotation .RequiresApi ;
33
- import androidx .annotation .VisibleForTesting ;
34
31
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 ;
37
36
38
37
/*
39
38
* A presentation used for hosting a single Android view in a virtual display.
@@ -360,7 +359,7 @@ public Object getSystemService(String name) {
360
359
361
360
private WindowManager getWindowManager () {
362
361
if (windowManager == null ) {
363
- windowManager = windowManagerHandler ;
362
+ windowManager = windowManagerHandler . getWindowManager () ;
364
363
}
365
364
return windowManager ;
366
365
}
@@ -378,18 +377,21 @@ private boolean isCalledFromAlertDialog() {
378
377
}
379
378
380
379
/*
381
- * A static proxy handler for a WindowManager with custom overrides.
380
+ * A dynamic proxy handler for a WindowManager with custom overrides.
382
381
*
383
382
* The presentation's window manager delegates all calls to the default window manager.
384
383
* WindowManager#addView calls triggered by views that are attached to the virtual display are crashing
385
384
* (see: https://github.com/flutter/flutter/issues/20714). This was triggered when selecting text in an embedded
386
385
* WebView (as the selection handles are implemented as popup windows).
387
386
*
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
390
393
*/
391
- @ VisibleForTesting
392
- static class WindowManagerHandler implements WindowManager {
394
+ static class WindowManagerHandler implements InvocationHandler {
393
395
private static final String TAG = "PlatformViewsController" ;
394
396
395
397
private final WindowManager delegate ;
@@ -400,86 +402,72 @@ static class WindowManagerHandler implements WindowManager {
400
402
fakeWindowRootView = fakeWindowViewGroup ;
401
403
}
402
404
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 );
407
409
}
408
410
409
411
@ 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 ();
414
431
}
415
- view .clearAnimation ();
416
- fakeWindowRootView .removeView (view );
417
432
}
418
433
419
- @ Override
420
- public void addView (View view , ViewGroup .LayoutParams params ) {
434
+ private void addView (Object [] args ) {
421
435
if (fakeWindowRootView == null ) {
422
436
Log .w (TAG , "Embedded view called addView while detached from presentation" );
423
437
return ;
424
438
}
425
- fakeWindowRootView .addView (view , params );
439
+ View view = (View ) args [0 ];
440
+ WindowManager .LayoutParams layoutParams = (WindowManager .LayoutParams ) args [1 ];
441
+ fakeWindowRootView .addView (view , layoutParams );
426
442
}
427
443
428
- @ Override
429
- public void updateViewLayout (View view , ViewGroup .LayoutParams params ) {
444
+ private void removeView (Object [] args ) {
430
445
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" );
432
447
return ;
433
448
}
434
- fakeWindowRootView .updateViewLayout (view , params );
449
+ View view = (View ) args [0 ];
450
+ fakeWindowRootView .removeView (view );
435
451
}
436
452
437
- @ Override
438
- public void removeView (View view ) {
453
+ private void removeViewImmediate (Object [] args ) {
439
454
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" );
441
456
return ;
442
457
}
458
+ View view = (View ) args [0 ];
459
+ view .clearAnimation ();
443
460
fakeWindowRootView .removeView (view );
444
461
}
445
462
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 );
483
471
}
484
472
}
485
473
0 commit comments