blob: 57c647a55f3d7719eef03d3bbd93f1a94fde5a66 [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';
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());
}
final List<EmulatorDiscovery> _emulatorDiscoverers = <EmulatorDiscovery>[];
String _specifiedEmulatorId;
/// A user-specified emulator ID.
String get specifiedEmulatorId {
if (_specifiedEmulatorId == null || _specifiedEmulatorId == 'all')
return null;
return _specifiedEmulatorId;
}
set specifiedEmulatorId(String id) {
_specifiedEmulatorId = id;
}
/// True when the user has specified a single specific emulator.
bool get hasSpecifiedEmulatorId => specifiedEmulatorId != null;
/// True when the user has specified all emulators by setting
/// specifiedEmulatorId = 'all'.
bool get hasSpecifiedAllEmulators => _specifiedEmulatorId == 'all';
Stream<Emulator> getEmulatorsById(String emulatorId) async* {
final List<Emulator> emulators = await getAllAvailableEmulators().toList();
emulatorId = emulatorId.toLowerCase();
bool exactlyMatchesEmulatorId(Emulator emulator) =>
emulator.id.toLowerCase() == emulatorId ||
emulator.name.toLowerCase() == emulatorId;
bool startsWithEmulatorId(Emulator emulator) =>
emulator.id.toLowerCase().startsWith(emulatorId) ||
emulator.name.toLowerCase().startsWith(emulatorId);
final Emulator exactMatch = emulators.firstWhere(
exactlyMatchesEmulatorId, orElse: () => null);
if (exactMatch != null) {
yield exactMatch;
return;
}
// Match on a id or name starting with [emulatorId].
for (Emulator emulator in emulators.where(startsWithEmulatorId))
yield emulator;
}
/// Return the list of available emulators, filtered by any user-specified emulator id.
Stream<Emulator> getEmulators() {
return hasSpecifiedEmulatorId
? getEmulatorsById(specifiedEmulatorId)
: getAllAvailableEmulators();
}
Iterable<EmulatorDiscovery> get _platformDiscoverers {
return _emulatorDiscoverers.where((EmulatorDiscovery discoverer) => discoverer.supportsPlatform);
}
/// Return the list of all connected emulators.
Stream<Emulator> getAllAvailableEmulators() async* {
for (EmulatorDiscovery discoverer in _platformDiscoverers) {
for (Emulator emulator in await discoverer.emulators) {
yield emulator;
}
}
}
/// Whether we're capable of listing any emulators given the current environment configuration.
bool get canListAnything {
return _platformDiscoverers.any((EmulatorDiscovery discoverer) => discoverer.canListAnything);
}
/// Get diagnostics about issues with any emulators.
Future<List<String>> getEmulatorDiagnostics() async {
final List<String> diagnostics = <String>[];
for (EmulatorDiscovery discoverer in _platformDiscoverers) {
diagnostics.addAll(await discoverer.getDiagnostics());
}
return diagnostics;
}
}
/// 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;
/// Gets a list of diagnostic messages pertaining to issues with any available
/// emulators (will be an empty list if there are no issues).
Future<List<String>> getDiagnostics() => new Future<List<String>>.value(<String>[]);
}
abstract class Emulator {
Emulator(this.id);
final String id;
String get name => id;
@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;
}
@override
String toString() => name;
static Stream<String> descriptions(List<Emulator> emulators) async* {
if (emulators.isEmpty)
return;
// Extract emulators information
final List<List<String>> table = <List<String>>[];
for (Emulator emulator in emulators) {
table.add(<String>[
emulator.name,
emulator.id,
]);
}
// 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
for (List<String> row in table) {
yield indices.map((int i) => row[i].padRight(widths[i])).join(' • ') + ' • ${row.last}';
}
}
static Future<Null> printEmulators(List<Emulator> emulators) async {
await descriptions(emulators).forEach(printStatus);
}
}