@@ -2707,6 +2707,101 @@ public void ime_windowInsetsSync_notLaidOutBehindNavigation_excludesNavigationBa
27072707 }
27082708 }
27092709
2710+ @ Test
2711+ @ TargetApi (API_LEVELS .API_35 )
2712+ @ Config (minSdk = API_LEVELS .API_35 )
2713+ @ SuppressWarnings ("deprecation" )
2714+ // getWindowSystemUiVisibility, SYSTEM_UI_FLAG_LAYOUT_STABLE.
2715+ // flutter#133074 tracks migration work.
2716+ public void ime_windowInsetsSync_notLaidOutBehindNavigation_post15_includesNavigationBars () {
2717+ try (ActivityScenario <Activity > scenario = ActivityScenario .launch (Activity .class )) {
2718+ scenario .onActivity (
2719+ activity -> {
2720+ FlutterView testView = spy (new FlutterView (activity ));
2721+ when (testView .getWindowSystemUiVisibility ())
2722+ .thenReturn (View .SYSTEM_UI_FLAG_LAYOUT_STABLE );
2723+
2724+ TextInputChannel textInputChannel = new TextInputChannel (mock (DartExecutor .class ));
2725+ ScribeChannel scribeChannel = new ScribeChannel (mock (DartExecutor .class ));
2726+ TextInputPlugin textInputPlugin =
2727+ new TextInputPlugin (
2728+ testView ,
2729+ textInputChannel ,
2730+ scribeChannel ,
2731+ mock (PlatformViewsController .class ),
2732+ mock (PlatformViewsController2 .class ));
2733+ ImeSyncDeferringInsetsCallback imeSyncCallback = textInputPlugin .getImeSyncCallback ();
2734+ FlutterEngine flutterEngine =
2735+ spy (new FlutterEngine (ctx , mockFlutterLoader , mockFlutterJni ));
2736+ FlutterRenderer flutterRenderer = spy (new FlutterRenderer (mockFlutterJni ));
2737+ when (flutterEngine .getRenderer ()).thenReturn (flutterRenderer );
2738+ testView .attachToFlutterEngine (flutterEngine );
2739+
2740+ WindowInsetsAnimation animation = mock (WindowInsetsAnimation .class );
2741+ when (animation .getTypeMask ()).thenReturn (WindowInsets .Type .ime ());
2742+
2743+ List <WindowInsetsAnimation > animationList = new ArrayList ();
2744+ animationList .add (animation );
2745+
2746+ ArgumentCaptor <FlutterRenderer .ViewportMetrics > viewportMetricsCaptor =
2747+ ArgumentCaptor .forClass (FlutterRenderer .ViewportMetrics .class );
2748+
2749+ WindowInsets .Builder builder = new WindowInsets .Builder ();
2750+
2751+ // Set the initial insets and verify that they were set and the bottom view inset is
2752+ // correct
2753+ imeSyncCallback .getInsetsListener ().onApplyWindowInsets (testView , builder .build ());
2754+
2755+ verify (flutterRenderer , atLeast (1 )).setViewportMetrics (viewportMetricsCaptor .capture ());
2756+ assertEquals (0 , viewportMetricsCaptor .getValue ().viewInsetBottom );
2757+
2758+ // Call onPrepare and set the lastWindowInsets - these should be stored for the end of
2759+ // the
2760+ // animation instead of being applied immediately
2761+ imeSyncCallback .getAnimationCallback ().onPrepare (animation );
2762+ builder .setInsets (WindowInsets .Type .ime (), Insets .of (0 , 0 , 0 , 100 ));
2763+ builder .setInsets (WindowInsets .Type .navigationBars (), Insets .of (0 , 0 , 0 , 0 ));
2764+ imeSyncCallback .getInsetsListener ().onApplyWindowInsets (testView , builder .build ());
2765+
2766+ verify (flutterRenderer , atLeast (1 )).setViewportMetrics (viewportMetricsCaptor .capture ());
2767+ assertEquals (0 , viewportMetricsCaptor .getValue ().viewInsetBottom );
2768+
2769+ // Call onStart and apply new insets - these should be ignored completely
2770+ imeSyncCallback .getAnimationCallback ().onStart (animation , null );
2771+ builder .setInsets (WindowInsets .Type .ime (), Insets .of (0 , 0 , 0 , 50 ));
2772+ builder .setInsets (WindowInsets .Type .navigationBars (), Insets .of (0 , 0 , 0 , 40 ));
2773+ imeSyncCallback .getInsetsListener ().onApplyWindowInsets (testView , builder .build ());
2774+
2775+ verify (flutterRenderer , atLeast (1 )).setViewportMetrics (viewportMetricsCaptor .capture ());
2776+ assertEquals (0 , viewportMetricsCaptor .getValue ().viewInsetBottom );
2777+
2778+ // Progress the animation and ensure that the navigation bar insets have not been
2779+ // subtracted from the IME insets
2780+ builder .setInsets (WindowInsets .Type .ime (), Insets .of (0 , 0 , 0 , 25 ));
2781+ builder .setInsets (WindowInsets .Type .navigationBars (), Insets .of (0 , 0 , 0 , 40 ));
2782+ imeSyncCallback .getAnimationCallback ().onProgress (builder .build (), animationList );
2783+
2784+ verify (flutterRenderer , atLeast (1 )).setViewportMetrics (viewportMetricsCaptor .capture ());
2785+ assertEquals (25 , viewportMetricsCaptor .getValue ().viewInsetBottom );
2786+
2787+ builder .setInsets (WindowInsets .Type .ime (), Insets .of (0 , 0 , 0 , 50 ));
2788+ builder .setInsets (WindowInsets .Type .navigationBars (), Insets .of (0 , 0 , 0 , 40 ));
2789+ imeSyncCallback .getAnimationCallback ().onProgress (builder .build (), animationList );
2790+
2791+ verify (flutterRenderer , atLeast (1 )).setViewportMetrics (viewportMetricsCaptor .capture ());
2792+ assertEquals (50 , viewportMetricsCaptor .getValue ().viewInsetBottom );
2793+
2794+ // End the animation and ensure that the bottom insets match the lastWindowInsets that
2795+ // we set
2796+ // during onPrepare
2797+ imeSyncCallback .getAnimationCallback ().onEnd (animation );
2798+
2799+ verify (flutterRenderer , atLeast (1 )).setViewportMetrics (viewportMetricsCaptor .capture ());
2800+ assertEquals (100 , viewportMetricsCaptor .getValue ().viewInsetBottom );
2801+ });
2802+ }
2803+ }
2804+
27102805 @ Test
27112806 @ TargetApi (API_LEVELS .API_30 )
27122807 @ Config (sdk = API_LEVELS .API_30 )
0 commit comments