Beginning implementation of IOSDevice. Implements list and install.
Also update tests to be compatible with the presence of iOS and add tests for list and install.
diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart
index 0739f77..34afb4b 100644
--- a/packages/flutter_tools/lib/src/application_package.dart
+++ b/packages/flutter_tools/lib/src/application_package.dart
@@ -42,6 +42,15 @@
: super(path.join(appDir, 'apks'), appPackageID, appFileName);
}
+class IOSApp extends ApplicationPackage {
+ static const String _appName = 'SkyShell.app';
+ static const String _packageID = 'com.google.SkyShell';
+
+ IOSApp(String appDir,
+ {String appPackageID: _packageID, String appFileName: _appName})
+ : super(appDir, appPackageID, appFileName);
+}
+
enum BuildType { prebuilt, release, debug, }
enum BuildPlatform { android, iOS, iOSSimulator, mac, linux, }
@@ -62,7 +71,10 @@
static BuildType defaultBuildType = BuildType.prebuilt;
/// Default BuildPlatforms chosen if no BuildPlatforms are specified.
- static List<BuildPlatform> defaultBuildPlatforms = [BuildPlatform.android];
+ static List<BuildPlatform> defaultBuildPlatforms = [
+ BuildPlatform.android,
+ BuildPlatform.iOS,
+ ];
static Map<BuildPlatform, ApplicationPackage> getAvailableApplicationPackages(
{BuildType requestedType, List<BuildPlatform> requestedPlatforms}) {
@@ -80,6 +92,9 @@
case BuildPlatform.android:
packages[platform] = new AndroidApk(buildPath);
break;
+ case BuildPlatform.iOS:
+ packages[platform] = new IOSApp(buildPath);
+ break;
default:
// TODO(iansf): Add other platforms
assert(false);
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index 6c15b3b..b692a07 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -24,6 +24,8 @@
if (id == null) {
if (className == AndroidDevice.className) {
id = AndroidDevice.defaultDeviceID;
+ } else if (className == IOSDevice.className) {
+ id = IOSDevice.defaultDeviceID;
} else {
throw 'Attempted to create a Device of unknown type $className';
}
@@ -34,6 +36,10 @@
final device = new AndroidDevice._(id);
_deviceCache[id] = device;
return device;
+ } else if (className == IOSDevice.className) {
+ final device = new IOSDevice._(id);
+ _deviceCache[id] = device;
+ return device;
} else {
throw 'Attempted to create a Device of unknown type $className';
}
@@ -52,13 +58,120 @@
bool isAppInstalled(ApplicationPackage app);
}
+class IOSDevice extends _Device {
+ static const String className = 'IOSDevice';
+ static final String defaultDeviceID = 'default_ios_id';
+
+ static const String _macInstructions =
+ 'To work with iOS devices, please install ideviceinstaller. '
+ 'If you use homebrew, you can install it with '
+ '"\$ brew install ideviceinstaller".';
+ static const String _linuxInstructions =
+ 'To work with iOS devices, please install ideviceinstaller. '
+ 'On Ubuntu or Debian, you can install it with '
+ '"\$ apt-get install ideviceinstaller".';
+
+ String _installerPath;
+ String get installerPath => _installerPath;
+
+ String _listerPath;
+ String get listerPath => _listerPath;
+
+ String _informerPath;
+ String get informerPath => _informerPath;
+
+ String _name;
+ String get name => _name;
+
+ factory IOSDevice({String id, String name}) {
+ IOSDevice device = new _Device(className, id);
+ device._name = name;
+ return device;
+ }
+
+ IOSDevice._(String id) : super._(id) {
+ _installerPath = _checkForCommand('ideviceinstaller');
+ _listerPath = _checkForCommand('idevice_id');
+ _informerPath = _checkForCommand('ideviceinfo');
+ }
+
+ static List<IOSDevice> getAttachedDevices([IOSDevice mockIOS]) {
+ List<IOSDevice> devices = [];
+ for (String id in _getAttachedDeviceIDs(mockIOS)) {
+ String name = _getDeviceName(id, mockIOS);
+ devices.add(new IOSDevice(id: id, name: name));
+ }
+ return devices;
+ }
+
+ static List<String> _getAttachedDeviceIDs([IOSDevice mockIOS]) {
+ String listerPath =
+ (mockIOS != null) ? mockIOS.listerPath : _checkForCommand('idevice_id');
+ return runSync([listerPath, '-l']).trim().split('\n');
+ }
+
+ static String _getDeviceName(String deviceID, [IOSDevice mockIOS]) {
+ String informerPath = (mockIOS != null)
+ ? mockIOS.informerPath
+ : _checkForCommand('ideviceinfo');
+ return runSync([informerPath, '-k', 'DeviceName', '-u', deviceID]);
+ }
+
+ static final Map<String, String> _commandMap = {};
+ static String _checkForCommand(String command,
+ [String macInstructions = _macInstructions,
+ String linuxInstructions = _linuxInstructions]) {
+ return _commandMap.putIfAbsent(command, () {
+ try {
+ command = runCheckedSync(['which', command]).trim();
+ } catch (e) {
+ if (Platform.isMacOS) {
+ _logging.severe(macInstructions);
+ } else if (Platform.isLinux) {
+ _logging.severe(linuxInstructions);
+ } else {
+ _logging.severe('$command is not available on your platform.');
+ }
+ exit(2);
+ }
+ return command;
+ });
+ }
+
+ @override
+ bool installApp(ApplicationPackage app) {
+ if (id == defaultDeviceID) {
+ runCheckedSync([installerPath, '-i', app.appPath]);
+ } else {
+ runCheckedSync([installerPath, '-u', id, '-i', app.appPath]);
+ }
+ return false;
+ }
+
+ @override
+ bool isConnected() {
+ List<String> ids = _getAttachedDeviceIDs();
+ for (String id in ids) {
+ if (id == this.id || this.id == defaultDeviceID) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @override
+ bool isAppInstalled(ApplicationPackage app) {
+ return false;
+ }
+}
+
class AndroidDevice extends _Device {
static const String _ADB_PATH = 'adb';
static const String _observatoryPort = '8181';
static const String _serverPort = '9888';
static const String className = 'AndroidDevice';
- static final String defaultDeviceID = 'default';
+ static final String defaultDeviceID = 'default_android_device';
String productID;
String modelID;
@@ -85,10 +198,8 @@
/// we don't have to rely on the test setup having adb available to it.
static List<AndroidDevice> getAttachedDevices([AndroidDevice mockAndroid]) {
List<AndroidDevice> devices = [];
- String adbPath = _getAdbPath();
- if (mockAndroid != null) {
- adbPath = mockAndroid.adbPath;
- }
+ String adbPath =
+ (mockAndroid != null) ? mockAndroid.adbPath : _getAdbPath();
List<String> output =
runSync([adbPath, 'devices', '-l']).trim().split('\n');
RegExp deviceInfo = new RegExp(
diff --git a/packages/flutter_tools/lib/src/install.dart b/packages/flutter_tools/lib/src/install.dart
index f7ee8fc..d29ded1 100644
--- a/packages/flutter_tools/lib/src/install.dart
+++ b/packages/flutter_tools/lib/src/install.dart
@@ -16,15 +16,12 @@
final description = 'Install your Flutter app on attached devices.';
AndroidDevice android = null;
+ IOSDevice ios;
- InstallCommand([this.android]);
+ InstallCommand({this.android, this.ios});
@override
Future<int> run() async {
- if (android == null) {
- android = new AndroidDevice();
- }
-
if (install()) {
return 0;
} else {
@@ -33,15 +30,28 @@
}
bool install() {
+ if (android == null) {
+ android = new AndroidDevice();
+ }
+ if (ios == null) {
+ ios = new IOSDevice();
+ }
+
bool installedSomewhere = false;
Map<BuildPlatform, ApplicationPackage> packages =
ApplicationPackageFactory.getAvailableApplicationPackages();
ApplicationPackage androidApp = packages[BuildPlatform.android];
+ ApplicationPackage iosApp = packages[BuildPlatform.iOS];
+
if (androidApp != null && android.isConnected()) {
installedSomewhere = android.installApp(androidApp) || installedSomewhere;
}
+ if (iosApp != null && ios.isConnected()) {
+ installedSomewhere = ios.installApp(iosApp) || installedSomewhere;
+ }
+
return installedSomewhere;
}
}
diff --git a/packages/flutter_tools/lib/src/list.dart b/packages/flutter_tools/lib/src/list.dart
index 2270331..752fe35 100644
--- a/packages/flutter_tools/lib/src/list.dart
+++ b/packages/flutter_tools/lib/src/list.dart
@@ -16,8 +16,9 @@
final name = 'list';
final description = 'List all connected devices.';
AndroidDevice android;
+ IOSDevice ios;
- ListCommand([this.android]) {
+ ListCommand({this.android, this.ios}) {
argParser.addFlag('details',
abbr: 'd',
negatable: false,
@@ -40,6 +41,18 @@
print(device.id);
}
}
+
+ if (details) {
+ print('iOS Devices:');
+ }
+ for (IOSDevice device in IOSDevice.getAttachedDevices(ios)) {
+ if (details) {
+ print('${device.id}\t${device.name}');
+ } else {
+ print(device.id);
+ }
+ }
+
return 0;
}
}
diff --git a/packages/flutter_tools/lib/src/start.dart b/packages/flutter_tools/lib/src/start.dart
index dcc381e..08af1a1 100644
--- a/packages/flutter_tools/lib/src/start.dart
+++ b/packages/flutter_tools/lib/src/start.dart
@@ -20,8 +20,9 @@
final name = 'start';
final description = 'Start your Flutter app on attached devices.';
AndroidDevice android = null;
+ IOSDevice ios = null;
- StartCommand([this.android]) {
+ StartCommand({this.android, this.ios}) {
argParser.addFlag('poke',
negatable: false,
help: 'Restart the connection to the server (Android only).');
@@ -48,7 +49,7 @@
stopper.stop();
// Only install if the user did not specify a poke
- InstallCommand installer = new InstallCommand(android);
+ InstallCommand installer = new InstallCommand(android: android, ios: ios);
startedSomewhere = installer.install();
}