Skip to content

Commit e8c2b17

Browse files
Added support for transparent FlutterActivitys (flutter#32740). (flutter#9115)
1 parent 19c5029 commit e8c2b17

File tree

1 file changed

+86
-4
lines changed

1 file changed

+86
-4
lines changed

shell/platform/android/io/flutter/embedding/android/FlutterActivity.java

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import android.content.pm.ApplicationInfo;
1111
import android.content.pm.PackageManager;
1212
import android.graphics.Color;
13+
import android.graphics.drawable.ColorDrawable;
1314
import android.graphics.drawable.Drawable;
1415
import android.os.Build;
1516
import android.os.Bundle;
@@ -75,10 +76,12 @@ public class FlutterActivity extends FragmentActivity implements OnFirstFrameRen
7576
// Intent extra arguments.
7677
protected static final String EXTRA_DART_ENTRYPOINT = "dart_entrypoint";
7778
protected static final String EXTRA_INITIAL_ROUTE = "initial_route";
79+
protected static final String EXTRA_BACKGROUND_MODE = "background_mode";
7880

7981
// Default configuration.
8082
protected static final String DEFAULT_DART_ENTRYPOINT = "main";
8183
protected static final String DEFAULT_INITIAL_ROUTE = "/";
84+
protected static final String DEFAULT_BACKGROUND_MODE = BackgroundMode.opaque.name();
8285

8386
// FlutterFragment management.
8487
private static final String TAG_FLUTTER_FRAGMENT = "flutter_fragment";
@@ -114,6 +117,7 @@ public static class IntentBuilder {
114117
private final Class<? extends FlutterActivity> activityClass;
115118
private String dartEntrypoint = DEFAULT_DART_ENTRYPOINT;
116119
private String initialRoute = DEFAULT_INITIAL_ROUTE;
120+
private String backgroundMode = DEFAULT_BACKGROUND_MODE;
117121

118122
protected IntentBuilder(@NonNull Class<? extends FlutterActivity> activityClass) {
119123
this.activityClass = activityClass;
@@ -138,6 +142,28 @@ public IntentBuilder initialRoute(@NonNull String initialRoute) {
138142
return this;
139143
}
140144

145+
/**
146+
* The mode of {@code FlutterActivity}'s background, either {@link BackgroundMode#opaque} or
147+
* {@link BackgroundMode#transparent}.
148+
* <p>
149+
* The default background mode is {@link BackgroundMode#opaque}.
150+
* <p>
151+
* Choosing a background mode of {@link BackgroundMode#transparent} will configure the inner
152+
* {@link FlutterView} of this {@code FlutterActivity} to be configured with a
153+
* {@link FlutterTextureView} to support transparency. This choice has a non-trivial performance
154+
* impact. A transparent background should only be used if it is necessary for the app design
155+
* being implemented.
156+
* <p>
157+
* A {@code FlutterActivity} that is configured with a background mode of
158+
* {@link BackgroundMode#transparent} must have a theme applied to it that includes the
159+
* following property: {@code <item name="android:windowIsTranslucent">true</item>}.
160+
*/
161+
@NonNull
162+
public IntentBuilder backgroundMode(@NonNull BackgroundMode backgroundMode) {
163+
this.backgroundMode = backgroundMode.name();
164+
return this;
165+
}
166+
141167
/**
142168
* Creates and returns an {@link Intent} that will launch a {@code FlutterActivity} with
143169
* the desired configuration.
@@ -146,20 +172,40 @@ public IntentBuilder initialRoute(@NonNull String initialRoute) {
146172
public Intent build(@NonNull Context context) {
147173
return new Intent(context, activityClass)
148174
.putExtra(EXTRA_DART_ENTRYPOINT, dartEntrypoint)
149-
.putExtra(EXTRA_INITIAL_ROUTE, initialRoute);
175+
.putExtra(EXTRA_INITIAL_ROUTE, initialRoute)
176+
.putExtra(EXTRA_BACKGROUND_MODE, backgroundMode);
150177
}
151178
}
152179

153180
@Override
154181
public void onCreate(Bundle savedInstanceState) {
155182
Log.d(TAG, "onCreate()");
156183
super.onCreate(savedInstanceState);
184+
configureWindowForTransparency();
157185
setContentView(createFragmentContainer());
158186
showCoverView();
159187
configureStatusBarForFullscreenFlutterExperience();
160188
ensureFlutterFragmentCreated();
161189
}
162190

191+
/**
192+
* Sets this {@code Activity}'s {@code Window} background to be transparent, and hides the status
193+
* bar, if this {@code Activity}'s desired {@link BackgroundMode} is {@link BackgroundMode#transparent}.
194+
* <p>
195+
* For {@code Activity} transparency to work as expected, the theme applied to this {@code Activity}
196+
* must include {@code <item name="android:windowIsTranslucent">true</item>}.
197+
*/
198+
private void configureWindowForTransparency() {
199+
BackgroundMode backgroundMode = getBackgroundMode();
200+
if (backgroundMode == BackgroundMode.transparent) {
201+
getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
202+
getWindow().setFlags(
203+
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
204+
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
205+
);
206+
}
207+
}
208+
163209
/**
164210
* Cover all visible {@code Activity} area with a {@code View} that paints everything the same
165211
* color as the {@code Window}.
@@ -170,6 +216,11 @@ public void onCreate(Bundle savedInstanceState) {
170216
* itself transparent.
171217
*/
172218
private void showCoverView() {
219+
if (getBackgroundMode() == BackgroundMode.transparent) {
220+
// Don't display an opaque cover view if the Activity is intended to be transparent.
221+
return;
222+
}
223+
173224
// Create the coverView.
174225
if (coverView == null) {
175226
coverView = new View(this);
@@ -210,7 +261,9 @@ private Drawable createCoverViewBackground() {
210261
* for details.
211262
*/
212263
private void hideCoverView() {
213-
coverView.setVisibility(View.GONE);
264+
if (coverView != null) {
265+
coverView.setVisibility(View.GONE);
266+
}
214267
}
215268

216269
private void configureStatusBarForFullscreenFlutterExperience() {
@@ -267,13 +320,19 @@ private void ensureFlutterFragmentCreated() {
267320
*/
268321
@NonNull
269322
protected FlutterFragment createFlutterFragment() {
323+
BackgroundMode backgroundMode = getBackgroundMode();
324+
270325
return new FlutterFragment.Builder()
271326
.dartEntrypoint(getDartEntrypoint())
272327
.initialRoute(getInitialRoute())
273328
.appBundlePath(getAppBundlePath())
274329
.flutterShellArgs(FlutterShellArgs.fromIntent(getIntent()))
275-
.renderMode(FlutterView.RenderMode.surface)
276-
.transparencyMode(FlutterView.TransparencyMode.opaque)
330+
.renderMode(backgroundMode == BackgroundMode.opaque
331+
? FlutterView.RenderMode.surface
332+
: FlutterView.RenderMode.texture)
333+
.transparencyMode(backgroundMode == BackgroundMode.opaque
334+
? FlutterView.TransparencyMode.opaque
335+
: FlutterView.TransparencyMode.transparent)
277336
.shouldAttachEngineToActivity(shouldAttachEngineToActivity())
278337
.build();
279338
}
@@ -432,6 +491,19 @@ protected String getInitialRoute() {
432491
}
433492
}
434493

494+
/**
495+
* The desired window background mode of this {@code Activity}, which defaults to
496+
* {@link BackgroundMode#opaque}.
497+
*/
498+
@NonNull
499+
protected BackgroundMode getBackgroundMode() {
500+
if (getIntent().hasExtra(EXTRA_BACKGROUND_MODE)) {
501+
return BackgroundMode.valueOf(getIntent().getStringExtra(EXTRA_BACKGROUND_MODE));
502+
} else {
503+
return BackgroundMode.opaque;
504+
}
505+
}
506+
435507
/**
436508
* Returns true if Flutter is running in "debug mode", and false otherwise.
437509
* <p>
@@ -445,4 +517,14 @@ private boolean isDebuggable() {
445517
public void onFirstFrameRendered() {
446518
hideCoverView();
447519
}
520+
521+
/**
522+
* The mode of the background of a {@code FlutterActivity}, either opaque or transparent.
523+
*/
524+
public enum BackgroundMode {
525+
/** Indicates a FlutterActivity with an opaque background. This is the default. */
526+
opaque,
527+
/** Indicates a FlutterActivity with a transparent background. */
528+
transparent
529+
}
448530
}

0 commit comments

Comments
 (0)