Detect Android SDK 28/28.0.3 (#26942) * Detect Android SDK 28/28.0.3 * revert minimumAndroidSdkVersion const change
diff --git a/packages/flutter_tools/lib/src/android/android_workflow.dart b/packages/flutter_tools/lib/src/android/android_workflow.dart index d1978a2..dfbc083 100644 --- a/packages/flutter_tools/lib/src/android/android_workflow.dart +++ b/packages/flutter_tools/lib/src/android/android_workflow.dart
@@ -18,6 +18,9 @@ import '../globals.dart'; import 'android_sdk.dart'; +const int kAndroidSdkMinVersion = 28; +final Version kAndroidSdkBuildToolsMinVersion = Version(28, 0, 3); + AndroidWorkflow get androidWorkflow => context[AndroidWorkflow]; AndroidValidator get androidValidator => context[AndroidValidator]; AndroidLicenseValidator get androidLicenseValidator => context[AndroidLicenseValidator]; @@ -110,11 +113,19 @@ String sdkVersionText; if (androidSdk.latestVersion != null) { + if (androidSdk.latestVersion.sdkLevel < 28 || androidSdk.latestVersion.buildToolsVersion < kAndroidSdkBuildToolsMinVersion) { + messages.add(ValidationMessage.error( + userMessages.androidSdkBuildToolsOutdated(androidSdk.sdkManagerPath, kAndroidSdkMinVersion, kAndroidSdkBuildToolsMinVersion.toString())), + ); + return ValidationResult(ValidationType.missing, messages); + } sdkVersionText = userMessages.androidStatusInfo(androidSdk.latestVersion.buildToolsVersionName); messages.add(ValidationMessage(userMessages.androidSdkPlatformToolsVersion( androidSdk.latestVersion.platformName, androidSdk.latestVersion.buildToolsVersionName))); + } else { + messages.add(ValidationMessage.error(userMessages.androidMissingSdkInstructions(kAndroidHome))); } if (platform.environment.containsKey(kAndroidHome)) { @@ -271,7 +282,7 @@ final Version sdkManagerVersion = Version.parse(androidSdk.sdkManagerVersion); if (sdkManagerVersion == null || sdkManagerVersion.major < 26) { // SDK manager is found, but needs to be updated. - throwToolExit(userMessages.androidSdkOutdated(androidSdk.sdkManagerPath)); + throwToolExit(userMessages.androidSdkManagerOutdated(androidSdk.sdkManagerPath)); } final Process process = await runCommand(
diff --git a/packages/flutter_tools/lib/src/base/user_messages.dart b/packages/flutter_tools/lib/src/base/user_messages.dart index 2248d11..813247c 100644 --- a/packages/flutter_tools/lib/src/base/user_messages.dart +++ b/packages/flutter_tools/lib/src/base/user_messages.dart
@@ -76,7 +76,7 @@ String get androidLicensesSome => 'Some Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses'; String get androidLicensesNone => 'Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses'; String get androidLicensesUnknown => 'Android license status unknown.'; - String androidSdkOutdated(String managerPath) => + String androidSdkManagerOutdated(String managerPath) => 'A newer version of the Android SDK is required. To update, run:\n' '$managerPath --update\n'; String androidLicensesTimeout(String managerPath) => 'Intentionally killing $managerPath'; @@ -85,6 +85,11 @@ 'Android sdkmanager tool not found ($sdkManagerPath).\n' 'Try re-installing or updating your Android SDK,\n' 'visit https://flutter.io/setup/#android-setup for detailed instructions.'; + String androidSdkBuildToolsOutdated(String managerPath, int sdkMinVersion, String buildToolsMinVersion) => + 'Flutter requires Android SDK $sdkMinVersion and the Android BuildTools $buildToolsMinVersion\n' + 'To update using sdkmanager, run:\n' + ' $managerPath "platforms;android-$sdkMinVersion" "build-tools;$buildToolsMinVersion"\n' + 'or visit https://flutter.io/setup/#android-setup for detailed instructions.'; // Messages used in AndroidStudioValidator String androidStudioVersion(String version) => 'version $version';
diff --git a/packages/flutter_tools/test/android/android_workflow_test.dart b/packages/flutter_tools/test/android/android_workflow_test.dart index 2cb57c0..4764b1b 100644 --- a/packages/flutter_tools/test/android/android_workflow_test.dart +++ b/packages/flutter_tools/test/android/android_workflow_test.dart
@@ -9,6 +9,9 @@ import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/android/android_sdk.dart'; import 'package:flutter_tools/src/android/android_workflow.dart'; +import 'package:flutter_tools/src/base/user_messages.dart'; +import 'package:flutter_tools/src/base/version.dart'; +import 'package:flutter_tools/src/doctor.dart'; import 'package:mockito/mockito.dart'; import 'package:platform/platform.dart'; import 'package:process/process.dart'; @@ -17,6 +20,8 @@ import '../src/context.dart'; import '../src/mocks.dart' show MockAndroidSdk, MockProcess, MockProcessManager, MockStdio; +class MockAndroidSdkVersion extends Mock implements AndroidSdkVersion {} + void main() { AndroidSdk sdk; MemoryFileSystem fs; @@ -172,4 +177,57 @@ ProcessManager: () => processManager, Stdio: () => stdio, }); + + testUsingContext('detects minium required SDK and buildtools', () async { + final AndroidSdkVersion mockSdkVersion = MockAndroidSdkVersion(); + + // Test with invalid SDK and build tools + when(mockSdkVersion.sdkLevel).thenReturn(26); + when(mockSdkVersion.buildToolsVersion).thenReturn(Version(26, 0, 3)); + when(sdk.sdkManagerPath).thenReturn('/foo/bar/sdkmanager'); + when(sdk.latestVersion).thenReturn(mockSdkVersion); + when(sdk.validateSdkWellFormed()).thenReturn(<String>[]); + final String errorMessage = userMessages.androidSdkBuildToolsOutdated( + sdk.sdkManagerPath, + kAndroidSdkMinVersion, + kAndroidSdkBuildToolsMinVersion.toString(), + ); + + ValidationResult validationResult = await AndroidValidator().validate(); + expect(validationResult.type, ValidationType.missing); + expect( + validationResult.messages.last.message, + errorMessage, + ); + + // Test with valid SDK but invalid build tools + when(mockSdkVersion.sdkLevel).thenReturn(28); + when(mockSdkVersion.buildToolsVersion).thenReturn(Version(28, 0, 2)); + + validationResult = await AndroidValidator().validate(); + expect(validationResult.type, ValidationType.missing); + expect( + validationResult.messages.last.message, + errorMessage, + ); + + // Test with valid SDK and valid build tools + // Will still be partial because AnroidSdk.findJavaBinary is static :( + when(mockSdkVersion.sdkLevel).thenReturn(kAndroidSdkMinVersion); + when(mockSdkVersion.buildToolsVersion).thenReturn(kAndroidSdkBuildToolsMinVersion); + + validationResult = await AndroidValidator().validate(); + expect(validationResult.type, ValidationType.partial); // No Java binary + expect( + validationResult.messages.any((ValidationMessage message) => message.message == errorMessage), + isFalse, + ); + }, overrides: <Type, Generator>{ + AndroidSdk: () => sdk, + FileSystem: () => fs, + Platform: () => FakePlatform()..environment = <String, String>{'HOME': '/home/me'}, + ProcessManager: () => processManager, + Stdio: () => stdio, + }); + }