Skip to content

Commit

Permalink
[tools] Apply Android Studio version detection logic to explicitly co…
Browse files Browse the repository at this point in the history
…nfigured installation directory (`flutter config --android-studio-dir`) (#125596)

Fixes #121468 (when considered along with #125247).

This applies the pre-existing Android Studio version detection logic to the install configured with `flutter config android-studio-dir`.
  • Loading branch information
andrewkolos committed May 1, 2023
1 parent 4b74232 commit 9dbd6e6
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 34 deletions.
87 changes: 62 additions & 25 deletions packages/flutter_tools/lib/src/android/android_studio.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ class AndroidStudio {
_initAndValidate();
}

static AndroidStudio? fromMacOSBundle(String bundlePath) {
static AndroidStudio? fromMacOSBundle(
String bundlePath, {
String? configuredPath,
}) {
final String studioPath = globals.fs.path.join(bundlePath, 'Contents');
final String plistFile = globals.fs.path.join(studioPath, 'Info.plist');
final Map<String, dynamic> plistValues = globals.plistParser.parseFile(plistFile);
Expand Down Expand Up @@ -99,7 +102,12 @@ class AndroidStudio {
);
}
}
return AndroidStudio(studioPath, version: version, presetPluginsPath: presetPluginsPath);
return AndroidStudio(
studioPath,
version: version,
presetPluginsPath: presetPluginsPath,
configuredPath: configuredPath,
);
}

static AndroidStudio? fromHomeDot(Directory homeDotDir) {
Expand Down Expand Up @@ -238,31 +246,32 @@ class AndroidStudio {
/// invalid.
static AndroidStudio? latestValid() {
final String? configuredStudioPath = globals.config.getValue('android-studio-dir') as String?;
if (configuredStudioPath != null) {
String correctedConfiguredStudioPath = configuredStudioPath;
if (globals.platform.isMacOS && !correctedConfiguredStudioPath.endsWith('Contents')) {
correctedConfiguredStudioPath = globals.fs.path.join(correctedConfiguredStudioPath, 'Contents');
}

if (!globals.fs.directory(correctedConfiguredStudioPath).existsSync()) {
throwToolExit('''
if (configuredStudioPath != null && !globals.fs.directory(configuredStudioPath).existsSync()) {
throwToolExit('''
Could not find the Android Studio installation at the manually configured path "$configuredStudioPath".
Please verify that the path is correct and update it by running this command: flutter config --android-studio-dir '<path>'
To have flutter search for Android Studio installations automatically, remove
the configured path by running this command: flutter config --android-studio-dir ''
''');
}

return AndroidStudio(correctedConfiguredStudioPath,
configuredPath: configuredStudioPath);
}

// Find all available Studio installations.
final List<AndroidStudio> studios = allInstalled();
if (studios.isEmpty) {
return null;
}

final AndroidStudio? manuallyConfigured = studios
.where((AndroidStudio studio) => studio.configuredPath != null &&
configuredStudioPath != null &&
_pathsAreEqual(studio.configuredPath!, configuredStudioPath))
.firstOrNull;

if (manuallyConfigured != null) {
return manuallyConfigured;
}

AndroidStudio? newest;
for (final AndroidStudio studio in studios.where((AndroidStudio s) => s.isValid)) {
if (newest == null) {
Expand Down Expand Up @@ -324,14 +333,15 @@ the configured path by running this command: flutter config --android-studio-dir
}

final String? configuredStudioDir = globals.config.getValue('android-studio-dir') as String?;
FileSystemEntity? configuredStudioDirAsEntity;
if (configuredStudioDir != null) {
FileSystemEntity configuredStudio = globals.fs.file(configuredStudioDir);
if (configuredStudio.basename == 'Contents') {
configuredStudio = configuredStudio.parent;
configuredStudioDirAsEntity = globals.fs.directory(configuredStudioDir);
if (configuredStudioDirAsEntity.basename == 'Contents') {
configuredStudioDirAsEntity = configuredStudioDirAsEntity.parent;
}
if (!candidatePaths
.any((FileSystemEntity e) => e.path == configuredStudio.path)) {
candidatePaths.add(configuredStudio);
.any((FileSystemEntity e) => _pathsAreEqual(e.path, configuredStudioDirAsEntity!.path))) {
candidatePaths.add(configuredStudioDirAsEntity);
}
}

Expand All @@ -355,9 +365,18 @@ the configured path by running this command: flutter config --android-studio-dir
}

return candidatePaths
.map<AndroidStudio?>((FileSystemEntity e) => AndroidStudio.fromMacOSBundle(e.path))
.whereType<AndroidStudio>()
.toList();
.map<AndroidStudio?>((FileSystemEntity e) {
if (configuredStudioDirAsEntity == null) {
return AndroidStudio.fromMacOSBundle(e.path);
}

return AndroidStudio.fromMacOSBundle(
e.path,
configuredPath: _pathsAreEqual(configuredStudioDirAsEntity.path, e.path) ? configuredStudioDir : null,
);
})
.whereType<AndroidStudio>()
.toList();
}

static List<AndroidStudio> _allLinuxOrWindows() {
Expand Down Expand Up @@ -440,7 +459,7 @@ the configured path by running this command: flutter config --android-studio-dir
studioAppName: title,
);
if (!alreadyFoundStudioAt(studio.directory, newerThan: studio.version)) {
studios.removeWhere((AndroidStudio other) => other.directory == studio.directory);
studios.removeWhere((AndroidStudio other) => _pathsAreEqual(other.directory, studio.directory));
studios.add(studio);
}
}
Expand All @@ -450,9 +469,23 @@ the configured path by running this command: flutter config --android-studio-dir
}

final String? configuredStudioDir = globals.config.getValue('android-studio-dir') as String?;
if (configuredStudioDir != null && !alreadyFoundStudioAt(configuredStudioDir)) {
studios.add(AndroidStudio(configuredStudioDir,
if (configuredStudioDir != null) {
final AndroidStudio? matchingAlreadyFoundInstall = studios
.where((AndroidStudio other) => _pathsAreEqual(configuredStudioDir, other.directory))
.firstOrNull;
if (matchingAlreadyFoundInstall != null) {
studios.remove(matchingAlreadyFoundInstall);
studios.add(
AndroidStudio(
configuredStudioDir,
configuredPath: configuredStudioDir,
version: matchingAlreadyFoundInstall.version,
),
);
} else {
studios.add(AndroidStudio(configuredStudioDir,
configuredPath: configuredStudioDir));
}
}

if (globals.platform.isLinux) {
Expand Down Expand Up @@ -528,3 +561,7 @@ the configured path by running this command: flutter config --android-studio-dir
@override
String toString() => 'Android Studio ($version)';
}

bool _pathsAreEqual(String path, String other) {
return globals.fs.path.canonicalize(path) == globals.fs.path.canonicalize(other);
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,15 @@ void main() {
},
};

late Config config;
late FileSystem fileSystem;
late FileSystemUtils fsUtils;
late Platform platform;
late FakePlistUtils plistUtils;
late FakeProcessManager processManager;

setUp(() {
config = Config.test();
fileSystem = MemoryFileSystem.test();
plistUtils = FakePlistUtils();
platform = FakePlatform(
Expand Down Expand Up @@ -590,14 +592,6 @@ void main() {

final String jdkPathFor2022 = fileSystem.path.join(studioInApplicationPlistFolder, 'jbr', 'Contents', 'Home');

processManager.addCommand(FakeCommand(
command: <String>[
fileSystem.path.join(jdkPathFor2022, 'bin', 'java'),
'-version',
],
stderr: '123',
)
);
final AndroidStudio studio = AndroidStudio.fromMacOSBundle(
fileSystem.directory(studioInApplicationPlistFolder).parent.path,
)!;
Expand All @@ -607,18 +601,70 @@ void main() {
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
FileSystemUtils: () => fsUtils,
ProcessManager: () => processManager,
ProcessManager: () => FakeProcessManager.any(),
Platform: () => platform,
PlistParser: () => plistUtils,
});

testUsingContext('discovers explicitly configured Android Studio', () {
final String extractedDownloadZip = fileSystem.path.join(
'/',
'Users',
'Dash',
'Desktop',
'android-studio'
);
config.setValue('android-studio-dir', extractedDownloadZip);
final String studioInApplicationPlistFolder = fileSystem.path.join(
extractedDownloadZip,
'Contents',
);
fileSystem.directory(studioInApplicationPlistFolder).createSync(recursive: true);
final String plistFilePath = fileSystem.path.join(studioInApplicationPlistFolder, 'Info.plist');
plistUtils.fileContents[plistFilePath] = macStudioInfoPlist2022_1;

final String studioInApplicationJavaBinary = fileSystem.path.join(
extractedDownloadZip,
'Contents', 'jbr', 'Contents', 'Home', 'bin', 'java',
);

processManager.addCommands(<FakeCommand>[
FakeCommand(
command: const <String>[
'mdfind',
'kMDItemCFBundleIdentifier="com.google.android.studio*"',
],
stdout: extractedDownloadZip,
),
FakeCommand(
command: <String>[
studioInApplicationJavaBinary,
'-version',
],
),
]);

final AndroidStudio studio = AndroidStudio.allInstalled().single;

expect(studio.configuredPath, extractedDownloadZip);
expect(processManager, hasNoRemainingExpectations);
}, overrides: <Type, Generator>{
Config:() => config,
FileSystem: () => fileSystem,
FileSystemUtils: () => fsUtils,
ProcessManager: () => processManager,
Platform: () => platform,
PlistParser: () => plistUtils,
});
});

group('installation detection on Windows', () {
late Config config;
late Platform platform;
late FileSystem fileSystem;

setUp(() {
config = Config.test();
platform = FakePlatform(
operatingSystem: 'windows',
environment: <String, String>{
Expand Down Expand Up @@ -800,6 +846,27 @@ void main() {
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
});

testUsingContext('discovers explicitly configured Android Studio', () {
const String androidStudioDir = r'C:\Users\Dash\Desktop\android-studio';
config.setValue('android-studio-dir', androidStudioDir);
fileSystem.file(r'C:\Users\Dash\AppData\Local\Google\AndroidStudio2022.1\.home')
..createSync(recursive: true)
..writeAsStringSync(androidStudioDir);
fileSystem.directory(androidStudioDir)
.createSync(recursive: true);

final AndroidStudio studio = AndroidStudio.allInstalled().single;

expect(studio.version, equals(Version(2022, 1, null)));
expect(studio.configuredPath, androidStudioDir);
expect(studio.javaPath, fileSystem.path.join(androidStudioDir, 'jbr'));
}, overrides: <Type, Generator>{
Config: () => config,
Platform: () => platform,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
});

group('installation detection on Linux', () {
Expand Down Expand Up @@ -1015,6 +1082,28 @@ void main() {
platform: platform,
),
});

testUsingContext('discovers explicitly configured Android Studio', () {
const String androidStudioDir = '/Users/Dash/Desktop/android-studio';
config.setValue('android-studio-dir', androidStudioDir);
const String studioHome = '$homeLinux/.cache/Google/AndroidStudio2022.3/.home';
fileSystem.file(studioHome)
..createSync(recursive: true)
..writeAsStringSync(androidStudioDir);
fileSystem.directory(androidStudioDir)
.createSync(recursive: true);

final AndroidStudio studio = AndroidStudio.allInstalled().single;

expect(studio.version, equals(Version(2022, 3, null)));
expect(studio.configuredPath, androidStudioDir);
expect(studio.javaPath, fileSystem.path.join(androidStudioDir, 'jbr'));
}, overrides: <Type, Generator>{
Config: () => config,
Platform: () => platform,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
});

group('latestValid', () {
Expand Down Expand Up @@ -1162,6 +1251,16 @@ void main() {
}

expect(AndroidStudio.allInstalled().length, 4);

for (final String javaPath in validJavaPaths) {
(globals.processManager as FakeProcessManager).addCommand(FakeCommand(
command: <String>[
fileSystem.path.join(javaPath),
'-version',
],
));
}

final AndroidStudio chosenInstall = AndroidStudio.latestValid()!;
expect(chosenInstall.directory, configuredAndroidStudioDir);
expect(chosenInstall.isValid, false);
Expand Down

0 comments on commit 9dbd6e6

Please sign in to comment.