mirror of
https://github.com/flutter/flutter
synced 2024-10-13 03:32:55 +00:00
Switch many Device
methods to be async (#9587)
`adb` can sometimes hang, which will in turn hang the Dart isolate if we're using `Process.runSync()`. This changes many of the `Device` methods to return `Future<T>` in order to allow them to use the async process methods. A future change will add timeouts to the associated calls so that we can properly alert the user to the hung `adb` process. This is work towards #7102, #9567
This commit is contained in:
parent
596eb033c7
commit
60c5ffc1a9
|
@ -51,7 +51,7 @@ class AndroidDevice extends Device {
|
|||
bool _isLocalEmulator;
|
||||
TargetPlatform _platform;
|
||||
|
||||
String _getProperty(String name) {
|
||||
Future<String> _getProperty(String name) async {
|
||||
if (_properties == null) {
|
||||
_properties = <String, String>{};
|
||||
|
||||
|
@ -79,19 +79,19 @@ class AndroidDevice extends Device {
|
|||
}
|
||||
|
||||
@override
|
||||
bool get isLocalEmulator {
|
||||
Future<bool> get isLocalEmulator async {
|
||||
if (_isLocalEmulator == null) {
|
||||
final String characteristics = _getProperty('ro.build.characteristics');
|
||||
final String characteristics = await _getProperty('ro.build.characteristics');
|
||||
_isLocalEmulator = characteristics != null && characteristics.contains('emulator');
|
||||
}
|
||||
return _isLocalEmulator;
|
||||
}
|
||||
|
||||
@override
|
||||
TargetPlatform get targetPlatform {
|
||||
Future<TargetPlatform> get targetPlatform async {
|
||||
if (_platform == null) {
|
||||
// http://developer.android.com/ndk/guides/abis.html (x86, armeabi-v7a, ...)
|
||||
switch (_getProperty('ro.product.cpu.abi')) {
|
||||
switch (await _getProperty('ro.product.cpu.abi')) {
|
||||
case 'x86_64':
|
||||
_platform = TargetPlatform.android_x64;
|
||||
break;
|
||||
|
@ -108,11 +108,12 @@ class AndroidDevice extends Device {
|
|||
}
|
||||
|
||||
@override
|
||||
String get sdkNameAndVersion => 'Android $_sdkVersion (API $_apiVersion)';
|
||||
Future<String> get sdkNameAndVersion async =>
|
||||
'Android ${await _sdkVersion} (API ${await _apiVersion})';
|
||||
|
||||
String get _sdkVersion => _getProperty('ro.build.version.release');
|
||||
Future<String> get _sdkVersion => _getProperty('ro.build.version.release');
|
||||
|
||||
String get _apiVersion => _getProperty('ro.build.version.sdk');
|
||||
Future<String> get _apiVersion => _getProperty('ro.build.version.sdk');
|
||||
|
||||
_AdbLogReader _logReader;
|
||||
_AndroidDevicePortForwarder _portForwarder;
|
||||
|
@ -160,16 +161,16 @@ class AndroidDevice extends Device {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool _checkForSupportedAndroidVersion() {
|
||||
Future<bool> _checkForSupportedAndroidVersion() async {
|
||||
try {
|
||||
// If the server is automatically restarted, then we get irrelevant
|
||||
// output lines like this, which we want to ignore:
|
||||
// adb server is out of date. killing..
|
||||
// * daemon started successfully *
|
||||
runCheckedSync(<String>[getAdbPath(androidSdk), 'start-server']);
|
||||
await runCheckedAsync(<String>[getAdbPath(androidSdk), 'start-server']);
|
||||
|
||||
// Sample output: '22'
|
||||
final String sdkVersion = _getProperty('ro.build.version.sdk');
|
||||
final String sdkVersion = await _getProperty('ro.build.version.sdk');
|
||||
|
||||
final int sdkVersionParsed = int.parse(sdkVersion, onError: (String source) => null);
|
||||
if (sdkVersionParsed == null) {
|
||||
|
@ -195,8 +196,9 @@ class AndroidDevice extends Device {
|
|||
return '/data/local/tmp/sky.${app.id}.sha1';
|
||||
}
|
||||
|
||||
String _getDeviceApkSha1(ApplicationPackage app) {
|
||||
return runSync(adbCommandForDevice(<String>['shell', 'cat', _getDeviceSha1Path(app)]));
|
||||
Future<String> _getDeviceApkSha1(ApplicationPackage app) async {
|
||||
final RunResult result = await runAsync(adbCommandForDevice(<String>['shell', 'cat', _getDeviceSha1Path(app)]));
|
||||
return result.stdout;
|
||||
}
|
||||
|
||||
String _getSourceSha1(ApplicationPackage app) {
|
||||
|
@ -209,15 +211,15 @@ class AndroidDevice extends Device {
|
|||
String get name => modelID;
|
||||
|
||||
@override
|
||||
bool isAppInstalled(ApplicationPackage app) {
|
||||
Future<bool> isAppInstalled(ApplicationPackage app) async {
|
||||
// This call takes 400ms - 600ms.
|
||||
final String listOut = runCheckedSync(adbCommandForDevice(<String>['shell', 'pm', 'list', 'packages', app.id]));
|
||||
return LineSplitter.split(listOut).contains("package:${app.id}");
|
||||
final RunResult listOut = await runCheckedAsync(adbCommandForDevice(<String>['shell', 'pm', 'list', 'packages', app.id]));
|
||||
return LineSplitter.split(listOut.stdout).contains("package:${app.id}");
|
||||
}
|
||||
|
||||
@override
|
||||
bool isLatestBuildInstalled(ApplicationPackage app) {
|
||||
final String installedSha1 = _getDeviceApkSha1(app);
|
||||
Future<bool> isLatestBuildInstalled(ApplicationPackage app) async {
|
||||
final String installedSha1 = await _getDeviceApkSha1(app);
|
||||
return installedSha1.isNotEmpty && installedSha1 == _getSourceSha1(app);
|
||||
}
|
||||
|
||||
|
@ -229,7 +231,7 @@ class AndroidDevice extends Device {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!_checkForSupportedAdbVersion() || !_checkForSupportedAndroidVersion())
|
||||
if (!_checkForSupportedAdbVersion() || !await _checkForSupportedAndroidVersion())
|
||||
return false;
|
||||
|
||||
final Status status = logger.startProgress('Installing ${apk.apkPath}...', expectSlowOperation: true);
|
||||
|
@ -249,11 +251,11 @@ class AndroidDevice extends Device {
|
|||
}
|
||||
|
||||
@override
|
||||
bool uninstallApp(ApplicationPackage app) {
|
||||
if (!_checkForSupportedAdbVersion() || !_checkForSupportedAndroidVersion())
|
||||
Future<bool> uninstallApp(ApplicationPackage app) async {
|
||||
if (!_checkForSupportedAdbVersion() || !await _checkForSupportedAndroidVersion())
|
||||
return false;
|
||||
|
||||
final String uninstallOut = runCheckedSync(adbCommandForDevice(<String>['uninstall', app.id]));
|
||||
final String uninstallOut = (await runCheckedAsync(adbCommandForDevice(<String>['uninstall', app.id]))).stdout;
|
||||
final RegExp failureExp = new RegExp(r'^Failure.*$', multiLine: true);
|
||||
final String failure = failureExp.stringMatch(uninstallOut);
|
||||
if (failure != null) {
|
||||
|
@ -265,9 +267,9 @@ class AndroidDevice extends Device {
|
|||
}
|
||||
|
||||
Future<bool> _installLatestApp(ApplicationPackage package) async {
|
||||
final bool wasInstalled = isAppInstalled(package);
|
||||
final bool wasInstalled = await isAppInstalled(package);
|
||||
if (wasInstalled) {
|
||||
if (isLatestBuildInstalled(package)) {
|
||||
if (await isLatestBuildInstalled(package)) {
|
||||
printTrace('Latest build already installed.');
|
||||
return true;
|
||||
}
|
||||
|
@ -277,7 +279,7 @@ class AndroidDevice extends Device {
|
|||
printTrace('Warning: Failed to install APK.');
|
||||
if (wasInstalled) {
|
||||
printStatus('Uninstalling old version...');
|
||||
if (!uninstallApp(package)) {
|
||||
if (!await uninstallApp(package)) {
|
||||
printError('Error: Uninstalling old version failed.');
|
||||
return false;
|
||||
}
|
||||
|
@ -304,10 +306,10 @@ class AndroidDevice extends Device {
|
|||
bool prebuiltApplication: false,
|
||||
bool applicationNeedsRebuild: false,
|
||||
}) async {
|
||||
if (!_checkForSupportedAdbVersion() || !_checkForSupportedAndroidVersion())
|
||||
if (!_checkForSupportedAdbVersion() || !await _checkForSupportedAndroidVersion())
|
||||
return new LaunchResult.failed();
|
||||
|
||||
if (targetPlatform != TargetPlatform.android_arm && mode != BuildMode.debug) {
|
||||
if (await targetPlatform != TargetPlatform.android_arm && mode != BuildMode.debug) {
|
||||
printError('Profile and release builds are only supported on ARM targets.');
|
||||
return new LaunchResult.failed();
|
||||
}
|
||||
|
@ -369,7 +371,7 @@ class AndroidDevice extends Device {
|
|||
cmd.addAll(<String>['--ez', 'use-test-fonts', 'true']);
|
||||
}
|
||||
cmd.add(apk.launchActivity);
|
||||
final String result = runCheckedSync(cmd);
|
||||
final String result = (await runCheckedAsync(cmd)).stdout;
|
||||
// This invocation returns 0 even when it fails.
|
||||
if (result.contains('Error: ')) {
|
||||
printError(result.trim());
|
||||
|
@ -455,16 +457,15 @@ class AndroidDevice extends Device {
|
|||
bool get supportsScreenshot => true;
|
||||
|
||||
@override
|
||||
Future<Null> takeScreenshot(File outputFile) {
|
||||
Future<Null> takeScreenshot(File outputFile) async {
|
||||
const String remotePath = '/data/local/tmp/flutter_screenshot.png';
|
||||
runCheckedSync(adbCommandForDevice(<String>['shell', 'screencap', '-p', remotePath]));
|
||||
runCheckedSync(adbCommandForDevice(<String>['pull', remotePath, outputFile.path]));
|
||||
runCheckedSync(adbCommandForDevice(<String>['shell', 'rm', remotePath]));
|
||||
return new Future<Null>.value();
|
||||
await runCheckedAsync(adbCommandForDevice(<String>['shell', 'screencap', '-p', remotePath]));
|
||||
await runCheckedAsync(adbCommandForDevice(<String>['pull', remotePath, outputFile.path]));
|
||||
await runCheckedAsync(adbCommandForDevice(<String>['shell', 'rm', remotePath]));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<DiscoveredApp>> discoverApps() {
|
||||
Future<List<DiscoveredApp>> discoverApps() async {
|
||||
final RegExp discoverExp = new RegExp(r'DISCOVER: (.*)');
|
||||
final List<DiscoveredApp> result = <DiscoveredApp>[];
|
||||
final StreamSubscription<String> logs = getLogReader().logLines.listen((String line) {
|
||||
|
@ -475,14 +476,13 @@ class AndroidDevice extends Device {
|
|||
}
|
||||
});
|
||||
|
||||
runCheckedSync(adbCommandForDevice(<String>[
|
||||
await runCheckedAsync(adbCommandForDevice(<String>[
|
||||
'shell', 'am', 'broadcast', '-a', 'io.flutter.view.DISCOVER'
|
||||
]));
|
||||
|
||||
return new Future<List<DiscoveredApp>>.delayed(const Duration(seconds: 1), () {
|
||||
logs.cancel();
|
||||
return result;
|
||||
});
|
||||
await new Future<Null>.delayed(const Duration(seconds: 1));
|
||||
logs.cancel();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -744,7 +744,7 @@ class _AndroidDevicePortForwarder extends DevicePortForwarder {
|
|||
hostPort = await portScanner.findAvailablePort();
|
||||
}
|
||||
|
||||
runCheckedSync(device.adbCommandForDevice(
|
||||
await runCheckedAsync(device.adbCommandForDevice(
|
||||
<String>['forward', 'tcp:$hostPort', 'tcp:$devicePort']
|
||||
));
|
||||
|
||||
|
@ -753,7 +753,7 @@ class _AndroidDevicePortForwarder extends DevicePortForwarder {
|
|||
|
||||
@override
|
||||
Future<Null> unforward(ForwardedPort forwardedPort) async {
|
||||
runCheckedSync(device.adbCommandForDevice(
|
||||
await runCheckedAsync(device.adbCommandForDevice(
|
||||
<String>['forward', '--remove', 'tcp:${forwardedPort.hostPort}']
|
||||
));
|
||||
}
|
||||
|
|
|
@ -221,6 +221,15 @@ bool exitsHappy(List<String> cli) {
|
|||
}
|
||||
}
|
||||
|
||||
Future<bool> exitsHappyAsync(List<String> cli) async {
|
||||
_traceCommand(cli);
|
||||
try {
|
||||
return (await processManager.run(cli)).exitCode == 0;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Run cmd and return stdout.
|
||||
///
|
||||
/// Throws an error if cmd exits with a non-zero value.
|
||||
|
@ -241,16 +250,6 @@ String runCheckedSync(List<String> cmd, {
|
|||
);
|
||||
}
|
||||
|
||||
/// Run cmd and return stdout on success.
|
||||
///
|
||||
/// Throws the standard error output if cmd exits with a non-zero value.
|
||||
String runSyncAndThrowStdErrOnError(List<String> cmd) {
|
||||
return _runWithLoggingSync(cmd,
|
||||
checked: true,
|
||||
throwStandardErrorOnError: true,
|
||||
hideStdout: true);
|
||||
}
|
||||
|
||||
/// Run cmd and return stdout.
|
||||
String runSync(List<String> cmd, {
|
||||
String workingDirectory,
|
||||
|
|
|
@ -273,24 +273,24 @@ Future<String> _buildAotSnapshot(
|
|||
final List<String> commonBuildOptions = <String>['-arch', 'arm64', '-miphoneos-version-min=8.0'];
|
||||
|
||||
if (interpreter) {
|
||||
runCheckedSync(<String>['mv', vmSnapshotData, fs.path.join(outputDir.path, kVmSnapshotData)]);
|
||||
runCheckedSync(<String>['mv', isolateSnapshotData, fs.path.join(outputDir.path, kIsolateSnapshotData)]);
|
||||
await runCheckedAsync(<String>['mv', vmSnapshotData, fs.path.join(outputDir.path, kVmSnapshotData)]);
|
||||
await runCheckedAsync(<String>['mv', isolateSnapshotData, fs.path.join(outputDir.path, kIsolateSnapshotData)]);
|
||||
|
||||
runCheckedSync(<String>[
|
||||
await runCheckedAsync(<String>[
|
||||
'xxd', '--include', kVmSnapshotData, fs.path.basename(kVmSnapshotDataC)
|
||||
], workingDirectory: outputDir.path);
|
||||
runCheckedSync(<String>[
|
||||
await runCheckedAsync(<String>[
|
||||
'xxd', '--include', kIsolateSnapshotData, fs.path.basename(kIsolateSnapshotDataC)
|
||||
], workingDirectory: outputDir.path);
|
||||
|
||||
runCheckedSync(<String>['xcrun', 'cc']
|
||||
await runCheckedAsync(<String>['xcrun', 'cc']
|
||||
..addAll(commonBuildOptions)
|
||||
..addAll(<String>['-c', kVmSnapshotDataC, '-o', kVmSnapshotDataO]));
|
||||
runCheckedSync(<String>['xcrun', 'cc']
|
||||
await runCheckedAsync(<String>['xcrun', 'cc']
|
||||
..addAll(commonBuildOptions)
|
||||
..addAll(<String>['-c', kIsolateSnapshotDataC, '-o', kIsolateSnapshotDataO]));
|
||||
} else {
|
||||
runCheckedSync(<String>['xcrun', 'cc']
|
||||
await runCheckedAsync(<String>['xcrun', 'cc']
|
||||
..addAll(commonBuildOptions)
|
||||
..addAll(<String>['-c', assembly, '-o', assemblyO]));
|
||||
}
|
||||
|
@ -313,7 +313,7 @@ Future<String> _buildAotSnapshot(
|
|||
} else {
|
||||
linkCommand.add(assemblyO);
|
||||
}
|
||||
runCheckedSync(linkCommand);
|
||||
await runCheckedAsync(linkCommand);
|
||||
}
|
||||
|
||||
return outputPath;
|
||||
|
|
|
@ -44,7 +44,7 @@ class ConfigCommand extends FlutterCommand {
|
|||
|
||||
/// Return `null` to disable tracking of the `config` command.
|
||||
@override
|
||||
String get usagePath => null;
|
||||
Future<String> get usagePath => null;
|
||||
|
||||
@override
|
||||
Future<Null> runCommand() async {
|
||||
|
|
|
@ -346,7 +346,7 @@ class AppDomain extends Domain {
|
|||
String packagesFilePath,
|
||||
String projectAssets,
|
||||
}) async {
|
||||
if (device.isLocalEmulator && !isEmulatorBuildMode(options.buildMode))
|
||||
if (await device.isLocalEmulator && !isEmulatorBuildMode(options.buildMode))
|
||||
throw '${toTitleCase(getModeName(options.buildMode))} mode is not supported for emulators.';
|
||||
|
||||
// We change the current working directory for the duration of the `start` command.
|
||||
|
@ -381,7 +381,7 @@ class AppDomain extends Domain {
|
|||
_sendAppEvent(app, 'start', <String, dynamic>{
|
||||
'deviceId': device.id,
|
||||
'directory': projectDirectory,
|
||||
'supportsRestart': isRestartSupported(enableHotReload, device)
|
||||
'supportsRestart': isRestartSupported(enableHotReload, device),
|
||||
});
|
||||
|
||||
Completer<DebugConnectionInfo> connectionInfoCompleter;
|
||||
|
@ -505,6 +505,8 @@ class AppDomain extends Domain {
|
|||
}
|
||||
}
|
||||
|
||||
typedef void _DeviceEventHandler(Device device);
|
||||
|
||||
/// This domain lets callers list and monitor connected devices.
|
||||
///
|
||||
/// It exports a `getDevices()` call, as well as firing `device.added` and
|
||||
|
@ -530,15 +532,20 @@ class DeviceDomain extends Domain {
|
|||
_discoverers.add(deviceDiscovery);
|
||||
|
||||
for (PollingDeviceDiscovery discoverer in _discoverers) {
|
||||
discoverer.onAdded.listen((Device device) {
|
||||
sendEvent('device.added', _deviceToMap(device));
|
||||
});
|
||||
discoverer.onRemoved.listen((Device device) {
|
||||
sendEvent('device.removed', _deviceToMap(device));
|
||||
});
|
||||
discoverer.onAdded.listen(_onDeviceEvent('device.added'));
|
||||
discoverer.onRemoved.listen(_onDeviceEvent('device.removed'));
|
||||
}
|
||||
}
|
||||
|
||||
Future<Null> _deviceEvents = new Future<Null>.value();
|
||||
_DeviceEventHandler _onDeviceEvent(String eventName) {
|
||||
return (Device device) {
|
||||
_deviceEvents = _deviceEvents.then((_) async {
|
||||
sendEvent(eventName, await _deviceToMap(device));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
final List<PollingDeviceDiscovery> _discoverers = <PollingDeviceDiscovery>[];
|
||||
|
||||
Future<List<Device>> getDevices([Map<String, dynamic> args]) {
|
||||
|
@ -638,18 +645,16 @@ void stdoutCommandResponse(Map<String, dynamic> command) {
|
|||
}
|
||||
|
||||
dynamic _jsonEncodeObject(dynamic object) {
|
||||
if (object is Device)
|
||||
return _deviceToMap(object);
|
||||
if (object is OperationResult)
|
||||
return _operationResultToMap(object);
|
||||
return object;
|
||||
}
|
||||
|
||||
Map<String, dynamic> _deviceToMap(Device device) {
|
||||
Future<Map<String, dynamic>> _deviceToMap(Device device) async {
|
||||
return <String, dynamic>{
|
||||
'id': device.id,
|
||||
'name': device.name,
|
||||
'platform': getNameForTargetPlatform(device.targetPlatform),
|
||||
'platform': getNameForTargetPlatform(await device.targetPlatform),
|
||||
'emulator': device.isLocalEmulator
|
||||
};
|
||||
}
|
||||
|
@ -664,8 +669,6 @@ Map<String, dynamic> _operationResultToMap(OperationResult result) {
|
|||
dynamic _toJsonable(dynamic obj) {
|
||||
if (obj is String || obj is int || obj is bool || obj is Map<dynamic, dynamic> || obj is List<dynamic> || obj == null)
|
||||
return obj;
|
||||
if (obj is Device)
|
||||
return obj;
|
||||
if (obj is OperationResult)
|
||||
return obj;
|
||||
return '$obj';
|
||||
|
|
|
@ -27,7 +27,7 @@ class DevicesCommand extends FlutterCommand {
|
|||
exitCode: 1);
|
||||
}
|
||||
|
||||
final List<Device> devices = await deviceManager.getAllConnectedDevices();
|
||||
final List<Device> devices = await deviceManager.getAllConnectedDevices().toList();
|
||||
|
||||
if (devices.isEmpty) {
|
||||
printStatus(
|
||||
|
@ -36,7 +36,7 @@ class DevicesCommand extends FlutterCommand {
|
|||
'potential issues, or visit https://flutter.io/setup/ for troubleshooting tips.');
|
||||
} else {
|
||||
printStatus('${devices.length} connected ${pluralize('device', devices.length)}:\n');
|
||||
Device.printDevices(devices);
|
||||
await Device.printDevices(devices);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -183,7 +183,7 @@ void restoreTargetDeviceFinder() {
|
|||
}
|
||||
|
||||
Future<Device> findTargetDevice() async {
|
||||
final List<Device> devices = await deviceManager.getDevices();
|
||||
final List<Device> devices = await deviceManager.getDevices().toList();
|
||||
|
||||
if (deviceManager.hasSpecifiedDeviceId) {
|
||||
if (devices.isEmpty) {
|
||||
|
@ -192,7 +192,7 @@ Future<Device> findTargetDevice() async {
|
|||
}
|
||||
if (devices.length > 1) {
|
||||
printStatus("Found ${devices.length} devices with name or id matching '${deviceManager.specifiedDeviceId}':");
|
||||
Device.printDevices(devices);
|
||||
await Device.printDevices(devices);
|
||||
return null;
|
||||
}
|
||||
return devices.first;
|
||||
|
@ -203,16 +203,24 @@ Future<Device> findTargetDevice() async {
|
|||
// On Mac we look for the iOS Simulator. If available, we use that. Then
|
||||
// we look for an Android device. If there's one, we use that. Otherwise,
|
||||
// we launch a new iOS Simulator.
|
||||
final Device reusableDevice = devices.firstWhere(
|
||||
(Device d) => d.isLocalEmulator,
|
||||
orElse: () {
|
||||
return devices.firstWhere((Device d) => d is AndroidDevice,
|
||||
orElse: () => null);
|
||||
Device reusableDevice;
|
||||
for (Device device in devices) {
|
||||
if (await device.isLocalEmulator) {
|
||||
reusableDevice = device;
|
||||
break;
|
||||
}
|
||||
);
|
||||
}
|
||||
if (reusableDevice == null) {
|
||||
for (Device device in devices) {
|
||||
if (device is AndroidDevice) {
|
||||
reusableDevice = device;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reusableDevice != null) {
|
||||
printStatus('Found connected ${reusableDevice.isLocalEmulator ? "emulator" : "device"} "${reusableDevice.name}"; will reuse it.');
|
||||
printStatus('Found connected ${await reusableDevice.isLocalEmulator ? "emulator" : "device"} "${reusableDevice.name}"; will reuse it.');
|
||||
return reusableDevice;
|
||||
}
|
||||
|
||||
|
@ -262,8 +270,8 @@ Future<LaunchResult> _startApp(DriveCommand command) async {
|
|||
|
||||
printTrace('Installing application package.');
|
||||
final ApplicationPackage package = command.applicationPackages
|
||||
.getPackageForPlatform(command.device.targetPlatform);
|
||||
if (command.device.isAppInstalled(package))
|
||||
.getPackageForPlatform(await command.device.targetPlatform);
|
||||
if (await command.device.isAppInstalled(package))
|
||||
command.device.uninstallApp(package);
|
||||
command.device.installApp(package);
|
||||
|
||||
|
@ -335,7 +343,7 @@ void restoreAppStopper() {
|
|||
|
||||
Future<bool> _stopApp(DriveCommand command) async {
|
||||
printTrace('Stopping application.');
|
||||
final ApplicationPackage package = command.applicationPackages.getPackageForPlatform(command.device.targetPlatform);
|
||||
final ApplicationPackage package = command.applicationPackages.getPackageForPlatform(await command.device.targetPlatform);
|
||||
final bool stopped = await command.device.stopApp(package);
|
||||
await command._deviceLogSubscription?.cancel();
|
||||
return stopped;
|
||||
|
|
|
@ -31,7 +31,7 @@ class InstallCommand extends FlutterCommand {
|
|||
|
||||
@override
|
||||
Future<Null> runCommand() async {
|
||||
final ApplicationPackage package = applicationPackages.getPackageForPlatform(device.targetPlatform);
|
||||
final ApplicationPackage package = applicationPackages.getPackageForPlatform(await device.targetPlatform);
|
||||
|
||||
Cache.releaseLockEarly();
|
||||
|
||||
|
@ -46,9 +46,9 @@ Future<bool> installApp(Device device, ApplicationPackage package, { bool uninst
|
|||
if (package == null)
|
||||
return false;
|
||||
|
||||
if (uninstall && device.isAppInstalled(package)) {
|
||||
if (uninstall && await device.isAppInstalled(package)) {
|
||||
printStatus('Uninstalling old version...');
|
||||
if (!device.uninstallApp(package))
|
||||
if (!await device.uninstallApp(package))
|
||||
printError('Warning: uninstalling old version failed');
|
||||
}
|
||||
|
||||
|
|
|
@ -153,14 +153,14 @@ class RunCommand extends RunCommandBase {
|
|||
Device device;
|
||||
|
||||
@override
|
||||
String get usagePath {
|
||||
Future<String> get usagePath async {
|
||||
final String command = shouldUseHotMode() ? 'hotrun' : name;
|
||||
|
||||
if (device == null)
|
||||
return command;
|
||||
|
||||
// Return 'run/ios'.
|
||||
return '$command/${getNameForTargetPlatform(device.targetPlatform)}';
|
||||
return '$command/${getNameForTargetPlatform(await device.targetPlatform)}';
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -249,7 +249,7 @@ class RunCommand extends RunCommandBase {
|
|||
return null;
|
||||
}
|
||||
|
||||
if (device.isLocalEmulator && !isEmulatorBuildMode(getBuildMode()))
|
||||
if (await device.isLocalEmulator && !isEmulatorBuildMode(getBuildMode()))
|
||||
throwToolExit('${toTitleCase(getModeName(getBuildMode()))} mode is not supported for emulators.');
|
||||
|
||||
if (hotMode) {
|
||||
|
|
|
@ -31,9 +31,10 @@ class StopCommand extends FlutterCommand {
|
|||
|
||||
@override
|
||||
Future<Null> runCommand() async {
|
||||
final ApplicationPackage app = applicationPackages.getPackageForPlatform(device.targetPlatform);
|
||||
final TargetPlatform targetPlatform = await device.targetPlatform;
|
||||
final ApplicationPackage app = applicationPackages.getPackageForPlatform(targetPlatform);
|
||||
if (app == null) {
|
||||
final String platformName = getNameForTargetPlatform(device.targetPlatform);
|
||||
final String platformName = getNameForTargetPlatform(targetPlatform);
|
||||
throwToolExit('No Flutter application for $platformName found in the current directory.');
|
||||
}
|
||||
printStatus('Stopping apps on ${device.name}.');
|
||||
|
|
|
@ -28,7 +28,7 @@ class UpgradeCommand extends FlutterCommand {
|
|||
@override
|
||||
Future<Null> runCommand() async {
|
||||
try {
|
||||
runCheckedSync(<String>[
|
||||
await runCheckedAsync(<String>[
|
||||
'git', 'rev-parse', '@{u}'
|
||||
], workingDirectory: Cache.flutterRoot);
|
||||
} catch (e) {
|
||||
|
|
|
@ -37,42 +37,40 @@ class DeviceManager {
|
|||
|
||||
bool get hasSpecifiedDeviceId => specifiedDeviceId != null;
|
||||
|
||||
/// Return the devices with a name or id matching [deviceId].
|
||||
/// This does a case insentitive compare with [deviceId].
|
||||
Future<List<Device>> getDevicesById(String deviceId) async {
|
||||
Stream<Device> getDevicesById(String deviceId) async* {
|
||||
final Stream<Device> devices = getAllConnectedDevices();
|
||||
deviceId = deviceId.toLowerCase();
|
||||
final List<Device> devices = await getAllConnectedDevices();
|
||||
final Device device = devices.firstWhere(
|
||||
(Device device) =>
|
||||
device.id.toLowerCase() == deviceId ||
|
||||
device.name.toLowerCase() == deviceId,
|
||||
orElse: () => null);
|
||||
bool exactlyMatchesDeviceId(Device device) =>
|
||||
device.id.toLowerCase() == deviceId ||
|
||||
device.name.toLowerCase() == deviceId;
|
||||
bool startsWithDeviceId(Device device) =>
|
||||
device.id.toLowerCase().startsWith(deviceId) ||
|
||||
device.name.toLowerCase().startsWith(deviceId);
|
||||
|
||||
if (device != null)
|
||||
return <Device>[device];
|
||||
final Device exactMatch = await devices.firstWhere(
|
||||
exactlyMatchesDeviceId, defaultValue: () => null);
|
||||
if (exactMatch != null) {
|
||||
yield exactMatch;
|
||||
return;
|
||||
}
|
||||
|
||||
// Match on a id or name starting with [deviceId].
|
||||
return devices.where((Device device) {
|
||||
return (device.id.toLowerCase().startsWith(deviceId) ||
|
||||
device.name.toLowerCase().startsWith(deviceId));
|
||||
}).toList();
|
||||
await for (Device device in devices.where(startsWithDeviceId))
|
||||
yield device;
|
||||
}
|
||||
|
||||
/// Return the list of connected devices, filtered by any user-specified device id.
|
||||
Future<List<Device>> getDevices() async {
|
||||
if (specifiedDeviceId == null) {
|
||||
return getAllConnectedDevices();
|
||||
} else {
|
||||
return getDevicesById(specifiedDeviceId);
|
||||
}
|
||||
Stream<Device> getDevices() {
|
||||
return hasSpecifiedDeviceId
|
||||
? getDevicesById(specifiedDeviceId)
|
||||
: getAllConnectedDevices();
|
||||
}
|
||||
|
||||
/// Return the list of all connected devices.
|
||||
Future<List<Device>> getAllConnectedDevices() async {
|
||||
return _deviceDiscoverers
|
||||
Stream<Device> getAllConnectedDevices() {
|
||||
return new Stream<Device>.fromIterable(_deviceDiscoverers
|
||||
.where((DeviceDiscovery discoverer) => discoverer.supportsPlatform)
|
||||
.expand((DeviceDiscovery discoverer) => discoverer.devices)
|
||||
.toList();
|
||||
.expand((DeviceDiscovery discoverer) => discoverer.devices));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,19 +139,19 @@ abstract class Device {
|
|||
bool get supportsStartPaused => true;
|
||||
|
||||
/// Whether it is an emulated device running on localhost.
|
||||
bool get isLocalEmulator;
|
||||
Future<bool> get isLocalEmulator;
|
||||
|
||||
/// Check if a version of the given app is already installed
|
||||
bool isAppInstalled(ApplicationPackage app);
|
||||
Future<bool> isAppInstalled(ApplicationPackage app);
|
||||
|
||||
/// Check if the latest build of the [app] is already installed.
|
||||
bool isLatestBuildInstalled(ApplicationPackage app);
|
||||
Future<bool> isLatestBuildInstalled(ApplicationPackage app);
|
||||
|
||||
/// Install an app package on the current device
|
||||
Future<bool> installApp(ApplicationPackage app);
|
||||
|
||||
/// Uninstall an app package from the current device
|
||||
bool uninstallApp(ApplicationPackage app);
|
||||
Future<bool> uninstallApp(ApplicationPackage app);
|
||||
|
||||
/// Check if the device is supported by Flutter
|
||||
bool isSupported();
|
||||
|
@ -162,9 +160,10 @@ abstract class Device {
|
|||
// supported by Flutter, and, if not, why.
|
||||
String supportMessage() => isSupported() ? "Supported" : "Unsupported";
|
||||
|
||||
TargetPlatform get targetPlatform;
|
||||
/// The device's platform.
|
||||
Future<TargetPlatform> get targetPlatform;
|
||||
|
||||
String get sdkNameAndVersion;
|
||||
Future<String> get sdkNameAndVersion;
|
||||
|
||||
/// Get a log reader for this device.
|
||||
/// If [app] is specified, this will return a log reader specific to that
|
||||
|
@ -238,23 +237,24 @@ abstract class Device {
|
|||
@override
|
||||
String toString() => name;
|
||||
|
||||
static Iterable<String> descriptions(List<Device> devices) {
|
||||
static Stream<String> descriptions(List<Device> devices) async* {
|
||||
if (devices.isEmpty)
|
||||
return <String>[];
|
||||
return;
|
||||
|
||||
// Extract device information
|
||||
final List<List<String>> table = <List<String>>[];
|
||||
for (Device device in devices) {
|
||||
String supportIndicator = device.isSupported() ? '' : ' (unsupported)';
|
||||
if (device.isLocalEmulator) {
|
||||
final String type = device.targetPlatform == TargetPlatform.ios ? 'simulator' : 'emulator';
|
||||
final TargetPlatform targetPlatform = await device.targetPlatform;
|
||||
if (await device.isLocalEmulator) {
|
||||
final String type = targetPlatform == TargetPlatform.ios ? 'simulator' : 'emulator';
|
||||
supportIndicator += ' ($type)';
|
||||
}
|
||||
table.add(<String>[
|
||||
device.name,
|
||||
device.id,
|
||||
'${getNameForTargetPlatform(device.targetPlatform)}',
|
||||
'${device.sdkNameAndVersion}$supportIndicator',
|
||||
'${getNameForTargetPlatform(targetPlatform)}',
|
||||
'${await device.sdkNameAndVersion}$supportIndicator',
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -266,13 +266,13 @@ abstract class Device {
|
|||
}
|
||||
|
||||
// Join columns into lines of text
|
||||
return table.map((List<String> row) =>
|
||||
indices.map((int i) => row[i].padRight(widths[i])).join(' • ') +
|
||||
' • ${row.last}');
|
||||
for (List<String> row in table) {
|
||||
yield indices.map((int i) => row[i].padRight(widths[i])).join(' • ') + ' • ${row.last}';
|
||||
}
|
||||
}
|
||||
|
||||
static void printDevices(List<Device> devices) {
|
||||
descriptions(devices).forEach(printStatus);
|
||||
static Future<Null> printDevices(List<Device> devices) async {
|
||||
await descriptions(devices).forEach(printStatus);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -486,12 +486,12 @@ class DeviceValidator extends DoctorValidator {
|
|||
|
||||
@override
|
||||
Future<ValidationResult> validate() async {
|
||||
final List<Device> devices = await deviceManager.getAllConnectedDevices();
|
||||
final List<Device> devices = await deviceManager.getAllConnectedDevices().toList();
|
||||
List<ValidationMessage> messages;
|
||||
if (devices.isEmpty) {
|
||||
messages = <ValidationMessage>[new ValidationMessage('None')];
|
||||
} else {
|
||||
messages = Device.descriptions(devices)
|
||||
messages = await Device.descriptions(devices)
|
||||
.map((String msg) => new ValidationMessage(msg)).toList();
|
||||
}
|
||||
return new ValidationResult(ValidationType.installed, messages);
|
||||
|
|
|
@ -37,22 +37,22 @@ class FuchsiaDevice extends Device {
|
|||
final String name;
|
||||
|
||||
@override
|
||||
bool get isLocalEmulator => false;
|
||||
Future<bool> get isLocalEmulator async => false;
|
||||
|
||||
@override
|
||||
bool get supportsStartPaused => false;
|
||||
|
||||
@override
|
||||
bool isAppInstalled(ApplicationPackage app) => false;
|
||||
Future<bool> isAppInstalled(ApplicationPackage app) async => false;
|
||||
|
||||
@override
|
||||
bool isLatestBuildInstalled(ApplicationPackage app) => false;
|
||||
Future<bool> isLatestBuildInstalled(ApplicationPackage app) async => false;
|
||||
|
||||
@override
|
||||
Future<bool> installApp(ApplicationPackage app) => new Future<bool>.value(false);
|
||||
|
||||
@override
|
||||
bool uninstallApp(ApplicationPackage app) => false;
|
||||
Future<bool> uninstallApp(ApplicationPackage app) async => false;
|
||||
|
||||
@override
|
||||
bool isSupported() => true;
|
||||
|
@ -77,10 +77,10 @@ class FuchsiaDevice extends Device {
|
|||
}
|
||||
|
||||
@override
|
||||
TargetPlatform get targetPlatform => TargetPlatform.fuchsia;
|
||||
Future<TargetPlatform> get targetPlatform async => TargetPlatform.fuchsia;
|
||||
|
||||
@override
|
||||
String get sdkNameAndVersion => 'Fuchsia';
|
||||
Future<String> get sdkNameAndVersion async => 'Fuchsia';
|
||||
|
||||
_FuchsiaLogReader _logReader;
|
||||
@override
|
||||
|
|
|
@ -87,7 +87,7 @@ class IOSDevice extends Device {
|
|||
_IOSDevicePortForwarder _portForwarder;
|
||||
|
||||
@override
|
||||
bool get isLocalEmulator => false;
|
||||
Future<bool> get isLocalEmulator async => false;
|
||||
|
||||
@override
|
||||
bool get supportsStartPaused => false;
|
||||
|
@ -139,10 +139,10 @@ class IOSDevice extends Device {
|
|||
}
|
||||
|
||||
@override
|
||||
bool isAppInstalled(ApplicationPackage app) {
|
||||
Future<bool> isAppInstalled(ApplicationPackage app) async {
|
||||
try {
|
||||
final String apps = runCheckedSync(<String>[installerPath, '--list-apps']);
|
||||
if (new RegExp(app.id, multiLine: true).hasMatch(apps)) {
|
||||
final RunResult apps = await runCheckedAsync(<String>[installerPath, '--list-apps']);
|
||||
if (new RegExp(app.id, multiLine: true).hasMatch(apps.stdout)) {
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
|
@ -152,7 +152,7 @@ class IOSDevice extends Device {
|
|||
}
|
||||
|
||||
@override
|
||||
bool isLatestBuildInstalled(ApplicationPackage app) => false;
|
||||
Future<bool> isLatestBuildInstalled(ApplicationPackage app) async => false;
|
||||
|
||||
@override
|
||||
Future<bool> installApp(ApplicationPackage app) async {
|
||||
|
@ -164,7 +164,7 @@ class IOSDevice extends Device {
|
|||
}
|
||||
|
||||
try {
|
||||
runCheckedSync(<String>[installerPath, '-i', iosApp.deviceBundlePath]);
|
||||
await runCheckedAsync(<String>[installerPath, '-i', iosApp.deviceBundlePath]);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
|
@ -172,9 +172,9 @@ class IOSDevice extends Device {
|
|||
}
|
||||
|
||||
@override
|
||||
bool uninstallApp(ApplicationPackage app) {
|
||||
Future<bool> uninstallApp(ApplicationPackage app) async {
|
||||
try {
|
||||
runCheckedSync(<String>[installerPath, '-U', app.id]);
|
||||
await runCheckedAsync(<String>[installerPath, '-U', app.id]);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
|
@ -343,10 +343,10 @@ class IOSDevice extends Device {
|
|||
}
|
||||
|
||||
@override
|
||||
TargetPlatform get targetPlatform => TargetPlatform.ios;
|
||||
Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios;
|
||||
|
||||
@override
|
||||
String get sdkNameAndVersion => 'iOS $_sdkVersion ($_buildVersion)';
|
||||
Future<String> get sdkNameAndVersion async => 'iOS $_sdkVersion ($_buildVersion)';
|
||||
|
||||
String get _sdkVersion => _getDeviceInfo(id, 'ProductVersion');
|
||||
|
||||
|
@ -370,8 +370,7 @@ class IOSDevice extends Device {
|
|||
|
||||
@override
|
||||
Future<Null> takeScreenshot(File outputFile) {
|
||||
runCheckedSync(<String>[screenshotPath, outputFile.path]);
|
||||
return new Future<Null>.value();
|
||||
return runCheckedAsync(<String>[screenshotPath, outputFile.path]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -165,7 +165,7 @@ class IOSWorkflow extends DoctorValidator implements Workflow {
|
|||
// Check for compatibility between libimobiledevice and Xcode.
|
||||
// TODO(cbracken) remove this check once libimobiledevice > 1.2.0 is released.
|
||||
final ProcessResult result = (await runAsync(<String>['idevice_id', '-l'])).processResult;
|
||||
if (result.exitCode == 0 && result.stdout.isNotEmpty && !exitsHappy(<String>['ideviceName'])) {
|
||||
if (result.exitCode == 0 && result.stdout.isNotEmpty && !await exitsHappyAsync(<String>['ideviceName'])) {
|
||||
brewStatus = ValidationType.partial;
|
||||
messages.add(new ValidationMessage.error(
|
||||
'libimobiledevice is incompatible with the installed Xcode version. To update, run:\n'
|
||||
|
|
|
@ -399,7 +399,7 @@ Future<Null> _copyServiceFrameworks(List<Map<String, String>> services, Director
|
|||
continue;
|
||||
}
|
||||
// Shell out so permissions on the dylib are preserved.
|
||||
runCheckedSync(<String>['/bin/cp', dylib.path, frameworksDirectory.path]);
|
||||
await runCheckedAsync(<String>['/bin/cp', dylib.path, frameworksDirectory.path]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -224,8 +224,8 @@ class SimControl {
|
|||
|
||||
bool _isAnyConnected() => getConnectedDevices().isNotEmpty;
|
||||
|
||||
bool isInstalled(String appId) {
|
||||
return exitsHappy(<String>[
|
||||
Future<bool> isInstalled(String appId) {
|
||||
return exitsHappyAsync(<String>[
|
||||
_xcrunPath,
|
||||
'simctl',
|
||||
'get_app_container',
|
||||
|
@ -234,23 +234,23 @@ class SimControl {
|
|||
]);
|
||||
}
|
||||
|
||||
void install(String deviceId, String appPath) {
|
||||
runCheckedSync(<String>[_xcrunPath, 'simctl', 'install', deviceId, appPath]);
|
||||
Future<Null> install(String deviceId, String appPath) {
|
||||
return runCheckedAsync(<String>[_xcrunPath, 'simctl', 'install', deviceId, appPath]);
|
||||
}
|
||||
|
||||
void uninstall(String deviceId, String appId) {
|
||||
runCheckedSync(<String>[_xcrunPath, 'simctl', 'uninstall', deviceId, appId]);
|
||||
Future<Null> uninstall(String deviceId, String appId) {
|
||||
return runCheckedAsync(<String>[_xcrunPath, 'simctl', 'uninstall', deviceId, appId]);
|
||||
}
|
||||
|
||||
void launch(String deviceId, String appIdentifier, [List<String> launchArgs]) {
|
||||
Future<Null> launch(String deviceId, String appIdentifier, [List<String> launchArgs]) {
|
||||
final List<String> args = <String>[_xcrunPath, 'simctl', 'launch', deviceId, appIdentifier];
|
||||
if (launchArgs != null)
|
||||
args.addAll(launchArgs);
|
||||
runCheckedSync(args);
|
||||
return runCheckedAsync(args);
|
||||
}
|
||||
|
||||
void takeScreenshot(String outputPath) {
|
||||
runCheckedSync(<String>[_xcrunPath, 'simctl', 'io', 'booted', 'screenshot', outputPath]);
|
||||
Future<Null> takeScreenshot(String outputPath) {
|
||||
return runCheckedAsync(<String>[_xcrunPath, 'simctl', 'io', 'booted', 'screenshot', outputPath]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -313,7 +313,7 @@ class IOSSimulator extends Device {
|
|||
final String category;
|
||||
|
||||
@override
|
||||
bool get isLocalEmulator => true;
|
||||
Future<bool> get isLocalEmulator async => true;
|
||||
|
||||
@override
|
||||
bool get supportsHotMode => true;
|
||||
|
@ -335,12 +335,12 @@ class IOSSimulator extends Device {
|
|||
}
|
||||
|
||||
@override
|
||||
bool isAppInstalled(ApplicationPackage app) {
|
||||
Future<bool> isAppInstalled(ApplicationPackage app) {
|
||||
return SimControl.instance.isInstalled(app.id);
|
||||
}
|
||||
|
||||
@override
|
||||
bool isLatestBuildInstalled(ApplicationPackage app) => false;
|
||||
Future<bool> isLatestBuildInstalled(ApplicationPackage app) async => false;
|
||||
|
||||
@override
|
||||
Future<bool> installApp(ApplicationPackage app) async {
|
||||
|
@ -354,9 +354,9 @@ class IOSSimulator extends Device {
|
|||
}
|
||||
|
||||
@override
|
||||
bool uninstallApp(ApplicationPackage app) {
|
||||
Future<bool> uninstallApp(ApplicationPackage app) async {
|
||||
try {
|
||||
SimControl.instance.uninstall(id, app.id);
|
||||
await SimControl.instance.uninstall(id, app.id);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
|
@ -495,21 +495,18 @@ class IOSSimulator extends Device {
|
|||
}
|
||||
}
|
||||
|
||||
bool _applicationIsInstalledAndRunning(ApplicationPackage app) {
|
||||
final bool isInstalled = isAppInstalled(app);
|
||||
|
||||
final bool isRunning = exitsHappy(<String>[
|
||||
'/usr/bin/killall',
|
||||
'Runner',
|
||||
Future<bool> _applicationIsInstalledAndRunning(ApplicationPackage app) async {
|
||||
final List<bool> criteria = await Future.wait(<Future<bool>>[
|
||||
isAppInstalled(app),
|
||||
exitsHappyAsync(<String>['/usr/bin/killall', 'Runner']),
|
||||
]);
|
||||
|
||||
return isInstalled && isRunning;
|
||||
return criteria.reduce((bool a, bool b) => a && b);
|
||||
}
|
||||
|
||||
Future<Null> _setupUpdatedApplicationBundle(ApplicationPackage app) async {
|
||||
await _sideloadUpdatedAssetsForInstalledApplicationBundle(app);
|
||||
|
||||
if (!_applicationIsInstalledAndRunning(app))
|
||||
if (!await _applicationIsInstalledAndRunning(app))
|
||||
return _buildAndInstallApplicationBundle(app);
|
||||
}
|
||||
|
||||
|
@ -544,7 +541,7 @@ class IOSSimulator extends Device {
|
|||
ApplicationPackage app, String localFile, String targetFile) async {
|
||||
if (platform.isMacOS) {
|
||||
final String simulatorHomeDirectory = _getSimulatorAppHomeDirectory(app);
|
||||
runCheckedSync(<String>['cp', localFile, fs.path.join(simulatorHomeDirectory, targetFile)]);
|
||||
await runCheckedAsync(<String>['cp', localFile, fs.path.join(simulatorHomeDirectory, targetFile)]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -555,10 +552,10 @@ class IOSSimulator extends Device {
|
|||
}
|
||||
|
||||
@override
|
||||
TargetPlatform get targetPlatform => TargetPlatform.ios;
|
||||
Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios;
|
||||
|
||||
@override
|
||||
String get sdkNameAndVersion => category;
|
||||
Future<String> get sdkNameAndVersion async => category;
|
||||
|
||||
@override
|
||||
DeviceLogReader getLogReader({ApplicationPackage app}) {
|
||||
|
@ -595,8 +592,7 @@ class IOSSimulator extends Device {
|
|||
|
||||
@override
|
||||
Future<Null> takeScreenshot(File outputFile) {
|
||||
SimControl.instance.takeScreenshot(outputFile.path);
|
||||
return new Future<Null>.value();
|
||||
return SimControl.instance.takeScreenshot(outputFile.path);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -61,11 +61,12 @@ class ColdRunner extends ResidentRunner {
|
|||
printStatus('Launching ${getDisplayPath(mainPath)} on ${device.name} in $modeName mode...');
|
||||
}
|
||||
|
||||
package = getApplicationPackageForPlatform(device.targetPlatform, applicationBinary: applicationBinary);
|
||||
final TargetPlatform targetPlatform = await device.targetPlatform;
|
||||
package = getApplicationPackageForPlatform(targetPlatform, applicationBinary: applicationBinary);
|
||||
|
||||
if (package == null) {
|
||||
String message = 'No application found for ${device.targetPlatform}.';
|
||||
final String hint = getMissingPackageHintForPlatform(device.targetPlatform);
|
||||
String message = 'No application found for $targetPlatform.';
|
||||
final String hint = getMissingPackageHintForPlatform(targetPlatform);
|
||||
if (hint != null)
|
||||
message += '\n$hint';
|
||||
printError(message);
|
||||
|
|
|
@ -177,11 +177,12 @@ class HotRunner extends ResidentRunner {
|
|||
final String modeName = getModeName(debuggingOptions.buildMode);
|
||||
printStatus('Launching ${getDisplayPath(mainPath)} on ${device.name} in $modeName mode...');
|
||||
|
||||
package = getApplicationPackageForPlatform(device.targetPlatform, applicationBinary: applicationBinary);
|
||||
final TargetPlatform targetPlatform = await device.targetPlatform;
|
||||
package = getApplicationPackageForPlatform(targetPlatform, applicationBinary: applicationBinary);
|
||||
|
||||
if (package == null) {
|
||||
String message = 'No application found for ${device.targetPlatform}.';
|
||||
final String hint = getMissingPackageHintForPlatform(device.targetPlatform);
|
||||
String message = 'No application found for $targetPlatform.';
|
||||
final String hint = getMissingPackageHintForPlatform(targetPlatform);
|
||||
if (hint != null)
|
||||
message += '\n$hint';
|
||||
printError(message);
|
||||
|
|
|
@ -102,7 +102,7 @@ abstract class FlutterCommand extends Command<Null> {
|
|||
|
||||
/// The path to send to Google Analytics. Return `null` here to disable
|
||||
/// tracking of the command.
|
||||
String get usagePath => name;
|
||||
Future<String> get usagePath async => name;
|
||||
|
||||
/// Runs this command.
|
||||
///
|
||||
|
@ -144,9 +144,9 @@ abstract class FlutterCommand extends Command<Null> {
|
|||
|
||||
setupApplicationPackages();
|
||||
|
||||
final String commandPath = usagePath;
|
||||
final String commandPath = await usagePath;
|
||||
if (commandPath != null)
|
||||
flutterUsage.sendCommand(usagePath);
|
||||
flutterUsage.sendCommand(commandPath);
|
||||
|
||||
await runCommand();
|
||||
}
|
||||
|
@ -158,14 +158,14 @@ abstract class FlutterCommand extends Command<Null> {
|
|||
/// devices and criteria entered by the user on the command line.
|
||||
/// If a device cannot be found that meets specified criteria,
|
||||
/// then print an error message and return `null`.
|
||||
Future<Device> findTargetDevice({bool androidOnly: false}) async {
|
||||
Future<Device> findTargetDevice() async {
|
||||
if (!doctor.canLaunchAnything) {
|
||||
printError("Unable to locate a development device; please run 'flutter doctor' "
|
||||
"for information about installing additional components.");
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Device> devices = await deviceManager.getDevices();
|
||||
List<Device> devices = await deviceManager.getDevices().toList();
|
||||
|
||||
if (devices.isEmpty && deviceManager.hasSpecifiedDeviceId) {
|
||||
printStatus("No devices found with name or id "
|
||||
|
@ -178,9 +178,6 @@ abstract class FlutterCommand extends Command<Null> {
|
|||
|
||||
devices = devices.where((Device device) => device.isSupported()).toList();
|
||||
|
||||
if (androidOnly)
|
||||
devices = devices.where((Device device) => device.targetPlatform == TargetPlatform.android_arm).toList();
|
||||
|
||||
if (devices.isEmpty) {
|
||||
printStatus('No supported devices connected.');
|
||||
return null;
|
||||
|
@ -191,10 +188,10 @@ abstract class FlutterCommand extends Command<Null> {
|
|||
} else {
|
||||
printStatus("More than one device connected; please specify a device with "
|
||||
"the '-d <deviceId>' flag.");
|
||||
devices = await deviceManager.getAllConnectedDevices();
|
||||
devices = await deviceManager.getAllConnectedDevices().toList();
|
||||
}
|
||||
printStatus('');
|
||||
Device.printDevices(devices);
|
||||
await Device.printDevices(devices);
|
||||
return null;
|
||||
}
|
||||
return devices.single;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:usage/usage_io.dart';
|
||||
|
||||
import 'base/context.dart';
|
||||
|
@ -96,7 +97,8 @@ class Usage {
|
|||
_analytics.sendException('${exception.runtimeType}\n${sanitizeStacktrace(trace)}');
|
||||
}
|
||||
|
||||
/// Fires whenever analytics data is sent over the network; public for testing.
|
||||
/// Fires whenever analytics data is sent over the network.
|
||||
@visibleForTesting
|
||||
Stream<Map<String, dynamic>> get onSend => _analytics.onSend;
|
||||
|
||||
/// Returns when the last analytics event has been sent, or after a fixed
|
||||
|
|
|
@ -86,7 +86,7 @@ class _ZipToolBuilder extends ZipBuilder {
|
|||
|
||||
final Iterable<String> compressedNames = _getCompressedNames();
|
||||
if (compressedNames.isNotEmpty) {
|
||||
runCheckedSync(
|
||||
await runCheckedAsync(
|
||||
<String>['zip', '-q', outFile.absolute.path]..addAll(compressedNames),
|
||||
workingDirectory: zipBuildDir.path
|
||||
);
|
||||
|
@ -94,7 +94,7 @@ class _ZipToolBuilder extends ZipBuilder {
|
|||
|
||||
final Iterable<String> storedNames = _getStoredNames();
|
||||
if (storedNames.isNotEmpty) {
|
||||
runCheckedSync(
|
||||
await runCheckedAsync(
|
||||
<String>['zip', '-q', '-0', outFile.absolute.path]..addAll(storedNames),
|
||||
workingDirectory: zipBuildDir.path
|
||||
);
|
||||
|
|
|
@ -56,7 +56,7 @@ void main() {
|
|||
Usage: () => new Usage(),
|
||||
});
|
||||
|
||||
// Ensure we con't send for the 'flutter config' command.
|
||||
// Ensure we don't send for the 'flutter config' command.
|
||||
testUsingContext('config doesn\'t send', () async {
|
||||
int count = 0;
|
||||
flutterUsage.onSend.listen((Map<String, dynamic> data) => count++);
|
||||
|
|
|
@ -14,7 +14,7 @@ void main() {
|
|||
testUsingContext('getDevices', () async {
|
||||
// Test that DeviceManager.getDevices() doesn't throw.
|
||||
final DeviceManager deviceManager = new DeviceManager();
|
||||
final List<Device> devices = await deviceManager.getDevices();
|
||||
final List<Device> devices = await deviceManager.getDevices().toList();
|
||||
expect(devices, isList);
|
||||
});
|
||||
|
||||
|
@ -26,7 +26,7 @@ void main() {
|
|||
final DeviceManager deviceManager = new TestDeviceManager(devices);
|
||||
|
||||
Future<Null> expectDevice(String id, List<Device> expected) async {
|
||||
expect(await deviceManager.getDevicesById(id), expected);
|
||||
expect(await deviceManager.getDevicesById(id).toList(), expected);
|
||||
}
|
||||
expectDevice('01abfc49119c410e', <Device>[device2]);
|
||||
expectDevice('Nexus 5X', <Device>[device2]);
|
||||
|
@ -44,8 +44,8 @@ class TestDeviceManager extends DeviceManager {
|
|||
TestDeviceManager(this.allDevices);
|
||||
|
||||
@override
|
||||
Future<List<Device>> getAllConnectedDevices() async {
|
||||
return allDevices;
|
||||
Stream<Device> getAllConnectedDevices() {
|
||||
return new Stream<Device>.fromIterable(allDevices);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -134,20 +134,19 @@ class MockDeviceManager implements DeviceManager {
|
|||
bool get hasSpecifiedDeviceId => specifiedDeviceId != null;
|
||||
|
||||
@override
|
||||
Future<List<Device>> getAllConnectedDevices() => new Future<List<Device>>.value(devices);
|
||||
Stream<Device> getAllConnectedDevices() => new Stream<Device>.fromIterable(devices);
|
||||
|
||||
@override
|
||||
Future<List<Device>> getDevicesById(String deviceId) async {
|
||||
return devices.where((Device device) => device.id == deviceId).toList();
|
||||
Stream<Device> getDevicesById(String deviceId) {
|
||||
return new Stream<Device>.fromIterable(
|
||||
devices.where((Device device) => device.id == deviceId));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Device>> getDevices() async {
|
||||
if (specifiedDeviceId == null) {
|
||||
return getAllConnectedDevices();
|
||||
} else {
|
||||
return getDevicesById(specifiedDeviceId);
|
||||
}
|
||||
Stream<Device> getDevices() {
|
||||
return hasSpecifiedDeviceId
|
||||
? getDevicesById(specifiedDeviceId)
|
||||
: getAllConnectedDevices();
|
||||
}
|
||||
|
||||
void addDevice(Device device) => devices.add(device);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// 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:io' show ProcessResult;
|
||||
|
||||
import 'package:file/file.dart';
|
||||
|
@ -38,23 +39,23 @@ void main() {
|
|||
// Let everything else return exit code 0 so process.dart doesn't crash.
|
||||
// The matcher order is important.
|
||||
when(
|
||||
mockProcessManager.runSync(any, environment: null, workingDirectory: null)
|
||||
mockProcessManager.run(any, environment: null, workingDirectory: null)
|
||||
).thenReturn(
|
||||
new ProcessResult(2, 0, '', null)
|
||||
new Future<ProcessResult>.value(new ProcessResult(2, 0, '', ''))
|
||||
);
|
||||
// Let `which idevicescreenshot` fail with exit code 1.
|
||||
when(
|
||||
mockProcessManager.runSync(
|
||||
<String>['which', 'idevicescreenshot'], environment: null, workingDirectory: null)
|
||||
).thenReturn(
|
||||
new ProcessResult(1, 1, '', null)
|
||||
new ProcessResult(1, 1, '', '')
|
||||
);
|
||||
|
||||
iosDeviceUnderTest = new IOSDevice('1234');
|
||||
iosDeviceUnderTest.takeScreenshot(mockOutputFile);
|
||||
await iosDeviceUnderTest.takeScreenshot(mockOutputFile);
|
||||
verify(mockProcessManager.runSync(
|
||||
<String>['which', 'idevicescreenshot'], environment: null, workingDirectory: null));
|
||||
verifyNever(mockProcessManager.runSync(
|
||||
verifyNever(mockProcessManager.run(
|
||||
<String>['idevicescreenshot', fs.path.join('some', 'test', 'path', 'image.png')],
|
||||
environment: null,
|
||||
workingDirectory: null
|
||||
|
@ -74,23 +75,23 @@ void main() {
|
|||
// Let everything else return exit code 0.
|
||||
// The matcher order is important.
|
||||
when(
|
||||
mockProcessManager.runSync(any, environment: null, workingDirectory: null)
|
||||
mockProcessManager.run(any, environment: null, workingDirectory: null)
|
||||
).thenReturn(
|
||||
new ProcessResult(4, 0, '', null)
|
||||
new Future<ProcessResult>.value(new ProcessResult(4, 0, '', ''))
|
||||
);
|
||||
// Let there be idevicescreenshot in the PATH.
|
||||
when(
|
||||
mockProcessManager.runSync(
|
||||
<String>['which', 'idevicescreenshot'], environment: null, workingDirectory: null)
|
||||
).thenReturn(
|
||||
new ProcessResult(3, 0, fs.path.join('some', 'path', 'to', 'iscreenshot'), null)
|
||||
new ProcessResult(3, 0, fs.path.join('some', 'path', 'to', 'iscreenshot'), '')
|
||||
);
|
||||
|
||||
iosDeviceUnderTest = new IOSDevice('1234');
|
||||
iosDeviceUnderTest.takeScreenshot(mockOutputFile);
|
||||
await iosDeviceUnderTest.takeScreenshot(mockOutputFile);
|
||||
verify(mockProcessManager.runSync(
|
||||
<String>['which', 'idevicescreenshot'], environment: null, workingDirectory: null));
|
||||
verify(mockProcessManager.runSync(
|
||||
verify(mockProcessManager.run(
|
||||
<String>[
|
||||
fs.path.join('some', 'path', 'to', 'iscreenshot'),
|
||||
fs.path.join('some', 'test', 'path', 'image.png')
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io' show ProcessResult;
|
||||
|
||||
import 'package:file/file.dart';
|
||||
|
@ -128,9 +129,9 @@ void main() {
|
|||
mockProcessManager = new MockProcessManager();
|
||||
// Let everything else return exit code 0 so process.dart doesn't crash.
|
||||
when(
|
||||
mockProcessManager.runSync(any, environment: null, workingDirectory: null)
|
||||
mockProcessManager.run(any, environment: null, workingDirectory: null)
|
||||
).thenReturn(
|
||||
new ProcessResult(2, 0, '', null)
|
||||
new Future<ProcessResult>.value(new ProcessResult(2, 0, '', ''))
|
||||
);
|
||||
// Doesn't matter what the device is.
|
||||
deviceUnderTest = new IOSSimulator('x', name: 'iPhone SE');
|
||||
|
@ -148,14 +149,14 @@ void main() {
|
|||
|
||||
testUsingContext(
|
||||
'Xcode 8.2+ supports screenshots',
|
||||
() {
|
||||
() async {
|
||||
when(mockXcode.xcodeMajorVersion).thenReturn(8);
|
||||
when(mockXcode.xcodeMinorVersion).thenReturn(2);
|
||||
expect(deviceUnderTest.supportsScreenshot, true);
|
||||
final MockFile mockFile = new MockFile();
|
||||
when(mockFile.path).thenReturn(fs.path.join('some', 'path', 'to', 'screenshot.png'));
|
||||
deviceUnderTest.takeScreenshot(mockFile);
|
||||
verify(mockProcessManager.runSync(
|
||||
await deviceUnderTest.takeScreenshot(mockFile);
|
||||
verify(mockProcessManager.run(
|
||||
<String>[
|
||||
'/usr/bin/xcrun',
|
||||
'simctl',
|
||||
|
|
|
@ -31,7 +31,7 @@ class MockApplicationPackageStore extends ApplicationPackageStore {
|
|||
|
||||
class MockAndroidDevice extends Mock implements AndroidDevice {
|
||||
@override
|
||||
TargetPlatform get targetPlatform => TargetPlatform.android_arm;
|
||||
Future<TargetPlatform> get targetPlatform async => TargetPlatform.android_arm;
|
||||
|
||||
@override
|
||||
bool isSupported() => true;
|
||||
|
@ -39,7 +39,7 @@ class MockAndroidDevice extends Mock implements AndroidDevice {
|
|||
|
||||
class MockIOSDevice extends Mock implements IOSDevice {
|
||||
@override
|
||||
TargetPlatform get targetPlatform => TargetPlatform.ios;
|
||||
Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios;
|
||||
|
||||
@override
|
||||
bool isSupported() => true;
|
||||
|
@ -47,7 +47,7 @@ class MockIOSDevice extends Mock implements IOSDevice {
|
|||
|
||||
class MockIOSSimulator extends Mock implements IOSSimulator {
|
||||
@override
|
||||
TargetPlatform get targetPlatform => TargetPlatform.ios;
|
||||
Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios;
|
||||
|
||||
@override
|
||||
bool isSupported() => true;
|
||||
|
|
Loading…
Reference in a new issue