1package io.flutter.embedding.android;
3import static org.junit.Assert.assertArrayEquals;
4import static org.junit.Assert.assertEquals;
5import static org.junit.Assert.assertFalse;
6import static org.junit.Assert.assertNotNull;
7import static org.junit.Assert.assertNull;
8import static org.junit.Assert.assertTrue;
9import static org.mockito.ArgumentMatchers.any;
10import static org.mockito.Mockito.doAnswer;
11import static org.mockito.Mockito.mock;
12import static org.mockito.Mockito.never;
13import static org.mockito.Mockito.spy;
14import static org.mockito.Mockito.times;
15import static org.mockito.Mockito.verify;
16import static org.mockito.Mockito.when;
19import androidx.activity.OnBackPressedCallback;
20import androidx.fragment.app.FragmentActivity;
21import androidx.test.core.app.ApplicationProvider;
22import androidx.test.ext.junit.runners.AndroidJUnit4;
23import io.flutter.embedding.engine.FlutterEngine;
24import io.flutter.embedding.engine.FlutterEngineCache;
25import io.flutter.embedding.engine.FlutterJNI;
26import io.flutter.embedding.engine.loader.FlutterLoader;
27import java.util.ArrayList;
28import java.util.Arrays;
29import java.util.concurrent.atomic.AtomicBoolean;
31import org.junit.runner.RunWith;
32import org.robolectric.Robolectric;
33import org.robolectric.annotation.Config;
36@RunWith(AndroidJUnit4.class)
38 private final Context ctx = ApplicationProvider.getApplicationContext();
45 this.delegate = delegate;
79 .dartEntrypoint(
"custom_entrypoint")
80 .dartLibraryUri(
"package:foo/bar.dart")
81 .dartEntrypointArgs(
new ArrayList<String>(Arrays.asList(
"foo",
"bar")))
82 .initialRoute(
"/custom/route")
83 .shouldAttachEngineToActivity(
false)
84 .handleDeeplinking(
true)
109 .dartEntrypoint(
"custom_entrypoint")
110 .initialRoute(
"/custom/route")
111 .shouldAttachEngineToActivity(
false)
112 .handleDeeplinking(
true)
156 .destroyEngineWithFragment(
true)
168 .shouldDelayFirstAndroidViewDraw(
true)
181 .destroyEngineWithFragment(
true)
184 isDelegateAttached =
true;
185 when(mockDelegate.isAttached()).thenAnswer(invocation -> isDelegateAttached);
186 doAnswer(invocation -> isDelegateAttached =
false).when(mockDelegate).onDetach();
193 verify(mockDelegate,
times(1)).onStart();
194 verify(mockDelegate,
times(1)).onResume();
195 verify(mockDelegate,
times(1)).onPostResume();
199 verify(mockDelegate,
times(1)).onPause();
200 verify(mockDelegate,
times(1)).onDestroyView();
201 verify(mockDelegate,
times(1)).onDetach();
204 verify(mockDelegate, never()).onStop();
210 verify(mockDelegate,
times(1)).onStart();
211 verify(mockDelegate,
times(1)).onResume();
212 verify(mockDelegate,
times(1)).onPostResume();
214 fragment.onDestroy();
217 verify(mockDelegate,
times(1)).onDetach();
224 isDelegateAttached =
true;
225 when(mockDelegate.isAttached()).thenAnswer(invocation -> isDelegateAttached);
226 doAnswer(invocation -> isDelegateAttached =
false).when(mockDelegate).onDetach();
231 .destroyEngineWithFragment(
true)
240 assertTrue(mockDelegate.isAttached());
242 verify(mockDelegate,
times(1)).onDetach();
243 verify(mockDelegate, never()).release();
244 assertFalse(mockDelegate.isAttached());
251 isDelegateAttached =
true;
252 when(mockDelegate.isAttached()).thenAnswer(invocation -> isDelegateAttached);
253 doAnswer(invocation -> isDelegateAttached =
false).when(mockDelegate).onDetach();
259 .destroyEngineWithFragment(
true)
269 assertTrue(mockDelegate.isAttached());
271 verify(mockDelegate,
times(1)).onDetach();
272 verify(mockDelegate,
times(1)).release();
273 assertFalse(mockDelegate.isAttached());
286 @SuppressWarnings(
"deprecation")
287 private FragmentActivity getMockFragmentActivity() {
289 return Robolectric.setupActivity(FragmentActivity.class);
295 FlutterJNI flutterJNI = mock(FlutterJNI.class);
296 when(flutterJNI.isAttached()).thenReturn(
true);
299 new FlutterEngine(ctx,
new FlutterLoader(), flutterJNI,
null,
false);
300 FlutterEngineCache.getInstance().put(
"my_cached_engine", flutterEngine);
304 .shouldAutomaticallyHandleOnBackPressed(
true)
306 FragmentActivity activity = getMockFragmentActivity();
308 .getSupportFragmentManager()
310 .add(
android.R.id.content, fragment)
315 isDelegateAttached =
true;
316 when(mockDelegate.isAttached()).thenAnswer(invocation -> isDelegateAttached);
317 doAnswer(invocation -> isDelegateAttached =
false).when(mockDelegate).onDetach();
321 activity.onBackPressed();
323 verify(mockDelegate,
times(1)).onBackPressed();
326 @SuppressWarnings(
"deprecation")
332 FlutterJNI flutterJNI = mock(FlutterJNI.class);
333 when(flutterJNI.isAttached()).thenReturn(
true);
336 new FlutterEngine(ctx,
new FlutterLoader(), flutterJNI,
null,
false);
337 FlutterEngineCache.getInstance().put(
"my_cached_engine", flutterEngine);
341 .shouldAutomaticallyHandleOnBackPressed(
true)
343 FragmentActivity activity = getMockFragmentActivity();
345 .getSupportFragmentManager()
347 .add(
android.R.id.content, fragment)
349 final AtomicBoolean onBackPressedCalled =
new AtomicBoolean(
false);
351 new OnBackPressedCallback(
true) {
353 public void handleOnBackPressed() {
354 onBackPressedCalled.set(
true);
357 activity.getOnBackPressedDispatcher().addCallback(
callback);
366 verify(mockDelegate, never()).onBackPressed();
367 assertTrue(onBackPressedCalled.get());
374 isDelegateAttached =
true;
375 when(mockDelegate.isAttached()).thenAnswer(invocation -> isDelegateAttached);
376 doAnswer(invocation -> isDelegateAttached =
false).when(mockDelegate).onDetach();
381 FlutterJNI flutterJNI = mock(FlutterJNI.class);
382 when(flutterJNI.isAttached()).thenReturn(
true);
385 new FlutterEngine(spyCtx,
new FlutterLoader(), flutterJNI,
null,
false);
386 FlutterEngineCache.getInstance().put(
"my_cached_engine", flutterEngine);
389 when(fragment.
getContext()).thenReturn(spyCtx);
393 verify(spyCtx,
times(1)).registerComponentCallbacks(any());
394 verify(spyCtx, never()).unregisterComponentCallbacks(any());
397 verify(spyCtx,
times(1)).registerComponentCallbacks(any());
398 verify(spyCtx,
times(1)).unregisterComponentCallbacks(any());
static SkISize times(const SkISize &size, float factor)
TestDelegateFactory(FlutterActivityAndFragmentDelegate delegate)
FlutterActivityAndFragmentDelegate delegate
FlutterActivityAndFragmentDelegate createDelegate(FlutterActivityAndFragmentDelegate.Host host)
void itCreatesNewEngineFragmentThatDelaysFirstDrawWhenRequested()
void itReleaseEngineWhenOnDetach()
void itDelegatesOnBackPressedAutomaticallyWhenEnabled()
void itReturnsExclusiveAppComponent()
void itHandlesPopSystemNavigationAutomaticallyWhenEnabled()
void itCreatesCachedEngineFragmentThatDestroysTheEngine()
void itCreatesCachedEngineFragmentThatDelaysFirstDrawWhenRequested()
void itCanBeDetachedFromTheEngineAndStopSendingFurtherEvents()
void itDoesNotReleaseEnginewhenDetachFromFlutterEngine()
void itCreatesNewEngineInGroupFragmentWithRequestedSettings()
boolean isDelegateAttached
void itCreatesNewEngineFragmentWithRequestedSettings()
void itCreatesCachedEngineFragmentWithExpectedDefaults()
void itCreatesDefaultFragmentWithExpectedDefaults()
void itRegistersComponentCallbacks()
public< T extends FlutterFragment > T build()
String getCachedEngineGroupId()
List< String > getDartEntrypointArgs()
FlutterShellArgs getFlutterShellArgs()
boolean popSystemNavigator()
static NewEngineFragmentBuilder withNewEngine()
boolean shouldHandleDeeplinking()
static FlutterFragment createDefault()
String getDartEntrypointFunctionName()
boolean shouldDelayFirstAndroidViewDraw()
static NewEngineInGroupFragmentBuilder withNewEngineInGroup( @NonNull String engineGroupId)
void detachFromFlutterEngine()
boolean shouldAttachEngineToActivity()
static CachedEngineFragmentBuilder withCachedEngine(@NonNull String engineId)
String getDartEntrypointLibraryUri()
TransparencyMode getTransparencyMode()
RenderMode getRenderMode()
ExclusiveAppComponent< Activity > getExclusiveAppComponent()
boolean shouldDestroyEngineWithHost()
String getCachedEngineId()
void setDelegateFactory( @NonNull FlutterActivityAndFragmentDelegate.DelegateFactory delegateFactory)
void onAttach(@NonNull Context context)
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback