Add Android embedding version analytics (#44043)
This commit is contained in:
parent
14ac88d3bc
commit
ba26f926c7
@ -87,6 +87,8 @@ class PackagesGetCommand extends FlutterCommand {
|
||||
usageValues[CustomDimensions.commandPackagesNumberPlugins] = '0';
|
||||
}
|
||||
usageValues[CustomDimensions.commandPackagesProjectModule] = '${rootProject.isModule}';
|
||||
usageValues[CustomDimensions.commandPackagesAndroidEmbeddingVersion] =
|
||||
rootProject.android.getEmbeddingVersion().toString().split('.').last;
|
||||
return usageValues;
|
||||
}
|
||||
|
||||
|
@ -250,6 +250,7 @@ class RunCommand extends RunCommandBase {
|
||||
CustomDimensions.commandRunModeName: modeName,
|
||||
CustomDimensions.commandRunProjectModule: '${FlutterProject.current().isModule}',
|
||||
CustomDimensions.commandRunProjectHostLanguage: hostLanguage.join(','),
|
||||
CustomDimensions.commandRunAndroidEmbeddingVersion: androidProject.getEmbeddingVersion().toString().split('.').last,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,6 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:mustache/mustache.dart' as mustache;
|
||||
import 'package:xml/xml.dart' as xml;
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
import 'android/gradle.dart';
|
||||
@ -364,30 +363,10 @@ List<Map<String, dynamic>> _extractPlatformMaps(List<Plugin> plugins, String typ
|
||||
|
||||
/// Returns the version of the Android embedding that the current
|
||||
/// [project] is using.
|
||||
String _getAndroidEmbeddingVersion(FlutterProject project) {
|
||||
AndroidEmbeddingVersion _getAndroidEmbeddingVersion(FlutterProject project) {
|
||||
assert(project.android != null);
|
||||
|
||||
final File androidManifest = project.android.appManifestFile;
|
||||
if (androidManifest == null || !androidManifest.existsSync()) {
|
||||
return '1';
|
||||
}
|
||||
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';
|
||||
return project.android.getEmbeddingVersion();
|
||||
}
|
||||
|
||||
Future<void> _writeAndroidPluginRegistrant(FlutterProject project, List<Plugin> plugins) async {
|
||||
@ -412,9 +391,9 @@ Future<void> _writeAndroidPluginRegistrant(FlutterProject project, List<Plugin>
|
||||
'GeneratedPluginRegistrant.java',
|
||||
);
|
||||
String templateContent;
|
||||
final String appEmbeddingVersion = _getAndroidEmbeddingVersion(project);
|
||||
final AndroidEmbeddingVersion appEmbeddingVersion = _getAndroidEmbeddingVersion(project);
|
||||
switch (appEmbeddingVersion) {
|
||||
case '2':
|
||||
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.
|
||||
@ -425,8 +404,9 @@ Future<void> _writeAndroidPluginRegistrant(FlutterProject project, List<Plugin>
|
||||
}
|
||||
}
|
||||
templateContent = _androidPluginRegistryTemplateNewEmbedding;
|
||||
break;
|
||||
case '1':
|
||||
break;
|
||||
case AndroidEmbeddingVersion.v1:
|
||||
default:
|
||||
for (Map<String, dynamic> plugin in androidPlugins) {
|
||||
if (!plugin['supportsEmbeddingV1'] && plugin['supportsEmbeddingV2']) {
|
||||
throwToolExit(
|
||||
@ -437,9 +417,7 @@ Future<void> _writeAndroidPluginRegistrant(FlutterProject project, List<Plugin>
|
||||
}
|
||||
}
|
||||
templateContent = _androidPluginRegistryTemplateOldEmbedding;
|
||||
break;
|
||||
default:
|
||||
throwToolExit('Unsupported Android embedding');
|
||||
break;
|
||||
}
|
||||
printTrace('Generating $registryPath');
|
||||
_renderTemplateToFile(
|
||||
|
@ -5,6 +5,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:xml/xml.dart' as xml;
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
import 'android/gradle_utils.dart' as gradle;
|
||||
@ -642,6 +643,43 @@ class AndroidProject {
|
||||
overwriteExisting: true,
|
||||
);
|
||||
}
|
||||
|
||||
AndroidEmbeddingVersion getEmbeddingVersion() {
|
||||
if (appManifestFile == null || !appManifestFile.existsSync()) {
|
||||
return AndroidEmbeddingVersion.v1;
|
||||
}
|
||||
xml.XmlDocument document;
|
||||
try {
|
||||
document = xml.parse(appManifestFile.readAsStringSync());
|
||||
} on xml.XmlParserException {
|
||||
throwToolExit('Error parsing $appManifestFile '
|
||||
'Please ensure that the android manifest is a valid XML document and try again.');
|
||||
} on FileSystemException {
|
||||
throwToolExit('Error reading $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') {
|
||||
final String embeddingVersionString = metaData.getAttribute('android:value');
|
||||
if (embeddingVersionString == '1') {
|
||||
return AndroidEmbeddingVersion.v1;
|
||||
}
|
||||
if (embeddingVersionString == '2') {
|
||||
return AndroidEmbeddingVersion.v2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return AndroidEmbeddingVersion.v1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Iteration of the embedding Java API in the engine used by the Android project.
|
||||
enum AndroidEmbeddingVersion {
|
||||
/// V1 APIs based on io.flutter.app.FlutterActivity.
|
||||
v1,
|
||||
/// V2 APIs based on io.flutter.embedding.android.FlutterActivity.
|
||||
v2,
|
||||
}
|
||||
|
||||
/// Represents the web sub-project of a Flutter project.
|
||||
|
@ -55,6 +55,8 @@ enum CustomDimensions {
|
||||
commandBuildAppBundleBuildMode, // cd42
|
||||
buildEventError, // cd43
|
||||
commandResultEventMaxRss, // cd44
|
||||
commandRunAndroidEmbeddingVersion, // cd45
|
||||
commandPackagesAndroidEmbeddingVersion, // cd46
|
||||
}
|
||||
|
||||
String cdKey(CustomDimensions cd) => 'cd${cd.index + 1}';
|
||||
|
@ -11,12 +11,14 @@ import 'package:flutter_tools/src/base/utils.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/commands/packages.dart';
|
||||
import 'package:flutter_tools/src/dart/pub.dart';
|
||||
import 'package:flutter_tools/src/features.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
import 'package:process/process.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
import '../../src/context.dart';
|
||||
import '../../src/mocks.dart' show MockProcessManager, MockStdio, PromptingProcess;
|
||||
import '../../src/testbed.dart';
|
||||
|
||||
class AlwaysTrueBotDetector implements BotDetector {
|
||||
const AlwaysTrueBotDetector();
|
||||
@ -266,6 +268,36 @@ void main() {
|
||||
Pub: () => const Pub(),
|
||||
});
|
||||
|
||||
testUsingContext('indicate that Android project reports v1 in usage value', () async {
|
||||
final String projectPath = await createProject(tempDir,
|
||||
arguments: <String>['--no-pub']);
|
||||
removeGeneratedFiles(projectPath);
|
||||
|
||||
final PackagesCommand command = await runCommandIn(projectPath, 'get');
|
||||
final PackagesGetCommand getCommand = command.subcommands['get'] as PackagesGetCommand;
|
||||
|
||||
expect(await getCommand.usageValues,
|
||||
containsPair(CustomDimensions.commandPackagesAndroidEmbeddingVersion, 'v1'));
|
||||
}, overrides: <Type, Generator>{
|
||||
FeatureFlags: () => TestFeatureFlags(isAndroidEmbeddingV2Enabled: false),
|
||||
Pub: () => const Pub(),
|
||||
});
|
||||
|
||||
testUsingContext('indicate that Android project reports v2 in usage value', () async {
|
||||
final String projectPath = await createProject(tempDir,
|
||||
arguments: <String>['--no-pub']);
|
||||
removeGeneratedFiles(projectPath);
|
||||
|
||||
final PackagesCommand command = await runCommandIn(projectPath, 'get');
|
||||
final PackagesGetCommand getCommand = command.subcommands['get'] as PackagesGetCommand;
|
||||
|
||||
expect(await getCommand.usageValues,
|
||||
containsPair(CustomDimensions.commandPackagesAndroidEmbeddingVersion, 'v2'));
|
||||
}, overrides: <Type, Generator>{
|
||||
FeatureFlags: () => TestFeatureFlags(isAndroidEmbeddingV2Enabled: true),
|
||||
Pub: () => const Pub(),
|
||||
});
|
||||
|
||||
testUsingContext('upgrade fetches packages', () async {
|
||||
final String projectPath = await createProject(tempDir,
|
||||
arguments: <String>['--no-pub', '--template=module']);
|
||||
|
@ -125,22 +125,6 @@ void main() {
|
||||
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);
|
||||
@ -154,13 +138,7 @@ void main() {
|
||||
|
||||
testUsingContext('Registrant uses old embedding in app project', () async {
|
||||
when(flutterProject.isModule).thenReturn(false);
|
||||
|
||||
final File androidManifest = flutterProject.directory
|
||||
.childDirectory('android')
|
||||
.childFile('AndroidManifest.xml')
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync(kAndroidManifestUsingOldEmbedding);
|
||||
when(androidProject.appManifestFile).thenReturn(androidManifest);
|
||||
when(androidProject.getEmbeddingVersion()).thenReturn(AndroidEmbeddingVersion.v1);
|
||||
|
||||
await injectPlugins(flutterProject);
|
||||
|
||||
@ -179,13 +157,7 @@ void main() {
|
||||
|
||||
testUsingContext('Registrant uses new embedding if app uses new embedding', () async {
|
||||
when(flutterProject.isModule).thenReturn(false);
|
||||
|
||||
final File androidManifest = flutterProject.directory
|
||||
.childDirectory('android')
|
||||
.childFile('AndroidManifest.xml')
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync(kAndroidManifestUsingNewEmbedding);
|
||||
when(androidProject.appManifestFile).thenReturn(androidManifest);
|
||||
when(androidProject.getEmbeddingVersion()).thenReturn(AndroidEmbeddingVersion.v2);
|
||||
|
||||
await injectPlugins(flutterProject);
|
||||
|
||||
@ -204,13 +176,7 @@ void main() {
|
||||
|
||||
testUsingContext('Registrant uses shim for plugins using old embedding if app uses new embedding', () async {
|
||||
when(flutterProject.isModule).thenReturn(false);
|
||||
|
||||
final File androidManifest = flutterProject.directory
|
||||
.childDirectory('android')
|
||||
.childFile('AndroidManifest.xml')
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync(kAndroidManifestUsingNewEmbedding);
|
||||
when(androidProject.appManifestFile).thenReturn(androidManifest);
|
||||
when(androidProject.getEmbeddingVersion()).thenReturn(AndroidEmbeddingVersion.v2);
|
||||
|
||||
final Directory pluginUsingJavaAndNewEmbeddingDir =
|
||||
fs.systemTempDirectory.createTempSync('flutter_plugin_using_java_and_new_embedding_dir.');
|
||||
@ -301,13 +267,7 @@ plugin3:${pluginUsingOldEmbeddingDir.childDirectory('lib').uri.toString()}
|
||||
|
||||
testUsingContext('exits the tool if an app uses the v1 embedding and a plugin only supports the v2 embedding', () async {
|
||||
when(flutterProject.isModule).thenReturn(false);
|
||||
|
||||
final File androidManifest = flutterProject.directory
|
||||
.childDirectory('android')
|
||||
.childFile('AndroidManifest.xml')
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync(kAndroidManifestUsingOldEmbedding);
|
||||
when(androidProject.appManifestFile).thenReturn(androidManifest);
|
||||
when(androidProject.getEmbeddingVersion()).thenReturn(AndroidEmbeddingVersion.v1);
|
||||
|
||||
final Directory pluginUsingJavaAndNewEmbeddingDir =
|
||||
fs.systemTempDirectory.createTempSync('flutter_plugin_using_java_and_new_embedding_dir.');
|
||||
@ -352,13 +312,7 @@ plugin1:${pluginUsingJavaAndNewEmbeddingDir.childDirectory('lib').uri.toString()
|
||||
|
||||
testUsingContext('allows app use a plugin that supports v1 and v2 embedding', () async {
|
||||
when(flutterProject.isModule).thenReturn(false);
|
||||
|
||||
final File androidManifest = flutterProject.directory
|
||||
.childDirectory('android')
|
||||
.childFile('AndroidManifest.xml')
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync(kAndroidManifestUsingOldEmbedding);
|
||||
when(androidProject.appManifestFile).thenReturn(androidManifest);
|
||||
when(androidProject.getEmbeddingVersion()).thenReturn(AndroidEmbeddingVersion.v1);
|
||||
|
||||
final Directory pluginUsingJavaAndNewEmbeddingDir =
|
||||
fs.systemTempDirectory.createTempSync('flutter_plugin_using_java_and_new_embedding_dir.');
|
||||
@ -406,13 +360,7 @@ plugin1:${pluginUsingJavaAndNewEmbeddingDir.childDirectory('lib').uri.toString()
|
||||
|
||||
testUsingContext('Registrant doesn\'t use new embedding if app doesn\'t use new embedding', () async {
|
||||
when(flutterProject.isModule).thenReturn(false);
|
||||
|
||||
final File androidManifest = flutterProject.directory
|
||||
.childDirectory('android')
|
||||
.childFile('AndroidManifest.xml')
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync(kAndroidManifestUsingOldEmbedding);
|
||||
when(androidProject.appManifestFile).thenReturn(androidManifest);
|
||||
when(androidProject.getEmbeddingVersion()).thenReturn(AndroidEmbeddingVersion.v1);
|
||||
|
||||
await injectPlugins(flutterProject);
|
||||
|
||||
@ -431,13 +379,7 @@ plugin1:${pluginUsingJavaAndNewEmbeddingDir.childDirectory('lib').uri.toString()
|
||||
|
||||
testUsingContext('Registrant uses old embedding in module project', () async {
|
||||
when(flutterProject.isModule).thenReturn(true);
|
||||
|
||||
final File androidManifest = flutterProject.directory
|
||||
.childDirectory('android')
|
||||
.childFile('AndroidManifest.xml')
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync(kAndroidManifestUsingOldEmbedding);
|
||||
when(androidProject.appManifestFile).thenReturn(androidManifest);
|
||||
when(androidProject.getEmbeddingVersion()).thenReturn(AndroidEmbeddingVersion.v1);
|
||||
|
||||
await injectPlugins(flutterProject);
|
||||
|
||||
@ -456,13 +398,7 @@ plugin1:${pluginUsingJavaAndNewEmbeddingDir.childDirectory('lib').uri.toString()
|
||||
|
||||
testUsingContext('Registrant uses new embedding if module uses new embedding', () async {
|
||||
when(flutterProject.isModule).thenReturn(true);
|
||||
|
||||
final File androidManifest = flutterProject.directory
|
||||
.childDirectory('android')
|
||||
.childFile('AndroidManifest.xml')
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync(kAndroidManifestUsingNewEmbedding);
|
||||
when(androidProject.appManifestFile).thenReturn(androidManifest);
|
||||
when(androidProject.getEmbeddingVersion()).thenReturn(AndroidEmbeddingVersion.v2);
|
||||
|
||||
await injectPlugins(flutterProject);
|
||||
|
||||
@ -481,13 +417,7 @@ plugin1:${pluginUsingJavaAndNewEmbeddingDir.childDirectory('lib').uri.toString()
|
||||
|
||||
testUsingContext('Registrant doesn\'t use new embedding if module doesn\'t use new embedding', () async {
|
||||
when(flutterProject.isModule).thenReturn(true);
|
||||
|
||||
final File androidManifest = flutterProject.directory
|
||||
.childDirectory('android')
|
||||
.childFile('AndroidManifest.xml')
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync(kAndroidManifestUsingOldEmbedding);
|
||||
when(androidProject.appManifestFile).thenReturn(androidManifest);
|
||||
when(androidProject.getEmbeddingVersion()).thenReturn(AndroidEmbeddingVersion.v1);
|
||||
|
||||
await injectPlugins(flutterProject);
|
||||
|
||||
@ -522,14 +452,6 @@ plugin1:${pluginUsingJavaAndNewEmbeddingDir.childDirectory('lib').uri.toString()
|
||||
when(flutterProject.isModule).thenReturn(true);
|
||||
when(featureFlags.isWebEnabled).thenReturn(true);
|
||||
|
||||
// injectPlugins will crash if there is no AndroidManifest
|
||||
final File androidManifest = flutterProject.directory
|
||||
.childDirectory('android')
|
||||
.childFile('AndroidManifest.xml')
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync(kAndroidManifestUsingOldEmbedding);
|
||||
when(androidProject.appManifestFile).thenReturn(androidManifest);
|
||||
|
||||
final Directory webPluginWithNestedFile =
|
||||
fs.systemTempDirectory.createTempSync('web_plugin_with_nested');
|
||||
webPluginWithNestedFile.childFile('pubspec.yaml').writeAsStringSync('''
|
||||
|
Loading…
x
Reference in New Issue
Block a user