10
10
import android .content .pm .ApplicationInfo ;
11
11
import android .content .pm .PackageManager ;
12
12
import android .graphics .Color ;
13
+ import android .graphics .drawable .ColorDrawable ;
13
14
import android .graphics .drawable .Drawable ;
14
15
import android .os .Build ;
15
16
import android .os .Bundle ;
@@ -75,10 +76,12 @@ public class FlutterActivity extends FragmentActivity implements OnFirstFrameRen
75
76
// Intent extra arguments.
76
77
protected static final String EXTRA_DART_ENTRYPOINT = "dart_entrypoint" ;
77
78
protected static final String EXTRA_INITIAL_ROUTE = "initial_route" ;
79
+ protected static final String EXTRA_BACKGROUND_MODE = "background_mode" ;
78
80
79
81
// Default configuration.
80
82
protected static final String DEFAULT_DART_ENTRYPOINT = "main" ;
81
83
protected static final String DEFAULT_INITIAL_ROUTE = "/" ;
84
+ protected static final String DEFAULT_BACKGROUND_MODE = BackgroundMode .opaque .name ();
82
85
83
86
// FlutterFragment management.
84
87
private static final String TAG_FLUTTER_FRAGMENT = "flutter_fragment" ;
@@ -114,6 +117,7 @@ public static class IntentBuilder {
114
117
private final Class <? extends FlutterActivity > activityClass ;
115
118
private String dartEntrypoint = DEFAULT_DART_ENTRYPOINT ;
116
119
private String initialRoute = DEFAULT_INITIAL_ROUTE ;
120
+ private String backgroundMode = DEFAULT_BACKGROUND_MODE ;
117
121
118
122
protected IntentBuilder (@ NonNull Class <? extends FlutterActivity > activityClass ) {
119
123
this .activityClass = activityClass ;
@@ -138,6 +142,28 @@ public IntentBuilder initialRoute(@NonNull String initialRoute) {
138
142
return this ;
139
143
}
140
144
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
+
141
167
/**
142
168
* Creates and returns an {@link Intent} that will launch a {@code FlutterActivity} with
143
169
* the desired configuration.
@@ -146,20 +172,40 @@ public IntentBuilder initialRoute(@NonNull String initialRoute) {
146
172
public Intent build (@ NonNull Context context ) {
147
173
return new Intent (context , activityClass )
148
174
.putExtra (EXTRA_DART_ENTRYPOINT , dartEntrypoint )
149
- .putExtra (EXTRA_INITIAL_ROUTE , initialRoute );
175
+ .putExtra (EXTRA_INITIAL_ROUTE , initialRoute )
176
+ .putExtra (EXTRA_BACKGROUND_MODE , backgroundMode );
150
177
}
151
178
}
152
179
153
180
@ Override
154
181
public void onCreate (Bundle savedInstanceState ) {
155
182
Log .d (TAG , "onCreate()" );
156
183
super .onCreate (savedInstanceState );
184
+ configureWindowForTransparency ();
157
185
setContentView (createFragmentContainer ());
158
186
showCoverView ();
159
187
configureStatusBarForFullscreenFlutterExperience ();
160
188
ensureFlutterFragmentCreated ();
161
189
}
162
190
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
+
163
209
/**
164
210
* Cover all visible {@code Activity} area with a {@code View} that paints everything the same
165
211
* color as the {@code Window}.
@@ -170,6 +216,11 @@ public void onCreate(Bundle savedInstanceState) {
170
216
* itself transparent.
171
217
*/
172
218
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
+
173
224
// Create the coverView.
174
225
if (coverView == null ) {
175
226
coverView = new View (this );
@@ -210,7 +261,9 @@ private Drawable createCoverViewBackground() {
210
261
* for details.
211
262
*/
212
263
private void hideCoverView () {
213
- coverView .setVisibility (View .GONE );
264
+ if (coverView != null ) {
265
+ coverView .setVisibility (View .GONE );
266
+ }
214
267
}
215
268
216
269
private void configureStatusBarForFullscreenFlutterExperience () {
@@ -267,13 +320,19 @@ private void ensureFlutterFragmentCreated() {
267
320
*/
268
321
@ NonNull
269
322
protected FlutterFragment createFlutterFragment () {
323
+ BackgroundMode backgroundMode = getBackgroundMode ();
324
+
270
325
return new FlutterFragment .Builder ()
271
326
.dartEntrypoint (getDartEntrypoint ())
272
327
.initialRoute (getInitialRoute ())
273
328
.appBundlePath (getAppBundlePath ())
274
329
.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 )
277
336
.shouldAttachEngineToActivity (shouldAttachEngineToActivity ())
278
337
.build ();
279
338
}
@@ -432,6 +491,19 @@ protected String getInitialRoute() {
432
491
}
433
492
}
434
493
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
+
435
507
/**
436
508
* Returns true if Flutter is running in "debug mode", and false otherwise.
437
509
* <p>
@@ -445,4 +517,14 @@ private boolean isDebuggable() {
445
517
public void onFirstFrameRendered () {
446
518
hideCoverView ();
447
519
}
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
+ }
448
530
}
0 commit comments