Read information about AVDs from config.ini
diff --git a/packages/flutter_tools/lib/src/android/android_emulator.dart b/packages/flutter_tools/lib/src/android/android_emulator.dart index 3602aa0..30dd0fc 100644 --- a/packages/flutter_tools/lib/src/android/android_emulator.dart +++ b/packages/flutter_tools/lib/src/android/android_emulator.dart
@@ -8,6 +8,7 @@ import '../android/android_sdk.dart'; import '../android/android_workflow.dart'; +import '../base/file_system.dart'; import '../base/process.dart'; import '../emulator.dart'; import 'android_sdk.dart'; @@ -24,12 +25,19 @@ } class AndroidEmulator extends Emulator { - AndroidEmulator( - String id - ) : super(id); + AndroidEmulator(String id, [this._properties]) + : super(id, _properties != null && _properties.isNotEmpty); + + Map<String, String> _properties; @override - String get name => id; + String get name => _properties['hw.device.name']; + + @override + String get manufacturer => _properties['hw.device.manufacturer']; + + @override + String get label => _properties['avd.ini.displayname']; // @override // Future<bool> launch() async { @@ -41,20 +49,57 @@ /// Return the list of available emulator AVDs. List<AndroidEmulator> getEmulatorAvds() { final String emulatorPath = getEmulatorPath(androidSdk); - if (emulatorPath == null) + if (emulatorPath == null) { return <AndroidEmulator>[]; - final String text = runSync(<String>[emulatorPath, '-list-avds']); + } + + final String listAvdsOutput = runSync(<String>[emulatorPath, '-list-avds']); + final List<AndroidEmulator> emulators = <AndroidEmulator>[]; - parseEmulatorAvdOutput(text, emulators); + extractEmulatorAvdInfo(listAvdsOutput, emulators); return emulators; } /// Parse the given `emulator -list-avds` output in [text], and fill out the given list -/// of emulators. -@visibleForTesting -void parseEmulatorAvdOutput(String text, - List<AndroidEmulator> emulators) { - for (String line in text.trim().split('\n')) { - emulators.add(new AndroidEmulator(line)); +/// of emulators by reading information from the relevant ini files. +void extractEmulatorAvdInfo(String text, List<AndroidEmulator> emulators) { + for (String id in text.trim().split('\n')) { + emulators.add(_createEmulator(id)); } } + +AndroidEmulator _createEmulator(String id) { + id = id.trim(); + final File iniFile = fs.file(fs.path.join(getAvdPath(), '$id.ini')); + final Map<String, String> ini = _parseIniLines(iniFile.readAsLinesSync()); + + if (ini['path'] != null) { + final File configFile = fs.file(fs.path.join(ini['path'], 'config.ini')); + if (configFile.existsSync()) { + final Map<String, String> properties = _parseIniLines(configFile.readAsLinesSync()); + return new AndroidEmulator(id, properties); + } + } + + return new AndroidEmulator(id); +} + +// TODO: Tests +@visibleForTesting +Map<String, String> _parseIniLines(List<String> contents) { + final Map<String, String> results = <String, String>{}; + + final Iterable<List<String>> properties = contents + .map((String l) => l.trim()) + .where((String l) => + l != '' && !l.startsWith('#')) // Strip blank lines/comments + .where((String l) => + l.contains('=')) // Discard anything that isn't simple name=value + .map((String l) => l.split('=')); // Split into name/value + + for (List<String> property in properties) { + results[property[0].trim()] = property[1].trim(); + } + + return results; +}
diff --git a/packages/flutter_tools/lib/src/android/android_sdk.dart b/packages/flutter_tools/lib/src/android/android_sdk.dart index 1875c48..bc10b95 100644 --- a/packages/flutter_tools/lib/src/android/android_sdk.dart +++ b/packages/flutter_tools/lib/src/android/android_sdk.dart
@@ -59,9 +59,9 @@ } } -/// Locate ADB. Prefer to use one from an Android SDK, if we can locate that. -/// This should be used over accessing androidSdk.adbPath directly because it -/// will work for those users who have Android Platform Tools installed but +/// Locate 'emulator'. Prefer to use one from an Android SDK, if we can locate that. +/// This should be used over accessing androidSdk.emulatorPath directly because it +/// will work for those users who have Android Tools installed but /// not the full SDK. String getEmulatorPath([AndroidSdk existingSdk]) { if (existingSdk?.emulatorPath != null) @@ -76,6 +76,18 @@ } } +/// Locate the path for storing AVD emulator images. Returns null if none found. +String getAvdPath() { + final List<String> searchPaths = <String>[ + platform.environment['ANDROID_AVD_HOME'], + fs.path.join(platform.environment['HOME'], '.android', 'avd'), + ]; + return searchPaths.where((String p) => p != null).firstWhere( + (String p) => fs.directory(p).existsSync(), + orElse: () => null, + ); +} + class AndroidSdk { AndroidSdk(this.directory, [this.ndkDirectory, this.ndkCompiler, this.ndkCompilerArgs]) {
diff --git a/packages/flutter_tools/lib/src/android/android_workflow.dart b/packages/flutter_tools/lib/src/android/android_workflow.dart index db40d65..737eb1f 100644 --- a/packages/flutter_tools/lib/src/android/android_workflow.dart +++ b/packages/flutter_tools/lib/src/android/android_workflow.dart
@@ -43,7 +43,7 @@ bool get canLaunchDevices => androidSdk != null && androidSdk.validateSdkWellFormed().isEmpty; @override - bool get canListEmulators => getEmulatorPath(androidSdk) != null; + bool get canListEmulators => getEmulatorPath(androidSdk) != null && getAvdPath() != null; static const String _kJdkDownload = 'https://www.oracle.com/technetwork/java/javase/downloads/';
diff --git a/packages/flutter_tools/lib/src/emulator.dart b/packages/flutter_tools/lib/src/emulator.dart index 57c647a..af243ed 100644 --- a/packages/flutter_tools/lib/src/emulator.dart +++ b/packages/flutter_tools/lib/src/emulator.dart
@@ -115,11 +115,13 @@ } abstract class Emulator { - Emulator(this.id); + Emulator(this.id, this.hasConfig); final String id; - - String get name => id; + final bool hasConfig; + String get name; + String get manufacturer; + String get label; @override int get hashCode => id.hashCode; @@ -145,6 +147,8 @@ for (Emulator emulator in emulators) { table.add(<String>[ emulator.name, + emulator.manufacturer, + emulator.label, emulator.id, ]); }
diff --git a/packages/flutter_tools/test/emulator_test.dart b/packages/flutter_tools/test/emulator_test.dart index 784d326..d7e3945 100644 --- a/packages/flutter_tools/test/emulator_test.dart +++ b/packages/flutter_tools/test/emulator_test.dart
@@ -19,9 +19,9 @@ }); testUsingContext('getEmulatorsById', () async { - final _MockEmulator emulator1 = new _MockEmulator('Nexus_5'); - final _MockEmulator emulator2 = new _MockEmulator('Nexus_5X_API_27_x86'); - final _MockEmulator emulator3 = new _MockEmulator('iOS Simulator'); + final _MockEmulator emulator1 = new _MockEmulator('Nexus_5', 'Nexus 5', 'Google', ''); + final _MockEmulator emulator2 = new _MockEmulator('Nexus_5X_API_27_x86', 'Nexus 5X', 'Google', ''); + final _MockEmulator emulator3 = new _MockEmulator('iOS Simulator', 'iOS Simulator', 'Apple', ''); final List<Emulator> emulators = <Emulator>[emulator1, emulator2, emulator3]; final EmulatorManager emulatorManager = new TestEmulatorManager(emulators); @@ -50,8 +50,15 @@ } class _MockEmulator extends Emulator { - _MockEmulator(String id) : super(id); + _MockEmulator(String id, this.name, this.manufacturer, this.label) : super(id, true); @override - void noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); + final String name; + + @override + final String manufacturer; + + @override + final String label; + }