1package io.flutter.embedding.android;
3import static org.junit.Assert.assertNotNull;
4import static org.mockito.ArgumentMatchers.any;
5import static org.mockito.ArgumentMatchers.isNull;
6import static org.mockito.Mockito.doAnswer;
7import static org.mockito.Mockito.inOrder;
8import static org.mockito.Mockito.mock;
9import static org.mockito.Mockito.never;
10import static org.mockito.Mockito.spy;
11import static org.mockito.Mockito.times;
12import static org.mockito.Mockito.verify;
13import static org.mockito.Mockito.verifyNoMoreInteractions;
14import static org.mockito.Mockito.when;
15import static org.mockito.Mockito.withSettings;
21import androidx.annotation.NonNull;
22import androidx.annotation.Nullable;
23import androidx.lifecycle.Lifecycle;
24import androidx.test.core.app.ApplicationProvider;
25import androidx.test.ext.junit.runners.AndroidJUnit4;
26import io.flutter.embedding.engine.FlutterEngine;
27import io.flutter.embedding.engine.FlutterEngineCache;
28import io.flutter.embedding.engine.FlutterJNI;
29import io.flutter.embedding.engine.FlutterShellArgs;
30import io.flutter.embedding.engine.loader.FlutterLoader;
31import io.flutter.embedding.engine.plugins.FlutterPlugin;
32import io.flutter.embedding.engine.plugins.activity.ActivityAware;
33import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
34import io.flutter.embedding.engine.systemchannels.LifecycleChannel;
35import io.flutter.plugin.platform.PlatformPlugin;
38import org.junit.runner.RunWith;
39import org.mockito.ArgumentCaptor;
40import org.mockito.InOrder;
41import org.mockito.invocation.InvocationOnMock;
42import org.mockito.stubbing.Answer;
43import org.robolectric.Robolectric;
44import org.robolectric.android.controller.ActivityController;
45import org.robolectric.annotation.Config;
48@RunWith(AndroidJUnit4.class)
50 private final Context ctx = ApplicationProvider.getApplicationContext();
58 when(mockFlutterJni.
isAttached()).thenReturn(
true);
67 FakeHost fakeHost =
new FakeHost(cachedEngine);
68 fakeHost.shouldDestroyEngineWithHost =
true;
80 verify(mockPlugin,
times(1)).onAttachedToEngine(pluginBindingCaptor.capture());
81 FlutterPlugin.FlutterPluginBinding binding = pluginBindingCaptor.getValue();
82 assertNotNull(binding.getApplicationContext());
83 assertNotNull(binding.getBinaryMessenger());
84 assertNotNull(binding.getTextureRegistry());
85 assertNotNull(binding.getPlatformViewRegistry());
98 verify(mockPlugin,
times(1)).onDetachedFromEngine(pluginBindingCaptor.capture());
99 binding = pluginBindingCaptor.getValue();
100 assertNotNull(binding.getApplicationContext());
101 assertNotNull(binding.getBinaryMessenger());
102 assertNotNull(binding.getTextureRegistry());
103 assertNotNull(binding.getPlatformViewRegistry());
112 when(mockFlutterJni.
isAttached()).thenReturn(
true);
120 ActivityPluginBinding.OnSaveInstanceStateListener mockSaveStateListener =
127 public Object answer(InvocationOnMock invocation)
throws Throwable {
134 .when(activityAwarePlugin)
140 FlutterActivityAndFragmentDelegate.Host fakeHost =
new FakeHost(cachedEngine);
149 ArgumentCaptor<ActivityPluginBinding> pluginBindingCaptor =
151 verify(activityAwarePlugin,
times(1)).onAttachedToActivity(pluginBindingCaptor.capture());
159 verify(mockSaveStateListener,
times(1)).onRestoreInstanceState(isNull());
169 verify(mockSaveStateListener,
times(1)).onSaveInstanceState(
any(Bundle.class));
175 verify(activityAwarePlugin,
times(1)).onDetachedFromActivity();
184 when(mockFlutterJni.
isAttached()).thenReturn(
true);
189 FakeHost fakeHost =
new FakeHost(cachedEngine);
207 verify(delegate, never()).detachFromFlutterEngine();
216 when(mockFlutterJni.
isAttached()).thenReturn(
true);
223 ActivityController<FlutterActivity> activityController1 =
225 activityController1.create().start().resume();
227 InOrder inOrder = inOrder(mockLifecycleChannel);
228 inOrder.verify(mockLifecycleChannel,
times(1)).appIsResumed();
229 verifyNoMoreInteractions(mockLifecycleChannel);
231 activityController1.pause();
233 ActivityController<FlutterActivity> activityController2 =
235 activityController2.create().start().resume();
238 inOrder.verify(mockLifecycleChannel,
times(1)).appIsInactive();
240 inOrder.verify(mockLifecycleChannel,
times(1)).appIsDetached();
242 inOrder.verify(mockLifecycleChannel,
times(1)).appIsResumed();
243 verifyNoMoreInteractions(mockLifecycleChannel);
248 activityController1.stop().destroy();
249 verifyNoMoreInteractions(mockLifecycleChannel);
255 boolean shouldDestroyEngineWithHost =
false;
256 Lifecycle lifecycle = mock(Lifecycle.class);
259 cachedEngine = flutterEngine;
264 public Context getContext() {
265 return ApplicationProvider.getApplicationContext();
268 @SuppressWarnings(
"deprecation")
273 public Activity getActivity() {
274 if (activity ==
null) {
275 activity = Robolectric.setupActivity(Activity.class);
282 public Lifecycle getLifecycle() {
288 public FlutterShellArgs getFlutterShellArgs() {
289 return new FlutterShellArgs(
new String[] {});
294 public String getCachedEngineId() {
295 return "my_flutter_engine";
300 public String getCachedEngineGroupId() {
301 return "my_flutter_engine_group";
305 public boolean shouldDestroyEngineWithHost() {
306 return shouldDestroyEngineWithHost;
311 public String getDartEntrypointFunctionName() {
317 public String getDartEntrypointLibraryUri() {
329 public String getAppBundlePath() {
335 public String getInitialRoute() {
341 public RenderMode getRenderMode() {
342 return RenderMode.surface;
347 public TransparencyMode getTransparencyMode() {
348 return TransparencyMode.transparent;
352 public ExclusiveAppComponent<Activity> getExclusiveAppComponent() {
358 public FlutterEngine provideFlutterEngine(@NonNull Context context) {
364 public PlatformPlugin providePlatformPlugin(
365 @Nullable Activity activity, @NonNull
FlutterEngine flutterEngine) {
370 public void configureFlutterEngine(@NonNull
FlutterEngine flutterEngine) {}
373 public void cleanUpFlutterEngine(@NonNull
FlutterEngine flutterEngine) {}
376 public boolean shouldAttachEngineToActivity() {
381 public boolean shouldHandleDeeplinking() {
386 public boolean shouldRestoreAndSaveState() {
391 public boolean shouldDispatchAppLifecycleState() {
396 public boolean attachToEngineAutomatically() {
401 public void onFlutterSurfaceViewCreated(@NonNull FlutterSurfaceView flutterSurfaceView) {}
404 public void onFlutterTextureViewCreated(@NonNull FlutterTextureView flutterTextureView) {}
407 public void onFlutterUiDisplayed() {}
410 public void onFlutterUiNoLongerDisplayed() {}
413 public void detachFromFlutterEngine() {}
416 public void updateSystemUiOverlays() {}
419 public boolean popSystemNavigator() {
424 public void setFrameworkHandlesBack(
boolean frameworkHandlesBack) {}
static SkISize times(const SkISize &size, float factor)
View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState, int flutterViewId, boolean shouldDelayFirstAndroidViewDraw)
void onAttach(@NonNull Context context)
void onSaveInstanceState(@Nullable Bundle bundle)
void onRestoreInstanceState(@Nullable Bundle bundle)
Intent build(@NonNull Context context)
static CachedEngineIntentBuilder withCachedEngine(@NonNull String cachedEngineId)
void twoOverlappingFlutterActivitiesDoNotCrosstalk()
void pluginsReceiveFlutterPluginBinding()
void normalLifecycleStepsDoNotTriggerADetachFromFlutterEngine()
void activityAwarePluginsReceiveActivityBinding()
static FlutterEngineCache getInstance()
void put(@NonNull String engineId, @Nullable FlutterEngine engine)
LifecycleChannel getLifecycleChannel()
PluginRegistry getPlugins()
void add(@NonNull FlutterPlugin plugin)
void addOnSaveStateListener(@NonNull OnSaveInstanceStateListener listener)
SIT bool any(const Vec< 1, T > &x)