Unverified Commit ba26f926 authored by xster's avatar xster Committed by GitHub

Add Android embedding version analytics (#44043)

parent 14ac88d3
......@@ -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('''
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment