Fix v1 embedding support heuristic for plugins (#44214)
diff --git a/packages/flutter_tools/lib/src/platform_plugins.dart b/packages/flutter_tools/lib/src/platform_plugins.dart
index cfadd8f..07fc58e 100644
--- a/packages/flutter_tools/lib/src/platform_plugins.dart
+++ b/packages/flutter_tools/lib/src/platform_plugins.dart
@@ -64,17 +64,20 @@
'name': name,
'package': package,
'class': pluginClass,
- 'usesEmbedding2': _embeddingVersion == '2',
+ // Mustache doesn't support complex types.
+ 'supportsEmbeddingV1': _supportedEmbedings.contains('1'),
+ 'supportsEmbeddingV2': _supportedEmbedings.contains('2'),
};
}
- String _cachedEmbeddingVersion;
+ Set<String> _cachedEmbeddingVersion;
/// Returns the version of the Android embedding.
- String get _embeddingVersion => _cachedEmbeddingVersion ??= _getEmbeddingVersion();
+ Set<String> get _supportedEmbedings => _cachedEmbeddingVersion ??= _getSupportedEmbeddings();
- String _getEmbeddingVersion() {
+ Set<String> _getSupportedEmbeddings() {
assert(pluginPath != null);
+ final Set<String> supportedEmbeddings = <String>{};
final String baseMainPath = fs.path.join(
pluginPath,
'android',
@@ -113,9 +116,15 @@
}
if (mainClassContent
.contains('io.flutter.embedding.engine.plugins.FlutterPlugin')) {
- return '2';
+ supportedEmbeddings.add('2');
+ } else {
+ supportedEmbeddings.add('1');
}
- return '1';
+ if (mainClassContent.contains('PluginRegistry')
+ && mainClassContent.contains('registerWith')) {
+ supportedEmbeddings.add('1');
+ }
+ return supportedEmbeddings;
}
}
diff --git a/packages/flutter_tools/lib/src/plugins.dart b/packages/flutter_tools/lib/src/plugins.dart
index 11dda77..613400c 100644
--- a/packages/flutter_tools/lib/src/plugins.dart
+++ b/packages/flutter_tools/lib/src/plugins.dart
@@ -337,12 +337,14 @@
ShimPluginRegistry shimPluginRegistry = new ShimPluginRegistry(flutterEngine);
{{/needsShim}}
{{#plugins}}
- {{#usesEmbedding2}}
+ {{#supportsEmbeddingV2}}
flutterEngine.getPlugins().add(new {{package}}.{{class}}());
- {{/usesEmbedding2}}
- {{^usesEmbedding2}}
- {{package}}.{{class}}.registerWith(shimPluginRegistry.registrarFor("{{package}}.{{class}}"));
- {{/usesEmbedding2}}
+ {{/supportsEmbeddingV2}}
+ {{^supportsEmbeddingV2}}
+ {{#supportsEmbeddingV1}}
+ {{package}}.{{class}}.registerWith(shimPluginRegistry.registrarFor("{{package}}.{{class}}"));
+ {{/supportsEmbeddingV1}}
+ {{/supportsEmbeddingV2}}
{{/plugins}}
}
}
@@ -396,14 +398,24 @@
// If a plugin is using an embedding version older than 2.0 and the app is using 2.0,
// then add shim for the old plugins.
for (Map<String, dynamic> plugin in androidPlugins) {
- if (!plugin['usesEmbedding2']) {
+ if (plugin['supportsEmbeddingV1'] && !plugin['supportsEmbeddingV2']) {
templateContext['needsShim'] = true;
break;
}
}
templateContent = _androidPluginRegistryTemplateNewEmbedding;
- break;
+ break;
+ case AndroidEmbeddingVersion.v1:
default:
+ for (Map<String, dynamic> plugin in androidPlugins) {
+ if (!plugin['supportsEmbeddingV1'] && plugin['supportsEmbeddingV2']) {
+ throwToolExit(
+ 'The plugin `${plugin['name']}` requires your app to be migrated to '
+ 'the Android embedding v2. Follow the steps on https://flutter.dev/go/android-project-migration '
+ 'and re-run this command.'
+ );
+ }
+ }
templateContent = _androidPluginRegistryTemplateOldEmbedding;
break;
}
diff --git a/packages/flutter_tools/test/general.shard/plugins_test.dart b/packages/flutter_tools/test/general.shard/plugins_test.dart
index fe45f10..ea41d4d 100644
--- a/packages/flutter_tools/test/general.shard/plugins_test.dart
+++ b/packages/flutter_tools/test/general.shard/plugins_test.dart
@@ -265,6 +265,100 @@
XcodeProjectInterpreter: () => xcodeProjectInterpreter,
});
+ testUsingContext('exits the tool if an app uses the v1 embedding and a plugin only supports the v2 embedding', () async {
+ when(flutterProject.isModule).thenReturn(false);
+ when(androidProject.getEmbeddingVersion()).thenReturn(AndroidEmbeddingVersion.v1);
+
+ final Directory pluginUsingJavaAndNewEmbeddingDir =
+ fs.systemTempDirectory.createTempSync('flutter_plugin_using_java_and_new_embedding_dir.');
+ pluginUsingJavaAndNewEmbeddingDir
+ .childFile('pubspec.yaml')
+ .writeAsStringSync('''
+ flutter:
+ plugin:
+ androidPackage: plugin1
+ pluginClass: UseNewEmbedding
+ ''');
+ pluginUsingJavaAndNewEmbeddingDir
+ .childDirectory('android')
+ .childDirectory('src')
+ .childDirectory('main')
+ .childDirectory('java')
+ .childDirectory('plugin1')
+ .childFile('UseNewEmbedding.java')
+ ..createSync(recursive: true)
+ ..writeAsStringSync('import io.flutter.embedding.engine.plugins.FlutterPlugin;');
+
+ flutterProject.directory
+ .childFile('.packages')
+ .writeAsStringSync('''
+plugin1:${pluginUsingJavaAndNewEmbeddingDir.childDirectory('lib').uri.toString()}
+''');
+ await expectLater(
+ () async {
+ await injectPlugins(flutterProject);
+ },
+ throwsToolExit(
+ message: 'The plugin `plugin1` requires your app to be migrated to the Android embedding v2. '
+ 'Follow the steps on https://flutter.dev/go/android-project-migration and re-run this command.'
+ ),
+ );
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fs,
+ ProcessManager: () => FakeProcessManager.any(),
+ FeatureFlags: () => featureFlags,
+ XcodeProjectInterpreter: () => xcodeProjectInterpreter,
+ });
+
+ testUsingContext('allows app use a plugin that supports v1 and v2 embedding', () async {
+ when(flutterProject.isModule).thenReturn(false);
+ when(androidProject.getEmbeddingVersion()).thenReturn(AndroidEmbeddingVersion.v1);
+
+ final Directory pluginUsingJavaAndNewEmbeddingDir =
+ fs.systemTempDirectory.createTempSync('flutter_plugin_using_java_and_new_embedding_dir.');
+ pluginUsingJavaAndNewEmbeddingDir
+ .childFile('pubspec.yaml')
+ .writeAsStringSync('''
+ flutter:
+ plugin:
+ androidPackage: plugin1
+ pluginClass: UseNewEmbedding
+ ''');
+ pluginUsingJavaAndNewEmbeddingDir
+ .childDirectory('android')
+ .childDirectory('src')
+ .childDirectory('main')
+ .childDirectory('java')
+ .childDirectory('plugin1')
+ .childFile('UseNewEmbedding.java')
+ ..createSync(recursive: true)
+ ..writeAsStringSync(
+ 'import io.flutter.embedding.engine.plugins.FlutterPlugin;\n'
+ 'PluginRegistry\n'
+ 'registerWith(Irrelevant registrar)\n'
+ );
+
+ flutterProject.directory
+ .childFile('.packages')
+ .writeAsStringSync('''
+plugin1:${pluginUsingJavaAndNewEmbeddingDir.childDirectory('lib').uri.toString()}
+''');
+ await injectPlugins(flutterProject);
+
+ final File registrant = flutterProject.directory
+ .childDirectory(fs.path.join('android', 'app', 'src', 'main', 'java', 'io', 'flutter', 'plugins'))
+ .childFile('GeneratedPluginRegistrant.java');
+
+ expect(registrant.existsSync(), isTrue);
+ expect(registrant.readAsStringSync(), contains('package io.flutter.plugins'));
+ expect(registrant.readAsStringSync(), contains('class GeneratedPluginRegistrant'));
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fs,
+ ProcessManager: () => FakeProcessManager.any(),
+ FeatureFlags: () => featureFlags,
+ XcodeProjectInterpreter: () => xcodeProjectInterpreter,
+ });
+
testUsingContext('Registrant doesn\'t use new embedding if app doesn\'t use new embedding', () async {
when(flutterProject.isModule).thenReturn(false);
when(androidProject.getEmbeddingVersion()).thenReturn(AndroidEmbeddingVersion.v1);