diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index c02d6ef79e5e3..f8cba6e7c66a6 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -655,8 +655,10 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/Flutt FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterSurfaceView.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterTextureView.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/FlutterView.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/RenderMode.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/SplashScreen.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/SplashScreenProvider.java +FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/android/TransparencyMode.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngineCache.java FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEnginePluginRegistry.java diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index 92130115f13e0..0564b4f0cdab3 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -138,8 +138,10 @@ android_java_sources = [ "io/flutter/embedding/android/FlutterSurfaceView.java", "io/flutter/embedding/android/FlutterTextureView.java", "io/flutter/embedding/android/FlutterView.java", + "io/flutter/embedding/android/RenderMode.java", "io/flutter/embedding/android/SplashScreen.java", "io/flutter/embedding/android/SplashScreenProvider.java", + "io/flutter/embedding/android/TransparencyMode.java", "io/flutter/embedding/engine/FlutterEngine.java", "io/flutter/embedding/engine/FlutterEngineCache.java", "io/flutter/embedding/engine/FlutterEnginePluginRegistry.java", diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index 31105bea8139f..fa74bc53fd7d5 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -782,28 +782,26 @@ private boolean isDebuggable() { /** * {@link FlutterActivityAndFragmentDelegate.Host} method that is used by {@link - * FlutterActivityAndFragmentDelegate} to obtain the desired {@link FlutterView.RenderMode} that - * should be used when instantiating a {@link FlutterView}. + * FlutterActivityAndFragmentDelegate} to obtain the desired {@link RenderMode} that should be + * used when instantiating a {@link FlutterView}. */ @NonNull @Override - public FlutterView.RenderMode getRenderMode() { - return getBackgroundMode() == BackgroundMode.opaque - ? FlutterView.RenderMode.surface - : FlutterView.RenderMode.texture; + public RenderMode getRenderMode() { + return getBackgroundMode() == BackgroundMode.opaque ? RenderMode.surface : RenderMode.texture; } /** * {@link FlutterActivityAndFragmentDelegate.Host} method that is used by {@link - * FlutterActivityAndFragmentDelegate} to obtain the desired {@link FlutterView.TransparencyMode} - * that should be used when instantiating a {@link FlutterView}. + * FlutterActivityAndFragmentDelegate} to obtain the desired {@link TransparencyMode} that should + * be used when instantiating a {@link FlutterView}. */ @NonNull @Override - public FlutterView.TransparencyMode getTransparencyMode() { + public TransparencyMode getTransparencyMode() { return getBackgroundMode() == BackgroundMode.opaque - ? FlutterView.TransparencyMode.opaque - : FlutterView.TransparencyMode.transparent; + ? TransparencyMode.opaque + : TransparencyMode.transparent; } /** @@ -917,6 +915,16 @@ public boolean shouldAttachEngineToActivity() { return true; } + @Override + public void onFlutterSurfaceViewCreated(@NonNull FlutterSurfaceView flutterSurfaceView) { + // Hook for subclasses. + } + + @Override + public void onFlutterTextureViewCreated(@NonNull FlutterTextureView flutterTextureView) { + // Hook for subclasses. + } + @Override public void onFlutterUiDisplayed() { // Notifies Android that we're fully drawn so that performance metrics can be collected by diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java index 24689fc40ace1..0ca628ba37866 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java @@ -251,8 +251,28 @@ View onCreateView( LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { Log.v(TAG, "Creating FlutterView."); ensureAlive(); - flutterView = - new FlutterView(host.getActivity(), host.getRenderMode(), host.getTransparencyMode()); + + if (host.getRenderMode() == RenderMode.surface) { + FlutterSurfaceView flutterSurfaceView = + new FlutterSurfaceView( + host.getActivity(), host.getTransparencyMode() == TransparencyMode.transparent); + + // Allow our host to customize FlutterSurfaceView, if desired. + host.onFlutterSurfaceViewCreated(flutterSurfaceView); + + // Create the FlutterView that owns the FlutterSurfaceView. + flutterView = new FlutterView(host.getActivity(), flutterSurfaceView); + } else { + FlutterTextureView flutterTextureView = new FlutterTextureView(host.getActivity()); + + // Allow our host to customize FlutterSurfaceView, if desired. + host.onFlutterTextureViewCreated(flutterTextureView); + + // Create the FlutterView that owns the FlutterTextureView. + flutterView = new FlutterView(host.getActivity(), flutterTextureView); + } + + // Add listener to be notified when Flutter renders its first frame. flutterView.addOnFirstFrameRenderedListener(flutterUiDisplayListener); flutterSplashView = new FlutterSplashView(host.getContext()); @@ -702,18 +722,18 @@ private void ensureAlive() { String getInitialRoute(); /** - * Returns the {@link FlutterView.RenderMode} used by the {@link FlutterView} that displays the - * {@link FlutterEngine}'s content. + * Returns the {@link RenderMode} used by the {@link FlutterView} that displays the {@link + * FlutterEngine}'s content. */ @NonNull - FlutterView.RenderMode getRenderMode(); + RenderMode getRenderMode(); /** - * Returns the {@link FlutterView.TransparencyMode} used by the {@link FlutterView} that - * displays the {@link FlutterEngine}'s content. + * Returns the {@link TransparencyMode} used by the {@link FlutterView} that displays the {@link + * FlutterEngine}'s content. */ @NonNull - FlutterView.TransparencyMode getTransparencyMode(); + TransparencyMode getTransparencyMode(); @Nullable SplashScreen provideSplashScreen(); @@ -749,6 +769,36 @@ PlatformPlugin providePlatformPlugin( */ boolean shouldAttachEngineToActivity(); + /** + * Invoked by this delegate when the {@link FlutterSurfaceView} that renders the Flutter UI is + * initially instantiated. + * + *
This method is only invoked if the {@link + * io.flutter.embedding.android.FlutterView.RenderMode} is set to {@link + * io.flutter.embedding.android.FlutterView.RenderMode#surface}. Otherwise, {@link + * #onFlutterTextureViewCreated(FlutterTextureView)} is invoked. + * + *
This method is invoked before the given {@link FlutterSurfaceView} is attached to the + * {@code View} hierarchy. Implementers should not attempt to climb the {@code View} hierarchy + * or make assumptions about relationships with other {@code View}s. + */ + void onFlutterSurfaceViewCreated(@NonNull FlutterSurfaceView flutterSurfaceView); + + /** + * Invoked by this delegate when the {@link FlutterTextureView} that renders the Flutter UI is + * initially instantiated. + * + *
This method is only invoked if the {@link + * io.flutter.embedding.android.FlutterView.RenderMode} is set to {@link + * io.flutter.embedding.android.FlutterView.RenderMode#texture}. Otherwise, {@link + * #onFlutterSurfaceViewCreated(FlutterSurfaceView)} is invoked. + * + *
This method is invoked before the given {@link FlutterTextureView} is attached to the + * {@code View} hierarchy. Implementers should not attempt to climb the {@code View} hierarchy + * or make assumptions about relationships with other {@code View}s. + */ + void onFlutterTextureViewCreated(@NonNull FlutterTextureView flutterTextureView); + /** Invoked by this delegate when its {@link FlutterView} starts painting pixels. */ void onFlutterUiDisplayed(); diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java index 70bf2b5b76a17..ff18172ae131a 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java @@ -68,9 +68,11 @@ *
The following illustrates how to pre-warm and cache a {@link FlutterEngine}: * *
{@code - * // Create and pre-warm a FlutterEngine. FlutterEngine flutterEngine = new - * FlutterEngine(context); - * flutterEngine.getDartExecutor().executeDartEntrypoint(DartEntrypoint.createDefault()); + * // Create and pre-warm a FlutterEngine. + * FlutterEngine flutterEngine = new FlutterEngine(context); + * flutterEngine + * .getDartExecutor() + * .executeDartEntrypoint(DartEntrypoint.createDefault()); * * // Cache the pre-warmed FlutterEngine in the FlutterEngineCache. * FlutterEngineCache.getInstance().put("my_engine", flutterEngine); @@ -91,14 +93,10 @@ public class FlutterFragment extends Fragment implements FlutterActivityAndFragm protected static final String ARG_APP_BUNDLE_PATH = "app_bundle_path"; /** Flutter shell arguments. */ protected static final String ARG_FLUTTER_INITIALIZATION_ARGS = "initialization_args"; - /** - * {@link FlutterView.RenderMode} to be used for the {@link FlutterView} in this {@code - * FlutterFragment} - */ + /** {@link RenderMode} to be used for the {@link FlutterView} in this {@code FlutterFragment} */ protected static final String ARG_FLUTTERVIEW_RENDER_MODE = "flutterview_render_mode"; /** - * {@link FlutterView.TransparencyMode} to be used for the {@link FlutterView} in this {@code - * FlutterFragment} + * {@link TransparencyMode} to be used for the {@link FlutterView} in this {@code FlutterFragment} */ protected static final String ARG_FLUTTERVIEW_TRANSPARENCY_MODE = "flutterview_transparency_mode"; /** See {@link #shouldAttachEngineToActivity()}. */ @@ -185,9 +183,8 @@ public static class NewEngineFragmentBuilder { private String initialRoute = "/"; private String appBundlePath = null; private FlutterShellArgs shellArgs = null; - private FlutterView.RenderMode renderMode = FlutterView.RenderMode.surface; - private FlutterView.TransparencyMode transparencyMode = - FlutterView.TransparencyMode.transparent; + private RenderMode renderMode = RenderMode.surface; + private TransparencyMode transparencyMode = TransparencyMode.transparent; private boolean shouldAttachEngineToActivity = true; /** @@ -241,27 +238,25 @@ public NewEngineFragmentBuilder flutterShellArgs(@NonNull FlutterShellArgs shell } /** - * Render Flutter either as a {@link FlutterView.RenderMode#surface} or a {@link - * FlutterView.RenderMode#texture}. You should use {@code surface} unless you have a specific - * reason to use {@code texture}. {@code texture} comes with a significant performance impact, - * but {@code texture} can be displayed beneath other Android {@code View}s and animated, - * whereas {@code surface} cannot. + * Render Flutter either as a {@link RenderMode#surface} or a {@link RenderMode#texture}. You + * should use {@code surface} unless you have a specific reason to use {@code texture}. {@code + * texture} comes with a significant performance impact, but {@code texture} can be displayed + * beneath other Android {@code View}s and animated, whereas {@code surface} cannot. */ @NonNull - public NewEngineFragmentBuilder renderMode(@NonNull FlutterView.RenderMode renderMode) { + public NewEngineFragmentBuilder renderMode(@NonNull RenderMode renderMode) { this.renderMode = renderMode; return this; } /** - * Support a {@link FlutterView.TransparencyMode#transparent} background within {@link - * FlutterView}, or force an {@link FlutterView.TransparencyMode#opaque} background. + * Support a {@link TransparencyMode#transparent} background within {@link FlutterView}, or + * force an {@link TransparencyMode#opaque} background. * - *See {@link FlutterView.TransparencyMode} for implications of this selection. + *
See {@link TransparencyMode} for implications of this selection. */ @NonNull - public NewEngineFragmentBuilder transparencyMode( - @NonNull FlutterView.TransparencyMode transparencyMode) { + public NewEngineFragmentBuilder transparencyMode(@NonNull TransparencyMode transparencyMode) { this.transparencyMode = transparencyMode; return this; } @@ -326,12 +321,10 @@ protected Bundle createArgs() { } args.putString( ARG_FLUTTERVIEW_RENDER_MODE, - renderMode != null ? renderMode.name() : FlutterView.RenderMode.surface.name()); + renderMode != null ? renderMode.name() : RenderMode.surface.name()); args.putString( ARG_FLUTTERVIEW_TRANSPARENCY_MODE, - transparencyMode != null - ? transparencyMode.name() - : FlutterView.TransparencyMode.transparent.name()); + transparencyMode != null ? transparencyMode.name() : TransparencyMode.transparent.name()); args.putBoolean(ARG_SHOULD_ATTACH_ENGINE_TO_ACTIVITY, shouldAttachEngineToActivity); args.putBoolean(ARG_DESTROY_ENGINE_WITH_FRAGMENT, true); return args; @@ -412,9 +405,8 @@ public static class CachedEngineFragmentBuilder { private final Class extends FlutterFragment> fragmentClass; private final String engineId; private boolean destroyEngineWithFragment = false; - private FlutterView.RenderMode renderMode = FlutterView.RenderMode.surface; - private FlutterView.TransparencyMode transparencyMode = - FlutterView.TransparencyMode.transparent; + private RenderMode renderMode = RenderMode.surface; + private TransparencyMode transparencyMode = TransparencyMode.transparent; private boolean shouldAttachEngineToActivity = true; private CachedEngineFragmentBuilder(@NonNull String engineId) { @@ -440,27 +432,26 @@ public CachedEngineFragmentBuilder destroyEngineWithFragment( } /** - * Render Flutter either as a {@link FlutterView.RenderMode#surface} or a {@link - * FlutterView.RenderMode#texture}. You should use {@code surface} unless you have a specific - * reason to use {@code texture}. {@code texture} comes with a significant performance impact, - * but {@code texture} can be displayed beneath other Android {@code View}s and animated, - * whereas {@code surface} cannot. + * Render Flutter either as a {@link RenderMode#surface} or a {@link RenderMode#texture}. You + * should use {@code surface} unless you have a specific reason to use {@code texture}. {@code + * texture} comes with a significant performance impact, but {@code texture} can be displayed + * beneath other Android {@code View}s and animated, whereas {@code surface} cannot. */ @NonNull - public CachedEngineFragmentBuilder renderMode(@NonNull FlutterView.RenderMode renderMode) { + public CachedEngineFragmentBuilder renderMode(@NonNull RenderMode renderMode) { this.renderMode = renderMode; return this; } /** - * Support a {@link FlutterView.TransparencyMode#transparent} background within {@link - * FlutterView}, or force an {@link FlutterView.TransparencyMode#opaque} background. + * Support a {@link TransparencyMode#transparent} background within {@link FlutterView}, or + * force an {@link TransparencyMode#opaque} background. * - *
See {@link FlutterView.TransparencyMode} for implications of this selection. + *
See {@link TransparencyMode} for implications of this selection. */ @NonNull public CachedEngineFragmentBuilder transparencyMode( - @NonNull FlutterView.TransparencyMode transparencyMode) { + @NonNull TransparencyMode transparencyMode) { this.transparencyMode = transparencyMode; return this; } @@ -519,12 +510,10 @@ protected Bundle createArgs() { args.putBoolean(ARG_DESTROY_ENGINE_WITH_FRAGMENT, destroyEngineWithFragment); args.putString( ARG_FLUTTERVIEW_RENDER_MODE, - renderMode != null ? renderMode.name() : FlutterView.RenderMode.surface.name()); + renderMode != null ? renderMode.name() : RenderMode.surface.name()); args.putString( ARG_FLUTTERVIEW_TRANSPARENCY_MODE, - transparencyMode != null - ? transparencyMode.name() - : FlutterView.TransparencyMode.transparent.name()); + transparencyMode != null ? transparencyMode.name() : TransparencyMode.transparent.name()); args.putBoolean(ARG_SHOULD_ATTACH_ENGINE_TO_ACTIVITY, shouldAttachEngineToActivity); return args; } @@ -832,38 +821,36 @@ public String getInitialRoute() { } /** - * Returns the desired {@link FlutterView.RenderMode} for the {@link FlutterView} displayed in - * this {@code FlutterFragment}. + * Returns the desired {@link RenderMode} for the {@link FlutterView} displayed in this {@code + * FlutterFragment}. * - *
Defaults to {@link FlutterView.RenderMode#surface}. + *
Defaults to {@link RenderMode#surface}. * *
Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host} */ @Override @NonNull - public FlutterView.RenderMode getRenderMode() { + public RenderMode getRenderMode() { String renderModeName = - getArguments() - .getString(ARG_FLUTTERVIEW_RENDER_MODE, FlutterView.RenderMode.surface.name()); - return FlutterView.RenderMode.valueOf(renderModeName); + getArguments().getString(ARG_FLUTTERVIEW_RENDER_MODE, RenderMode.surface.name()); + return RenderMode.valueOf(renderModeName); } /** - * Returns the desired {@link FlutterView.TransparencyMode} for the {@link FlutterView} displayed - * in this {@code FlutterFragment}. + * Returns the desired {@link TransparencyMode} for the {@link FlutterView} displayed in this + * {@code FlutterFragment}. * - *
Defaults to {@link FlutterView.TransparencyMode#transparent}. + *
Defaults to {@link TransparencyMode#transparent}. * *
Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host} */ @Override @NonNull - public FlutterView.TransparencyMode getTransparencyMode() { + public TransparencyMode getTransparencyMode() { String transparencyModeName = getArguments() - .getString( - ARG_FLUTTERVIEW_TRANSPARENCY_MODE, FlutterView.TransparencyMode.transparent.name()); - return FlutterView.TransparencyMode.valueOf(transparencyModeName); + .getString(ARG_FLUTTERVIEW_TRANSPARENCY_MODE, TransparencyMode.transparent.name()); + return TransparencyMode.valueOf(transparencyModeName); } @Override @@ -983,6 +970,16 @@ public boolean shouldAttachEngineToActivity() { return getArguments().getBoolean(ARG_SHOULD_ATTACH_ENGINE_TO_ACTIVITY); } + @Override + public void onFlutterSurfaceViewCreated(@NonNull FlutterSurfaceView flutterSurfaceView) { + // Hook for subclasses. + } + + @Override + public void onFlutterTextureViewCreated(@NonNull FlutterTextureView flutterTextureView) { + // Hook for subclasses. + } + /** * Invoked after the {@link FlutterView} within this {@code FlutterFragment} starts rendering * pixels to the screen. diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java index f01f99891073b..5860d6f8c986d 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java @@ -399,14 +399,12 @@ private void ensureFlutterFragmentCreated() { @NonNull protected FlutterFragment createFlutterFragment() { BackgroundMode backgroundMode = getBackgroundMode(); - FlutterView.RenderMode renderMode = + RenderMode renderMode = + backgroundMode == BackgroundMode.opaque ? RenderMode.surface : RenderMode.texture; + TransparencyMode transparencyMode = backgroundMode == BackgroundMode.opaque - ? FlutterView.RenderMode.surface - : FlutterView.RenderMode.texture; - FlutterView.TransparencyMode transparencyMode = - backgroundMode == BackgroundMode.opaque - ? FlutterView.TransparencyMode.opaque - : FlutterView.TransparencyMode.transparent; + ? TransparencyMode.opaque + : TransparencyMode.transparent; if (getCachedEngineId() != null) { Log.v( diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterView.java b/shell/platform/android/io/flutter/embedding/android/FlutterView.java index df382ce254b96..0e1ceb602c4e4 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterView.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterView.java @@ -47,21 +47,23 @@ * *
A {@code FlutterView}'s UI is painted by a corresponding {@link FlutterEngine}. * - *
A {@code FlutterView} can operate in 2 different {@link RenderMode}s: + *
A {@code FlutterView} can operate in 2 different {@link + * io.flutter.embedding.android.RenderMode}s: * *
- *
* * See https://source.android.com/devices/graphics/arch-tv#surface_or_texture for more @@ -70,11 +72,9 @@ public class FlutterView extends FrameLayout { private static final String TAG = "FlutterView"; - // Behavior configuration of this FlutterView. - @NonNull private RenderMode renderMode; - @Nullable private TransparencyMode transparencyMode; - // Internal view hierarchy references. + @Nullable private FlutterSurfaceView flutterSurfaceView; + @Nullable private FlutterTextureView flutterTextureView; @Nullable private RenderSurface renderSurface; private final Set- {@link RenderMode#surface}, which paints a Flutter UI to a {@link - * android.view.SurfaceView}. This mode has the best performance, but a {@code FlutterView} in - * this mode cannot be positioned between 2 other Android {@code View}s in the z-index, nor - * can it be animated/transformed. Unless the special capabilities of a {@link + *
- {@link io.flutter.embedding.android.RenderMode#surface}, which paints a Flutter UI to a + * {@link android.view.SurfaceView}. This mode has the best performance, but a {@code + * FlutterView} in this mode cannot be positioned between 2 other Android {@code View}s in the + * z-index, nor can it be animated/transformed. Unless the special capabilities of a {@link * android.graphics.SurfaceTexture} are required, developers should strongly prefer this * render mode. - *
- {@link RenderMode#texture}, which paints a Flutter UI to a {@link - * android.graphics.SurfaceTexture}. This mode is not as performant as {@link - * RenderMode#surface}, but a {@code FlutterView} in this mode can be animated and - * transformed, as well as positioned in the z-index between 2+ other Android {@code Views}. - * Unless the special capabilities of a {@link android.graphics.SurfaceTexture} are required, - * developers should strongly prefer the {@link RenderMode#surface} render mode. + *
- {@link io.flutter.embedding.android.RenderMode#texture}, which paints a Flutter UI to a + * {@link android.graphics.SurfaceTexture}. This mode is not as performant as {@link + * io.flutter.embedding.android.RenderMode#surface}, but a {@code FlutterView} in this mode + * can be animated and transformed, as well as positioned in the z-index between 2+ other + * Android {@code Views}. Unless the special capabilities of a {@link + * android.graphics.SurfaceTexture} are required, developers should strongly prefer the {@link + * io.flutter.embedding.android.RenderMode#surface} render mode. *
flutterUiDisplayListeners = new HashSet<>(); private boolean isFlutterUiDisplayed; @@ -136,75 +136,135 @@ public void onFlutterUiNoLongerDisplayed() { * * *
- *
* * {@code FlutterView} requires an {@code Activity} instead of a generic {@code Context} to be * compatible with {@link PlatformViewsController}. */ public FlutterView(@NonNull Context context) { - this(context, null, null, null); + this(context, null, new FlutterSurfaceView(context)); + } + + /** + * Deprecated - use {@link #FlutterView(Context, FlutterSurfaceView)} or {@link + * #FlutterView(Context, FlutterTextureView)} instead. + */ + @Deprecated + public FlutterView(@NonNull Context context, @NonNull RenderMode renderMode) { + super(context, null); + + if (renderMode == RenderMode.surface) { + flutterSurfaceView = new FlutterSurfaceView(context); + renderSurface = flutterSurfaceView; + } else { + flutterTextureView = new FlutterTextureView(context); + renderSurface = flutterTextureView; + } + + init(); } /** - * Constructs a {@code FlutterView} programmatically, without any XML attributes, and allows - * selection of a {@link #renderMode}. + * Deprecated - use {@link #FlutterView(Context, FlutterSurfaceView)} or {@link + * #FlutterView(Context, FlutterTextureView)} instead, and configure the incoming {@code + * FlutterSurfaceView} or {@code FlutterTextureView} for transparency as desired. * - *- {@link #renderMode} defaults to {@link RenderMode#surface}. - *
- {@link #transparencyMode} defaults to {@link TransparencyMode#opaque}. + *
- A {@link FlutterSurfaceView} is used to render the Flutter UI. + *
- {@code transparencyMode} defaults to {@link TransparencyMode#opaque}. *
{@link #transparencyMode} defaults to {@link TransparencyMode#opaque}. + *
Constructs a {@code FlutterView} programmatically, without any XML attributes, uses a {@link + * FlutterSurfaceView} to render the Flutter UI, and allows selection of a {@code + * transparencyMode}. * *
{@code FlutterView} requires an {@code Activity} instead of a generic {@code Context} to be * compatible with {@link PlatformViewsController}. */ - public FlutterView(@NonNull Context context, @NonNull RenderMode renderMode) { - this(context, null, renderMode, null); + @Deprecated + public FlutterView(@NonNull Context context, @NonNull TransparencyMode transparencyMode) { + this( + context, + null, + new FlutterSurfaceView(context, transparencyMode == TransparencyMode.transparent)); } /** - * Constructs a {@code FlutterView} programmatically, without any XML attributes, assumes the use - * of {@link RenderMode#surface}, and allows selection of a {@link #transparencyMode}. + * Constructs a {@code FlutterView} programmatically, without any XML attributes, uses the given + * {@link FlutterSurfaceView} to render the Flutter UI, and allows selection of a {@code + * transparencyMode}. * *
{@code FlutterView} requires an {@code Activity} instead of a generic {@code Context} to be * compatible with {@link PlatformViewsController}. */ - public FlutterView(@NonNull Context context, @NonNull TransparencyMode transparencyMode) { - this(context, null, RenderMode.surface, transparencyMode); + public FlutterView(@NonNull Context context, @NonNull FlutterSurfaceView flutterSurfaceView) { + this(context, null, flutterSurfaceView); } /** - * Constructs a {@code FlutterView} programmatically, without any XML attributes, and allows a - * selection of {@link #renderMode} and {@link #transparencyMode}. + * Constructs a {@code FlutterView} programmatically, without any XML attributes, uses the given + * {@link FlutterTextureView} to render the Flutter UI, and allows selection of a {@code + * transparencyMode}. * *
{@code FlutterView} requires an {@code Activity} instead of a generic {@code Context} to be * compatible with {@link PlatformViewsController}. */ - public FlutterView( - @NonNull Context context, - @NonNull RenderMode renderMode, - @NonNull TransparencyMode transparencyMode) { - this(context, null, renderMode, transparencyMode); + public FlutterView(@NonNull Context context, @NonNull FlutterTextureView flutterTextureView) { + this(context, null, flutterTextureView); } /** - * Constructs a {@code FlutterSurfaceView} in an XML-inflation-compliant manner. + * Constructs a {@code FlutterView} in an XML-inflation-compliant manner. * *
{@code FlutterView} requires an {@code Activity} instead of a generic {@code Context} to be * compatible with {@link PlatformViewsController}. */ // TODO(mattcarroll): expose renderMode in XML when build system supports R.attr public FlutterView(@NonNull Context context, @Nullable AttributeSet attrs) { - this(context, attrs, null, null); + this(context, attrs, new FlutterSurfaceView(context)); + } + + /** + * Deprecated - use {@link #FlutterView(Context, FlutterSurfaceView)} or {@link + * #FlutterView(Context, FlutterTextureView)} instead, and configure the incoming {@code + * FlutterSurfaceView} or {@code FlutterTextureView} for transparency as desired. + */ + @Deprecated + public FlutterView( + @NonNull Context context, + @NonNull RenderMode renderMode, + @NonNull TransparencyMode transparencyMode) { + super(context, null); + + if (renderMode == RenderMode.surface) { + flutterSurfaceView = + new FlutterSurfaceView(context, transparencyMode == TransparencyMode.transparent); + renderSurface = flutterSurfaceView; + } else { + flutterTextureView = new FlutterTextureView(context); + renderSurface = flutterTextureView; + } + + init(); + } + + private FlutterView( + @NonNull Context context, + @Nullable AttributeSet attrs, + @NonNull FlutterSurfaceView flutterSurfaceView) { + super(context, attrs); + + this.flutterSurfaceView = flutterSurfaceView; + this.renderSurface = flutterSurfaceView; + + init(); } private FlutterView( @NonNull Context context, @Nullable AttributeSet attrs, - @Nullable RenderMode renderMode, - @Nullable TransparencyMode transparencyMode) { + @NonNull FlutterTextureView flutterTextureView) { super(context, attrs); - this.renderMode = renderMode == null ? RenderMode.surface : renderMode; - this.transparencyMode = transparencyMode != null ? transparencyMode : TransparencyMode.opaque; + this.flutterTextureView = flutterTextureView; + this.renderSurface = flutterSurfaceView; init(); } @@ -212,20 +272,12 @@ private FlutterView( private void init() { Log.v(TAG, "Initializing FlutterView"); - switch (renderMode) { - case surface: - Log.v(TAG, "Internally using a FlutterSurfaceView."); - FlutterSurfaceView flutterSurfaceView = - new FlutterSurfaceView(getContext(), transparencyMode == TransparencyMode.transparent); - renderSurface = flutterSurfaceView; - addView(flutterSurfaceView); - break; - case texture: - Log.v(TAG, "Internally using a FlutterTextureView."); - FlutterTextureView flutterTextureView = new FlutterTextureView(getContext()); - renderSurface = flutterTextureView; - addView(flutterTextureView); - break; + if (flutterSurfaceView != null) { + Log.v(TAG, "Internally using a FlutterSurfaceView."); + addView(flutterSurfaceView); + } else { + Log.v(TAG, "Internally using a FlutterTextureView."); + addView(flutterTextureView); } // FlutterView needs to be focusable so that the InputMethodManager can interact with it. @@ -846,7 +898,12 @@ private void sendViewportMetricsToFlutter() { flutterEngine.getRenderer().setViewportMetrics(viewportMetrics); } - /** Render modes for a {@link FlutterView}. */ + /** + * Render modes for a {@link FlutterView}. + * + *
Deprecated - please use {@link io.flutter.embedding.android.RenderMode} instead. + */ + @Deprecated() public enum RenderMode { /** * {@code RenderMode}, which paints a Flutter UI to a {@link android.view.SurfaceView}. This @@ -870,6 +927,8 @@ public enum RenderMode { /** * Transparency mode for a {@code FlutterView}. * + *
Deprecated - please use {@link io.flutter.embedding.android.TransparencyMode} instead. + * *
{@code TransparencyMode} impacts the visual behavior and performance of a {@link * FlutterSurfaceView}, which is displayed when a {@code FlutterView} uses {@link * RenderMode#surface}. @@ -878,27 +937,29 @@ public enum RenderMode { * a {@code FlutterView} uses {@link RenderMode#texture}, because a {@link FlutterTextureView} * automatically comes with transparency. */ + @Deprecated public enum TransparencyMode { /** * Renders a {@code FlutterView} without any transparency. This affects {@code FlutterView}s in - * {@link RenderMode#surface} by introducing a base color of black, and places the {@link - * FlutterSurfaceView}'s {@code Window} behind all other content. + * {@link io.flutter.embedding.android.RenderMode#surface} by introducing a base color of black, + * and places the {@link FlutterSurfaceView}'s {@code Window} behind all other content. * - *
In {@link RenderMode#surface}, this mode is the most performant and is a good choice for - * fullscreen Flutter UIs that will not undergo {@code Fragment} transactions. If this mode is - * used within a {@code Fragment}, and that {@code Fragment} is replaced by another one, a brief - * black flicker may be visible during the switch. + *
In {@link io.flutter.embedding.android.RenderMode#surface}, this mode is the most + * performant and is a good choice for fullscreen Flutter UIs that will not undergo {@code + * Fragment} transactions. If this mode is used within a {@code Fragment}, and that {@code + * Fragment} is replaced by another one, a brief black flicker may be visible during the switch. */ opaque, /** * Renders a {@code FlutterView} with transparency. This affects {@code FlutterView}s in {@link - * RenderMode#surface} by allowing background transparency, and places the {@link - * FlutterSurfaceView}'s {@code Window} on top of all other content. + * io.flutter.embedding.android.RenderMode#surface} by allowing background transparency, and + * places the {@link FlutterSurfaceView}'s {@code Window} on top of all other content. * - *
In {@link RenderMode#surface}, this mode is less performant than {@link #opaque}, but this - * mode avoids the black flicker problem that {@link #opaque} has when going through {@code - * Fragment} transactions. Consider using this {@code TransparencyMode} if you intend to switch - * {@code Fragment}s at runtime that contain a Flutter UI. + *
In {@link io.flutter.embedding.android.RenderMode#surface}, this mode is less performant + * than {@link #opaque}, but this mode avoids the black flicker problem that {@link #opaque} has + * when going through {@code Fragment} transactions. Consider using this {@code + * TransparencyMode} if you intend to switch {@code Fragment}s at runtime that contain a Flutter + * UI. */ transparent } diff --git a/shell/platform/android/io/flutter/embedding/android/RenderMode.java b/shell/platform/android/io/flutter/embedding/android/RenderMode.java new file mode 100644 index 0000000000000..3c0907bb850a3 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/android/RenderMode.java @@ -0,0 +1,25 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.embedding.android; + +/** Render modes for a Flutter UI. */ +public enum RenderMode { + /** + * {@code RenderMode}, which paints a Flutter UI to a {@link android.view.SurfaceView}. This mode + * has the best performance, but a Flutter UI in this mode cannot be positioned between 2 other + * Android {@code View}s in the z-index, nor can it be animated/transformed. Unless the special + * capabilities of a {@link android.graphics.SurfaceTexture} are required, developers should + * strongly prefer this render mode. + */ + surface, + /** + * {@code RenderMode}, which paints a Flutter UI to a {@link android.graphics.SurfaceTexture}. + * This mode is not as performant as {@link #surface}, but a Flutter UI in this mode can be + * animated and transformed, as well as positioned in the z-index between 2+ other Android {@code + * Views}. Unless the special capabilities of a {@link android.graphics.SurfaceTexture} are + * required, developers should strongly prefer the {@link #surface} render mode. + */ + texture +} diff --git a/shell/platform/android/io/flutter/embedding/android/TransparencyMode.java b/shell/platform/android/io/flutter/embedding/android/TransparencyMode.java new file mode 100644 index 0000000000000..97736c7161653 --- /dev/null +++ b/shell/platform/android/io/flutter/embedding/android/TransparencyMode.java @@ -0,0 +1,40 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.embedding.android; + +/** + * Transparency mode for a Flutter UI. + * + *
{@code TransparencyMode} impacts the visual behavior and performance of a {@link + * FlutterSurfaceView}, which is displayed when a Flutter UI uses {@link RenderMode#surface}. + * + *
{@code TransparencyMode} does not impact {@link FlutterTextureView}, which is displayed when a + * Flutter UI uses {@link RenderMode#texture}, because a {@link FlutterTextureView} automatically + * comes with transparency. + */ +public enum TransparencyMode { + /** + * Renders a Flutter UI without any transparency. This affects Flutter UI's with {@link + * RenderMode#surface} by introducing a base color of black, and places the {@link + * FlutterSurfaceView}'s {@code Window} behind all other content. + * + *
In {@link RenderMode#surface}, this mode is the most performant and is a good choice for + * fullscreen Flutter UIs that will not undergo {@code Fragment} transactions. If this mode is + * used within a {@code Fragment}, and that {@code Fragment} is replaced by another one, a brief + * black flicker may be visible during the switch. + */ + opaque, + /** + * Renders a Flutter UI with transparency. This affects Flutter UI's in {@link RenderMode#surface} + * by allowing background transparency, and places the {@link FlutterSurfaceView}'s {@code Window} + * on top of all other content. + * + *
In {@link RenderMode#surface}, this mode is less performant than {@link #opaque}, but this + * mode avoids the black flicker problem that {@link #opaque} has when going through {@code + * Fragment} transactions. Consider using this {@code TransparencyMode} if you intend to switch + * {@code Fragment}s at runtime that contain a Flutter UI. + */ + transparent +} diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java index d75298f353148..422673439b551 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java @@ -5,6 +5,7 @@ import static org.junit.Assert.assertNull; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.notNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -16,6 +17,7 @@ import android.content.Context; import android.content.Intent; import android.support.annotation.NonNull; +import io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.Host; import io.flutter.embedding.engine.FlutterEngine; import io.flutter.embedding.engine.FlutterEngineCache; import io.flutter.embedding.engine.FlutterShellArgs; @@ -58,8 +60,8 @@ public void setup() { when(mockHost.getDartEntrypointFunctionName()).thenReturn("main"); when(mockHost.getAppBundlePath()).thenReturn("/fake/path"); when(mockHost.getInitialRoute()).thenReturn("/"); - when(mockHost.getRenderMode()).thenReturn(FlutterView.RenderMode.surface); - when(mockHost.getTransparencyMode()).thenReturn(FlutterView.TransparencyMode.transparent); + when(mockHost.getRenderMode()).thenReturn(RenderMode.surface); + when(mockHost.getTransparencyMode()).thenReturn(TransparencyMode.transparent); when(mockHost.provideFlutterEngine(any(Context.class))).thenReturn(mockFlutterEngine); when(mockHost.shouldAttachEngineToActivity()).thenReturn(true); when(mockHost.shouldDestroyEngineWithHost()).thenReturn(true); @@ -200,6 +202,38 @@ public void itGivesHostAnOpportunityToConfigureFlutterEngine() { verify(mockHost, times(1)).configureFlutterEngine(mockFlutterEngine); } + @Test + public void itGivesHostAnOpportunityToConfigureFlutterSurfaceView() { + // ---- Test setup ---- + // Create the real object that we're testing. + FlutterActivityAndFragmentDelegate delegate = new FlutterActivityAndFragmentDelegate(mockHost); + + // --- Execute the behavior under test --- + delegate.onAttach(RuntimeEnvironment.application); + delegate.onCreateView(null, null, null); + + // Verify that the host was asked to configure a FlutterSurfaceView. + verify(mockHost, times(1)).onFlutterSurfaceViewCreated(notNull(FlutterSurfaceView.class)); + } + + @Test + public void itGivesHostAnOpportunityToConfigureFlutterTextureView() { + // ---- Test setup ---- + Host customMockHost = mock(Host.class); + when(customMockHost.getRenderMode()).thenReturn(RenderMode.texture); + + // Create the real object that we're testing. + FlutterActivityAndFragmentDelegate delegate = + new FlutterActivityAndFragmentDelegate(customMockHost); + + // --- Execute the behavior under test --- + delegate.onAttach(RuntimeEnvironment.application); + delegate.onCreateView(null, null, null); + + // Verify that the host was asked to configure a FlutterTextureView. + verify(customMockHost, times(1)).onFlutterTextureViewCreated(notNull(FlutterTextureView.class)); + } + @Test public void itGivesHostAnOpportunityToCleanUpFlutterEngine() { // ---- Test setup ---- diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java index e85ad1346fa95..3f476ebc8b72e 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java @@ -57,8 +57,8 @@ public void itCreatesDefaultIntentWithExpectedDefaults() { assertNull(flutterActivity.getCachedEngineId()); assertTrue(flutterActivity.shouldDestroyEngineWithHost()); assertEquals(BackgroundMode.opaque, flutterActivity.getBackgroundMode()); - assertEquals(FlutterView.RenderMode.surface, flutterActivity.getRenderMode()); - assertEquals(FlutterView.TransparencyMode.opaque, flutterActivity.getTransparencyMode()); + assertEquals(RenderMode.surface, flutterActivity.getRenderMode()); + assertEquals(TransparencyMode.opaque, flutterActivity.getTransparencyMode()); } @Test @@ -108,8 +108,8 @@ public void itCreatesNewEngineIntentWithRequestedSettings() { assertNull(flutterActivity.getCachedEngineId()); assertTrue(flutterActivity.shouldDestroyEngineWithHost()); assertEquals(BackgroundMode.transparent, flutterActivity.getBackgroundMode()); - assertEquals(FlutterView.RenderMode.texture, flutterActivity.getRenderMode()); - assertEquals(FlutterView.TransparencyMode.transparent, flutterActivity.getTransparencyMode()); + assertEquals(RenderMode.texture, flutterActivity.getRenderMode()); + assertEquals(TransparencyMode.transparent, flutterActivity.getTransparencyMode()); } @Test diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterAndroidComponentTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterAndroidComponentTest.java index 10f0739cb997c..05072e01c2f8a 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterAndroidComponentTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterAndroidComponentTest.java @@ -232,14 +232,14 @@ public String getInitialRoute() { @NonNull @Override - public FlutterView.RenderMode getRenderMode() { - return FlutterView.RenderMode.surface; + public RenderMode getRenderMode() { + return RenderMode.surface; } @NonNull @Override - public FlutterView.TransparencyMode getTransparencyMode() { - return FlutterView.TransparencyMode.transparent; + public TransparencyMode getTransparencyMode() { + return TransparencyMode.transparent; } @Nullable @@ -272,6 +272,12 @@ public boolean shouldAttachEngineToActivity() { return true; } + @Override + public void onFlutterSurfaceViewCreated(@NonNull FlutterSurfaceView flutterSurfaceView) {} + + @Override + public void onFlutterTextureViewCreated(@NonNull FlutterTextureView flutterTextureView) {} + @Override public void onFlutterUiDisplayed() {} diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java index a2257f57ede5c..6dea8ec6b946d 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java @@ -25,8 +25,8 @@ public void itCreatesDefaultFragmentWithExpectedDefaults() { assertTrue(fragment.shouldAttachEngineToActivity()); assertNull(fragment.getCachedEngineId()); assertTrue(fragment.shouldDestroyEngineWithHost()); - assertEquals(FlutterView.RenderMode.surface, fragment.getRenderMode()); - assertEquals(FlutterView.TransparencyMode.transparent, fragment.getTransparencyMode()); + assertEquals(RenderMode.surface, fragment.getRenderMode()); + assertEquals(TransparencyMode.transparent, fragment.getTransparencyMode()); } @Test @@ -36,8 +36,8 @@ public void itCreatesNewEngineFragmentWithRequestedSettings() { .dartEntrypoint("custom_entrypoint") .initialRoute("/custom/route") .shouldAttachEngineToActivity(false) - .renderMode(FlutterView.RenderMode.texture) - .transparencyMode(FlutterView.TransparencyMode.opaque) + .renderMode(RenderMode.texture) + .transparencyMode(TransparencyMode.opaque) .build(); fragment.setDelegate(new FlutterActivityAndFragmentDelegate(fragment)); @@ -47,8 +47,8 @@ public void itCreatesNewEngineFragmentWithRequestedSettings() { assertFalse(fragment.shouldAttachEngineToActivity()); assertNull(fragment.getCachedEngineId()); assertTrue(fragment.shouldDestroyEngineWithHost()); - assertEquals(FlutterView.RenderMode.texture, fragment.getRenderMode()); - assertEquals(FlutterView.TransparencyMode.opaque, fragment.getTransparencyMode()); + assertEquals(RenderMode.texture, fragment.getRenderMode()); + assertEquals(TransparencyMode.opaque, fragment.getTransparencyMode()); } @Test diff --git a/shell/platform/android/test/io/flutter/external/FlutterLaunchTests.java b/shell/platform/android/test/io/flutter/external/FlutterLaunchTests.java index 391a1a8c5deff..3154483d814d6 100644 --- a/shell/platform/android/test/io/flutter/external/FlutterLaunchTests.java +++ b/shell/platform/android/test/io/flutter/external/FlutterLaunchTests.java @@ -8,8 +8,9 @@ import android.content.Intent; import io.flutter.embedding.android.FlutterActivity; import io.flutter.embedding.android.FlutterActivityLaunchConfigs.BackgroundMode; -import io.flutter.embedding.android.FlutterView; +import io.flutter.embedding.android.RenderMode; import io.flutter.embedding.android.RobolectricFlutterActivity; +import io.flutter.embedding.android.TransparencyMode; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -32,7 +33,7 @@ public void launchFlutterActivity_with_defaultIntent_expect_defaultConfiguration assertTrue(flutterActivity.shouldDestroyEngineWithHost()); assertEquals( BackgroundMode.opaque, RobolectricFlutterActivity.getBackgroundMode(flutterActivity)); - assertEquals(FlutterView.RenderMode.surface, flutterActivity.getRenderMode()); - assertEquals(FlutterView.TransparencyMode.opaque, flutterActivity.getTransparencyMode()); + assertEquals(RenderMode.surface, flutterActivity.getRenderMode()); + assertEquals(TransparencyMode.opaque, flutterActivity.getTransparencyMode()); } }