Allow the root layout to be overriden. (#22887)

a
diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java
index f5ef171..16763a2 100644
--- a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java
+++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java
@@ -358,7 +358,7 @@
    */
   @NonNull
   private View createFragmentContainer() {
-    FrameLayout container = new FrameLayout(this);
+    FrameLayout container = provideRootLayout(this);
     container.setId(FRAGMENT_CONTAINER_ID);
     container.setLayoutParams(
         new ViewGroup.LayoutParams(
@@ -741,4 +741,10 @@
   private boolean isDebuggable() {
     return (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
   }
+
+  /** Returns a {@link FrameLayout} that is used as the content view of this activity. */
+  @NonNull
+  protected FrameLayout provideRootLayout(Context context) {
+    return new FrameLayout(context);
+  }
 }
diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentActivityTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentActivityTest.java
index 448646d..dc4b601 100644
--- a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentActivityTest.java
+++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentActivityTest.java
@@ -12,6 +12,8 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import io.flutter.embedding.android.FlutterActivityLaunchConfigs.BackgroundMode;
@@ -127,6 +129,24 @@
     assertFalse(spyFlutterActivity.shouldHandleDeeplinking());
   }
 
+  @Test
+  public void itAllowsRootLayoutOverride() {
+    FlutterFragmentActivityWithRootLayout activity =
+        Robolectric.buildActivity(FlutterFragmentActivityWithRootLayout.class).get();
+
+    activity.onCreate(null);
+    ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content);
+    boolean foundCustomView = false;
+    for (int i = 0; i < contentView.getChildCount(); i++) {
+      foundCustomView =
+          contentView.getChildAt(i) instanceof FlutterFragmentActivityWithRootLayout.CustomLayout;
+      if (foundCustomView) {
+        break;
+      }
+    }
+    assertTrue(foundCustomView);
+  }
+
   static class FlutterFragmentActivityWithProvidedEngine extends FlutterFragmentActivity {
     @Override
     protected FlutterFragment createFlutterFragment() {
@@ -171,6 +191,20 @@
     }
   }
 
+  private static class FlutterFragmentActivityWithRootLayout
+      extends FlutterFragmentActivityWithProvidedEngine {
+    public static class CustomLayout extends FrameLayout {
+      public CustomLayout(Context context) {
+        super(context);
+      }
+    }
+
+    @Override
+    protected FrameLayout provideRootLayout(Context context) {
+      return new CustomLayout(context);
+    }
+  }
+
   // This is just a compile time check to ensure that it's possible for FlutterFragmentActivity
   // subclasses
   // to provide their own intent builders which builds their own runtime types.