Flutter Engine
The Flutter Engine
FlutterFragmentActivityTest.java
Go to the documentation of this file.
1package io.flutter.embedding.android;
2
3import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.HANDLE_DEEPLINKING_META_DATA_KEY;
4import static org.junit.Assert.assertEquals;
5import static org.junit.Assert.assertFalse;
6import static org.junit.Assert.assertNotNull;
7import static org.junit.Assert.assertTrue;
8import static org.mockito.Mockito.mock;
9import static org.mockito.Mockito.spy;
10import static org.mockito.Mockito.when;
11
12import android.content.Context;
13import android.content.Intent;
14import android.content.pm.PackageManager;
15import android.os.Bundle;
16import android.view.View;
17import android.view.ViewGroup;
18import android.widget.FrameLayout;
19import androidx.annotation.NonNull;
20import androidx.annotation.Nullable;
21import androidx.test.core.app.ActivityScenario;
22import androidx.test.core.app.ApplicationProvider;
23import androidx.test.ext.junit.runners.AndroidJUnit4;
24import io.flutter.FlutterInjector;
25import io.flutter.embedding.android.FlutterActivityLaunchConfigs.BackgroundMode;
26import io.flutter.embedding.engine.FlutterEngine;
27import io.flutter.embedding.engine.FlutterJNI;
28import io.flutter.embedding.engine.loader.FlutterLoader;
29import io.flutter.plugins.GeneratedPluginRegistrant;
30import java.util.List;
31import org.junit.After;
32import org.junit.Before;
33import org.junit.Test;
34import org.junit.runner.RunWith;
35import org.robolectric.Robolectric;
36import org.robolectric.annotation.Config;
37
38@Config(manifest = Config.NONE)
39@RunWith(AndroidJUnit4.class)
41 private final Context ctx = ApplicationProvider.getApplicationContext();
42
43 @Before
44 public void setUp() {
47 FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
48 when(mockFlutterJNI.isAttached()).thenReturn(true);
49 FlutterJNI.Factory mockFlutterJNIFactory = mock(FlutterJNI.Factory.class);
50 when(mockFlutterJNIFactory.provideFlutterJNI()).thenReturn(mockFlutterJNI);
52 new FlutterInjector.Builder().setFlutterJNIFactory(mockFlutterJNIFactory).build());
53 }
54
55 @After
56 public void tearDown() {
59 }
60
61 @Test
63 final FlutterFragmentActivity activity = new FakeFlutterFragmentActivity();
64 assertEquals(activity.createFlutterFragment().getRenderMode(), RenderMode.surface);
65 }
66
67 @Test
69 final FlutterFragmentActivity activity =
70 new FakeFlutterFragmentActivity() {
71 @Override
72 protected BackgroundMode getBackgroundMode() {
74 }
75 };
76 assertEquals(activity.createFlutterFragment().getRenderMode(), RenderMode.texture);
77 }
78
79 @Test
81 final FlutterFragmentActivity activity =
82 new FakeFlutterFragmentActivity() {
83 @Override
84 protected RenderMode getRenderMode() {
85 return RenderMode.texture;
86 }
87 };
88 assertEquals(activity.createFlutterFragment().getRenderMode(), RenderMode.texture);
89 }
90
91 @Test
93 final FlutterFragmentActivity activity =
94 new FakeFlutterFragmentActivity() {
95 @Override
96 public String getDartEntrypointLibraryUri() {
97 return "package:foo/bar.dart";
98 }
99 };
100 assertEquals(
101 activity.createFlutterFragment().getDartEntrypointLibraryUri(), "package:foo/bar.dart");
102 }
103
104 @Test
105 public void hasRootLayoutId() {
106 FlutterFragmentActivityWithRootLayout activity =
107 Robolectric.buildActivity(FlutterFragmentActivityWithRootLayout.class).get();
108 activity.onCreate(null);
109 assertNotNull(activity.FRAGMENT_CONTAINER_ID);
110 assertTrue(activity.FRAGMENT_CONTAINER_ID != View.NO_ID);
111 }
112
113 @Test
115 try (ActivityScenario<FlutterFragmentActivity> scenario =
116 ActivityScenario.launch(FlutterFragmentActivity.class)) {
117 scenario.onActivity(
118 activity -> {
119 List<FlutterEngine> registeredEngines =
121 assertEquals(1, registeredEngines.size());
122 assertEquals(activity.getFlutterEngine(), registeredEngines.get(0));
123 });
124 }
125 }
126
127 @Test
129 try (ActivityScenario<FlutterFragmentActivity> scenario =
130 ActivityScenario.launch(FlutterFragmentActivity.class)) {
131 scenario.onActivity(
132 activity -> {
133 List<FlutterEngine> registeredEngines =
135 assertEquals(1, registeredEngines.size());
136 assertEquals(activity.getFlutterEngine(), registeredEngines.get(0));
137 });
138 }
139
141 // This might cause the plugins to be registered twice, once by the FlutterEngine constructor,
142 // and once by the default FlutterFragmentActivity.configureFlutterEngine implementation.
143 // Test that it doesn't happen.
144 assertEquals(1, registeredEngines.size());
145 }
146
147 @Test
149 throws PackageManager.NameNotFoundException {
150 FlutterFragmentActivity activity =
151 Robolectric.buildActivity(FlutterFragmentActivityWithProvidedEngine.class).get();
152 assertTrue(GeneratedPluginRegistrant.getRegisteredEngines().isEmpty());
153 Bundle bundle = new Bundle();
154 bundle.putBoolean(HANDLE_DEEPLINKING_META_DATA_KEY, true);
155 FlutterFragmentActivity spyFlutterActivity = spy(activity);
156 when(spyFlutterActivity.getMetaData()).thenReturn(bundle);
157 assertTrue(spyFlutterActivity.shouldHandleDeeplinking());
158 }
159
160 @Test
162 throws PackageManager.NameNotFoundException {
163 FlutterFragmentActivity activity =
164 Robolectric.buildActivity(FlutterFragmentActivityWithProvidedEngine.class).get();
165 assertTrue(GeneratedPluginRegistrant.getRegisteredEngines().isEmpty());
166 Bundle bundle = new Bundle();
167 bundle.putBoolean(HANDLE_DEEPLINKING_META_DATA_KEY, false);
168 FlutterFragmentActivity spyFlutterActivity = spy(activity);
169 when(spyFlutterActivity.getMetaData()).thenReturn(bundle);
170 assertFalse(spyFlutterActivity.shouldHandleDeeplinking());
171 }
172
173 @Test
175 throws PackageManager.NameNotFoundException {
176 FlutterFragmentActivity activity =
177 Robolectric.buildActivity(FlutterFragmentActivityWithProvidedEngine.class).get();
178 assertTrue(GeneratedPluginRegistrant.getRegisteredEngines().isEmpty());
179 // Creates an empty bundle.
180 Bundle bundle = new Bundle();
181 FlutterFragmentActivity spyFlutterActivity = spy(activity);
182 when(spyFlutterActivity.getMetaData()).thenReturn(bundle);
183 // Empty bundle should return false.
184 assertFalse(spyFlutterActivity.shouldHandleDeeplinking());
185 }
186
187 @Test
189 FlutterFragmentActivityWithRootLayout activity =
190 Robolectric.buildActivity(FlutterFragmentActivityWithRootLayout.class).get();
191
192 activity.onCreate(null);
193 ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content);
194 boolean foundCustomView = false;
195 for (int i = 0; i < contentView.getChildCount(); i++) {
196 foundCustomView =
197 contentView.getChildAt(i) instanceof FlutterFragmentActivityWithRootLayout.CustomLayout;
198 if (foundCustomView) {
199 break;
200 }
201 }
202 assertTrue(foundCustomView);
203 }
204
205 @Test
207 try (ActivityScenario<FlutterFragmentActivityWithProvidedEngine> scenario =
208 ActivityScenario.launch(FlutterFragmentActivityWithProvidedEngine.class)) {
209 scenario.onActivity(
210 activity -> {
211 assertNotNull(activity.getFlutterEngine());
212 assertEquals(1, activity.numberOfEnginesCreated);
213 });
214 }
215 }
216
217 @Test
220 spy(Robolectric.buildActivity(FlutterFragmentActivityWithProvidedEngine.class).get());
221
222 FlutterFragment fragment = mock(FlutterFragment.class);
223 when(activity.retrieveExistingFlutterFragmentIfPossible()).thenReturn(fragment);
224
225 FlutterEngine engine = mock(FlutterEngine.class);
226 when(fragment.getFlutterEngine()).thenReturn(engine);
227
228 activity.onCreate(null);
229 assertEquals(engine, activity.getFlutterEngine());
230 assertEquals(0, activity.numberOfEnginesCreated);
231 }
232
233 @Test
236 spy(Robolectric.buildActivity(FlutterFragmentActivityWithProvidedEngine.class).get());
237
238 FlutterFragment fragment = mock(FlutterFragment.class);
239 // Similar to the above case, except here, it's not just the activity that was destroyed and
240 // could have its fragment restored in the fragment manager. Here, both activity and fragment
241 // are destroyed. And the fragment manager recreated the fragment on activity recreate.
242 when(activity.retrieveExistingFlutterFragmentIfPossible()).thenReturn(null, fragment);
243
244 FlutterEngine engine = mock(FlutterEngine.class);
245 when(fragment.getFlutterEngine()).thenReturn(engine);
246
247 activity.onCreate(null);
248 // The framework would have recreated a new fragment but the fragment activity wouldn't have
249 // created a new one again.
250 assertEquals(0, activity.numberOfEnginesCreated);
251 }
252
254 int numberOfEnginesCreated = 0;
255
256 @Override
259 }
260
261 @Nullable
262 @Override
263 public FlutterEngine provideFlutterEngine(@NonNull Context context) {
264 FlutterJNI flutterJNI = mock(FlutterJNI.class);
265 FlutterLoader flutterLoader = mock(FlutterLoader.class);
266 when(flutterJNI.isAttached()).thenReturn(true);
267 when(flutterLoader.automaticallyRegisterPlugins()).thenReturn(true);
268
269 numberOfEnginesCreated++;
270 return new FlutterEngine(context, flutterLoader, flutterJNI, new String[] {}, true);
271 }
272 }
273
274 private static class FakeFlutterFragmentActivity extends FlutterFragmentActivity {
275 @Override
276 public Intent getIntent() {
277 return new Intent();
278 }
279
280 @Override
281 public String getDartEntrypointFunctionName() {
282 return "";
283 }
284
285 @Nullable
286 public String getDartEntrypointLibraryUri() {
287 return null;
288 }
289
290 @Override
291 protected String getInitialRoute() {
292 return "";
293 }
294
295 @Override
296 protected String getAppBundlePath() {
297 return "";
298 }
299
300 @Override
301 protected boolean shouldHandleDeeplinking() {
302 return false;
303 }
304 }
305
306 private static class FlutterFragmentActivityWithRootLayout
307 extends FlutterFragmentActivityWithProvidedEngine {
308 public static class CustomLayout extends FrameLayout {
309 public CustomLayout(Context context) {
310 super(context);
311 }
312 }
313
314 @Override
315 protected FrameLayout provideRootLayout(Context context) {
316 return new CustomLayout(context);
317 }
318 }
319
320 // This is just a compile time check to ensure that it's possible for FlutterFragmentActivity
321 // subclasses
322 // to provide their own intent builders which builds their own runtime types.
323 private static class FlutterFragmentActivityWithIntentBuilders extends FlutterFragmentActivity {
324 public static NewEngineIntentBuilder withNewEngine() {
325 return new NewEngineIntentBuilder(FlutterFragmentActivityWithIntentBuilders.class);
326 }
327
328 public static CachedEngineIntentBuilder withCachedEngine(@NonNull String cachedEngineId) {
329 return new CachedEngineIntentBuilder(
330 FlutterFragmentActivityWithIntentBuilders.class, cachedEngineId);
331 }
332
333 public static NewEngineInGroupIntentBuilder withNewEngineInGroup(
334 @NonNull String engineGroupId) {
335 return new NewEngineInGroupIntentBuilder(
336 FlutterFragmentActivityWithIntentBuilders.class, engineGroupId);
337 }
338 }
339}
Builder setFlutterJNIFactory(@NonNull FlutterJNI.Factory factory)
static void setInstance(@NonNull FlutterInjector injector)
void onCreate(@Nullable Bundle savedInstanceState)
FlutterEngine engine
Definition: main.cc:68