Merge pull request #77 from chinmaygarde/master
Fix Flutter project template
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index e0f058e..e51d27d 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -56,6 +56,12 @@
/// Check if the current version of the given app is already installed
bool isAppInstalled(ApplicationPackage app);
+
+ /// Start an app package on the current device
+ Future<bool> startApp(ApplicationPackage app);
+
+ /// Stop an app package on the current device
+ Future<bool> stopApp(ApplicationPackage app);
}
class IOSDevice extends _Device {
@@ -80,6 +86,12 @@
String _informerPath;
String get informerPath => _informerPath;
+ String _debuggerPath;
+ String get debuggerPath => _debuggerPath;
+
+ String _loggerPath;
+ String get loggerPath => _loggerPath;
+
String _name;
String get name => _name;
@@ -93,6 +105,8 @@
_installerPath = _checkForCommand('ideviceinstaller');
_listerPath = _checkForCommand('idevice_id');
_informerPath = _checkForCommand('ideviceinfo');
+ _debuggerPath = _checkForCommand('idevicedebug');
+ _loggerPath = _checkForCommand('idevicesyslog');
}
static List<IOSDevice> getAttachedDevices([IOSDevice mockIOS]) {
@@ -160,8 +174,44 @@
@override
bool isAppInstalled(ApplicationPackage app) {
+ try {
+ String apps = runCheckedSync([installerPath, '-l']);
+ if (new RegExp(app.appPackageID, multiLine: true).hasMatch(apps)) {
+ return true;
+ }
+ } catch (e) {
+ return false;
+ }
return false;
}
+
+ @override
+ Future<bool> startApp(ApplicationPackage app) async {
+ if (!isAppInstalled(app)) {
+ return false;
+ }
+ // idevicedebug hangs forever after launching the app, so kill it after
+ // giving it plenty of time to send the launch command.
+ return runAndKill(
+ [debuggerPath, 'run', app.appPackageID], new Duration(seconds: 3)).then(
+ (_) {
+ return true;
+ }, onError: (e) {
+ _logging.info('Failure running $debuggerPath: ', e);
+ return false;
+ });
+ }
+
+ @override
+ Future<bool> stopApp(ApplicationPackage app) async {
+ // Currently we don't have a way to stop an app running on iOS.
+ return false;
+ }
+
+ Future<int> logs({bool clear: false}) {
+ return runCommandAndStreamOutput([loggerPath],
+ prefix: 'IOS DEV: ', filter: new RegExp(r'.*SkyShell.*'));
+ }
}
class AndroidDevice extends _Device {
@@ -230,13 +280,15 @@
_adbPath = _getAdbPath();
_hasAdb = _checkForAdb();
- // Checking for lollipop only needs to be done if we are starting an
- // app, but it has an important side effect, which is to discard any
- // progress messages if the adb server is restarted.
- _hasValidAndroid = _checkForLollipopOrLater();
+ if (isConnected()) {
+ // Checking for lollipop only needs to be done if we are starting an
+ // app, but it has an important side effect, which is to discard any
+ // progress messages if the adb server is restarted.
+ _hasValidAndroid = _checkForLollipopOrLater();
- if (!_hasAdb || !_hasValidAndroid) {
- _logging.severe('Unable to run on Android.');
+ if (!_hasAdb || !_hasValidAndroid) {
+ _logging.severe('Unable to run on Android.');
+ }
}
}
@@ -456,7 +508,14 @@
return true;
}
- bool stop(AndroidApk apk) {
+ @override
+ Future<bool> startApp(AndroidApk apk) async {
+ // Android currently has to be started with startServer(...).
+ assert(false);
+ return false;
+ }
+
+ Future<bool> stopApp(AndroidApk apk) async {
// Turn off reverse port forwarding
runSync([adbPath, 'reverse', '--remove', 'tcp:$_serverPort']);
// Stop the app
diff --git a/packages/flutter_tools/lib/src/logs.dart b/packages/flutter_tools/lib/src/logs.dart
index d3f9081..518b75e 100644
--- a/packages/flutter_tools/lib/src/logs.dart
+++ b/packages/flutter_tools/lib/src/logs.dart
@@ -15,9 +15,10 @@
class LogsCommand extends Command {
final name = 'logs';
final description = 'Show logs for running Sky apps.';
- AndroidDevice android = null;
+ AndroidDevice android;
+ IOSDevice ios;
- LogsCommand([this.android]) {
+ LogsCommand({this.android, this.ios}) {
argParser.addFlag('clear',
negatable: false,
help: 'Clear log history before reading from logs (Android only).');
@@ -28,16 +29,28 @@
if (android == null) {
android = new AndroidDevice();
}
+ if (ios == null) {
+ ios = new IOSDevice();
+ }
Future<int> androidLogProcess = null;
if (android.isConnected()) {
androidLogProcess = android.logs(clear: argResults['clear']);
}
+ Future<int> iosLogProcess = null;
+ if (ios.isConnected()) {
+ iosLogProcess = ios.logs(clear: argResults['clear']);
+ }
+
if (androidLogProcess != null) {
await androidLogProcess;
}
+ if (iosLogProcess != null) {
+ await iosLogProcess;
+ }
+
return 0;
}
}
diff --git a/packages/flutter_tools/lib/src/process.dart b/packages/flutter_tools/lib/src/process.dart
index c703571..7be82b2 100644
--- a/packages/flutter_tools/lib/src/process.dart
+++ b/packages/flutter_tools/lib/src/process.dart
@@ -15,19 +15,43 @@
/// This runs the command and streams stdout/stderr from the child process to
/// this process' stdout/stderr.
Future<int> runCommandAndStreamOutput(List<String> cmd,
- {String prefix: ''}) async {
+ {String prefix: '', RegExp filter}) async {
_logging.info(cmd.join(' '));
Process proc =
await Process.start(cmd[0], cmd.getRange(1, cmd.length).toList());
- proc.stdout.transform(UTF8.decoder).listen((data) {
- stdout.write('$prefix${data.trimRight().split('\n').join('\n$prefix')}\n');
+ proc.stdout.transform(UTF8.decoder).listen((String data) {
+ List<String> dataLines = data.trimRight().split('\n');
+ if (filter != null) {
+ dataLines = dataLines.where((String s) => filter.hasMatch(s));
+ }
+ if (dataLines.length > 0) {
+ stdout.write('$prefix${dataLines.join('\n$prefix')}\n');
+ }
});
- proc.stderr.transform(UTF8.decoder).listen((data) {
- stderr.write('$prefix${data.trimRight().split('\n').join('\n$prefix')}\n');
+ proc.stderr.transform(UTF8.decoder).listen((String data) {
+ List<String> dataLines = data.trimRight().split('\n');
+ if (filter != null) {
+ dataLines = dataLines.where((String s) => filter.hasMatch(s));
+ }
+ if (dataLines.length > 0) {
+ stderr.write('$prefix${dataLines.join('\n$prefix')}\n');
+ }
});
return proc.exitCode;
}
+Future runAndKill(List<String> cmd, Duration timeout) async {
+ _logging.info(cmd.join(' '));
+ Future<Process> proc = Process.start(
+ cmd[0], cmd.getRange(1, cmd.length).toList(),
+ mode: ProcessStartMode.DETACHED);
+
+ return new Future.delayed(timeout, () async {
+ _logging.info('Intentionally killing ${cmd[0]}');
+ Process.killPid((await proc).pid);
+ });
+}
+
/// Run cmd and return stdout.
/// Throws an error if cmd exits with a non-zero value.
String runCheckedSync(List<String> cmd) =>
diff --git a/packages/flutter_tools/lib/src/start.dart b/packages/flutter_tools/lib/src/start.dart
index 08af1a1..8d2dbc0 100644
--- a/packages/flutter_tools/lib/src/start.dart
+++ b/packages/flutter_tools/lib/src/start.dart
@@ -41,22 +41,26 @@
if (android == null) {
android = new AndroidDevice();
}
+ if (ios == null) {
+ ios = new IOSDevice();
+ }
bool startedSomewhere = false;
bool poke = argResults['poke'];
if (!poke) {
- StopCommand stopper = new StopCommand(android);
+ StopCommand stopper = new StopCommand(android: android, ios: ios);
stopper.stop();
// Only install if the user did not specify a poke
InstallCommand installer = new InstallCommand(android: android, ios: ios);
- startedSomewhere = installer.install();
+ installer.install();
}
+ Map<BuildPlatform, ApplicationPackage> packages =
+ ApplicationPackageFactory.getAvailableApplicationPackages();
+
bool startedOnAndroid = false;
if (android.isConnected()) {
- Map<BuildPlatform, ApplicationPackage> packages =
- ApplicationPackageFactory.getAvailableApplicationPackages();
ApplicationPackage androidApp = packages[BuildPlatform.android];
String target = path.absolute(argResults['target']);
@@ -64,6 +68,12 @@
target, poke, argResults['checked'], androidApp);
}
+ if (ios.isConnected()) {
+ ApplicationPackage iosApp = packages[BuildPlatform.iOS];
+
+ startedSomewhere = await ios.startApp(iosApp) || startedSomewhere;
+ }
+
if (startedSomewhere || startedOnAndroid) {
return 0;
} else {
diff --git a/packages/flutter_tools/lib/src/stop.dart b/packages/flutter_tools/lib/src/stop.dart
index c3edaf6..d1f9844 100644
--- a/packages/flutter_tools/lib/src/stop.dart
+++ b/packages/flutter_tools/lib/src/stop.dart
@@ -17,29 +17,39 @@
final name = 'stop';
final description = 'Stop your Flutter app on all attached devices.';
AndroidDevice android = null;
+ IOSDevice ios = null;
- StopCommand([this.android]);
+ StopCommand({this.android, this.ios});
@override
Future<int> run() async {
- if (android == null) {
- android = new AndroidDevice();
- }
-
- if (stop()) {
+ if (await stop()) {
return 0;
} else {
return 2;
}
}
- bool stop() {
+ Future<bool> stop() async {
+ if (android == null) {
+ android = new AndroidDevice();
+ }
+ if (ios == null) {
+ ios = new IOSDevice();
+ }
+
bool stoppedSomething = false;
+ Map<BuildPlatform, ApplicationPackage> packages =
+ ApplicationPackageFactory.getAvailableApplicationPackages();
+
if (android.isConnected()) {
- Map<BuildPlatform, ApplicationPackage> packages =
- ApplicationPackageFactory.getAvailableApplicationPackages();
ApplicationPackage androidApp = packages[BuildPlatform.android];
- stoppedSomething = android.stop(androidApp) || stoppedSomething;
+ stoppedSomething = await android.stopApp(androidApp) || stoppedSomething;
+ }
+
+ if (ios.isConnected()) {
+ ApplicationPackage iosApp = packages[BuildPlatform.iOS];
+ stoppedSomething = await ios.stopApp(iosApp) || stoppedSomething;
}
return stoppedSomething;
diff --git a/packages/flutter_tools/test/logs_test.dart b/packages/flutter_tools/test/logs_test.dart
index 19a5872..e0f2c38 100644
--- a/packages/flutter_tools/test/logs_test.dart
+++ b/packages/flutter_tools/test/logs_test.dart
@@ -20,7 +20,10 @@
MockAndroidDevice android = new MockAndroidDevice();
when(android.isConnected()).thenReturn(false);
- LogsCommand command = new LogsCommand(android);
+ MockIOSDevice ios = new MockIOSDevice();
+ when(ios.isConnected()).thenReturn(false);
+
+ LogsCommand command = new LogsCommand(android: android, ios: ios);
CommandRunner runner = new CommandRunner('test_flutter', '')
..addCommand(command);
diff --git a/packages/flutter_tools/test/start_test.dart b/packages/flutter_tools/test/start_test.dart
index 732f18b..9ddfb0e 100644
--- a/packages/flutter_tools/test/start_test.dart
+++ b/packages/flutter_tools/test/start_test.dart
@@ -21,11 +21,36 @@
MockAndroidDevice android = new MockAndroidDevice();
when(android.isConnected()).thenReturn(true);
when(android.installApp(any)).thenReturn(true);
- when(android.stop(any)).thenReturn(true);
+ when(android.startServer(any, any, any, any)).thenReturn(true);
+ when(android.stopApp(any)).thenReturn(true);
MockIOSDevice ios = new MockIOSDevice();
when(ios.isConnected()).thenReturn(false);
when(ios.installApp(any)).thenReturn(false);
+ when(ios.startApp(any)).thenReturn(false);
+ when(ios.startApp(any)).thenReturn(false);
+
+ StartCommand command = new StartCommand(android: android, ios: ios);
+
+ CommandRunner runner = new CommandRunner('test_flutter', '')
+ ..addCommand(command);
+ runner.run(['start']).then((int code) => expect(code, equals(0)));
+ });
+
+ test('returns 0 when iOS is connected and ready to be started', () {
+ applicationPackageSetup();
+
+ MockAndroidDevice android = new MockAndroidDevice();
+ when(android.isConnected()).thenReturn(false);
+ when(android.installApp(any)).thenReturn(false);
+ when(android.startServer(any, any, any, any)).thenReturn(false);
+ when(android.stopApp(any)).thenReturn(false);
+
+ MockIOSDevice ios = new MockIOSDevice();
+ when(ios.isConnected()).thenReturn(true);
+ when(ios.installApp(any)).thenReturn(true);
+ when(ios.startApp(any)).thenReturn(true);
+ when(ios.stopApp(any)).thenReturn(false);
StartCommand command = new StartCommand(android: android, ios: ios);
diff --git a/packages/flutter_tools/test/stop_test.dart b/packages/flutter_tools/test/stop_test.dart
index af6ec19..8d67d66 100644
--- a/packages/flutter_tools/test/stop_test.dart
+++ b/packages/flutter_tools/test/stop_test.dart
@@ -20,8 +20,31 @@
MockAndroidDevice android = new MockAndroidDevice();
when(android.isConnected()).thenReturn(true);
- when(android.stop(any)).thenReturn(true);
- StopCommand command = new StopCommand(android);
+ when(android.stopApp(any)).thenReturn(true);
+
+ MockIOSDevice ios = new MockIOSDevice();
+ when(ios.isConnected()).thenReturn(false);
+ when(ios.stopApp(any)).thenReturn(false);
+
+ StopCommand command = new StopCommand(android: android, ios: ios);
+
+ CommandRunner runner = new CommandRunner('test_flutter', '')
+ ..addCommand(command);
+ runner.run(['stop']).then((int code) => expect(code, equals(0)));
+ });
+
+ test('returns 0 when iOS is connected and ready to be stopped', () {
+ applicationPackageSetup();
+
+ MockAndroidDevice android = new MockAndroidDevice();
+ when(android.isConnected()).thenReturn(false);
+ when(android.stopApp(any)).thenReturn(false);
+
+ MockIOSDevice ios = new MockIOSDevice();
+ when(ios.isConnected()).thenReturn(true);
+ when(ios.stopApp(any)).thenReturn(true);
+
+ StopCommand command = new StopCommand(android: android, ios: ios);
CommandRunner runner = new CommandRunner('test_flutter', '')
..addCommand(command);