Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Add auto plugin registration to FlutterFragmentActivity as well #20865

Merged
merged 3 commits into from
Aug 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugin
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/service/ServicePluginBinding.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistry.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/util/GeneratedPluginRegister.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterUiDisplayListener.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/RenderSurface.java
Expand Down
1 change: 1 addition & 0 deletions shell/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ android_java_sources = [
"io/flutter/embedding/engine/plugins/service/ServicePluginBinding.java",
"io/flutter/embedding/engine/plugins/shim/ShimPluginRegistry.java",
"io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java",
"io/flutter/embedding/engine/plugins/util/GeneratedPluginRegister.java",
"io/flutter/embedding/engine/renderer/FlutterRenderer.java",
"io/flutter/embedding/engine/renderer/FlutterUiDisplayListener.java",
"io/flutter/embedding/engine/renderer/RenderSurface.java",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.FlutterShellArgs;
import io.flutter.embedding.engine.plugins.activity.ActivityControlSurface;
import io.flutter.embedding.engine.plugins.util.GeneratedPluginRegister;
import io.flutter.plugin.platform.PlatformPlugin;
import io.flutter.view.FlutterMain;
import java.lang.reflect.Method;

/**
* {@code Activity} which displays a fullscreen Flutter UI.
Expand Down Expand Up @@ -872,7 +872,7 @@ public PlatformPlugin providePlatformPlugin(
*/
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
registerPlugins(flutterEngine);
GeneratedPluginRegister.registerGeneratedPlugins(flutterEngine);
}

/**
Expand Down Expand Up @@ -962,34 +962,4 @@ public boolean shouldRestoreAndSaveState() {
}
return true;
}

/**
* Registers all plugins that an app lists in its pubspec.yaml.
*
* <p>The Flutter tool generates a class called GeneratedPluginRegistrant, which includes the code
* necessary to register every plugin in the pubspec.yaml with a given {@code FlutterEngine}. The
* GeneratedPluginRegistrant must be generated per app, because each app uses different sets of
* plugins. Therefore, the Android embedding cannot place a compile-time dependency on this
* generated class. This method uses reflection to attempt to locate the generated file and then
* use it at runtime.
*
* <p>This method fizzles if the GeneratedPluginRegistrant cannot be found or invoked. This
* situation should never occur, but if any eventuality comes up that prevents an app from using
* this behavior, that app can still write code that explicitly registers plugins.
*/
private static void registerPlugins(@NonNull FlutterEngine flutterEngine) {
try {
Class<?> generatedPluginRegistrant =
Class.forName("io.flutter.plugins.GeneratedPluginRegistrant");
Method registrationMethod =
generatedPluginRegistrant.getDeclaredMethod("registerWith", FlutterEngine.class);
registrationMethod.invoke(null, flutterEngine);
} catch (Exception e) {
Log.w(
TAG,
"Tried to automatically register plugins with FlutterEngine ("
+ flutterEngine
+ ") but could not find and invoke the GeneratedPluginRegistrant.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import io.flutter.embedding.android.FlutterActivityLaunchConfigs.BackgroundMode;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.FlutterShellArgs;
import io.flutter.embedding.engine.plugins.util.GeneratedPluginRegister;
import io.flutter.plugin.platform.PlatformPlugin;
import io.flutter.view.FlutterMain;

Expand Down Expand Up @@ -554,13 +555,18 @@ public FlutterEngine provideFlutterEngine(@NonNull Context context) {
}

/**
* Hook for subclasses to easily configure a {@code FlutterEngine}, e.g., register plugins.
* Hook for subclasses to easily configure a {@code FlutterEngine}.
*
* <p>This method is called after {@link #provideFlutterEngine(Context)}.
*
* <p>All plugins listed in the app's pubspec are registered in the base implementation of this
* method. To avoid automatic plugin registration, override this method without invoking super().
* To keep automatic plugin registration and further configure the flutterEngine, override this
* method, invoke super(), and then configure the flutterEngine as desired.
*/
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
// No-op. Hook for subclasses.
GeneratedPluginRegister.registerGeneratedPlugins(flutterEngine);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.embedding.engine.plugins.util;

import androidx.annotation.NonNull;
import io.flutter.Log;
import io.flutter.embedding.engine.FlutterEngine;
import java.lang.reflect.Method;

public class GeneratedPluginRegister {
private static final String TAG = "GeneratedPluginsRegister";
/**
* Registers all plugins that an app lists in its pubspec.yaml.
*
* <p>The Flutter tool generates a class called GeneratedPluginRegistrant, which includes the code
* necessary to register every plugin in the pubspec.yaml with a given {@code FlutterEngine}. The
* GeneratedPluginRegistrant must be generated per app, because each app uses different sets of
* plugins. Therefore, the Android embedding cannot place a compile-time dependency on this
* generated class. This method uses reflection to attempt to locate the generated file and then
* use it at runtime.
*
* <p>This method fizzles if the GeneratedPluginRegistrant cannot be found or invoked. This
* situation should never occur, but if any eventuality comes up that prevents an app from using
* this behavior, that app can still write code that explicitly registers plugins.
*/
public static void registerGeneratedPlugins(@NonNull FlutterEngine flutterEngine) {
try {
Class<?> generatedPluginRegistrant =
Class.forName("io.flutter.plugins.GeneratedPluginRegistrant");
Method registrationMethod =
generatedPluginRegistrant.getDeclaredMethod("registerWith", FlutterEngine.class);
registrationMethod.invoke(null, flutterEngine);
} catch (Exception e) {
Log.w(
TAG,
"Tried to automatically register plugins with FlutterEngine ("
+ flutterEngine
+ ") but could not find and invoke the GeneratedPluginRegistrant.");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,40 @@
package io.flutter.embedding.android;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import android.content.Context;
import android.content.Intent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
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() {
GeneratedPluginRegistrant.clearRegisteredEngines();
}

@After
public void tearDown() {
GeneratedPluginRegistrant.clearRegisteredEngines();
}

@Test
public void createFlutterFragment__defaultRenderModeSurface() {
Expand Down Expand Up @@ -44,6 +66,39 @@ protected RenderMode getRenderMode() {
assertEquals(activity.createFlutterFragment().getRenderMode(), RenderMode.texture);
}

@Test
public void itRegistersPluginsAtConfigurationTime() {
FlutterFragmentActivity activity =
Robolectric.buildActivity(FlutterFragmentActivityWithProvidedEngine.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));
}

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);
when(flutterJNI.isAttached()).thenReturn(true);

return new FlutterEngine(
context, mock(FlutterLoader.class), flutterJNI, new String[] {}, false);
}
}

private static class FakeFlutterFragmentActivity extends FlutterFragmentActivity {
@Override
public Intent getIntent() {
Expand Down