@@ -194,13 +194,7 @@ public void onFlutterUiNoLongerDisplayed() {
194194 }
195195 };
196196
197- private final Consumer <WindowLayoutInfo > windowInfoListener =
198- new Consumer <WindowLayoutInfo >() {
199- @ Override
200- public void accept (WindowLayoutInfo layoutInfo ) {
201- setWindowInfoListenerDisplayFeatures (layoutInfo );
202- }
203- };
197+ private Consumer <WindowLayoutInfo > windowInfoListener ;
204198
205199 /**
206200 * Constructs a {@code FlutterView} programmatically, without any XML attributes.
@@ -512,6 +506,10 @@ protected void onAttachedToWindow() {
512506 this .windowInfoRepo = createWindowInfoRepo ();
513507 Activity activity = ViewUtils .getActivity (getContext ());
514508 if (windowInfoRepo != null && activity != null ) {
509+ // Creating windowInfoListener on-demand instead of at initialization is necessary in order to
510+ // prevent it from capturing the wrong instance of `this` when spying for testing.
511+ // See https://github.com/mockito/mockito/issues/3479
512+ windowInfoListener = this ::setWindowInfoListenerDisplayFeatures ;
515513 windowInfoRepo .addWindowLayoutInfoListener (
516514 activity , ContextCompat .getMainExecutor (getContext ()), windowInfoListener );
517515 }
@@ -524,9 +522,10 @@ protected void onAttachedToWindow() {
524522 */
525523 @ Override
526524 protected void onDetachedFromWindow () {
527- if (windowInfoRepo != null ) {
525+ if (windowInfoRepo != null && windowInfoListener != null ) {
528526 windowInfoRepo .removeWindowLayoutInfoListener (windowInfoListener );
529527 }
528+ windowInfoListener = null ;
530529 this .windowInfoRepo = null ;
531530 super .onDetachedFromWindow ();
532531 }
@@ -537,12 +536,12 @@ protected void onDetachedFromWindow() {
537536 */
538537 @ TargetApi (API_LEVELS .API_28 )
539538 protected void setWindowInfoListenerDisplayFeatures (WindowLayoutInfo layoutInfo ) {
540- List <DisplayFeature > displayFeatures = layoutInfo .getDisplayFeatures ();
541- List <FlutterRenderer .DisplayFeature > result = new ArrayList <>();
539+ List <DisplayFeature > newDisplayFeatures = layoutInfo .getDisplayFeatures ();
540+ List <FlutterRenderer .DisplayFeature > flutterDisplayFeatures = new ArrayList <>();
542541
543542 // Data from WindowInfoTracker display features. Fold and hinge areas are
544543 // populated here.
545- for (DisplayFeature displayFeature : displayFeatures ) {
544+ for (DisplayFeature displayFeature : newDisplayFeatures ) {
546545 Log .v (
547546 TAG ,
548547 "WindowInfoTracker Display Feature reported with bounds = "
@@ -565,31 +564,17 @@ protected void setWindowInfoListenerDisplayFeatures(WindowLayoutInfo layoutInfo)
565564 } else {
566565 state = DisplayFeatureState .UNKNOWN ;
567566 }
568- result .add (new FlutterRenderer .DisplayFeature (displayFeature .getBounds (), type , state ));
567+ flutterDisplayFeatures .add (
568+ new FlutterRenderer .DisplayFeature (displayFeature .getBounds (), type , state ));
569569 } else {
570- result .add (
570+ flutterDisplayFeatures .add (
571571 new FlutterRenderer .DisplayFeature (
572572 displayFeature .getBounds (),
573573 DisplayFeatureType .UNKNOWN ,
574574 DisplayFeatureState .UNKNOWN ));
575575 }
576576 }
577-
578- // Data from the DisplayCutout bounds. Cutouts for cameras and other sensors are
579- // populated here. DisplayCutout was introduced in API 28.
580- if (Build .VERSION .SDK_INT >= API_LEVELS .API_28 ) {
581- WindowInsets insets = getRootWindowInsets ();
582- if (insets != null ) {
583- DisplayCutout cutout = insets .getDisplayCutout ();
584- if (cutout != null ) {
585- for (Rect bounds : cutout .getBoundingRects ()) {
586- Log .v (TAG , "DisplayCutout area reported with bounds = " + bounds .toString ());
587- result .add (new FlutterRenderer .DisplayFeature (bounds , DisplayFeatureType .CUTOUT ));
588- }
589- }
590- }
591- }
592- viewportMetrics .displayFeatures = result ;
577+ viewportMetrics .setDisplayFeatures (flutterDisplayFeatures );
593578 sendViewportMetricsToFlutter ();
594579 }
595580
@@ -782,6 +767,22 @@ navigationBarVisible && guessBottomKeyboardInset(insets) == 0
782767 viewportMetrics .viewInsetLeft = 0 ;
783768 }
784769
770+ // Data from the DisplayCutout bounds. Cutouts for cameras and other sensors are
771+ // populated here. DisplayCutout was introduced in API 28.
772+ List <FlutterRenderer .DisplayFeature > displayCutouts = new ArrayList <>();
773+ if (Build .VERSION .SDK_INT >= API_LEVELS .API_28 ) {
774+ DisplayCutout cutout = insets .getDisplayCutout ();
775+ if (cutout != null ) {
776+ for (Rect bounds : cutout .getBoundingRects ()) {
777+ Log .v (TAG , "DisplayCutout area reported with bounds = " + bounds .toString ());
778+ displayCutouts .add (
779+ new FlutterRenderer .DisplayFeature (
780+ bounds , DisplayFeatureType .CUTOUT , DisplayFeatureState .UNKNOWN ));
781+ }
782+ }
783+ }
784+ viewportMetrics .setDisplayCutouts (displayCutouts );
785+
785786 // The caption bar inset is a new addition, and the APIs called to query it utilize a list of
786787 // bounding Rects instead of an Insets object, which is a newer API method, as compared to the
787788 // existing Insets-based method calls above.
0 commit comments