blob: 0872c56196ff2e5f7d2f8465afe277fd328c60b5 [file] [log] [blame]
package io.flutter.embedding.android;
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.HANDLE_DEEPLINKING_META_DATA_KEY;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.flutter.FlutterInjector;
import io.flutter.embedding.android.FlutterActivityLaunchConfigs.BackgroundMode;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.FlutterJNI;
import io.flutter.embedding.engine.loader.FlutterLoader;
import io.flutter.plugins.GeneratedPluginRegistrant;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
@Config(manifest = Config.NONE)
@RunWith(RobolectricTestRunner.class)
public class FlutterFragmentActivityTest {
@Before
public void setUp() {
FlutterInjector.reset();
GeneratedPluginRegistrant.clearRegisteredEngines();
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
when(mockFlutterJNI.isAttached()).thenReturn(true);
FlutterJNI.Factory mockFlutterJNIFactory = mock(FlutterJNI.Factory.class);
when(mockFlutterJNIFactory.provideFlutterJNI()).thenReturn(mockFlutterJNI);
FlutterInjector.setInstance(
new FlutterInjector.Builder().setFlutterJNIFactory(mockFlutterJNIFactory).build());
}
@After
public void tearDown() {
GeneratedPluginRegistrant.clearRegisteredEngines();
FlutterInjector.reset();
}
@Test
public void createFlutterFragment__defaultRenderModeSurface() {
final FlutterFragmentActivity activity = new FakeFlutterFragmentActivity();
assertEquals(activity.createFlutterFragment().getRenderMode(), RenderMode.surface);
}
@Test
public void createFlutterFragment__defaultRenderModeTexture() {
final FlutterFragmentActivity activity =
new FakeFlutterFragmentActivity() {
@Override
protected BackgroundMode getBackgroundMode() {
return BackgroundMode.transparent;
}
};
assertEquals(activity.createFlutterFragment().getRenderMode(), RenderMode.texture);
}
@Test
public void createFlutterFragment__customRenderMode() {
final FlutterFragmentActivity activity =
new FakeFlutterFragmentActivity() {
@Override
protected RenderMode getRenderMode() {
return RenderMode.texture;
}
};
assertEquals(activity.createFlutterFragment().getRenderMode(), RenderMode.texture);
}
@Test
public void itRegistersPluginsAtConfigurationTime() {
FlutterFragmentActivity activity =
Robolectric.buildActivity(FlutterFragmentActivity.class).get();
assertTrue(GeneratedPluginRegistrant.getRegisteredEngines().isEmpty());
// Calling onCreate on the FlutterFragmentActivity will create a FlutterFragment and
// commit it to the fragment manager. This attaches the fragment to the FlutterFragmentActivity
// creating and configuring the engine.
activity.onCreate(null);
List<FlutterEngine> registeredEngines = GeneratedPluginRegistrant.getRegisteredEngines();
assertEquals(1, registeredEngines.size());
assertEquals(activity.getFlutterEngine(), registeredEngines.get(0));
}
@Test
public void itDoesNotRegisterPluginsTwiceWhenUsingACachedEngine() {
FlutterFragmentActivity activity =
Robolectric.buildActivity(FlutterFragmentActivityWithProvidedEngine.class).get();
activity.onCreate(null);
activity.configureFlutterEngine(activity.getFlutterEngine());
List<FlutterEngine> registeredEngines = GeneratedPluginRegistrant.getRegisteredEngines();
// This might cause the plugins to be registered twice, once by the FlutterEngine constructor,
// and once by the default FlutterFragmentActivity.configureFlutterEngine implementation.
// Test that it doesn't happen.
assertEquals(1, registeredEngines.size());
}
@Test
public void itReturnsValueFromMetaDataWhenCallsShouldHandleDeepLinkingCase1()
throws PackageManager.NameNotFoundException {
FlutterFragmentActivity activity =
Robolectric.buildActivity(FlutterFragmentActivityWithProvidedEngine.class).get();
assertTrue(GeneratedPluginRegistrant.getRegisteredEngines().isEmpty());
Bundle bundle = new Bundle();
bundle.putBoolean(HANDLE_DEEPLINKING_META_DATA_KEY, true);
FlutterFragmentActivity spyFlutterActivity = spy(activity);
when(spyFlutterActivity.getMetaData()).thenReturn(bundle);
assertTrue(spyFlutterActivity.shouldHandleDeeplinking());
}
@Test
public void itReturnsValueFromMetaDataWhenCallsShouldHandleDeepLinkingCase2()
throws PackageManager.NameNotFoundException {
FlutterFragmentActivity activity =
Robolectric.buildActivity(FlutterFragmentActivityWithProvidedEngine.class).get();
assertTrue(GeneratedPluginRegistrant.getRegisteredEngines().isEmpty());
Bundle bundle = new Bundle();
bundle.putBoolean(HANDLE_DEEPLINKING_META_DATA_KEY, false);
FlutterFragmentActivity spyFlutterActivity = spy(activity);
when(spyFlutterActivity.getMetaData()).thenReturn(bundle);
assertFalse(spyFlutterActivity.shouldHandleDeeplinking());
}
@Test
public void itReturnsValueFromMetaDataWhenCallsShouldHandleDeepLinkingCase3()
throws PackageManager.NameNotFoundException {
FlutterFragmentActivity activity =
Robolectric.buildActivity(FlutterFragmentActivityWithProvidedEngine.class).get();
assertTrue(GeneratedPluginRegistrant.getRegisteredEngines().isEmpty());
// Creates an empty bundle.
Bundle bundle = new Bundle();
FlutterFragmentActivity spyFlutterActivity = spy(activity);
when(spyFlutterActivity.getMetaData()).thenReturn(bundle);
// Empty bundle should return false.
assertFalse(spyFlutterActivity.shouldHandleDeeplinking());
}
@Test
public void itAllowsRootLayoutOverride() {
FlutterFragmentActivityWithRootLayout activity =
Robolectric.buildActivity(FlutterFragmentActivityWithRootLayout.class).get();
activity.onCreate(null);
ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content);
boolean foundCustomView = false;
for (int i = 0; i < contentView.getChildCount(); i++) {
foundCustomView =
contentView.getChildAt(i) instanceof FlutterFragmentActivityWithRootLayout.CustomLayout;
if (foundCustomView) {
break;
}
}
assertTrue(foundCustomView);
}
static class FlutterFragmentActivityWithProvidedEngine extends FlutterFragmentActivity {
@Override
protected FlutterFragment createFlutterFragment() {
return FlutterFragment.createDefault();
}
@Nullable
@Override
public FlutterEngine provideFlutterEngine(@NonNull Context context) {
FlutterJNI flutterJNI = mock(FlutterJNI.class);
FlutterLoader flutterLoader = mock(FlutterLoader.class);
when(flutterJNI.isAttached()).thenReturn(true);
when(flutterLoader.automaticallyRegisterPlugins()).thenReturn(true);
return new FlutterEngine(context, flutterLoader, flutterJNI, new String[] {}, true);
}
}
private static class FakeFlutterFragmentActivity extends FlutterFragmentActivity {
@Override
public Intent getIntent() {
return new Intent();
}
@Override
public String getDartEntrypointFunctionName() {
return "";
}
@Override
protected String getInitialRoute() {
return "";
}
@Override
protected String getAppBundlePath() {
return "";
}
@Override
protected boolean shouldHandleDeeplinking() {
return false;
}
}
private static class FlutterFragmentActivityWithRootLayout
extends FlutterFragmentActivityWithProvidedEngine {
public static class CustomLayout extends FrameLayout {
public CustomLayout(Context context) {
super(context);
}
}
@Override
protected FrameLayout provideRootLayout(Context context) {
return new CustomLayout(context);
}
}
// This is just a compile time check to ensure that it's possible for FlutterFragmentActivity
// subclasses
// to provide their own intent builders which builds their own runtime types.
private static class FlutterFragmentActivityWithIntentBuilders extends FlutterFragmentActivity {
public static NewEngineIntentBuilder withNewEngine() {
return new NewEngineIntentBuilder(FlutterFragmentActivityWithIntentBuilders.class);
}
public static CachedEngineIntentBuilder withCachedEngine(@NonNull String cachedEngineId) {
return new CachedEngineIntentBuilder(
FlutterFragmentActivityWithIntentBuilders.class, cachedEngineId);
}
}
}