mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Generate projects using the new Android embedding (#41666)
* Generate projects using the new Android embedding * Add comment about usesNewEmbedding:true * Feedback * Rework way to detect new embedding in new apps
This commit is contained in:
parent
890b939401
commit
5961bcc505
@ -940,7 +940,6 @@ Future<void> _buildGradleProjectV2(
|
|||||||
/// Returns [true] if the current app uses AndroidX.
|
/// Returns [true] if the current app uses AndroidX.
|
||||||
// TODO(egarciad): https://github.com/flutter/flutter/issues/40800
|
// TODO(egarciad): https://github.com/flutter/flutter/issues/40800
|
||||||
// Remove `FlutterManifest.usesAndroidX` and provide a unified `AndroidProject.usesAndroidX`.
|
// Remove `FlutterManifest.usesAndroidX` and provide a unified `AndroidProject.usesAndroidX`.
|
||||||
@visibleForTesting
|
|
||||||
bool isAppUsingAndroidX(Directory androidDirectory) {
|
bool isAppUsingAndroidX(Directory androidDirectory) {
|
||||||
final File properties = androidDirectory.childFile('gradle.properties');
|
final File properties = androidDirectory.childFile('gradle.properties');
|
||||||
if (!properties.existsSync()) {
|
if (!properties.existsSync()) {
|
||||||
|
@ -623,6 +623,7 @@ To edit platform code in an IDE see https://flutter.dev/developing-packages/#edi
|
|||||||
'description': projectDescription,
|
'description': projectDescription,
|
||||||
'dartSdk': '$flutterRoot/bin/cache/dart-sdk',
|
'dartSdk': '$flutterRoot/bin/cache/dart-sdk',
|
||||||
'androidX': androidX,
|
'androidX': androidX,
|
||||||
|
'useNewAndroidEmbedding': featureFlags.isNewAndroidEmbeddingEnabled,
|
||||||
'androidMinApiLevel': android.minApiLevel,
|
'androidMinApiLevel': android.minApiLevel,
|
||||||
'androidSdkVersion': android_sdk.minimumAndroidSdkVersion,
|
'androidSdkVersion': android_sdk.minimumAndroidSdkVersion,
|
||||||
'androidFlutterJar': '$flutterRoot/bin/cache/artifacts/engine/android-arm/flutter.jar',
|
'androidFlutterJar': '$flutterRoot/bin/cache/artifacts/engine/android-arm/flutter.jar',
|
||||||
|
@ -36,6 +36,9 @@ class FeatureFlags {
|
|||||||
/// Whether flutter desktop for Windows is enabled.
|
/// Whether flutter desktop for Windows is enabled.
|
||||||
bool get isWindowsEnabled => _isEnabled(flutterWindowsDesktopFeature);
|
bool get isWindowsEnabled => _isEnabled(flutterWindowsDesktopFeature);
|
||||||
|
|
||||||
|
/// Whether the new Android embedding is enabled.
|
||||||
|
bool get isNewAndroidEmbeddingEnabled => _isEnabled(flutterNewAndroidEmbeddingFeature);
|
||||||
|
|
||||||
// Calculate whether a particular feature is enabled for the current channel.
|
// Calculate whether a particular feature is enabled for the current channel.
|
||||||
static bool _isEnabled(Feature feature) {
|
static bool _isEnabled(Feature feature) {
|
||||||
final String currentChannel = FlutterVersion.instance.channel;
|
final String currentChannel = FlutterVersion.instance.channel;
|
||||||
@ -66,6 +69,7 @@ const List<Feature> allFeatures = <Feature>[
|
|||||||
flutterMacOSDesktopFeature,
|
flutterMacOSDesktopFeature,
|
||||||
flutterWindowsDesktopFeature,
|
flutterWindowsDesktopFeature,
|
||||||
flutterBuildPluginAsAarFeature,
|
flutterBuildPluginAsAarFeature,
|
||||||
|
flutterNewAndroidEmbeddingFeature,
|
||||||
];
|
];
|
||||||
|
|
||||||
/// The [Feature] for flutter web.
|
/// The [Feature] for flutter web.
|
||||||
@ -126,6 +130,16 @@ const Feature flutterBuildPluginAsAarFeature = Feature(
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// The [Feature] for generating projects using the new Android embedding.
|
||||||
|
const Feature flutterNewAndroidEmbeddingFeature = Feature(
|
||||||
|
name: 'flutter create generates projects using the new Android embedding',
|
||||||
|
configSetting: 'enable-new-android-embedding',
|
||||||
|
master: FeatureChannelSetting(
|
||||||
|
available: true,
|
||||||
|
enabledByDefault: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
/// A [Feature] is a process for conditionally enabling tool features.
|
/// A [Feature] is a process for conditionally enabling tool features.
|
||||||
///
|
///
|
||||||
/// All settings are optional, and if not provided will generally default to
|
/// All settings are optional, and if not provided will generally default to
|
||||||
|
@ -5,6 +5,10 @@
|
|||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:yaml/yaml.dart';
|
import 'package:yaml/yaml.dart';
|
||||||
|
|
||||||
|
import 'base/common.dart';
|
||||||
|
import 'base/file_system.dart';
|
||||||
|
import 'features.dart';
|
||||||
|
|
||||||
/// Marker interface for all platform specific plugin config impls.
|
/// Marker interface for all platform specific plugin config impls.
|
||||||
abstract class PluginPlatform {
|
abstract class PluginPlatform {
|
||||||
const PluginPlatform();
|
const PluginPlatform();
|
||||||
@ -17,18 +21,20 @@ abstract class PluginPlatform {
|
|||||||
/// The required fields include: [name] of the plugin, [package] of the plugin and
|
/// The required fields include: [name] of the plugin, [package] of the plugin and
|
||||||
/// the [pluginClass] that will be the entry point to the plugin's native code.
|
/// the [pluginClass] that will be the entry point to the plugin's native code.
|
||||||
class AndroidPlugin extends PluginPlatform {
|
class AndroidPlugin extends PluginPlatform {
|
||||||
const AndroidPlugin({
|
AndroidPlugin({
|
||||||
@required this.name,
|
@required this.name,
|
||||||
@required this.package,
|
@required this.package,
|
||||||
@required this.pluginClass,
|
@required this.pluginClass,
|
||||||
|
@required this.pluginPath,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory AndroidPlugin.fromYaml(String name, YamlMap yaml) {
|
factory AndroidPlugin.fromYaml(String name, YamlMap yaml, String pluginPath) {
|
||||||
assert(validate(yaml));
|
assert(validate(yaml));
|
||||||
return AndroidPlugin(
|
return AndroidPlugin(
|
||||||
name: name,
|
name: name,
|
||||||
package: yaml['package'],
|
package: yaml['package'],
|
||||||
pluginClass: yaml['pluginClass'],
|
pluginClass: yaml['pluginClass'],
|
||||||
|
pluginPath: pluginPath,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,18 +47,80 @@ class AndroidPlugin extends PluginPlatform {
|
|||||||
|
|
||||||
static const String kConfigKey = 'android';
|
static const String kConfigKey = 'android';
|
||||||
|
|
||||||
|
/// The plugin name defined in pubspec.yaml.
|
||||||
final String name;
|
final String name;
|
||||||
|
|
||||||
|
/// The plugin package name defined in pubspec.yaml.
|
||||||
final String package;
|
final String package;
|
||||||
|
|
||||||
|
/// The plugin main class defined in pubspec.yaml.
|
||||||
final String pluginClass;
|
final String pluginClass;
|
||||||
|
|
||||||
|
/// The absolute path to the plugin in the pub cache.
|
||||||
|
final String pluginPath;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> toMap() {
|
Map<String, dynamic> toMap() {
|
||||||
return <String, dynamic>{
|
return <String, dynamic>{
|
||||||
'name': name,
|
'name': name,
|
||||||
'package': package,
|
'package': package,
|
||||||
'class': pluginClass,
|
'class': pluginClass,
|
||||||
|
'usesEmbedding2': _embeddingVersion == '2',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _cachedEmbeddingVersion;
|
||||||
|
|
||||||
|
/// Returns the version of the Android embedding.
|
||||||
|
String get _embeddingVersion => _cachedEmbeddingVersion ??= _getEmbeddingVersion();
|
||||||
|
|
||||||
|
String _getEmbeddingVersion() {
|
||||||
|
if (!featureFlags.isNewAndroidEmbeddingEnabled) {
|
||||||
|
return '1';
|
||||||
|
}
|
||||||
|
assert(pluginPath != null);
|
||||||
|
final String baseMainPath = fs.path.join(
|
||||||
|
pluginPath,
|
||||||
|
'android',
|
||||||
|
'src',
|
||||||
|
'main',
|
||||||
|
);
|
||||||
|
File mainPluginClass = fs.file(
|
||||||
|
fs.path.join(
|
||||||
|
baseMainPath,
|
||||||
|
'java',
|
||||||
|
package.replaceAll('.', fs.path.separator),
|
||||||
|
'$pluginClass.java',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
// Check if the plugin is implemented in Kotlin since the plugin's pubspec.yaml
|
||||||
|
// doesn't include this information.
|
||||||
|
if (!mainPluginClass.existsSync()) {
|
||||||
|
mainPluginClass = fs.file(
|
||||||
|
fs.path.join(
|
||||||
|
baseMainPath,
|
||||||
|
'kotlin',
|
||||||
|
package.replaceAll('.', fs.path.separator),
|
||||||
|
'$pluginClass.kt',
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assert(mainPluginClass.existsSync());
|
||||||
|
String mainClassContent;
|
||||||
|
try {
|
||||||
|
mainClassContent = mainPluginClass.readAsStringSync();
|
||||||
|
} on FileSystemException {
|
||||||
|
throwToolExit(
|
||||||
|
'Couldn\'t read file $mainPluginClass even though it exists. '
|
||||||
|
'Please verify that this file has read permission and try again.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (mainClassContent
|
||||||
|
.contains('io.flutter.embedding.engine.plugins.FlutterPlugin')) {
|
||||||
|
return '2';
|
||||||
|
}
|
||||||
|
return '1';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contains the parameters to template an iOS plugin.
|
/// Contains the parameters to template an iOS plugin.
|
||||||
|
@ -5,8 +5,10 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:mustache/mustache.dart' as mustache;
|
import 'package:mustache/mustache.dart' as mustache;
|
||||||
|
import 'package:xml/xml.dart' as xml;
|
||||||
import 'package:yaml/yaml.dart';
|
import 'package:yaml/yaml.dart';
|
||||||
|
|
||||||
|
import 'android/gradle.dart';
|
||||||
import 'base/common.dart';
|
import 'base/common.dart';
|
||||||
import 'base/file_system.dart';
|
import 'base/file_system.dart';
|
||||||
import 'dart/package_map.dart';
|
import 'dart/package_map.dart';
|
||||||
@ -63,9 +65,8 @@ class Plugin {
|
|||||||
}
|
}
|
||||||
if (pluginYaml != null && pluginYaml['platforms'] != null) {
|
if (pluginYaml != null && pluginYaml['platforms'] != null) {
|
||||||
return Plugin._fromMultiPlatformYaml(name, path, pluginYaml);
|
return Plugin._fromMultiPlatformYaml(name, path, pluginYaml);
|
||||||
} else {
|
|
||||||
return Plugin._fromLegacyYaml(name, path, pluginYaml); // ignore: deprecated_member_use_from_same_package
|
|
||||||
}
|
}
|
||||||
|
return Plugin._fromLegacyYaml(name, path, pluginYaml); // ignore: deprecated_member_use_from_same_package
|
||||||
}
|
}
|
||||||
|
|
||||||
factory Plugin._fromMultiPlatformYaml(String name, String path, dynamic pluginYaml) {
|
factory Plugin._fromMultiPlatformYaml(String name, String path, dynamic pluginYaml) {
|
||||||
@ -79,8 +80,11 @@ class Plugin {
|
|||||||
final Map<String, PluginPlatform> platforms = <String, PluginPlatform>{};
|
final Map<String, PluginPlatform> platforms = <String, PluginPlatform>{};
|
||||||
|
|
||||||
if (platformsYaml[AndroidPlugin.kConfigKey] != null) {
|
if (platformsYaml[AndroidPlugin.kConfigKey] != null) {
|
||||||
platforms[AndroidPlugin.kConfigKey] =
|
platforms[AndroidPlugin.kConfigKey] = AndroidPlugin.fromYaml(
|
||||||
AndroidPlugin.fromYaml(name, platformsYaml[AndroidPlugin.kConfigKey]);
|
name,
|
||||||
|
platformsYaml[AndroidPlugin.kConfigKey],
|
||||||
|
path,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (platformsYaml[IOSPlugin.kConfigKey] != null) {
|
if (platformsYaml[IOSPlugin.kConfigKey] != null) {
|
||||||
@ -122,12 +126,12 @@ class Plugin {
|
|||||||
if (pluginYaml != null && pluginClass != null) {
|
if (pluginYaml != null && pluginClass != null) {
|
||||||
final String androidPackage = pluginYaml['androidPackage'];
|
final String androidPackage = pluginYaml['androidPackage'];
|
||||||
if (androidPackage != null) {
|
if (androidPackage != null) {
|
||||||
platforms[AndroidPlugin.kConfigKey] =
|
platforms[AndroidPlugin.kConfigKey] = AndroidPlugin(
|
||||||
AndroidPlugin(
|
name: name,
|
||||||
name: name,
|
package: pluginYaml['androidPackage'],
|
||||||
package: pluginYaml['androidPackage'],
|
pluginClass: pluginClass,
|
||||||
pluginClass: pluginClass,
|
pluginPath: path,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final String iosPrefix = pluginYaml['iosPrefix'] ?? '';
|
final String iosPrefix = pluginYaml['iosPrefix'] ?? '';
|
||||||
@ -221,14 +225,21 @@ Plugin _pluginFromPubspec(String name, Uri packageRoot) {
|
|||||||
}
|
}
|
||||||
final String packageRootPath = fs.path.fromUri(packageRoot);
|
final String packageRootPath = fs.path.fromUri(packageRoot);
|
||||||
printTrace('Found plugin $name at $packageRootPath');
|
printTrace('Found plugin $name at $packageRootPath');
|
||||||
return Plugin.fromYaml(name, packageRootPath, flutterConfig['plugin']);
|
return Plugin.fromYaml(
|
||||||
|
name,
|
||||||
|
packageRootPath,
|
||||||
|
flutterConfig['plugin'],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Plugin> findPlugins(FlutterProject project) {
|
List<Plugin> findPlugins(FlutterProject project) {
|
||||||
final List<Plugin> plugins = <Plugin>[];
|
final List<Plugin> plugins = <Plugin>[];
|
||||||
Map<String, Uri> packages;
|
Map<String, Uri> packages;
|
||||||
try {
|
try {
|
||||||
final String packagesFile = fs.path.join(project.directory.path, PackageMap.globalPackagesPath);
|
final String packagesFile = fs.path.join(
|
||||||
|
project.directory.path,
|
||||||
|
PackageMap.globalPackagesPath,
|
||||||
|
);
|
||||||
packages = PackageMap(packagesFile).map;
|
packages = PackageMap(packagesFile).map;
|
||||||
} on FormatException catch (e) {
|
} on FormatException catch (e) {
|
||||||
printTrace('Invalid .packages file: $e');
|
printTrace('Invalid .packages file: $e');
|
||||||
@ -269,7 +280,7 @@ String _readFlutterPluginsList(FlutterProject project) {
|
|||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const String _androidPluginRegistryTemplate = '''package io.flutter.plugins;
|
const String _androidPluginRegistryTemplateOldEmbedding = '''package io.flutter.plugins;
|
||||||
|
|
||||||
import io.flutter.plugin.common.PluginRegistry;
|
import io.flutter.plugin.common.PluginRegistry;
|
||||||
{{#plugins}}
|
{{#plugins}}
|
||||||
@ -300,6 +311,41 @@ public final class GeneratedPluginRegistrant {
|
|||||||
}
|
}
|
||||||
''';
|
''';
|
||||||
|
|
||||||
|
const String _androidPluginRegistryTemplateNewEmbedding = '''package dev.flutter.plugins;
|
||||||
|
|
||||||
|
{{#androidX}}
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
{{/androidX}}
|
||||||
|
{{^androidX}}
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
{{/androidX}}
|
||||||
|
import io.flutter.embedding.engine.FlutterEngine;
|
||||||
|
{{#needsShim}}
|
||||||
|
import io.flutter.embedding.engine.plugins.shim.ShimPluginRegistry;
|
||||||
|
{{/needsShim}}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generated file. Do not edit.
|
||||||
|
* This file is generated by the Flutter tool based on the
|
||||||
|
* plugins that support the Android platform.
|
||||||
|
*/
|
||||||
|
public final class GeneratedPluginRegistrant {
|
||||||
|
public static void registerWith(@NonNull FlutterEngine flutterEngine) {
|
||||||
|
{{#needsShim}}
|
||||||
|
ShimPluginRegistry shimPluginRegistry = new ShimPluginRegistry(flutterEngine);
|
||||||
|
{{/needsShim}}
|
||||||
|
{{#plugins}}
|
||||||
|
{{#usesEmbedding2}}
|
||||||
|
flutterEngine.getPlugins().add(new {{package}}.{{class}}());
|
||||||
|
{{/usesEmbedding2}}
|
||||||
|
{{^usesEmbedding2}}
|
||||||
|
{{package}}.{{class}}.registerWith(shimPluginRegistry.registrarFor("{{package}}.{{class}}"));
|
||||||
|
{{/usesEmbedding2}}
|
||||||
|
{{/plugins}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
''';
|
||||||
|
|
||||||
List<Map<String, dynamic>> _extractPlatformMaps(List<Plugin> plugins, String type) {
|
List<Map<String, dynamic>> _extractPlatformMaps(List<Plugin> plugins, String type) {
|
||||||
final List<Map<String, dynamic>> pluginConfigs = <Map<String, dynamic>>[];
|
final List<Map<String, dynamic>> pluginConfigs = <Map<String, dynamic>>[];
|
||||||
for (Plugin p in plugins) {
|
for (Plugin p in plugins) {
|
||||||
@ -311,26 +357,92 @@ List<Map<String, dynamic>> _extractPlatformMaps(List<Plugin> plugins, String typ
|
|||||||
return pluginConfigs;
|
return pluginConfigs;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _writeAndroidPluginRegistrant(FlutterProject project, List<Plugin> plugins) async {
|
/// Returns the version of the Android embedding that the current
|
||||||
final List<Map<String, dynamic>> androidPlugins = _extractPlatformMaps(plugins, AndroidPlugin.kConfigKey);
|
/// [project] is using.
|
||||||
final Map<String, dynamic> context = <String, dynamic>{
|
String _getAndroidEmbeddingVersion(FlutterProject project) {
|
||||||
'plugins': androidPlugins,
|
if (!featureFlags.isNewAndroidEmbeddingEnabled) {
|
||||||
};
|
return '1';
|
||||||
|
}
|
||||||
|
assert(project.android != null);
|
||||||
|
final File androidManifest = project.android.appManifestFile;
|
||||||
|
assert(androidManifest.existsSync());
|
||||||
|
xml.XmlDocument document;
|
||||||
|
try {
|
||||||
|
document = xml.parse(androidManifest.readAsStringSync());
|
||||||
|
} on xml.XmlParserException {
|
||||||
|
throwToolExit('Error parsing ${project.android.appManifestFile} '
|
||||||
|
'Please ensure that the android manifest is a valid XML document and try again.');
|
||||||
|
} on FileSystemException {
|
||||||
|
throwToolExit('Error reading ${project.android.appManifestFile} even though it exists. '
|
||||||
|
'Please ensure that you have read permission to this file and try again.');
|
||||||
|
}
|
||||||
|
for (xml.XmlElement metaData in document.findAllElements('meta-data')) {
|
||||||
|
final String name = metaData.getAttribute('android:name');
|
||||||
|
if (name == 'flutterEmbedding') {
|
||||||
|
return metaData.getAttribute('android:value');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '1';
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _writeAndroidPluginRegistrant(FlutterProject project, List<Plugin> plugins) async {
|
||||||
|
final List<Map<String, dynamic>> androidPlugins =
|
||||||
|
_extractPlatformMaps(plugins, AndroidPlugin.kConfigKey);
|
||||||
|
|
||||||
|
final Map<String, dynamic> templateContext = <String, dynamic>{
|
||||||
|
'plugins': androidPlugins,
|
||||||
|
'androidX': isAppUsingAndroidX(project.android.hostAppGradleRoot),
|
||||||
|
};
|
||||||
final String javaSourcePath = fs.path.join(
|
final String javaSourcePath = fs.path.join(
|
||||||
project.android.pluginRegistrantHost.path,
|
project.android.pluginRegistrantHost.path,
|
||||||
'src',
|
'src',
|
||||||
'main',
|
'main',
|
||||||
'java',
|
'java',
|
||||||
);
|
);
|
||||||
final String registryPath = fs.path.join(
|
|
||||||
javaSourcePath,
|
String registryPath;
|
||||||
'io',
|
String templateContent;
|
||||||
'flutter',
|
|
||||||
'plugins',
|
final String appEmbeddingVersion = _getAndroidEmbeddingVersion(project);
|
||||||
'GeneratedPluginRegistrant.java',
|
switch (appEmbeddingVersion) {
|
||||||
|
case '2':
|
||||||
|
templateContext['needsShim'] = false;
|
||||||
|
// 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']) {
|
||||||
|
templateContext['needsShim'] = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
registryPath = fs.path.join(
|
||||||
|
javaSourcePath,
|
||||||
|
'dev',
|
||||||
|
'flutter',
|
||||||
|
'plugins',
|
||||||
|
'GeneratedPluginRegistrant.java',
|
||||||
|
);
|
||||||
|
templateContent = _androidPluginRegistryTemplateNewEmbedding;
|
||||||
|
break;
|
||||||
|
case '1':
|
||||||
|
registryPath = fs.path.join(
|
||||||
|
javaSourcePath,
|
||||||
|
'io',
|
||||||
|
'flutter',
|
||||||
|
'plugins',
|
||||||
|
'GeneratedPluginRegistrant.java',
|
||||||
|
);
|
||||||
|
templateContent = _androidPluginRegistryTemplateOldEmbedding;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throwToolExit('Unsupported Android embedding');
|
||||||
|
}
|
||||||
|
printTrace('Generating $registryPath');
|
||||||
|
_renderTemplateToFile(
|
||||||
|
templateContent,
|
||||||
|
templateContext,
|
||||||
|
registryPath,
|
||||||
);
|
);
|
||||||
_renderTemplateToFile(_androidPluginRegistryTemplate, context, registryPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const String _objcPluginRegistryHeaderTemplate = '''//
|
const String _objcPluginRegistryHeaderTemplate = '''//
|
||||||
|
@ -608,7 +608,11 @@ class AndroidProject {
|
|||||||
|
|
||||||
void _regenerateLibrary() {
|
void _regenerateLibrary() {
|
||||||
_deleteIfExistsSync(ephemeralDirectory);
|
_deleteIfExistsSync(ephemeralDirectory);
|
||||||
_overwriteFromTemplate(fs.path.join('module', 'android', 'library'), ephemeralDirectory);
|
_overwriteFromTemplate(fs.path.join(
|
||||||
|
'module',
|
||||||
|
'android',
|
||||||
|
featureFlags.isNewAndroidEmbeddingEnabled ? 'library_new_embedding' : 'library',
|
||||||
|
), ephemeralDirectory);
|
||||||
_overwriteFromTemplate(fs.path.join('module', 'android', 'gradle'), ephemeralDirectory);
|
_overwriteFromTemplate(fs.path.join('module', 'android', 'gradle'), ephemeralDirectory);
|
||||||
gradle.injectGradleWrapperIfNeeded(ephemeralDirectory);
|
gradle.injectGradleWrapperIfNeeded(ephemeralDirectory);
|
||||||
}
|
}
|
||||||
@ -621,6 +625,7 @@ class AndroidProject {
|
|||||||
'projectName': parent.manifest.appName,
|
'projectName': parent.manifest.appName,
|
||||||
'androidIdentifier': parent.manifest.androidPackage,
|
'androidIdentifier': parent.manifest.androidPackage,
|
||||||
'androidX': usesAndroidX,
|
'androidX': usesAndroidX,
|
||||||
|
'useNewAndroidEmbedding': featureFlags.isNewAndroidEmbeddingEnabled,
|
||||||
},
|
},
|
||||||
printStatusWhenWriting: false,
|
printStatusWhenWriting: false,
|
||||||
overwriteExisting: true,
|
overwriteExisting: true,
|
||||||
|
@ -1,5 +1,24 @@
|
|||||||
package {{androidIdentifier}};
|
package {{androidIdentifier}};
|
||||||
|
|
||||||
|
{{#useNewAndroidEmbedding}}
|
||||||
|
{{#androidX}}
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
{{/androidX}}
|
||||||
|
{{^androidX}}
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
{{/androidX}}
|
||||||
|
import dev.flutter.plugins.GeneratedPluginRegistrant;
|
||||||
|
import io.flutter.embedding.android.FlutterActivity;
|
||||||
|
import io.flutter.embedding.engine.FlutterEngine;
|
||||||
|
|
||||||
|
public class MainActivity extends FlutterActivity {
|
||||||
|
@Override
|
||||||
|
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
|
||||||
|
GeneratedPluginRegistrant.registerWith(flutterEngine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{{/useNewAndroidEmbedding}}
|
||||||
|
{{^useNewAndroidEmbedding}}
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import io.flutter.app.FlutterActivity;
|
import io.flutter.app.FlutterActivity;
|
||||||
import io.flutter.plugins.GeneratedPluginRegistrant;
|
import io.flutter.plugins.GeneratedPluginRegistrant;
|
||||||
@ -11,3 +30,4 @@ public class MainActivity extends FlutterActivity {
|
|||||||
GeneratedPluginRegistrant.registerWith(this);
|
GeneratedPluginRegistrant.registerWith(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{{/useNewAndroidEmbedding}}
|
||||||
|
@ -1,7 +1,24 @@
|
|||||||
package {{androidIdentifier}}
|
package {{androidIdentifier}}
|
||||||
|
|
||||||
import android.os.Bundle
|
{{#useNewAndroidEmbedding}}
|
||||||
|
{{#androidX}}
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
{{/androidX}}
|
||||||
|
{{^androidX}}
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
{{/androidX}}
|
||||||
|
import dev.flutter.plugins.GeneratedPluginRegistrant
|
||||||
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
import io.flutter.embedding.engine.FlutterEngine
|
||||||
|
|
||||||
|
class MainActivity: FlutterActivity() {
|
||||||
|
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
|
||||||
|
GeneratedPluginRegistrant.registerWith(flutterEngine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{{/useNewAndroidEmbedding}}
|
||||||
|
{{^useNewAndroidEmbedding}}
|
||||||
|
import android.os.Bundle
|
||||||
import io.flutter.app.FlutterActivity
|
import io.flutter.app.FlutterActivity
|
||||||
import io.flutter.plugins.GeneratedPluginRegistrant
|
import io.flutter.plugins.GeneratedPluginRegistrant
|
||||||
|
|
||||||
@ -11,3 +28,4 @@ class MainActivity: FlutterActivity() {
|
|||||||
GeneratedPluginRegistrant.registerWith(this)
|
GeneratedPluginRegistrant.registerWith(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{{/useNewAndroidEmbedding}}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="{{androidIdentifier}}">
|
package="{{androidIdentifier}}">
|
||||||
|
|
||||||
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
|
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
|
||||||
calls FlutterMain.startInitialization(this); in its onCreate method.
|
calls FlutterMain.startInitialization(this); in its onCreate method.
|
||||||
In most cases you can leave this as-is, but you if you want to provide
|
In most cases you can leave this as-is, but you if you want to provide
|
||||||
@ -29,5 +28,12 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
{{#useNewAndroidEmbedding}}
|
||||||
|
<!-- Don't delete the meta-data below.
|
||||||
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
|
<meta-data
|
||||||
|
android:name="flutterEmbedding"
|
||||||
|
android:value="2" />
|
||||||
|
{{/useNewAndroidEmbedding}}
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@ -36,5 +36,12 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
{{#useNewAndroidEmbedding}}
|
||||||
|
<!-- Don't delete the meta-data below.
|
||||||
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
|
<meta-data
|
||||||
|
android:name="flutterEmbedding"
|
||||||
|
android:value="2" />
|
||||||
|
{{/useNewAndroidEmbedding}}
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@ -1,6 +1,25 @@
|
|||||||
package {{androidIdentifier}}.host;
|
package {{androidIdentifier}}.host;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
{{#useNewAndroidEmbedding}}
|
||||||
|
{{#androidX}}
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
{{/androidX}}
|
||||||
|
{{^androidX}}
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
{{/androidX}}
|
||||||
|
import dev.flutter.plugins.GeneratedPluginRegistrant;
|
||||||
|
import io.flutter.embedding.android.FlutterActivity;
|
||||||
|
import io.flutter.embedding.engine.FlutterEngine;
|
||||||
|
|
||||||
|
public class MainActivity extends FlutterActivity {
|
||||||
|
@Override
|
||||||
|
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
|
||||||
|
GeneratedPluginRegistrant.registerWith(flutterEngine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{{/useNewAndroidEmbedding}}
|
||||||
|
{{^useNewAndroidEmbedding}}
|
||||||
import io.flutter.app.FlutterActivity;
|
import io.flutter.app.FlutterActivity;
|
||||||
import io.flutter.plugins.GeneratedPluginRegistrant;
|
import io.flutter.plugins.GeneratedPluginRegistrant;
|
||||||
|
|
||||||
@ -11,3 +30,4 @@ public class MainActivity extends FlutterActivity {
|
|||||||
GeneratedPluginRegistrant.registerWith(this);
|
GeneratedPluginRegistrant.registerWith(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{{/useNewAndroidEmbedding}}
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
// Generated file. Do not edit.
|
||||||
|
|
||||||
|
def localProperties = new Properties()
|
||||||
|
def localPropertiesFile = new File(buildscript.sourceFile.parentFile.parentFile, 'local.properties')
|
||||||
|
if (localPropertiesFile.exists()) {
|
||||||
|
localPropertiesFile.withReader('UTF-8') { reader ->
|
||||||
|
localProperties.load(reader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutterRoot = localProperties.getProperty('flutter.sdk')
|
||||||
|
if (flutterRoot == null) {
|
||||||
|
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
|
||||||
|
if (flutterVersionCode == null) {
|
||||||
|
flutterVersionCode = '1'
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutterVersionName = localProperties.getProperty('flutter.versionName')
|
||||||
|
if (flutterVersionName == null) {
|
||||||
|
flutterVersionName = '1.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
|
group '{{androidIdentifier}}'
|
||||||
|
version '1.0'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 28
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion 16
|
||||||
|
targetSdkVersion 28
|
||||||
|
versionCode flutterVersionCode.toInteger()
|
||||||
|
versionName flutterVersionName
|
||||||
|
{{#androidX}}
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
{{/androidX}}
|
||||||
|
{{^androidX}}
|
||||||
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
|
{{/androidX}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flutter {
|
||||||
|
source '../..'
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testImplementation 'junit:junit:4.12'
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module external.linked.project.id=":flutter" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/../../.." external.system.id="GRADLE" type="JAVA_MODULE" version="4">
|
||||||
|
<component name="FacetManager">
|
||||||
|
<facet type="android-gradle" name="Android-Gradle">
|
||||||
|
<configuration>
|
||||||
|
<option name="GRADLE_PROJECT_PATH" value=":flutter" />
|
||||||
|
</configuration>
|
||||||
|
</facet>
|
||||||
|
<facet type="android" name="Android">
|
||||||
|
<configuration>
|
||||||
|
<option name="ALLOW_USER_CONFIGURATION" value="false" />
|
||||||
|
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
|
||||||
|
</configuration>
|
||||||
|
</facet>
|
||||||
|
</component>
|
||||||
|
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
|
||||||
|
<output url="file://$MODULE_DIR$/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes" />
|
||||||
|
<output-test url="file://$MODULE_DIR$/build/intermediates/javac/debugUnitTest/compileDebugUnitTestJavaWithJavac/classes" />
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="Android API 28 Platform" jdkType="Android SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
@ -0,0 +1,16 @@
|
|||||||
|
<!-- Generated file. Do not edit. -->
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="{{androidIdentifier}}"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<application tools:node="merge">
|
||||||
|
<meta-data
|
||||||
|
android:name="flutterProjectType"
|
||||||
|
android:value="module" />
|
||||||
|
<!-- Don't delete the meta-data below.
|
||||||
|
It is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
|
<meta-data
|
||||||
|
android:name="flutterEmbedding"
|
||||||
|
android:value="2" />
|
||||||
|
</application>
|
||||||
|
</manifest>
|
@ -0,0 +1,48 @@
|
|||||||
|
// Generated file. Do not edit.
|
||||||
|
|
||||||
|
def scriptFile = getClass().protectionDomain.codeSource.location.toURI()
|
||||||
|
def flutterProjectRoot = new File(scriptFile).parentFile.parentFile
|
||||||
|
|
||||||
|
gradle.include ':flutter'
|
||||||
|
gradle.project(':flutter').projectDir = new File(flutterProjectRoot, '.android/Flutter')
|
||||||
|
|
||||||
|
if (System.getProperty('build-plugins-as-aars') != 'true') {
|
||||||
|
def plugins = new Properties()
|
||||||
|
def pluginsFile = new File(flutterProjectRoot, '.flutter-plugins')
|
||||||
|
if (pluginsFile.exists()) {
|
||||||
|
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins.each { name, path ->
|
||||||
|
def pluginDirectory = flutterProjectRoot.toPath().resolve(path).resolve('android').toFile()
|
||||||
|
gradle.include ":$name"
|
||||||
|
gradle.project(":$name").projectDir = pluginDirectory
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gradle.getGradle().projectsLoaded { g ->
|
||||||
|
g.rootProject.beforeEvaluate { p ->
|
||||||
|
_mainModuleName = binding.variables['mainModuleName']
|
||||||
|
if (_mainModuleName != null && !_mainModuleName.empty) {
|
||||||
|
p.ext.mainModuleName = _mainModuleName
|
||||||
|
}
|
||||||
|
def subprojects = []
|
||||||
|
def flutterProject
|
||||||
|
p.subprojects { sp ->
|
||||||
|
if (sp.name == 'flutter') {
|
||||||
|
flutterProject = sp
|
||||||
|
} else {
|
||||||
|
subprojects.add(sp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert flutterProject != null
|
||||||
|
flutterProject.ext.hostProjects = subprojects
|
||||||
|
flutterProject.ext.pluginBuildDir = new File(flutterProjectRoot, 'build/host')
|
||||||
|
}
|
||||||
|
g.rootProject.afterEvaluate { p ->
|
||||||
|
p.subprojects { sp ->
|
||||||
|
if (sp.name != 'flutter') {
|
||||||
|
sp.evaluationDependsOn(':flutter')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
// Generated file. Do not edit.
|
||||||
|
|
||||||
|
rootProject.name = 'android_generated'
|
||||||
|
setBinding(new Binding([gradle: this]))
|
||||||
|
evaluate(new File(settingsDir, 'include_flutter.groovy'))
|
@ -1,5 +1,37 @@
|
|||||||
package {{androidIdentifier}};
|
package {{androidIdentifier}};
|
||||||
|
|
||||||
|
{{#useNewAndroidEmbedding}}
|
||||||
|
{{#androidX}}
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
{{/androidX}}
|
||||||
|
{{^androidX}}
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
{{/androidX}}
|
||||||
|
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
||||||
|
import io.flutter.plugin.common.MethodCall;
|
||||||
|
import io.flutter.plugin.common.MethodChannel;
|
||||||
|
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
||||||
|
import io.flutter.plugin.common.MethodChannel.Result;
|
||||||
|
|
||||||
|
/** {{pluginClass}} */
|
||||||
|
public class {{pluginClass}} implements FlutterPlugin, MethodCallHandler {
|
||||||
|
@Override
|
||||||
|
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
|
||||||
|
final MethodChannel channel = new MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "{{projectName}}");
|
||||||
|
channel.setMethodCallHandler(new {{pluginClass}}());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
|
||||||
|
if (call.method.equals("getPlatformVersion")) {
|
||||||
|
result.success("Android " + android.os.Build.VERSION.RELEASE);
|
||||||
|
} else {
|
||||||
|
result.notImplemented();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{{/useNewAndroidEmbedding}}
|
||||||
|
{{^useNewAndroidEmbedding}}
|
||||||
import io.flutter.plugin.common.MethodCall;
|
import io.flutter.plugin.common.MethodCall;
|
||||||
import io.flutter.plugin.common.MethodChannel;
|
import io.flutter.plugin.common.MethodChannel;
|
||||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
||||||
@ -23,3 +55,4 @@ public class {{pluginClass}} implements MethodCallHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{{/useNewAndroidEmbedding}}
|
||||||
|
@ -1,5 +1,35 @@
|
|||||||
package {{androidIdentifier}}
|
package {{androidIdentifier}}
|
||||||
|
|
||||||
|
{{#useNewAndroidEmbedding}}
|
||||||
|
{{#androidX}}
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
{{/androidX}}
|
||||||
|
{{^androidX}}
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
{{/androidX}}
|
||||||
|
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||||
|
import io.flutter.plugin.common.MethodCall
|
||||||
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
||||||
|
import io.flutter.plugin.common.MethodChannel.Result
|
||||||
|
|
||||||
|
/** {{pluginClass}} */
|
||||||
|
public class {{pluginClass}}: FlutterPlugin, MethodCallHandler {
|
||||||
|
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPluginBinding) {
|
||||||
|
val channel = MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "{{projectName}}")
|
||||||
|
channel.setMethodCallHandler({{pluginClass}}());
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
|
||||||
|
if (call.method == "getPlatformVersion") {
|
||||||
|
result.success("Android ${android.os.Build.VERSION.RELEASE}")
|
||||||
|
} else {
|
||||||
|
result.notImplemented()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{{/useNewAndroidEmbedding}}
|
||||||
|
{{^useNewAndroidEmbedding}}
|
||||||
import io.flutter.plugin.common.MethodCall
|
import io.flutter.plugin.common.MethodCall
|
||||||
import io.flutter.plugin.common.MethodChannel
|
import io.flutter.plugin.common.MethodChannel
|
||||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
||||||
@ -23,3 +53,4 @@ class {{pluginClass}}: MethodCallHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{{/useNewAndroidEmbedding}}
|
||||||
|
@ -5,22 +5,22 @@
|
|||||||
import 'package:file/file.dart';
|
import 'package:file/file.dart';
|
||||||
import 'package:file/memory.dart';
|
import 'package:file/memory.dart';
|
||||||
import 'package:flutter_tools/src/dart/package_map.dart';
|
import 'package:flutter_tools/src/dart/package_map.dart';
|
||||||
|
import 'package:flutter_tools/src/features.dart';
|
||||||
|
import 'package:flutter_tools/src/ios/xcodeproj.dart';
|
||||||
import 'package:flutter_tools/src/plugins.dart';
|
import 'package:flutter_tools/src/plugins.dart';
|
||||||
import 'package:flutter_tools/src/project.dart';
|
import 'package:flutter_tools/src/project.dart';
|
||||||
|
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mockito/mockito.dart';
|
||||||
|
|
||||||
import '../src/common.dart';
|
import '../src/common.dart';
|
||||||
import '../src/context.dart';
|
import '../src/context.dart';
|
||||||
|
|
||||||
class MockFlutterProject extends Mock implements FlutterProject {}
|
|
||||||
class MockIosProject extends Mock implements IosProject {}
|
|
||||||
class MockMacOSProject extends Mock implements MacOSProject {}
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
FileSystem fs;
|
FileSystem fs;
|
||||||
MockFlutterProject flutterProject;
|
MockFlutterProject flutterProject;
|
||||||
MockIosProject iosProject;
|
MockIosProject iosProject;
|
||||||
MockMacOSProject macosProject;
|
MockMacOSProject macosProject;
|
||||||
|
MockAndroidProject androidProject;
|
||||||
File packagesFile;
|
File packagesFile;
|
||||||
Directory dummyPackageDirectory;
|
Directory dummyPackageDirectory;
|
||||||
|
|
||||||
@ -33,10 +33,17 @@ void main() {
|
|||||||
when(flutterProject.flutterPluginsFile).thenReturn(flutterProject.directory.childFile('.plugins'));
|
when(flutterProject.flutterPluginsFile).thenReturn(flutterProject.directory.childFile('.plugins'));
|
||||||
iosProject = MockIosProject();
|
iosProject = MockIosProject();
|
||||||
when(flutterProject.ios).thenReturn(iosProject);
|
when(flutterProject.ios).thenReturn(iosProject);
|
||||||
|
when(iosProject.pluginRegistrantHost).thenReturn(flutterProject.directory.childDirectory('Runner'));
|
||||||
|
when(iosProject.podfile).thenReturn(flutterProject.directory.childDirectory('ios').childFile('Podfile'));
|
||||||
when(iosProject.podManifestLock).thenReturn(flutterProject.directory.childDirectory('ios').childFile('Podfile.lock'));
|
when(iosProject.podManifestLock).thenReturn(flutterProject.directory.childDirectory('ios').childFile('Podfile.lock'));
|
||||||
macosProject = MockMacOSProject();
|
macosProject = MockMacOSProject();
|
||||||
when(flutterProject.macos).thenReturn(macosProject);
|
when(flutterProject.macos).thenReturn(macosProject);
|
||||||
|
when(macosProject.podfile).thenReturn(flutterProject.directory.childDirectory('macos').childFile('Podfile'));
|
||||||
when(macosProject.podManifestLock).thenReturn(flutterProject.directory.childDirectory('macos').childFile('Podfile.lock'));
|
when(macosProject.podManifestLock).thenReturn(flutterProject.directory.childDirectory('macos').childFile('Podfile.lock'));
|
||||||
|
androidProject = MockAndroidProject();
|
||||||
|
when(flutterProject.android).thenReturn(androidProject);
|
||||||
|
when(androidProject.pluginRegistrantHost).thenReturn(flutterProject.directory.childDirectory('android').childDirectory('app'));
|
||||||
|
when(androidProject.hostAppGradleRoot).thenReturn(flutterProject.directory.childDirectory('android'));
|
||||||
|
|
||||||
// Set up a simple .packages file for all the tests to use, pointing to one package.
|
// Set up a simple .packages file for all the tests to use, pointing to one package.
|
||||||
dummyPackageDirectory = fs.directory('/pubcache/apackage/lib/');
|
dummyPackageDirectory = fs.directory('/pubcache/apackage/lib/');
|
||||||
@ -103,4 +110,276 @@ flutter:
|
|||||||
FileSystem: () => fs,
|
FileSystem: () => fs,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('injectPlugins', () {
|
||||||
|
MockFeatureFlags featureFlags;
|
||||||
|
MockXcodeProjectInterpreter xcodeProjectInterpreter;
|
||||||
|
|
||||||
|
const String kAndroidManifestUsingOldEmbedding = '''
|
||||||
|
<manifest>
|
||||||
|
<application>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
||||||
|
''';
|
||||||
|
const String kAndroidManifestUsingNewEmbedding = '''
|
||||||
|
<manifest>
|
||||||
|
<application>
|
||||||
|
<meta-data
|
||||||
|
android:name="flutterEmbedding"
|
||||||
|
android:value="2" />
|
||||||
|
</application>
|
||||||
|
</manifest>
|
||||||
|
''';
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
featureFlags = MockFeatureFlags();
|
||||||
|
when(featureFlags.isLinuxEnabled).thenReturn(false);
|
||||||
|
when(featureFlags.isMacOSEnabled).thenReturn(false);
|
||||||
|
when(featureFlags.isWindowsEnabled).thenReturn(false);
|
||||||
|
when(featureFlags.isWebEnabled).thenReturn(false);
|
||||||
|
|
||||||
|
xcodeProjectInterpreter = MockXcodeProjectInterpreter();
|
||||||
|
when(xcodeProjectInterpreter.isInstalled).thenReturn(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('Registrant uses old embedding in app project', () async {
|
||||||
|
when(flutterProject.isModule).thenReturn(false);
|
||||||
|
when(featureFlags.isNewAndroidEmbeddingEnabled).thenReturn(false);
|
||||||
|
|
||||||
|
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,
|
||||||
|
FeatureFlags: () => featureFlags,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('Registrant uses new embedding if app uses new embedding', () async {
|
||||||
|
when(flutterProject.isModule).thenReturn(false);
|
||||||
|
when(featureFlags.isNewAndroidEmbeddingEnabled).thenReturn(true);
|
||||||
|
|
||||||
|
final File androidManifest = flutterProject.directory
|
||||||
|
.childDirectory('android')
|
||||||
|
.childFile('AndroidManifest.xml')
|
||||||
|
..createSync(recursive: true)
|
||||||
|
..writeAsStringSync(kAndroidManifestUsingNewEmbedding);
|
||||||
|
when(androidProject.appManifestFile).thenReturn(androidManifest);
|
||||||
|
|
||||||
|
await injectPlugins(flutterProject);
|
||||||
|
|
||||||
|
final File registrant = flutterProject.directory
|
||||||
|
.childDirectory(fs.path.join('android', 'app', 'src', 'main', 'java', 'dev', 'flutter', 'plugins'))
|
||||||
|
.childFile('GeneratedPluginRegistrant.java');
|
||||||
|
|
||||||
|
expect(registrant.existsSync(), isTrue);
|
||||||
|
expect(registrant.readAsStringSync(), contains('package dev.flutter.plugins'));
|
||||||
|
expect(registrant.readAsStringSync(), contains('class GeneratedPluginRegistrant'));
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
FileSystem: () => fs,
|
||||||
|
FeatureFlags: () => featureFlags,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('Registrant uses shim for plugins using old embedding if app uses new embedding', () async {
|
||||||
|
when(flutterProject.isModule).thenReturn(false);
|
||||||
|
when(featureFlags.isNewAndroidEmbeddingEnabled).thenReturn(true);
|
||||||
|
|
||||||
|
final File androidManifest = flutterProject.directory
|
||||||
|
.childDirectory('android')
|
||||||
|
.childFile('AndroidManifest.xml')
|
||||||
|
..createSync(recursive: true)
|
||||||
|
..writeAsStringSync(kAndroidManifestUsingNewEmbedding);
|
||||||
|
when(androidProject.appManifestFile).thenReturn(androidManifest);
|
||||||
|
|
||||||
|
final Directory pluginUsingJavaAndNewEmbeddingDir =
|
||||||
|
fs.systemTempDirectory.createTempSync('pluginUsingJavaAndNewEmbeddingDir.');
|
||||||
|
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;');
|
||||||
|
|
||||||
|
final Directory pluginUsingKotlinAndNewEmbeddingDir =
|
||||||
|
fs.systemTempDirectory.createTempSync('pluginUsingKotlinAndNewEmbeddingDir.');
|
||||||
|
pluginUsingKotlinAndNewEmbeddingDir
|
||||||
|
.childFile('pubspec.yaml')
|
||||||
|
.writeAsStringSync('''
|
||||||
|
flutter:
|
||||||
|
plugin:
|
||||||
|
androidPackage: plugin2
|
||||||
|
pluginClass: UseNewEmbedding
|
||||||
|
''');
|
||||||
|
pluginUsingKotlinAndNewEmbeddingDir
|
||||||
|
.childDirectory('android')
|
||||||
|
.childDirectory('src')
|
||||||
|
.childDirectory('main')
|
||||||
|
.childDirectory('kotlin')
|
||||||
|
.childDirectory('plugin2')
|
||||||
|
.childFile('UseNewEmbedding.kt')
|
||||||
|
..createSync(recursive: true)
|
||||||
|
..writeAsStringSync('import io.flutter.embedding.engine.plugins.FlutterPlugin');
|
||||||
|
|
||||||
|
final Directory pluginUsingOldEmbeddingDir =
|
||||||
|
fs.systemTempDirectory.createTempSync('pluginUsingOldEmbeddingDir.');
|
||||||
|
pluginUsingOldEmbeddingDir
|
||||||
|
.childFile('pubspec.yaml')
|
||||||
|
.writeAsStringSync('''
|
||||||
|
flutter:
|
||||||
|
plugin:
|
||||||
|
androidPackage: plugin3
|
||||||
|
pluginClass: UseOldEmbedding
|
||||||
|
''');
|
||||||
|
pluginUsingOldEmbeddingDir
|
||||||
|
.childDirectory('android')
|
||||||
|
.childDirectory('src')
|
||||||
|
.childDirectory('main')
|
||||||
|
.childDirectory('java')
|
||||||
|
.childDirectory('plugin3')
|
||||||
|
.childFile('UseOldEmbedding.java')
|
||||||
|
..createSync(recursive: true);
|
||||||
|
|
||||||
|
flutterProject.directory
|
||||||
|
.childFile('.packages')
|
||||||
|
.writeAsStringSync('''
|
||||||
|
plugin1:${pluginUsingJavaAndNewEmbeddingDir.childDirectory('lib').uri.toString()}
|
||||||
|
plugin2:${pluginUsingKotlinAndNewEmbeddingDir.childDirectory('lib').uri.toString()}
|
||||||
|
plugin3:${pluginUsingOldEmbeddingDir.childDirectory('lib').uri.toString()}
|
||||||
|
''');
|
||||||
|
|
||||||
|
await injectPlugins(flutterProject);
|
||||||
|
|
||||||
|
final File registrant = flutterProject.directory
|
||||||
|
.childDirectory(fs.path.join('android', 'app', 'src', 'main', 'java', 'dev', 'flutter', 'plugins'))
|
||||||
|
.childFile('GeneratedPluginRegistrant.java');
|
||||||
|
|
||||||
|
expect(registrant.readAsStringSync(),
|
||||||
|
contains('flutterEngine.getPlugins().add(new plugin1.UseNewEmbedding());'));
|
||||||
|
expect(registrant.readAsStringSync(),
|
||||||
|
contains('flutterEngine.getPlugins().add(new plugin2.UseNewEmbedding());'));
|
||||||
|
expect(registrant.readAsStringSync(),
|
||||||
|
contains('plugin3.UseOldEmbedding.registerWith(shimPluginRegistry.registrarFor("plugin3.UseOldEmbedding"));'));
|
||||||
|
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
FileSystem: () => fs,
|
||||||
|
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(featureFlags.isNewAndroidEmbeddingEnabled).thenReturn(true);
|
||||||
|
|
||||||
|
final File androidManifest = flutterProject.directory
|
||||||
|
.childDirectory('android')
|
||||||
|
.childFile('AndroidManifest.xml')
|
||||||
|
..createSync(recursive: true)
|
||||||
|
..writeAsStringSync(kAndroidManifestUsingOldEmbedding);
|
||||||
|
when(androidProject.appManifestFile).thenReturn(androidManifest);
|
||||||
|
|
||||||
|
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,
|
||||||
|
FeatureFlags: () => featureFlags,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('Registrant uses old embedding in module project', () async {
|
||||||
|
when(flutterProject.isModule).thenReturn(true);
|
||||||
|
when(featureFlags.isNewAndroidEmbeddingEnabled).thenReturn(false);
|
||||||
|
|
||||||
|
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,
|
||||||
|
FeatureFlags: () => featureFlags,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('Registrant uses new embedding if module uses new embedding', () async {
|
||||||
|
when(flutterProject.isModule).thenReturn(true);
|
||||||
|
when(featureFlags.isNewAndroidEmbeddingEnabled).thenReturn(true);
|
||||||
|
|
||||||
|
final File androidManifest = flutterProject.directory
|
||||||
|
.childDirectory('android')
|
||||||
|
.childFile('AndroidManifest.xml')
|
||||||
|
..createSync(recursive: true)
|
||||||
|
..writeAsStringSync(kAndroidManifestUsingNewEmbedding);
|
||||||
|
when(androidProject.appManifestFile).thenReturn(androidManifest);
|
||||||
|
|
||||||
|
await injectPlugins(flutterProject);
|
||||||
|
|
||||||
|
final File registrant = flutterProject.directory
|
||||||
|
.childDirectory(fs.path.join('android', 'app', 'src', 'main', 'java', 'dev', 'flutter', 'plugins'))
|
||||||
|
.childFile('GeneratedPluginRegistrant.java');
|
||||||
|
|
||||||
|
expect(registrant.existsSync(), isTrue);
|
||||||
|
expect(registrant.readAsStringSync(), contains('package dev.flutter.plugins'));
|
||||||
|
expect(registrant.readAsStringSync(), contains('class GeneratedPluginRegistrant'));
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
FileSystem: () => fs,
|
||||||
|
FeatureFlags: () => featureFlags,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('Registrant doesn\'t use new embedding if module doesn\'t use new embedding', () async {
|
||||||
|
when(flutterProject.isModule).thenReturn(true);
|
||||||
|
when(featureFlags.isNewAndroidEmbeddingEnabled).thenReturn(true);
|
||||||
|
|
||||||
|
final File androidManifest = flutterProject.directory
|
||||||
|
.childDirectory('android')
|
||||||
|
.childFile('AndroidManifest.xml')
|
||||||
|
..createSync(recursive: true)
|
||||||
|
..writeAsStringSync(kAndroidManifestUsingOldEmbedding);
|
||||||
|
when(androidProject.appManifestFile).thenReturn(androidManifest);
|
||||||
|
|
||||||
|
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,
|
||||||
|
FeatureFlags: () => featureFlags,
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MockAndroidProject extends Mock implements AndroidProject {}
|
||||||
|
class MockFeatureFlags extends Mock implements FeatureFlags {}
|
||||||
|
class MockFlutterProject extends Mock implements FlutterProject {}
|
||||||
|
class MockIosProject extends Mock implements IosProject {}
|
||||||
|
class MockMacOSProject extends Mock implements MacOSProject {}
|
||||||
|
class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter {}
|
||||||
|
@ -689,6 +689,7 @@ class TestFeatureFlags implements FeatureFlags {
|
|||||||
this.isMacOSEnabled = false,
|
this.isMacOSEnabled = false,
|
||||||
this.isWebEnabled = false,
|
this.isWebEnabled = false,
|
||||||
this.isWindowsEnabled = false,
|
this.isWindowsEnabled = false,
|
||||||
|
this.isNewAndroidEmbeddingEnabled = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -702,4 +703,7 @@ class TestFeatureFlags implements FeatureFlags {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
final bool isWindowsEnabled;
|
final bool isWindowsEnabled;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final bool isNewAndroidEmbeddingEnabled;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user