Print message and log event when app isn't using AndroidX (#42548)
diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart
index 87c05cf..26e28ba 100644
--- a/packages/flutter_tools/lib/src/android/gradle.dart
+++ b/packages/flutter_tools/lib/src/android/gradle.dart
@@ -745,6 +745,20 @@
);
}
+ final String exclamationMark = terminal.color('[!]', TerminalColor.red);
+ final bool usesAndroidX = isAppUsingAndroidX(flutterProject.android.hostAppGradleRoot);
+
+ if (usesAndroidX) {
+ BuildEvent('app-using-android-x').send();
+ } else if (!usesAndroidX) {
+ BuildEvent('app-not-using-android-x').send();
+ printStatus('$exclamationMark Your app isn\'t using AndroidX.', emphasis: true);
+ printStatus(
+ 'To avoid potential build failures, you can quickly migrate your app '
+ 'by following the steps on https://goo.gl/CP92wY.',
+ indent: 4,
+ );
+ }
final BuildInfo buildInfo = androidBuildInfo.buildInfo;
String assembleTask;
@@ -847,14 +861,12 @@
if (exitCode != 0) {
if (potentialR8Failure) {
- final String exclamationMark = terminal.color('[!]', TerminalColor.red);
printStatus('$exclamationMark The shrinker may have failed to optimize the Java bytecode.', emphasis: true);
printStatus('To disable the shrinker, pass the `--no-shrink` flag to this command.', indent: 4);
printStatus('To learn more, see: https://developer.android.com/studio/build/shrink-code', indent: 4);
BuildEvent('r8-failure').send();
} else if (potentialAndroidXFailure) {
final bool hasPlugins = flutterProject.flutterPluginsFile.existsSync();
- final bool usesAndroidX = isAppUsingAndroidX(flutterProject.android.hostAppGradleRoot);
if (!hasPlugins) {
// If the app doesn't use any plugin, then it's unclear where the incompatibility is coming from.
BuildEvent('android-x-failure', eventError: 'app-not-using-plugins').send();
diff --git a/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart b/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart
index c3316c6..a67c9fa 100644
--- a/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart
@@ -9,7 +9,9 @@
import 'package:flutter_tools/src/android/android_builder.dart';
import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/android/gradle.dart';
+import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/file_system.dart';
+import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/build_apk.dart';
@@ -312,6 +314,116 @@
Usage: () => mockUsage,
},
timeout: allowForCreateFlutterProject);
+
+ testUsingContext('reports when the app isn\'t using AndroidX', () async {
+ final String projectPath = await createProject(tempDir,
+ arguments: <String>['--no-pub', '--no-androidx', '--template=app']);
+
+ when(mockProcessManager.start(
+ <String>[
+ gradlew,
+ '-q',
+ '-Ptarget=${fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}',
+ '-Ptrack-widget-creation=false',
+ '-Pshrink=true',
+ '-Ptarget-platform=android-arm,android-arm64,android-x64',
+ 'assembleRelease',
+ ],
+ workingDirectory: anyNamed('workingDirectory'),
+ environment: anyNamed('environment'),
+ )).thenAnswer((_) {
+ return Future<Process>.value(
+ createMockProcess(
+ exitCode: 0,
+ stdout: '',
+ ),
+ );
+ });
+ // The command throws a [ToolExit] because it expects an APK in the file system.
+ await expectLater(() async {
+ await runBuildApkCommand(
+ projectPath,
+ );
+ }, throwsToolExit());
+
+ final BufferLogger logger = context.get<Logger>();
+ expect(logger.statusText, contains('[!] Your app isn\'t using AndroidX'));
+ expect(logger.statusText, contains(
+ 'To avoid potential build failures, you can quickly migrate your app by '
+ 'following the steps on https://goo.gl/CP92wY'
+ )
+ );
+ verify(mockUsage.sendEvent(
+ 'build',
+ 'apk',
+ label: 'app-not-using-android-x',
+ parameters: anyNamed('parameters'),
+ )).called(1);
+ },
+ overrides: <Type, Generator>{
+ AndroidSdk: () => mockAndroidSdk,
+ GradleUtils: () => GradleUtils(),
+ FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
+ ProcessManager: () => mockProcessManager,
+ Usage: () => mockUsage,
+ },
+ timeout: allowForCreateFlutterProject);
+
+ testUsingContext('reports when the app is using AndroidX', () async {
+ final String projectPath = await createProject(tempDir,
+ arguments: <String>['--no-pub', '--template=app']);
+
+ when(mockProcessManager.start(
+ <String>[
+ gradlew,
+ '-q',
+ '-Ptarget=${fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}',
+ '-Ptrack-widget-creation=false',
+ '-Pshrink=true',
+ '-Ptarget-platform=android-arm,android-arm64,android-x64',
+ 'assembleRelease',
+ ],
+ workingDirectory: anyNamed('workingDirectory'),
+ environment: anyNamed('environment'),
+ )).thenAnswer((_) {
+ return Future<Process>.value(
+ createMockProcess(
+ exitCode: 0,
+ stdout: '',
+ ),
+ );
+ });
+ // The command throws a [ToolExit] because it expects an APK in the file system.
+ await expectLater(() async {
+ await runBuildApkCommand(
+ projectPath,
+ );
+ }, throwsToolExit());
+
+ final BufferLogger logger = context.get<Logger>();
+ expect(logger.statusText.contains('[!] Your app isn\'t using AndroidX'), isFalse);
+ expect(
+ logger.statusText.contains(
+ 'To avoid potential build failures, you can quickly migrate your app by '
+ 'following the steps on https://goo.gl/CP92wY'
+ ),
+ isFalse,
+ );
+ verify(mockUsage.sendEvent(
+ 'build',
+ 'apk',
+ label: 'app-using-android-x',
+ parameters: anyNamed('parameters'),
+ )).called(1);
+ },
+ overrides: <Type, Generator>{
+ AndroidSdk: () => mockAndroidSdk,
+ GradleUtils: () => GradleUtils(),
+ FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
+ ProcessManager: () => mockProcessManager,
+ Usage: () => mockUsage,
+ },
+ timeout: allowForCreateFlutterProject);
});
}
diff --git a/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart b/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart
index 7071889..a00d279 100644
--- a/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart
@@ -9,7 +9,9 @@
import 'package:flutter_tools/src/android/android_builder.dart';
import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/android/gradle.dart';
+import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/file_system.dart';
+import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/build_appbundle.dart';
@@ -303,6 +305,116 @@
Usage: () => mockUsage,
},
timeout: allowForCreateFlutterProject);
+
+ testUsingContext('reports when the app isn\'t using AndroidX', () async {
+ final String projectPath = await createProject(tempDir,
+ arguments: <String>['--no-pub', '--no-androidx', '--template=app']);
+
+ when(mockProcessManager.start(
+ <String>[
+ gradlew,
+ '-q',
+ '-Ptarget=${fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}',
+ '-Ptrack-widget-creation=false',
+ '-Pshrink=true',
+ '-Ptarget-platform=android-arm,android-arm64,android-x64',
+ 'assembleRelease',
+ ],
+ workingDirectory: anyNamed('workingDirectory'),
+ environment: anyNamed('environment'),
+ )).thenAnswer((_) {
+ return Future<Process>.value(
+ createMockProcess(
+ exitCode: 0,
+ stdout: '',
+ ),
+ );
+ });
+ // The command throws a [ToolExit] because it expects an AAB in the file system.
+ await expectLater(() async {
+ await runBuildAppBundleCommand(
+ projectPath,
+ );
+ }, throwsToolExit());
+
+ final BufferLogger logger = context.get<Logger>();
+ expect(logger.statusText, contains('[!] Your app isn\'t using AndroidX'));
+ expect(logger.statusText, contains(
+ 'To avoid potential build failures, you can quickly migrate your app by '
+ 'following the steps on https://goo.gl/CP92wY'
+ )
+ );
+ verify(mockUsage.sendEvent(
+ 'build',
+ 'appbundle',
+ label: 'app-not-using-android-x',
+ parameters: anyNamed('parameters'),
+ )).called(1);
+ },
+ overrides: <Type, Generator>{
+ AndroidSdk: () => mockAndroidSdk,
+ GradleUtils: () => GradleUtils(),
+ FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
+ ProcessManager: () => mockProcessManager,
+ Usage: () => mockUsage,
+ },
+ timeout: allowForCreateFlutterProject);
+
+ testUsingContext('reports when the app is using AndroidX', () async {
+ final String projectPath = await createProject(tempDir,
+ arguments: <String>['--no-pub', '--template=app']);
+
+ when(mockProcessManager.start(
+ <String>[
+ gradlew,
+ '-q',
+ '-Ptarget=${fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}',
+ '-Ptrack-widget-creation=false',
+ '-Pshrink=true',
+ '-Ptarget-platform=android-arm,android-arm64,android-x64',
+ 'assembleRelease',
+ ],
+ workingDirectory: anyNamed('workingDirectory'),
+ environment: anyNamed('environment'),
+ )).thenAnswer((_) {
+ return Future<Process>.value(
+ createMockProcess(
+ exitCode: 0,
+ stdout: '',
+ ),
+ );
+ });
+ // The command throws a [ToolExit] because it expects an AAB in the file system.
+ await expectLater(() async {
+ await runBuildAppBundleCommand(
+ projectPath,
+ );
+ }, throwsToolExit());
+
+ final BufferLogger logger = context.get<Logger>();
+ expect(logger.statusText.contains('[!] Your app isn\'t using AndroidX'), isFalse);
+ expect(
+ logger.statusText.contains(
+ 'To avoid potential build failures, you can quickly migrate your app by '
+ 'following the steps on https://goo.gl/CP92wY'
+ ),
+ isFalse,
+ );
+ verify(mockUsage.sendEvent(
+ 'build',
+ 'appbundle',
+ label: 'app-using-android-x',
+ parameters: anyNamed('parameters'),
+ )).called(1);
+ },
+ overrides: <Type, Generator>{
+ AndroidSdk: () => mockAndroidSdk,
+ GradleUtils: () => GradleUtils(),
+ FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
+ ProcessManager: () => mockProcessManager,
+ Usage: () => mockUsage,
+ },
+ timeout: allowForCreateFlutterProject);
});
}