Remove embedding v1 code in framework (#144726)

Pre work for https://github.com/flutter/engine/pull/51229. Removes a lot of code referencing v1 of the android embedding, though not necessarily all of it (I may have missed some, it is hard to know).

Will hopefully make landing that PR less painful (or maybe painless?)
This commit is contained in:
Gray Mackall 2024-03-20 13:34:14 -07:00 committed by GitHub
parent f9f77a481a
commit 39bdff16c1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 136 additions and 603 deletions

View file

@ -277,38 +277,6 @@ String? _readFileContent(File file) {
return file.existsSync() ? file.readAsStringSync() : null;
}
const String _androidPluginRegistryTemplateOldEmbedding = '''
package io.flutter.plugins;
import io.flutter.plugin.common.PluginRegistry;
{{#methodChannelPlugins}}
import {{package}}.{{class}};
{{/methodChannelPlugins}}
/**
* Generated file. Do not edit.
*/
public final class GeneratedPluginRegistrant {
public static void registerWith(PluginRegistry registry) {
if (alreadyRegisteredWith(registry)) {
return;
}
{{#methodChannelPlugins}}
{{class}}.registerWith(registry.registrarFor("{{package}}.{{class}}"));
{{/methodChannelPlugins}}
}
private static boolean alreadyRegisteredWith(PluginRegistry registry) {
final String key = GeneratedPluginRegistrant.class.getCanonicalName();
if (registry.hasPlugin(key)) {
return true;
}
registry.registrarFor(key);
return false;
}
}
''';
const String _androidPluginRegistryTemplateNewEmbedding = '''
package io.flutter.plugins;
@ -317,9 +285,6 @@ import androidx.annotation.NonNull;
import io.flutter.Log;
import io.flutter.embedding.engine.FlutterEngine;
{{#needsShim}}
import io.flutter.embedding.engine.plugins.shim.ShimPluginRegistry;
{{/needsShim}}
/**
* Generated file. Do not edit.
@ -330,9 +295,6 @@ import io.flutter.embedding.engine.plugins.shim.ShimPluginRegistry;
public final class GeneratedPluginRegistrant {
private static final String TAG = "GeneratedPluginRegistrant";
public static void registerWith(@NonNull FlutterEngine flutterEngine) {
{{#needsShim}}
ShimPluginRegistry shimPluginRegistry = new ShimPluginRegistry(flutterEngine);
{{/needsShim}}
{{#methodChannelPlugins}}
{{#supportsEmbeddingV2}}
try {
@ -341,15 +303,6 @@ public final class GeneratedPluginRegistrant {
Log.e(TAG, "Error registering plugin {{name}}, {{package}}.{{class}}", e);
}
{{/supportsEmbeddingV2}}
{{^supportsEmbeddingV2}}
{{#supportsEmbeddingV1}}
try {
{{package}}.{{class}}.registerWith(shimPluginRegistry.registrarFor("{{package}}.{{class}}"));
} catch (Exception e) {
Log.e(TAG, "Error registering plugin {{name}}, {{package}}.{{class}}", e);
}
{{/supportsEmbeddingV1}}
{{/supportsEmbeddingV2}}
{{/methodChannelPlugins}}
}
}
@ -366,12 +319,6 @@ List<Map<String, Object?>> _extractPlatformMaps(List<Plugin> plugins, String typ
return pluginConfigs;
}
/// Returns the version of the Android embedding that the current
/// [project] is using.
AndroidEmbeddingVersion _getAndroidEmbeddingVersion(FlutterProject project) {
return project.android.getEmbeddingVersion();
}
Future<void> _writeAndroidPluginRegistrant(FlutterProject project, List<Plugin> plugins) async {
final List<Plugin> methodChannelPlugins = _filterMethodChannelPlugins(plugins, AndroidPlugin.kConfigKey);
final List<Map<String, Object?>> androidPlugins = _extractPlatformMaps(methodChannelPlugins, AndroidPlugin.kConfigKey);
@ -393,65 +340,7 @@ Future<void> _writeAndroidPluginRegistrant(FlutterProject project, List<Plugin>
'plugins',
'GeneratedPluginRegistrant.java',
);
String templateContent;
final AndroidEmbeddingVersion appEmbeddingVersion = _getAndroidEmbeddingVersion(project);
switch (appEmbeddingVersion) {
case AndroidEmbeddingVersion.v2:
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.
final List<String> pluginsUsingV1 = <String>[];
for (final Map<String, Object?> plugin in androidPlugins) {
final bool supportsEmbeddingV1 = (plugin['supportsEmbeddingV1'] as bool?) ?? false;
final bool supportsEmbeddingV2 = (plugin['supportsEmbeddingV2'] as bool?) ?? false;
if (supportsEmbeddingV1 && !supportsEmbeddingV2) {
templateContext['needsShim'] = true;
if (plugin['name'] != null) {
pluginsUsingV1.add(plugin['name']! as String);
}
}
}
if (pluginsUsingV1.length > 1) {
globals.printWarning(
'The plugins `${pluginsUsingV1.join(', ')}` use a deprecated version of the Android embedding.\n'
'To avoid unexpected runtime failures, or future build failures, try to see if these plugins '
'support the Android V2 embedding. Otherwise, consider removing them since a future release '
'of Flutter will remove these deprecated APIs.\n'
'If you are plugin author, take a look at the docs for migrating the plugin to the V2 embedding: '
'https://flutter.dev/go/android-plugin-migration.'
);
} else if (pluginsUsingV1.isNotEmpty) {
globals.printWarning(
'The plugin `${pluginsUsingV1.first}` uses a deprecated version of the Android embedding.\n'
'To avoid unexpected runtime failures, or future build failures, try to see if this plugin '
'supports the Android V2 embedding. Otherwise, consider removing it since a future release '
'of Flutter will remove these deprecated APIs.\n'
'If you are plugin author, take a look at the docs for migrating the plugin to the V2 embedding: '
'https://flutter.dev/go/android-plugin-migration.'
);
}
templateContent = _androidPluginRegistryTemplateNewEmbedding;
case AndroidEmbeddingVersion.v1:
globals.printWarning(
'This app is using a deprecated version of the Android embedding.\n'
'To avoid unexpected runtime failures, or future build failures, try to migrate this '
'app to the V2 embedding.\n'
'Take a look at the docs for migrating an app: https://github.com/flutter/flutter/wiki/Upgrading-pre-1.12-Android-projects'
);
for (final Map<String, Object?> plugin in androidPlugins) {
final bool supportsEmbeddingV1 = (plugin['supportsEmbeddingV1'] as bool?) ?? false;
final bool supportsEmbeddingV2 = (plugin['supportsEmbeddingV2'] as bool?) ?? false;
if (!supportsEmbeddingV1 && supportsEmbeddingV2) {
throwToolExit(
'The plugin `${plugin['name']}` requires your app to be migrated to '
'the Android embedding v2. Follow the steps on the migration doc above '
'and re-run this command.'
);
}
}
templateContent = _androidPluginRegistryTemplateOldEmbedding;
}
const String templateContent = _androidPluginRegistryTemplateNewEmbedding;
globals.printTrace('Generating $registryPath');
await _renderTemplateToFile(
templateContent,

View file

@ -3,7 +3,6 @@
// found in the LICENSE file.
import 'package:meta/meta.dart';
import 'package:unified_analytics/unified_analytics.dart';
import 'package:xml/xml.dart';
import 'package:yaml/yaml.dart';
@ -24,7 +23,6 @@ import 'flutter_plugins.dart';
import 'globals.dart' as globals;
import 'platform_plugins.dart';
import 'project_validator_result.dart';
import 'reporting/reporting.dart';
import 'template.dart';
import 'xcode_project.dart';
@ -806,47 +804,23 @@ $javaGradleCompatUrl
if (result.version != AndroidEmbeddingVersion.v1) {
return;
}
globals.printStatus(
'''
Warning
Your Flutter application is created using an older version of the Android
embedding. It is being deprecated in favor of Android embedding v2. To migrate
your project, follow the steps at:
https://github.com/flutter/flutter/wiki/Upgrading-pre-1.12-Android-projects
The detected reason was:
${result.reason}
''');
if (deprecationBehavior == DeprecationBehavior.ignore) {
BuildEvent('deprecated-v1-android-embedding-ignored', type: 'gradle', flutterUsage: globals.flutterUsage).send();
globals.analytics.send(
Event.flutterBuildInfo(
label: 'deprecated-v1-android-embedding-ignored',
buildType: 'gradle',
));
} else { // DeprecationBehavior.exit
globals.analytics.send(
Event.flutterBuildInfo(
label: 'deprecated-v1-android-embedding-failed',
buildType: 'gradle',
));
throwToolExit(
'Build failed due to use of deprecated Android v1 embedding.',
exitCode: 1,
);
}
// The v1 android embedding has been deleted.
throwToolExit(
'Build failed due to use of deleted Android v1 embedding.',
exitCode: 1,
);
}
AndroidEmbeddingVersion getEmbeddingVersion() {
return computeEmbeddingVersion().version;
final AndroidEmbeddingVersion androidEmbeddingVersion = computeEmbeddingVersion().version;
if (androidEmbeddingVersion == AndroidEmbeddingVersion.v1) {
throwToolExit(
'Build failed due to use of deleted Android v1 embedding.',
exitCode: 1,
);
}
return androidEmbeddingVersion;
}
AndroidEmbeddingVersionResult computeEmbeddingVersion() {

View file

@ -16,6 +16,17 @@ import 'package:test/fake.dart';
import '../../src/context.dart';
import '../../src/test_flutter_command_runner.dart';
const String minimalV2EmbeddingManifest = r'''
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:name="${applicationName}">
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
''';
void main() {
late FileSystem fileSystem;
late FakePub pub;
@ -48,6 +59,9 @@ void main() {
fileSystem.currentDirectory.childFile('pubspec.yaml').createSync();
fileSystem.currentDirectory.childFile('.flutter-plugins').createSync();
fileSystem.currentDirectory.childFile('.flutter-plugins-dependencies').createSync();
fileSystem.currentDirectory.childDirectory('android').childFile('AndroidManifest.xml')
..createSync(recursive: true)
..writeAsStringSync(minimalV2EmbeddingManifest);
final PackagesGetCommand command = PackagesGetCommand('get', '', PubContext.pubGet);
final CommandRunner<void> commandRunner = createTestCommandRunner(command);
@ -57,7 +71,7 @@ void main() {
expect(await command.usageValues, const CustomDimensions(
commandPackagesNumberPlugins: 0,
commandPackagesProjectModule: false,
commandPackagesAndroidEmbeddingVersion: 'v1',
commandPackagesAndroidEmbeddingVersion: 'v2',
));
}, overrides: <Type, Generator>{
Pub: () => pub,
@ -73,6 +87,9 @@ void main() {
fileSystem.currentDirectory.childFile('.dart_tool/package_config.json')
..createSync(recursive: true)
..writeAsBytesSync(<int>[0]);
fileSystem.currentDirectory.childDirectory('android').childFile('AndroidManifest.xml')
..createSync(recursive: true)
..writeAsStringSync(minimalV2EmbeddingManifest);
final PackagesGetCommand command = PackagesGetCommand('get', '', PubContext.pubGet);
final CommandRunner<void> commandRunner = createTestCommandRunner(command);
@ -82,7 +99,7 @@ void main() {
expect(await command.usageValues, const CustomDimensions(
commandPackagesNumberPlugins: 0,
commandPackagesProjectModule: false,
commandPackagesAndroidEmbeddingVersion: 'v1',
commandPackagesAndroidEmbeddingVersion: 'v2',
));
}, overrides: <Type, Generator>{
Pub: () => pub,
@ -137,6 +154,9 @@ void main() {
testUsingContext("pub get skips example directory if it doesn't contain a pubspec.yaml", () async {
fileSystem.currentDirectory.childFile('pubspec.yaml').createSync();
fileSystem.currentDirectory.childDirectory('example').createSync(recursive: true);
fileSystem.currentDirectory.childDirectory('android').childFile('AndroidManifest.xml')
..createSync(recursive: true)
..writeAsStringSync(minimalV2EmbeddingManifest);
final PackagesGetCommand command = PackagesGetCommand('get', '', PubContext.pubGet);
final CommandRunner<void> commandRunner = createTestCommandRunner(command);
@ -146,7 +166,7 @@ void main() {
expect(await command.usageValues, const CustomDimensions(
commandPackagesNumberPlugins: 0,
commandPackagesProjectModule: false,
commandPackagesAndroidEmbeddingVersion: 'v1',
commandPackagesAndroidEmbeddingVersion: 'v2',
));
}, overrides: <Type, Generator>{
Pub: () => pub,

View file

@ -7,15 +7,12 @@ import 'dart:async';
import 'package:args/command_runner.dart';
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_device.dart';
import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/application_package.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
@ -369,84 +366,6 @@ void main() {
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
});
testUsingContext('fails when v1 FlutterApplication is detected', () async {
fs.file('pubspec.yaml').createSync();
fs.file('android/AndroidManifest.xml')
..createSync(recursive: true)
..writeAsStringSync('''
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.v1">
<application
android:name="io.flutter.app.FlutterApplication">
</application>
</manifest>
''', flush: true);
fs.file('.packages').writeAsStringSync('\n');
fs.file('lib/main.dart').createSync(recursive: true);
final AndroidDevice device = AndroidDevice('1234',
modelID: 'TestModel',
logger: testLogger,
platform: FakePlatform(),
androidSdk: FakeAndroidSdk(),
fileSystem: fs,
processManager: FakeProcessManager.any(),
);
testDeviceManager.devices = <Device>[device];
final RunCommand command = RunCommand();
await expectLater(createTestCommandRunner(command).run(<String>[
'run',
'--pub',
]), throwsToolExit(message: 'Build failed due to use of deprecated Android v1 embedding.'));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
DeviceManager: () => testDeviceManager,
Stdio: () => FakeStdio(),
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
});
testUsingContext('fails when v1 metadata is detected', () async {
fs.file('pubspec.yaml').createSync();
fs.file('android/AndroidManifest.xml')
..createSync(recursive: true)
..writeAsStringSync('''
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.v1">
<application >
<meta-data
android:name="flutterEmbedding"
android:value="1" />
</application>
</manifest>
''', flush: true);
fs.file('.packages').writeAsStringSync('\n');
fs.file('lib/main.dart').createSync(recursive: true);
final AndroidDevice device = AndroidDevice('1234',
modelID: 'TestModel',
logger: testLogger,
platform: FakePlatform(),
androidSdk: FakeAndroidSdk(),
fileSystem: fs,
processManager: FakeProcessManager.any(),
);
testDeviceManager.devices = <Device>[device];
final RunCommand command = RunCommand();
await expectLater(createTestCommandRunner(command).run(<String>[
'run',
'--pub',
]), throwsToolExit(message: 'Build failed due to use of deprecated Android v1 embedding.'));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
DeviceManager: () => testDeviceManager,
Stdio: () => FakeStdio(),
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
});
testUsingContext('shows unsupported devices when no supported devices are found', () async {
final RunCommand command = RunCommand();
final FakeDevice mockDevice = FakeDevice(
@ -1288,11 +1207,6 @@ class TestDeviceManager extends DeviceManager {
}
}
class FakeAndroidSdk extends Fake implements AndroidSdk {
@override
String get adbPath => 'adb';
}
class FakeDevice extends Fake implements Device {
FakeDevice({
bool isLocalEmulator = false,

View file

@ -457,42 +457,6 @@ flutter:
),
});
testUsingContext('indicate that Android project reports v1 in usage value', () async {
final String projectPath = await createProject(tempDir,
arguments: <String>['--no-pub']);
removeGeneratedFiles(projectPath);
final File androidManifest = globals.fs.file(globals.fs.path.join(
projectPath,
'android/app/src/main/AndroidManifest.xml',
));
final String updatedAndroidManifestString =
androidManifest.readAsStringSync().replaceAll('android:value="2"', 'android:value="1"');
androidManifest.writeAsStringSync(updatedAndroidManifestString);
final PackagesCommand command = await runCommandIn(projectPath, 'get');
final PackagesGetCommand getCommand = command.subcommands['get']! as PackagesGetCommand;
expect((await getCommand.usageValues).commandPackagesAndroidEmbeddingVersion, 'v1');
expect(
(await getCommand.unifiedAnalyticsUsageValues('pub/get'))
.eventData['packagesAndroidEmbeddingVersion'],
'v1',
);
}, overrides: <Type, Generator>{
Stdio: () => mockStdio,
Pub: () => Pub.test(
fileSystem: globals.fs,
logger: globals.logger,
processManager: globals.processManager,
usage: globals.flutterUsage,
botDetector: globals.botDetector,
platform: globals.platform,
stdio: mockStdio,
),
});
testUsingContext('indicate that Android project reports v2 in usage value', () async {
final String projectPath = await createProject(tempDir,
arguments: <String>['--no-pub']);

View file

@ -30,6 +30,17 @@ import '../../src/context.dart';
import '../../src/fake_process_manager.dart';
import '../../src/fakes.dart';
const String minimalV2EmbeddingManifest = r'''
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:name="${applicationName}">
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
''';
void main() {
group('gradle build', () {
late BufferLogger logger;
@ -70,7 +81,7 @@ void main() {
'-q',
'-Ptarget-platform=android-arm,android-arm64,android-x64',
'-Ptarget=lib/main.dart',
'-Pbase-application-name=io.flutter.app.FlutterApplication',
'-Pbase-application-name=android.app.Application',
'-Pdart-obfuscation=false',
'-Ptrack-widget-creation=false',
'-Ptree-shake-icons=false',
@ -94,10 +105,15 @@ void main() {
..createSync(recursive: true)
..writeAsStringSync('apply from: irrelevant/flutter.gradle');
final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
project.android.appManifestFile
..createSync(recursive: true)
..writeAsStringSync(minimalV2EmbeddingManifest);
bool handlerCalled = false;
await expectLater(() async {
await builder.buildGradleApp(
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
project: project,
androidBuildInfo: const AndroidBuildInfo(
BuildInfo(
BuildMode.release,
@ -184,7 +200,7 @@ void main() {
'-Pverbose=true',
'-Ptarget-platform=android-arm,android-arm64,android-x64',
'-Ptarget=lib/main.dart',
'-Pbase-application-name=io.flutter.app.FlutterApplication',
'-Pbase-application-name=android.app.Application',
'-Pdart-obfuscation=false',
'-Ptrack-widget-creation=false',
'-Ptree-shake-icons=false',
@ -213,8 +229,13 @@ void main() {
.childFile('app-release.apk')
.createSync(recursive: true);
final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
project.android.appManifestFile
..createSync(recursive: true)
..writeAsStringSync(minimalV2EmbeddingManifest);
await builder.buildGradleApp(
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
project: project,
androidBuildInfo: const AndroidBuildInfo(
BuildInfo(
BuildMode.release,
@ -252,7 +273,7 @@ void main() {
'-q',
'-Ptarget-platform=android-arm,android-arm64,android-x64',
'-Ptarget=lib/main.dart',
'-Pbase-application-name=io.flutter.app.FlutterApplication',
'-Pbase-application-name=android.app.Application',
'-Pdart-obfuscation=false',
'-Ptrack-widget-creation=false',
'-Ptree-shake-icons=false',
@ -283,11 +304,16 @@ void main() {
..createSync(recursive: true)
..writeAsStringSync('apply from: irrelevant/flutter.gradle');
final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
project.android.appManifestFile
..createSync(recursive: true)
..writeAsStringSync(minimalV2EmbeddingManifest);
int testFnCalled = 0;
await expectLater(() async {
await builder.buildGradleApp(
maxRetries: maxRetries,
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
project: project,
androidBuildInfo: const AndroidBuildInfo(
BuildInfo(
BuildMode.release,
@ -366,7 +392,7 @@ void main() {
'-q',
'-Ptarget-platform=android-arm,android-arm64,android-x64',
'-Ptarget=lib/main.dart',
'-Pbase-application-name=io.flutter.app.FlutterApplication',
'-Pbase-application-name=android.app.Application',
'-Pdart-obfuscation=false',
'-Ptrack-widget-creation=false',
'-Ptree-shake-icons=false',
@ -390,10 +416,15 @@ void main() {
..createSync(recursive: true)
..writeAsStringSync('apply from: irrelevant/flutter.gradle');
final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
project.android.appManifestFile
..createSync(recursive: true)
..writeAsStringSync(minimalV2EmbeddingManifest);
bool handlerCalled = false;
await expectLater(() async {
await builder.buildGradleApp(
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
project: project,
androidBuildInfo: const AndroidBuildInfo(
BuildInfo(
BuildMode.release,
@ -469,7 +500,7 @@ void main() {
'-q',
'-Ptarget-platform=android-arm,android-arm64,android-x64',
'-Ptarget=lib/main.dart',
'-Pbase-application-name=io.flutter.app.FlutterApplication',
'-Pbase-application-name=android.app.Application',
'-Pdart-obfuscation=false',
'-Ptrack-widget-creation=false',
'-Ptree-shake-icons=false',
@ -495,9 +526,14 @@ void main() {
..createSync(recursive: true)
..writeAsStringSync('apply from: irrelevant/flutter.gradle');
final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
project.android.appManifestFile
..createSync(recursive: true)
..writeAsStringSync(minimalV2EmbeddingManifest);
await expectLater(() async {
await builder.buildGradleApp(
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
project: project,
androidBuildInfo: const AndroidBuildInfo(
BuildInfo(
BuildMode.release,
@ -535,7 +571,7 @@ void main() {
'-q',
'-Ptarget-platform=android-arm,android-arm64,android-x64',
'-Ptarget=lib/main.dart',
'-Pbase-application-name=io.flutter.app.FlutterApplication',
'-Pbase-application-name=android.app.Application',
'-Pdart-obfuscation=false',
'-Ptrack-widget-creation=false',
'-Ptree-shake-icons=false',
@ -550,7 +586,7 @@ void main() {
'-q',
'-Ptarget-platform=android-arm,android-arm64,android-x64',
'-Ptarget=lib/main.dart',
'-Pbase-application-name=io.flutter.app.FlutterApplication',
'-Pbase-application-name=android.app.Application',
'-Pdart-obfuscation=false',
'-Ptrack-widget-creation=false',
'-Ptree-shake-icons=false',
@ -579,8 +615,13 @@ void main() {
.childFile('app-release.apk')
.createSync(recursive: true);
final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
project.android.appManifestFile
..createSync(recursive: true)
..writeAsStringSync(minimalV2EmbeddingManifest);
await builder.buildGradleApp(
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
project: project,
androidBuildInfo: const AndroidBuildInfo(
BuildInfo(
BuildMode.release,
@ -643,7 +684,7 @@ void main() {
'-q',
'-Ptarget-platform=android-arm64',
'-Ptarget=lib/main.dart',
'-Pbase-application-name=io.flutter.app.FlutterApplication',
'-Pbase-application-name=android.app.Application',
'-Pdart-obfuscation=false',
'-Ptrack-widget-creation=false',
'-Ptree-shake-icons=false',
@ -696,8 +737,13 @@ void main() {
..createSync(recursive: true)
..writeAsStringSync('{}');
final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
project.android.appManifestFile
..createSync(recursive: true)
..writeAsStringSync(minimalV2EmbeddingManifest);
await builder.buildGradleApp(
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
project: project,
androidBuildInfo: const AndroidBuildInfo(
BuildInfo(
BuildMode.release,
@ -742,7 +788,7 @@ void main() {
'-q',
'-Ptarget-platform=android-arm,android-arm64,android-x64',
'-Ptarget=lib/main.dart',
'-Pbase-application-name=io.flutter.app.FlutterApplication',
'-Pbase-application-name=android.app.Application',
'-Pdart-obfuscation=false',
'-Ptrack-widget-creation=false',
'-Ptree-shake-icons=false',
@ -770,8 +816,13 @@ void main() {
.childFile('app-release.apk')
.createSync(recursive: true);
final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
project.android.appManifestFile
..createSync(recursive: true)
..writeAsStringSync(minimalV2EmbeddingManifest);
await builder.buildGradleApp(
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
project: project,
androidBuildInfo: const AndroidBuildInfo(
BuildInfo(
BuildMode.release,
@ -1194,7 +1245,7 @@ Gradle Crashed
'-Plocal-engine-host-out=out/host_release',
'-Ptarget-platform=android-arm',
'-Ptarget=lib/main.dart',
'-Pbase-application-name=io.flutter.app.FlutterApplication',
'-Pbase-application-name=android.app.Application',
'-Pdart-obfuscation=false',
'-Ptrack-widget-creation=false',
'-Ptree-shake-icons=false',
@ -1230,10 +1281,14 @@ Gradle Crashed
.childFile('build.gradle')
..createSync(recursive: true)
..writeAsStringSync('apply from: irrelevant/flutter.gradle');
final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
project.android.appManifestFile
..createSync(recursive: true)
..writeAsStringSync(minimalV2EmbeddingManifest);
await expectLater(() async {
await builder.buildGradleApp(
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
project: project,
androidBuildInfo: const AndroidBuildInfo(
BuildInfo(
BuildMode.release,
@ -1275,7 +1330,7 @@ Gradle Crashed
'-Plocal-engine-host-out=out/host_release',
'-Ptarget-platform=android-arm64',
'-Ptarget=lib/main.dart',
'-Pbase-application-name=io.flutter.app.FlutterApplication',
'-Pbase-application-name=android.app.Application',
'-Pdart-obfuscation=false',
'-Ptrack-widget-creation=false',
'-Ptree-shake-icons=false',
@ -1311,10 +1366,14 @@ Gradle Crashed
.childFile('build.gradle')
..createSync(recursive: true)
..writeAsStringSync('apply from: irrelevant/flutter.gradle');
final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
project.android.appManifestFile
..createSync(recursive: true)
..writeAsStringSync(minimalV2EmbeddingManifest);
await expectLater(() async {
await builder.buildGradleApp(
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
project: project,
androidBuildInfo: const AndroidBuildInfo(
BuildInfo(
BuildMode.release,
@ -1356,7 +1415,7 @@ Gradle Crashed
'-Plocal-engine-host-out=out/host_release',
'-Ptarget-platform=android-x86',
'-Ptarget=lib/main.dart',
'-Pbase-application-name=io.flutter.app.FlutterApplication',
'-Pbase-application-name=android.app.Application',
'-Pdart-obfuscation=false',
'-Ptrack-widget-creation=false',
'-Ptree-shake-icons=false',
@ -1392,10 +1451,14 @@ Gradle Crashed
.childFile('build.gradle')
..createSync(recursive: true)
..writeAsStringSync('apply from: irrelevant/flutter.gradle');
final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
project.android.appManifestFile
..createSync(recursive: true)
..writeAsStringSync(minimalV2EmbeddingManifest);
await expectLater(() async {
await builder.buildGradleApp(
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
project: project,
androidBuildInfo: const AndroidBuildInfo(
BuildInfo(
BuildMode.release,
@ -1437,7 +1500,7 @@ Gradle Crashed
'-Plocal-engine-host-out=out/host_release',
'-Ptarget-platform=android-x64',
'-Ptarget=lib/main.dart',
'-Pbase-application-name=io.flutter.app.FlutterApplication',
'-Pbase-application-name=android.app.Application',
'-Pdart-obfuscation=false',
'-Ptrack-widget-creation=false',
'-Ptree-shake-icons=false',
@ -1474,10 +1537,14 @@ Gradle Crashed
.childFile('build.gradle')
..createSync(recursive: true)
..writeAsStringSync('apply from: irrelevant/flutter.gradle');
final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
project.android.appManifestFile
..createSync(recursive: true)
..writeAsStringSync(minimalV2EmbeddingManifest);
await expectLater(() async {
await builder.buildGradleApp(
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
project: project,
androidBuildInfo: const AndroidBuildInfo(
BuildInfo(
BuildMode.release,
@ -1516,7 +1583,7 @@ Gradle Crashed
'--no-daemon',
'-Ptarget-platform=android-arm,android-arm64,android-x64',
'-Ptarget=lib/main.dart',
'-Pbase-application-name=io.flutter.app.FlutterApplication',
'-Pbase-application-name=android.app.Application',
'-Pdart-obfuscation=false',
'-Ptrack-widget-creation=false',
'-Ptree-shake-icons=false',
@ -1535,10 +1602,14 @@ Gradle Crashed
.childFile('build.gradle')
..createSync(recursive: true)
..writeAsStringSync('apply from: irrelevant/flutter.gradle');
final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
project.android.appManifestFile
..createSync(recursive: true)
..writeAsStringSync(minimalV2EmbeddingManifest);
await expectLater(() async {
await builder.buildGradleApp(
project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory),
project: project,
androidBuildInfo: const AndroidBuildInfo(
BuildInfo(
BuildMode.release,

View file

@ -286,63 +286,6 @@ flutter:
return pluginUsingJavaAndNewEmbeddingDir;
}
void createNewKotlinPlugin2() {
final Directory pluginUsingKotlinAndNewEmbeddingDir =
fs.systemTempDirectory.createTempSync('flutter_plugin_using_kotlin_and_new_embedding_dir.');
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');
flutterProject.directory
.childFile('.packages')
.writeAsStringSync(
'plugin2:${pluginUsingKotlinAndNewEmbeddingDir.childDirectory('lib').uri}\n',
mode: FileMode.append,
);
}
void createOldJavaPlugin(String pluginName) {
final Directory pluginUsingOldEmbeddingDir =
fs.systemTempDirectory.createTempSync('flutter_plugin_using_old_embedding_dir.');
pluginUsingOldEmbeddingDir
.childFile('pubspec.yaml')
.writeAsStringSync('''
flutter:
plugin:
androidPackage: $pluginName
pluginClass: UseOldEmbedding
''');
pluginUsingOldEmbeddingDir
.childDirectory('android')
.childDirectory('src')
.childDirectory('main')
.childDirectory('java')
.childDirectory(pluginName)
.childFile('UseOldEmbedding.java')
.createSync(recursive: true);
flutterProject.directory
.childFile('.packages')
.writeAsStringSync(
'$pluginName:${pluginUsingOldEmbeddingDir.childDirectory('lib').uri}\n',
mode: FileMode.append,
);
}
void createDualSupportJavaPlugin4() {
final Directory pluginUsingJavaAndNewEmbeddingDir =
fs.systemTempDirectory.createTempSync('flutter_plugin_using_java_and_new_embedding_dir.');
@ -714,24 +657,6 @@ dependencies:
xcodeProjectInterpreter = FakeXcodeProjectInterpreter();
});
testUsingContext('Registrant uses old embedding in app project', () async {
androidProject.embeddingVersion = AndroidEmbeddingVersion.v1;
await injectPlugins(flutterProject, androidPlatform: true);
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'));
expect(registrant.readAsStringSync(), contains('public static void registerWith(PluginRegistry registry)'));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('Registrant uses new embedding if app uses new embedding', () async {
androidProject.embeddingVersion = AndroidEmbeddingVersion.v2;
@ -750,54 +675,6 @@ dependencies:
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('Registrant uses shim for plugins using old embedding if app uses new embedding', () async {
androidProject.embeddingVersion = AndroidEmbeddingVersion.v2;
createNewJavaPlugin1();
createNewKotlinPlugin2();
createOldJavaPlugin('plugin3');
await injectPlugins(flutterProject, androidPlatform: true);
final File registrant = flutterProject.directory
.childDirectory(fs.path.join('android', 'app', 'src', 'main', 'java', 'io', '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"));'));
// There should be no warning message
expect(testLogger.statusText, isNot(contains('go/android-plugin-migration')));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
XcodeProjectInterpreter: () => xcodeProjectInterpreter,
});
testUsingContext('exits the tool if an app uses the v1 embedding and a plugin only supports the v2 embedding', () async {
androidProject.embeddingVersion = AndroidEmbeddingVersion.v1;
createNewJavaPlugin1();
await expectLater(
() async {
await injectPlugins(flutterProject, androidPlatform: true);
},
throwsToolExit(
message: 'The plugin `plugin1` requires your app to be migrated to the Android embedding v2. '
'Follow the steps on the migration doc above and re-run this command.'
),
);
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
XcodeProjectInterpreter: () => xcodeProjectInterpreter,
});
// Issue: https://github.com/flutter/flutter/issues/47803
testUsingContext('exits the tool if a plugin sets an invalid android package in pubspec.yaml', () async {
androidProject.embeddingVersion = AndroidEmbeddingVersion.v1;
@ -823,28 +700,6 @@ dependencies:
XcodeProjectInterpreter: () => xcodeProjectInterpreter,
});
testUsingContext('old embedding app uses a plugin that supports v1 and v2 embedding works', () async {
androidProject.embeddingVersion = AndroidEmbeddingVersion.v1;
createDualSupportJavaPlugin4();
await injectPlugins(flutterProject, androidPlatform: true);
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'));
expect(registrant.readAsStringSync(),
contains('UseBothEmbedding.registerWith(registry.registrarFor("plugin4.UseBothEmbedding"));'));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
XcodeProjectInterpreter: () => xcodeProjectInterpreter,
});
testUsingContext('new embedding app uses a plugin that supports v1 and v2 embedding', () async {
androidProject.embeddingVersion = AndroidEmbeddingVersion.v2;
@ -886,30 +741,6 @@ dependencies:
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('Module using old plugin shows warning', () async {
flutterProject.isModule = true;
androidProject.embeddingVersion = AndroidEmbeddingVersion.v2;
createOldJavaPlugin('plugin3');
await injectPlugins(flutterProject, androidPlatform: true);
final File registrant = flutterProject.directory
.childDirectory(fs.path.join('android', 'app', 'src', 'main', 'java', 'io', 'flutter', 'plugins'))
.childFile('GeneratedPluginRegistrant.java');
expect(registrant.readAsStringSync(),
contains('plugin3.UseOldEmbedding.registerWith(shimPluginRegistry.registrarFor("plugin3.UseOldEmbedding"));'));
expect(testLogger.warningText, equals(
'The plugin `plugin3` uses a deprecated version of the Android embedding.\n'
'To avoid unexpected runtime failures, or future build failures, try to see if this plugin supports the Android V2 embedding. '
'Otherwise, consider removing it since a future release of Flutter will remove these deprecated APIs.\n'
'If you are plugin author, take a look at the docs for migrating the plugin to the V2 embedding: https://flutter.dev/go/android-plugin-migration.\n'));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
XcodeProjectInterpreter: () => xcodeProjectInterpreter,
});
testUsingContext('Module using new plugin shows no warnings', () async {
flutterProject.isModule = true;
androidProject.embeddingVersion = AndroidEmbeddingVersion.v2;
@ -973,105 +804,6 @@ dependencies:
XcodeProjectInterpreter: () => xcodeProjectInterpreter,
});
testUsingContext('App using the v1 embedding shows warning', () async {
flutterProject.isModule = false;
androidProject.embeddingVersion = AndroidEmbeddingVersion.v1;
await injectPlugins(flutterProject, androidPlatform: true);
expect(testLogger.warningText, equals(
'This app is using a deprecated version of the Android embedding.\n'
'To avoid unexpected runtime failures, or future build failures, try to migrate this app to the V2 embedding.\n'
'Take a look at the docs for migrating an app: https://github.com/flutter/flutter/wiki/Upgrading-pre-1.12-Android-projects\n'
));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
XcodeProjectInterpreter: () => xcodeProjectInterpreter,
});
testUsingContext('Module using multiple old plugins all show warnings', () async {
flutterProject.isModule = true;
androidProject.embeddingVersion = AndroidEmbeddingVersion.v2;
createOldJavaPlugin('plugin3');
createOldJavaPlugin('plugin4');
await injectPlugins(flutterProject, androidPlatform: true);
final File registrant = flutterProject.directory
.childDirectory(fs.path.join('android', 'app', 'src', 'main', 'java', 'io', 'flutter', 'plugins'))
.childFile('GeneratedPluginRegistrant.java');
expect(registrant.readAsStringSync(),
contains('plugin3.UseOldEmbedding.registerWith(shimPluginRegistry.registrarFor("plugin3.UseOldEmbedding"));'));
expect(registrant.readAsStringSync(),
contains('plugin4.UseOldEmbedding.registerWith(shimPluginRegistry.registrarFor("plugin4.UseOldEmbedding"));'));
expect(testLogger.warningText, equals(
'The plugins `plugin3, plugin4` use a deprecated version of the Android embedding.\n'
'To avoid unexpected runtime failures, or future build failures, try to see if these plugins support the Android V2 embedding. '
'Otherwise, consider removing them since a future release of Flutter will remove these deprecated APIs.\n'
'If you are plugin author, take a look at the docs for migrating the plugin to the V2 embedding: https://flutter.dev/go/android-plugin-migration.\n'
));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
XcodeProjectInterpreter: () => xcodeProjectInterpreter,
});
testUsingContext('App using multiple old plugins all show warnings', () async {
flutterProject.isModule = false;
androidProject.embeddingVersion = AndroidEmbeddingVersion.v2;
createOldJavaPlugin('plugin3');
createOldJavaPlugin('plugin4');
await injectPlugins(flutterProject, androidPlatform: true);
final File registrant = flutterProject.directory
.childDirectory(fs.path.join('android', 'app', 'src', 'main', 'java', 'io', 'flutter', 'plugins'))
.childFile('GeneratedPluginRegistrant.java');
expect(registrant.readAsStringSync(),
contains('plugin3.UseOldEmbedding.registerWith(shimPluginRegistry.registrarFor("plugin3.UseOldEmbedding"));'));
expect(registrant.readAsStringSync(),
contains('plugin4.UseOldEmbedding.registerWith(shimPluginRegistry.registrarFor("plugin4.UseOldEmbedding"));'));
expect(testLogger.warningText, equals(
'The plugins `plugin3, plugin4` use a deprecated version of the Android embedding.\n'
'To avoid unexpected runtime failures, or future build failures, try to see if these plugins support the Android V2 embedding. '
'Otherwise, consider removing them since a future release of Flutter will remove these deprecated APIs.\n'
'If you are plugin author, take a look at the docs for migrating the plugin to the V2 embedding: https://flutter.dev/go/android-plugin-migration.\n'
));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
XcodeProjectInterpreter: () => xcodeProjectInterpreter,
});
testUsingContext('Module using multiple old and new plugins should be wrapped with try catch', () async {
flutterProject.isModule = true;
androidProject.embeddingVersion = AndroidEmbeddingVersion.v2;
createOldJavaPlugin('abcplugin1');
createNewJavaPlugin1();
await injectPlugins(flutterProject, androidPlatform: true);
final File registrant = flutterProject.directory
.childDirectory(fs.path.join('android', 'app', 'src', 'main', 'java', 'io', 'flutter', 'plugins'))
.childFile('GeneratedPluginRegistrant.java');
const String newPluginName = 'flutterEngine.getPlugins().add(new plugin1.UseNewEmbedding());';
const String oldPluginName = 'abcplugin1.UseOldEmbedding.registerWith(shimPluginRegistry.registrarFor("abcplugin1.UseOldEmbedding"));';
final String content = registrant.readAsStringSync();
for (final String plugin in <String>[newPluginName,oldPluginName]) {
expect(content, contains(plugin));
expect(content.split(plugin).first.trim().endsWith('try {'), isTrue);
expect(content.split(plugin).last.trim().startsWith('} catch (Exception e) {'), isTrue);
}
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
XcodeProjectInterpreter: () => xcodeProjectInterpreter,
});
testUsingContext('Does not throw when AndroidManifest.xml is not found', () async {
final File manifest = fs.file('AndroidManifest.xml');
androidProject.appManifestFile = manifest;

View file

@ -194,28 +194,6 @@ void main() {
throwsToolExit(message: 'Please ensure that the android manifest is a valid XML document and try again.'),
);
});
_testInMemory('Android project not on v2 embedding shows a warning', () async {
final FlutterProject project = await someProject(includePubspec: true);
// The default someProject with an empty <manifest> already indicates
// v1 embedding, as opposed to having <meta-data
// android:name="flutterEmbedding" android:value="2" />.
project.checkForDeprecation(deprecationBehavior: DeprecationBehavior.ignore);
expect(testLogger.statusText, contains('https://github.com/flutter/flutter/wiki/Upgrading-pre-1.12-Android-projects'));
});
_testInMemory('Android project not on v2 embedding exits', () async {
final FlutterProject project = await someProject(includePubspec: true);
// The default someProject with an empty <manifest> already indicates
// v1 embedding, as opposed to having <meta-data
// android:name="flutterEmbedding" android:value="2" />.
await expectToolExitLater(
Future<dynamic>.sync(() => project.checkForDeprecation(deprecationBehavior: DeprecationBehavior.exit)),
contains('Build failed due to use of deprecated Android v1 embedding.')
);
expect(testLogger.statusText, contains('https://github.com/flutter/flutter/wiki/Upgrading-pre-1.12-Android-projects'));
expect(testLogger.statusText, contains('No `<meta-data android:name="flutterEmbedding" android:value="2"/>` in '));
});
_testInMemory('Project not on v2 embedding does not warn if deprecation status is irrelevant', () async {
final FlutterProject project = await someProject(includePubspec: true);
// The default someProject with an empty <manifest> already indicates
@ -226,15 +204,6 @@ void main() {
project.checkForDeprecation();
expect(testLogger.statusText, isEmpty);
});
_testInMemory('Android project not on v2 embedding ignore continues', () async {
final FlutterProject project = await someProject(includePubspec: true);
// The default someProject with an empty <manifest> already indicates
// v1 embedding, as opposed to having <meta-data
// android:name="flutterEmbedding" android:value="2" />.
project.checkForDeprecation(deprecationBehavior: DeprecationBehavior.ignore);
expect(testLogger.statusText, contains('https://github.com/flutter/flutter/wiki/Upgrading-pre-1.12-Android-projects'));
});
_testInMemory('Android project no pubspec continues', () async {
final FlutterProject project = await someProject();
// The default someProject with an empty <manifest> already indicates