Unverified Commit 5c52498d authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Add device category, ephemeral, platformType for daemon (#33990)

parent 73330e12
......@@ -45,7 +45,7 @@ Any params for that command should be passed in through a `params` field. Here's
Events that come from the server will have an `event` field containing the type of event, along with a `params` field.
```
[{"event":"device.added","params":{"id":"1DD6786B-37D4-4355-AA15-B818A87A18B4","name":"iPhone XS Max","platform":"ios","emulator":true}}]
[{"event":"device.added","params":{"id":"1DD6786B-37D4-4355-AA15-B818A87A18B4","name":"iPhone XS Max","platform":"ios","emulator":true,"ephemeral":false,"platformType":"ios","category":"mobile"}}]
```
## Domains and Commands
......@@ -157,7 +157,15 @@ This is sent when an app is stopped or detached from. The `params` field will be
#### device.getDevices
Return a list of all connected devices. The `params` field will be a List; each item is a map with the fields `id`, `name`, `platform`, and `emulator` (a boolean).
Return a list of all connected devices. The `params` field will be a List; each item is a map with the fields `id`, `name`, `platform`, `category`, `platformType`, `ephemeral`, and `emulator` (a boolean).
`category` is string description of the kind of workflow the device supports. The current categories are "mobile", "web" and "desktop", or null if none.
`platformType` is a string description of the platform sub-folder the device
supports. The current catgetories are "android", "ios", "linux", "macos",
"fuchsia", "windows", and "web". These are kept in sync with the response from `daemon.getSupportedPlatforms`.
`ephemeral` is a boolean which indicates where the device needs to be manually connected to a development machine. For example, a physical Android device is ephemeral, but the "web" device (that is always present) is not.
#### device.enable
......@@ -181,11 +189,11 @@ Removed a forwarded port. It takes `deviceId`, `devicePort`, and `hostPort` as r
#### device.added
This is sent when a device is connected (and polling has been enabled via `enable()`). The `params` field will be a map with the fields `id`, `name`, `platform`, and `emulator`.
This is sent when a device is connected (and polling has been enabled via `enable()`). The `params` field will be a map with the fields `id`, `name`, `platform`, `category`, `platformType`, `ephemeral`, and `emulator`. For more information on `platform`, `category`, `platformType`, and `ephemeral` see `device.getDevices`.
#### device.removed
This is sent when a device is disconnected (and polling has been enabled via `enable()`). The `params` field will be a map with the fields `id`, `name`, `platform`, and `emulator`.
This is sent when a device is disconnected (and polling has been enabled via `enable()`). The `params` field will be a map with the fields `id`, `name`, `platform`, `category`, `platformType`, `ephemeral`, and `emulator`. For more information on `platform`, `category`, `platformType`, and `ephemeral` see `device.getDevices`.
### emulator domain
......@@ -250,6 +258,7 @@ See the [source](https://github.com/flutter/flutter/blob/master/packages/flutter
## Changelog
- 0.5.1: Added `platformType`, `ephemeral`, and `category` field to device.
- 0.5.0: Added `daemon.getSupportedPlatforms` command
- 0.4.2: Added `app.detach` command
- 0.4.1: Added `flutter attach --machine`
......
......@@ -72,7 +72,12 @@ class AndroidDevice extends Device {
this.productID,
this.modelID,
this.deviceCodeName,
}) : super(id);
}) : super(
id,
category: Category.mobile,
platformType: PlatformType.android,
ephemeral: true,
);
final String productID;
final String modelID;
......
......@@ -26,7 +26,7 @@ import '../run_hot.dart';
import '../runner/flutter_command.dart';
import '../vmservice.dart';
const String protocolVersion = '0.5.0';
const String protocolVersion = '0.5.1';
/// A server process command. This command will start up a long-lived server.
/// It reads JSON-RPC based commands from stdin, executes them, and returns
......@@ -659,7 +659,12 @@ class DeviceDomain extends Domain {
_DeviceEventHandler _onDeviceEvent(String eventName) {
return (Device device) {
_serializeDeviceEvents = _serializeDeviceEvents.then<void>((_) async {
sendEvent(eventName, await _deviceToMap(device));
try {
final Map<String, Object> response = await _deviceToMap(device);
sendEvent(eventName, response);
} catch (err) {
printError(err);
}
});
};
}
......@@ -768,6 +773,9 @@ Future<Map<String, dynamic>> _deviceToMap(Device device) async {
'name': device.name,
'platform': getNameForTargetPlatform(await device.targetPlatform),
'emulator': await device.isLocalEmulator,
'category': device.category?.toString(),
'platformType': device.platformType?.toString(),
'ephemeral': device.ephemeral,
};
}
......
......@@ -5,6 +5,8 @@
import 'dart:async';
import 'dart:math' as math;
import 'package:meta/meta.dart';
import 'android/android_device.dart';
import 'application_package.dart';
import 'artifacts.dart';
......@@ -28,6 +30,38 @@ import 'windows/windows_device.dart';
DeviceManager get deviceManager => context.get<DeviceManager>();
/// A description of the kind of workflow the device supports.
class Category {
const Category._(this.value);
static const Category web = Category._('web');
static const Category desktop = Category._('desktop');
static const Category mobile = Category._('mobile');
final String value;
@override
String toString() => value;
}
/// The platform sub-folder that a device type supports.
class PlatformType {
const PlatformType._(this.value);
static const PlatformType web = PlatformType._('web');
static const PlatformType android = PlatformType._('android');
static const PlatformType ios = PlatformType._('ios');
static const PlatformType linux = PlatformType._('linux');
static const PlatformType macos = PlatformType._('macos');
static const PlatformType windows = PlatformType._('windows');
static const PlatformType fuchsia = PlatformType._('fuchsia');
final String value;
@override
String toString() => value;
}
/// A class to get all available devices.
class DeviceManager {
......@@ -208,10 +242,19 @@ abstract class PollingDeviceDiscovery extends DeviceDiscovery {
abstract class Device {
Device(this.id);
Device(this.id, {@required this.category, @required this.platformType, @required this.ephemeral});
final String id;
/// The [Category] for this device type.
final Category category;
/// The [PlatformType] for this device.
final PlatformType platformType;
/// Whether this is an ephemeral device.
final bool ephemeral;
String get name;
bool get supportsStartPaused => true;
......
......@@ -175,7 +175,12 @@ List<FuchsiaDevice> parseListDevices(String text) {
}
class FuchsiaDevice extends Device {
FuchsiaDevice(String id, {this.name}) : super(id);
FuchsiaDevice(String id, {this.name}) : super(
id,
platformType: PlatformType.fuchsia,
category: null,
ephemeral: false,
);
@override
bool get supportsHotReload => true;
......
......@@ -114,7 +114,12 @@ class IOSDevices extends PollingDeviceDiscovery {
class IOSDevice extends Device {
IOSDevice(String id, { this.name, String sdkVersion })
: _sdkVersion = sdkVersion,
super(id) {
super(
id,
category: Category.mobile,
platformType: PlatformType.ios,
ephemeral: true,
) {
_installerPath = _checkForCommand('ideviceinstaller');
_iproxyPath = _checkForCommand('iproxy');
}
......
......@@ -50,7 +50,7 @@ class IOSSimulatorUtils {
return <IOSSimulator>[];
return SimControl.instance.getConnectedDevices().map<IOSSimulator>((SimDevice device) {
return IOSSimulator(device.udid, name: device.name, category: device.category);
return IOSSimulator(device.udid, name: device.name, simulatorCategory: device.category);
}).toList();
}
}
......@@ -215,12 +215,17 @@ class SimDevice {
}
class IOSSimulator extends Device {
IOSSimulator(String id, { this.name, this.category }) : super(id);
IOSSimulator(String id, { this.name, this.simulatorCategory }) : super(
id,
category: Category.mobile,
platformType: PlatformType.ios,
ephemeral: true,
);
@override
final String name;
final String category;
final String simulatorCategory;
@override
Future<bool> get isLocalEmulator async => true;
......@@ -435,7 +440,7 @@ class IOSSimulator extends Device {
Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios;
@override
Future<String> get sdkNameAndVersion async => category;
Future<String> get sdkNameAndVersion async => simulatorCategory;
final RegExp _iosSdkRegExp = RegExp(r'iOS( |-)(\d+)');
......
......@@ -19,7 +19,12 @@ import 'linux_workflow.dart';
/// A device that represents a desktop Linux target.
class LinuxDevice extends Device {
LinuxDevice() : super('Linux');
LinuxDevice() : super(
'Linux',
category: Category.desktop,
platformType: PlatformType.linux,
ephemeral: false,
);
@override
void clearLogs() { }
......
......@@ -20,7 +20,12 @@ import 'macos_workflow.dart';
/// A device that represents a desktop MacOS target.
class MacOSDevice extends Device {
MacOSDevice() : super('macOS');
MacOSDevice() : super(
'macOS',
category: Category.desktop,
platformType: PlatformType.macos,
ephemeral: false,
);
@override
void clearLogs() { }
......
......@@ -42,7 +42,12 @@ class FlutterTesterApp extends ApplicationPackage {
// TODO(scheglov): This device does not currently work with full restarts.
class FlutterTesterDevice extends Device {
FlutterTesterDevice(String deviceId) : super(deviceId);
FlutterTesterDevice(String deviceId) : super(
deviceId,
platformType: null,
category: null,
ephemeral: false,
);
Process _process;
final DevicePortForwarder _portForwarder = _NoopPortForwarder();
......
......@@ -28,7 +28,12 @@ class WebApplicationPackage extends ApplicationPackage {
}
class WebDevice extends Device {
WebDevice() : super('web');
WebDevice() : super(
'web',
category: Category.web,
platformType: PlatformType.web,
ephemeral: false,
);
@override
bool get supportsHotReload => true;
......
......@@ -21,7 +21,12 @@ import 'windows_workflow.dart';
/// A device that represents a desktop Windows target.
class WindowsDevice extends Device {
WindowsDevice() : super('Windows');
WindowsDevice() : super(
'Windows',
category: Category.desktop,
platformType: PlatformType.windows,
ephemeral: false,
);
@override
void clearLogs() { }
......
......@@ -58,6 +58,7 @@ List of devices attached
''', devices: devices);
expect(devices, hasLength(1));
expect(devices.first.name, 'Nexus 7');
expect(devices.first.category, Category.mobile);
});
testUsingContext('emulators and short listings', () {
......
......@@ -50,7 +50,12 @@ class TestDeviceManager extends DeviceManager {
}
class _MockDevice extends Device {
_MockDevice(this.name, String id) : super(id);
_MockDevice(this.name, String id) : super(
id,
platformType: PlatformType.web,
category: Category.mobile,
ephemeral: true,
);
@override
final String name;
......
......@@ -182,6 +182,7 @@ f577a7903cc54959be2e34bc4f7f80b7009efcf4
'This is a multi-line message,',
' with a non-Flutter log message following it.',
]);
expect(device.category, Category.mobile);
}, overrides: <Type, Generator>{
IMobileDevice: () => mockIMobileDevice,
});
......
......@@ -109,16 +109,22 @@ void main() {
group('sdkMajorVersion', () {
// This new version string appears in SimulatorApp-850 CoreSimulator-518.16 beta.
test('can be parsed from iOS-11-3', () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', category: 'com.apple.CoreSimulator.SimRuntime.iOS-11-3');
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-11-3');
expect(await device.sdkMajorVersion, 11);
});
test('can be parsed from iOS 11.2', () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', category: 'iOS 11.2');
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'iOS 11.2');
expect(await device.sdkMajorVersion, 11);
});
test('Has a simulator category', () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'iOS 11.2');
expect(device.category, Category.mobile);
});
});
group('IOSSimulator.isSupported', () {
......@@ -246,7 +252,7 @@ void main() {
});
testUsingContext('uses tail on iOS versions prior to iOS 11', () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', category: 'iOS 9.3');
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'iOS 9.3');
await launchDeviceLogTool(device);
expect(
verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single,
......@@ -258,7 +264,7 @@ void main() {
});
testUsingContext('uses /usr/bin/log on iOS 11 and above', () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', category: 'iOS 11.0');
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'iOS 11.0');
await launchDeviceLogTool(device);
expect(
verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single,
......@@ -280,7 +286,7 @@ void main() {
});
testUsingContext('uses tail on iOS versions prior to iOS 11', () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', category: 'iOS 9.3');
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'iOS 9.3');
await launchSystemLogTool(device);
expect(
verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single,
......@@ -292,7 +298,7 @@ void main() {
});
testUsingContext('uses /usr/bin/log on iOS 11 and above', () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', category: 'iOS 11.0');
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'iOS 11.0');
await launchSystemLogTool(device);
verifyNever(mockProcessManager.start(any, environment: null, workingDirectory: null));
},
......@@ -330,7 +336,7 @@ void main() {
return Future<Process>.value(mockProcess);
});
final IOSSimulator device = IOSSimulator('123456', category: 'iOS 11.0');
final IOSSimulator device = IOSSimulator('123456', simulatorCategory: 'iOS 11.0');
final DeviceLogReader logReader = device.getLogReader(
app: BuildableIOSApp(mockIosProject),
);
......@@ -430,7 +436,7 @@ void main() {
});
testUsingContext("startApp uses compiled app's Info.plist to find CFBundleIdentifier", () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', category: 'iOS 11.2');
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'iOS 11.2');
when(iosWorkflow.getPlistValueFromFile(any, any)).thenReturn('correct');
final Directory mockDir = fs.currentDirectory;
......
......@@ -43,6 +43,7 @@ void main() {
expect(await device.isLatestBuildInstalled(linuxApp), true);
expect(await device.isAppInstalled(linuxApp), true);
expect(await device.stopApp(linuxApp), true);
expect(device.category, Category.desktop);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
});
......
......@@ -44,6 +44,7 @@ void main() {
expect(await device.isLatestBuildInstalled(mockMacOSApp), true);
expect(await device.isAppInstalled(mockMacOSApp), true);
expect(await device.stopApp(mockMacOSApp), false);
expect(device.category, Category.desktop);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
});
......
......@@ -43,6 +43,7 @@ void main() {
expect(await device.isLatestBuildInstalled(windowsApp), true);
expect(await device.isAppInstalled(windowsApp), true);
expect(await device.stopApp(windowsApp), false);
expect(device.category, Category.desktop);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
});
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment