diff --git a/packages/flutter_tools/lib/src/android/android_studio_validator.dart b/packages/flutter_tools/lib/src/android/android_studio_validator.dart index c8a3982c8c..26cb46e1c0 100644 --- a/packages/flutter_tools/lib/src/android/android_studio_validator.dart +++ b/packages/flutter_tools/lib/src/android/android_studio_validator.dart @@ -90,7 +90,7 @@ class NoAndroidStudioValidator extends DoctorValidator { userMessages.androidStudioMissing(cfgAndroidStudio), )); } - messages.add(ValidationMessage(userMessages.androidStudioInstallation)); + messages.add(ValidationMessage(userMessages.androidStudioInstallation(globals.platform))); return ValidationResult( ValidationType.notAvailable, diff --git a/packages/flutter_tools/lib/src/android/android_workflow.dart b/packages/flutter_tools/lib/src/android/android_workflow.dart index 772050262f..04a73a8ba9 100644 --- a/packages/flutter_tools/lib/src/android/android_workflow.dart +++ b/packages/flutter_tools/lib/src/android/android_workflow.dart @@ -154,7 +154,7 @@ class AndroidValidator extends DoctorValidator { } else { // Instruct user to set [kAndroidSdkRoot] and not deprecated [kAndroidHome] // See https://github.com/flutter/flutter/issues/39301 - messages.add(ValidationMessage.error(_userMessages.androidMissingSdkInstructions(kAndroidSdkRoot))); + messages.add(ValidationMessage.error(_userMessages.androidMissingSdkInstructions(kAndroidSdkRoot, _platform))); } return ValidationResult(ValidationType.missing, messages); } @@ -174,7 +174,12 @@ class AndroidValidator extends DoctorValidator { if (_androidSdk.latestVersion != null) { if (_androidSdk.latestVersion.sdkLevel < 28 || _androidSdk.latestVersion.buildToolsVersion < kAndroidSdkBuildToolsMinVersion) { messages.add(ValidationMessage.error( - _userMessages.androidSdkBuildToolsOutdated(_androidSdk.sdkManagerPath, kAndroidSdkMinVersion, kAndroidSdkBuildToolsMinVersion.toString())), + _userMessages.androidSdkBuildToolsOutdated( + _androidSdk.sdkManagerPath, + kAndroidSdkMinVersion, + kAndroidSdkBuildToolsMinVersion.toString(), + _platform, + )), ); return ValidationResult(ValidationType.missing, messages); } @@ -184,7 +189,7 @@ class AndroidValidator extends DoctorValidator { _androidSdk.latestVersion.platformName, _androidSdk.latestVersion.buildToolsVersionName))); } else { - messages.add(ValidationMessage.error(_userMessages.androidMissingSdkInstructions(kAndroidHome))); + messages.add(ValidationMessage.error(_userMessages.androidMissingSdkInstructions(kAndroidHome, _platform))); } if (_platform.environment.containsKey(kAndroidHome)) { @@ -203,7 +208,7 @@ class AndroidValidator extends DoctorValidator { messages.addAll(validationResult.map((String message) { return ValidationMessage.error(message); })); - messages.add(ValidationMessage(_userMessages.androidSdkInstallHelp)); + messages.add(ValidationMessage(_userMessages.androidSdkInstallHelp(_platform))); return ValidationResult(ValidationType.partial, messages, statusInfo: sdkVersionText); } @@ -261,7 +266,7 @@ class AndroidLicenseValidator extends DoctorValidator { messages.add(ValidationMessage.error(userMessages.androidLicensesNone)); return ValidationResult(ValidationType.partial, messages, statusInfo: sdkVersionText); case LicensesAccepted.unknown: - messages.add(ValidationMessage.error(userMessages.androidLicensesUnknown)); + messages.add(ValidationMessage.error(userMessages.androidLicensesUnknown(globals.platform))); return ValidationResult(ValidationType.partial, messages, statusInfo: sdkVersionText); } return ValidationResult(ValidationType.installed, messages, statusInfo: sdkVersionText); @@ -356,7 +361,7 @@ class AndroidLicenseValidator extends DoctorValidator { } if (!_canRunSdkManager()) { - throwToolExit(userMessages.androidMissingSdkManager(androidSdk.sdkManagerPath)); + throwToolExit(userMessages.androidMissingSdkManager(androidSdk.sdkManagerPath, globals.platform)); } try { @@ -394,6 +399,7 @@ class AndroidLicenseValidator extends DoctorValidator { throwToolExit(userMessages.androidCannotRunSdkManager( androidSdk.sdkManagerPath, e.toString(), + globals.platform, )); return false; } diff --git a/packages/flutter_tools/lib/src/base/user_messages.dart b/packages/flutter_tools/lib/src/base/user_messages.dart index 7d2ab9f33c..f3878bed95 100644 --- a/packages/flutter_tools/lib/src/base/user_messages.dart +++ b/packages/flutter_tools/lib/src/base/user_messages.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:platform/platform.dart'; + import 'context.dart'; UserMessages get userMessages => context.get(); @@ -62,19 +64,19 @@ class UserMessages { String androidBadSdkDir(String envKey, String homeDir) => '$envKey = $homeDir\n' 'but Android SDK not found at this location.'; - String androidMissingSdkInstructions(String envKey) => + String androidMissingSdkInstructions(String envKey, Platform platform) => 'Unable to locate Android SDK.\n' 'Install Android Studio from: https://developer.android.com/studio/index.html\n' 'On first launch it will assist you in installing the Android SDK components.\n' - '(or visit https://flutter.dev/setup/#android-setup for detailed instructions).\n' + '(or visit ${_androidSdkInstallUrl(platform)} for detailed instructions).\n' 'If the Android SDK has been installed to a custom location, set $envKey to that location.\n' 'You may also want to add it to your PATH environment variable.\n'; String androidSdkLocation(String directory) => 'Android SDK at $directory'; String androidSdkPlatformToolsVersion(String platform, String tools) => 'Platform $platform, build-tools $tools'; - String get androidSdkInstallHelp => + String androidSdkInstallHelp(Platform platform) => 'Try re-installing or updating your Android SDK,\n' - 'visit https://flutter.dev/setup/#android-setup for detailed instructions.'; + 'visit ${_androidSdkInstallUrl(platform)} for detailed instructions.'; String get androidMissingNdk => 'Android NDK location not configured (optional; useful for native profiling support)'; String androidNdkLocation(String directory) => 'Android NDK at $directory'; // Also occurs in AndroidLicenseValidator @@ -89,29 +91,29 @@ class UserMessages { String get androidLicensesAll => 'All Android licenses accepted.'; 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 => + String androidLicensesUnknown(Platform platform) => 'Android license status unknown.\n' 'Try re-installing or updating your Android SDK Manager.\n' 'See https://developer.android.com/studio/#downloads or visit ' - 'https://flutter.dev/setup/#android-setup for detailed instructions.'; + 'visit ${_androidSdkInstallUrl(platform)} for detailed instructions.'; 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'; String get androidSdkShort => 'Unable to locate Android SDK.'; - String androidMissingSdkManager(String sdkManagerPath) => + String androidMissingSdkManager(String sdkManagerPath, Platform platform) => 'Android sdkmanager tool not found ($sdkManagerPath).\n' 'Try re-installing or updating your Android SDK,\n' - 'visit https://flutter.dev/setup/#android-setup for detailed instructions.'; - String androidCannotRunSdkManager(String sdkManagerPath, String error) => + 'visit ${_androidSdkInstallUrl(platform)} for detailed instructions.'; + String androidCannotRunSdkManager(String sdkManagerPath, String error, Platform platform) => 'Android sdkmanager tool was found, but failed to run ($sdkManagerPath): "$error".\n' 'Try re-installing or updating your Android SDK,\n' - 'visit https://flutter.dev/setup/#android-setup for detailed instructions.'; - String androidSdkBuildToolsOutdated(String managerPath, int sdkMinVersion, String buildToolsMinVersion) => + 'visit ${_androidSdkInstallUrl(platform)} for detailed instructions.'; + String androidSdkBuildToolsOutdated(String managerPath, int sdkMinVersion, String buildToolsMinVersion, Platform platform) => '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.dev/setup/#android-setup for detailed instructions.'; + 'or visit ${_androidSdkInstallUrl(platform)} for detailed instructions.'; // Messages used in AndroidStudioValidator String androidStudioVersion(String version) => 'version $version'; @@ -127,9 +129,9 @@ class UserMessages { String androidStudioMissing(String location) => 'android-studio-dir = $location\n' 'but Android Studio not found at this location.'; - String get androidStudioInstallation => + String androidStudioInstallation(Platform platform) => 'Android Studio not found; download from https://developer.android.com/studio/index.html\n' - '(or visit https://flutter.dev/setup/#android-setup for detailed instructions).'; + '(or visit ${_androidSdkInstallUrl(platform)} for detailed instructions).'; // Messages used in XcodeValidator String xcodeLocation(String location) => 'Xcode at $location'; @@ -292,4 +294,18 @@ class UserMessages { 'In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.\n' 'Read more about iOS versioning at\n' 'https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html\n'; + + String _androidSdkInstallUrl(Platform platform) { + const String baseUrl = 'https://flutter.dev/docs/get-started/install'; + const String fragment = '#android-setup'; + if (platform.isMacOS) { + return '$baseUrl/macos$fragment'; + } else if (platform.isLinux) { + return '$baseUrl/linux$fragment'; + } else if (platform.isWindows) { + return '$baseUrl/windows$fragment'; + } else { + return baseUrl; + } + } } diff --git a/packages/flutter_tools/test/general.shard/android/android_workflow_test.dart b/packages/flutter_tools/test/general.shard/android/android_workflow_test.dart index 8daf9c3b74..aacf779461 100644 --- a/packages/flutter_tools/test/general.shard/android/android_workflow_test.dart +++ b/packages/flutter_tools/test/general.shard/android/android_workflow_test.dart @@ -230,6 +230,7 @@ void main() { sdk.sdkManagerPath, kAndroidSdkMinVersion, kAndroidSdkBuildToolsMinVersion.toString(), + FakePlatform(), ); final AndroidValidator androidValidator = AndroidValidator( diff --git a/packages/flutter_tools/test/general.shard/base/user_messages_test.dart b/packages/flutter_tools/test/general.shard/base/user_messages_test.dart new file mode 100644 index 0000000000..b60c3f61bf --- /dev/null +++ b/packages/flutter_tools/test/general.shard/base/user_messages_test.dart @@ -0,0 +1,37 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +import 'package:flutter_tools/src/base/user_messages.dart'; +import 'package:platform/platform.dart'; + +import '../../src/common.dart'; + +typedef _InstallationMessage = String Function(Platform); + +void main() { + final FakePlatform macPlatform = FakePlatform.fromPlatform(const LocalPlatform()); + macPlatform.operatingSystem = 'macos'; + final FakePlatform linuxPlatform = FakePlatform.fromPlatform(const LocalPlatform()); + linuxPlatform.operatingSystem = 'linux'; + final FakePlatform windowsPlatform = FakePlatform.fromPlatform(const LocalPlatform()); + windowsPlatform.operatingSystem = 'windows'; + + void _checkInstallationURL(_InstallationMessage message) { + expect(message(macPlatform), contains('https://flutter.dev/docs/get-started/install/macos#android-setup')); + expect(message(linuxPlatform), contains('https://flutter.dev/docs/get-started/install/linux#android-setup')); + expect(message(windowsPlatform), contains('https://flutter.dev/docs/get-started/install/windows#android-setup')); + expect(message(FakePlatform()), contains('https://flutter.dev/docs/get-started/install ')); + } + + testWithoutContext('Android installation instructions', () { + final UserMessages userMessages = UserMessages(); + _checkInstallationURL((Platform platform) => userMessages.androidMissingSdkInstructions('ANDROID_SDK_ROOT', platform)); + _checkInstallationURL((Platform platform) => userMessages.androidSdkInstallHelp(platform)); + _checkInstallationURL((Platform platform) => userMessages.androidMissingSdkManager('/', platform)); + _checkInstallationURL((Platform platform) => userMessages.androidCannotRunSdkManager('/', '', platform)); + _checkInstallationURL((Platform platform) => userMessages.androidSdkBuildToolsOutdated('/', 0, '', platform)); + _checkInstallationURL((Platform platform) => userMessages.androidStudioInstallation(platform)); + }); +}