diff --git a/packages/flutter_tools/lib/src/android/device_android.dart b/packages/flutter_tools/lib/src/android/device_android.dart index 0270def4f2..404c0d8f1c 100644 --- a/packages/flutter_tools/lib/src/android/device_android.dart +++ b/packages/flutter_tools/lib/src/android/device_android.dart @@ -392,6 +392,8 @@ class AndroidDevice extends Device { bool isConnected() => _connected ?? androidSdk != null; + bool isSupported() => true; + void setConnected(bool value) { _connected = value; } diff --git a/packages/flutter_tools/lib/src/commands/devices.dart b/packages/flutter_tools/lib/src/commands/devices.dart index 6531c6389d..817c9ebabe 100644 --- a/packages/flutter_tools/lib/src/commands/devices.dart +++ b/packages/flutter_tools/lib/src/commands/devices.dart @@ -28,10 +28,12 @@ class DevicesCommand extends FlutterCommand { printStatus('No connected devices.'); } else { printStatus('${devices.length} connected ${pluralize('device', devices.length)}:'); - printStatus(''); for (Device device in devices) { - printStatus('${device.name} (${device.id})'); + printStatus('\t${_supportIndicator(device)}: ${device.name} (${device.id})'); + if (!device.isSupported()) { + printStatus("\t\t${device.supportMessage()}"); + } } } @@ -39,4 +41,6 @@ class DevicesCommand extends FlutterCommand { } } +String _supportIndicator(Device device) => device.isSupported() ? "[✔] Supported" : "[✘] Unsupported"; + String pluralize(String word, int count) => count == 1 ? word : word + 's'; diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index c4fcb7b900..7a92edbebf 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart @@ -179,6 +179,11 @@ Future startApp( if (package == null || !device.isConnected()) continue; + if (!device.isSupported()) { + printStatus("Skipping unsupported device: ${device.name}"); + continue; + } + printTrace('Running build command for $device.'); Map platformArgs = {}; @@ -215,11 +220,7 @@ Future startApp( } if (!startedSomething) { - if (!devices.all.any((device) => device.isConnected())) { - printError('Unable to run application - no connected devices.'); - } else { - printError('Unable to run application.'); - } + printError('Unable to run application. Please ensure that a supported device/simulator is connected.'); } return startedSomething ? 0 : 2; diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 28c9333654..04562ad9b3 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -106,6 +106,13 @@ abstract class Device { /// Check if the device is currently connected bool isConnected(); + /// Check if the device is supported by Flutter + bool isSupported(); + + // String meant to be displayed to the user indicating if the device is + // supported by Flutter, and, if not, why. + String supportMessage() => isSupported() ? "Supported" : "Unsupported"; + /// Check if the current version of the given app is already installed bool isAppInstalled(ApplicationPackage app); diff --git a/packages/flutter_tools/lib/src/ios/device_ios.dart b/packages/flutter_tools/lib/src/ios/device_ios.dart index 0225701723..10bb96519c 100644 --- a/packages/flutter_tools/lib/src/ios/device_ios.dart +++ b/packages/flutter_tools/lib/src/ios/device_ios.dart @@ -149,6 +149,9 @@ class IOSDevice extends Device { @override bool isConnected() => _getAttachedDeviceIDs().contains(id); + @override + bool isSupported() => true; + @override bool isAppInstalled(ApplicationPackage app) { try { @@ -291,6 +294,61 @@ class IOSSimulator extends Device { return SimControl.getConnectedDevices().any((SimDevice device) => device.udid == id); } + @override + bool isSupported() { + if (!Platform.isMacOS) { + _supportMessage = "Not supported on a non Mac host"; + return false; + } + + // Step 1: Check if the device is part of a blacklisted category. + // We do not support WatchOS or tvOS devices. + + RegExp blacklist = new RegExp(r'Apple (TV|Watch)', caseSensitive: false); + + if (blacklist.hasMatch(name)) { + _supportMessage = "Flutter does not support either the Apple TV or Watch. Choose an iPhone 5s or above."; + return false; + } + + // Step 2: Check if the device must be rejected because of its version. + // There is an artitifical check on older simulators where arm64 + // targetted applications cannot be run (even though the + // Flutter runner on the simulator is completely different). + + RegExp versionExp = new RegExp(r'iPhone ([0-9])+'); + Match match = versionExp.firstMatch(name); + + if (match == null) { + // Not an iPhone. All available non-iPhone simulators are compatible. + return true; + } + + if (int.parse(match.group(1)) > 5) { + // iPhones 6 and above are always fine. + return true; + } + + // The 's' subtype of 5 is compatible. + if (name.contains('iPhone 5s')) { + return true; + } + + _supportMessage = "The simulator version is too old. Choose an iPhone 5s or above."; + return false; + } + + String _supportMessage; + + @override + String supportMessage() { + if (isSupported()) { + return "Supported"; + } + + return _supportMessage != null ? _supportMessage : "Unknown"; + } + @override bool isAppInstalled(ApplicationPackage app) { try {