blob: 81bbe2aa65fb61a12c7b04458d6b67af63b9a39c [file] [log] [blame]
// Copyright 2018 The Chromium 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 'dart:async';
import 'dart:math' as math;
import 'android/android_emulator.dart';
import 'base/context.dart';
import 'globals.dart';
import 'ios/ios_emulators.dart';
EmulatorManager get emulatorManager => context[EmulatorManager];
/// A class to get all available emulators.
class EmulatorManager {
/// Constructing EmulatorManager is cheap; they only do expensive work if some
/// of their methods are called.
EmulatorManager() {
// Register the known discoverers.
_emulatorDiscoverers.add(new AndroidEmulators());
_emulatorDiscoverers.add(new IOSEmulators());
}
final List<EmulatorDiscovery> _emulatorDiscoverers = <EmulatorDiscovery>[];
Future<List<Emulator>> getEmulatorsMatching(String searchText) async {
final List<Emulator> emulators = await getAllAvailableEmulators();
searchText = searchText.toLowerCase();
bool exactlyMatchesEmulatorId(Emulator emulator) =>
emulator.id?.toLowerCase() == searchText ||
emulator.name?.toLowerCase() == searchText;
bool startsWithEmulatorId(Emulator emulator) =>
emulator.id?.toLowerCase()?.startsWith(searchText) == true ||
emulator.name?.toLowerCase()?.startsWith(searchText) == true;
final Emulator exactMatch = emulators.firstWhere(
exactlyMatchesEmulatorId, orElse: () => null);
if (exactMatch != null) {
return <Emulator>[exactMatch];
}
// Match on a id or name starting with [emulatorId].
return emulators.where(startsWithEmulatorId).toList();
}
Iterable<EmulatorDiscovery> get _platformDiscoverers {
return _emulatorDiscoverers.where((EmulatorDiscovery discoverer) => discoverer.supportsPlatform);
}
/// Return the list of all available emulators.
Future<List<Emulator>> getAllAvailableEmulators() async {
final List<Emulator> emulators = <Emulator>[];
await Future.forEach(_platformDiscoverers, (EmulatorDiscovery discoverer) async {
emulators.addAll(await discoverer.emulators);
});
return emulators;
}
/// Whether we're capable of listing any emulators given the current environment configuration.
bool get canListAnything {
return _platformDiscoverers.any((EmulatorDiscovery discoverer) => discoverer.canListAnything);
}
}
/// An abstract class to discover and enumerate a specific type of emulators.
abstract class EmulatorDiscovery {
bool get supportsPlatform;
/// Whether this emulator discovery is capable of listing any emulators given the
/// current environment configuration.
bool get canListAnything;
Future<List<Emulator>> get emulators;
}
abstract class Emulator {
Emulator(this.id, this.hasConfig);
final String id;
final bool hasConfig;
String get name;
String get manufacturer;
String get label;
@override
int get hashCode => id.hashCode;
@override
bool operator ==(dynamic other) {
if (identical(this, other))
return true;
if (other is! Emulator)
return false;
return id == other.id;
}
Future<void> launch();
@override
String toString() => name;
static List<String> descriptions(List<Emulator> emulators) {
if (emulators.isEmpty)
return <String>[];
// Extract emulators information
final List<List<String>> table = <List<String>>[];
for (Emulator emulator in emulators) {
table.add(<String>[
emulator.id ?? '',
emulator.name ?? '',
emulator.manufacturer ?? '',
emulator.label ?? '',
]);
}
// Calculate column widths
final List<int> indices = new List<int>.generate(table[0].length - 1, (int i) => i);
List<int> widths = indices.map((int i) => 0).toList();
for (List<String> row in table) {
widths = indices.map((int i) => math.max(widths[i], row[i].length)).toList();
}
// Join columns into lines of text
final RegExp whiteSpaceAndDots = new RegExp(r'[•\s]+$');
return table.map((List<String> row) {
return indices
.map((int i) => row[i].padRight(widths[i]))
.join(' • ') + ' • ${row.last}';
})
.map((String line) => line.replaceAll(whiteSpaceAndDots, ''))
.toList();
}
static void printEmulators(List<Emulator> emulators) {
descriptions(emulators).forEach(printStatus);
}
}