Commit 6bfd60f2 authored by Ian Fischer's avatar Ian Fischer

Merge pull request #56 from iansf/android_stop

Add stop command and supporting Android support.
parents f7e20f4a fa592337
...@@ -14,9 +14,11 @@ import 'package:sky_tools/src/cache.dart'; ...@@ -14,9 +14,11 @@ import 'package:sky_tools/src/cache.dart';
import 'package:sky_tools/src/init.dart'; import 'package:sky_tools/src/init.dart';
import 'package:sky_tools/src/install.dart'; import 'package:sky_tools/src/install.dart';
import 'package:sky_tools/src/run_mojo.dart'; import 'package:sky_tools/src/run_mojo.dart';
import 'package:sky_tools/src/stop.dart';
class FlutterCommandRunner extends CommandRunner { class FlutterCommandRunner extends CommandRunner {
FlutterCommandRunner() : super('flutter', 'Manage your flutter app development.') { FlutterCommandRunner()
: super('flutter', 'Manage your flutter app development.') {
argParser.addFlag('verbose', argParser.addFlag('verbose',
abbr: 'v', abbr: 'v',
negatable: false, negatable: false,
...@@ -41,7 +43,8 @@ class FlutterCommandRunner extends CommandRunner { ...@@ -41,7 +43,8 @@ class FlutterCommandRunner extends CommandRunner {
'not set. Note that release is not compatible with the listen command ' 'not set. Note that release is not compatible with the listen command '
'on iOS devices and simulators. Not normally required.'); 'on iOS devices and simulators. Not normally required.');
argParser.addOption('sky-src-path', argParser.addOption('sky-src-path',
help: 'Path to your Sky src directory, if you are building Sky locally. ' help:
'Path to your Sky src directory, if you are building Sky locally. '
'Ignored if neither debug nor release is set. Not normally required.'); 'Ignored if neither debug nor release is set. Not normally required.');
argParser.addOption('android-debug-build-path', argParser.addOption('android-debug-build-path',
help: help:
...@@ -120,8 +123,8 @@ class FlutterCommandRunner extends CommandRunner { ...@@ -120,8 +123,8 @@ class FlutterCommandRunner extends CommandRunner {
ApplicationPackageFactory.defaultBuildType = BuildType.release; ApplicationPackageFactory.defaultBuildType = BuildType.release;
ApplicationPackageFactory.setBuildPath(BuildType.release, ApplicationPackageFactory.setBuildPath(BuildType.release,
BuildPlatform.android, results['android-release-build-path']); BuildPlatform.android, results['android-release-build-path']);
ApplicationPackageFactory.setBuildPath(BuildType.release, BuildPlatform.iOS, ApplicationPackageFactory.setBuildPath(BuildType.release,
results['ios-release-build-path']); BuildPlatform.iOS, results['ios-release-build-path']);
ApplicationPackageFactory.setBuildPath(BuildType.release, ApplicationPackageFactory.setBuildPath(BuildType.release,
BuildPlatform.iOSSimulator, results['ios-sim-release-build-path']); BuildPlatform.iOSSimulator, results['ios-sim-release-build-path']);
} }
...@@ -141,6 +144,7 @@ void main(List<String> args) { ...@@ -141,6 +144,7 @@ void main(List<String> args) {
}); });
new FlutterCommandRunner() new FlutterCommandRunner()
..addCommand(new StopCommand())
..addCommand(new BuildCommand()) ..addCommand(new BuildCommand())
..addCommand(new CacheCommand()) ..addCommand(new CacheCommand())
..addCommand(new InitCommand()) ..addCommand(new InitCommand())
......
...@@ -29,10 +29,16 @@ abstract class ApplicationPackage { ...@@ -29,10 +29,16 @@ abstract class ApplicationPackage {
class AndroidApk extends ApplicationPackage { class AndroidApk extends ApplicationPackage {
static const String _apkName = 'SkyShell.apk'; static const String _apkName = 'SkyShell.apk';
static const String _androidPackage = 'org.domokit.sky.shell'; static const String _packageID = 'org.domokit.sky.shell';
static const String _componentID = '$_packageID/$_packageID.SkyActivity';
/// The path to the activity that should be launched.
/// Defaults to 'org.domokit.sky.shell/org.domokit.sky.shell.SkyActivity'
String component;
AndroidApk(String appDir, AndroidApk(String appDir,
[String appPackageID = _androidPackage, String appFileName = _apkName]) {String appPackageID: _packageID,
String appFileName: _apkName,
this.component: _componentID})
: super(path.join(appDir, 'apks'), appPackageID, appFileName); : super(path.join(appDir, 'apks'), appPackageID, appFileName);
} }
......
...@@ -52,6 +52,7 @@ abstract class _Device { ...@@ -52,6 +52,7 @@ abstract class _Device {
class AndroidDevice extends _Device { class AndroidDevice extends _Device {
static const String _ADB_PATH = 'adb'; static const String _ADB_PATH = 'adb';
static const String _serverPort = '9888';
static const String className = 'AndroidDevice'; static const String className = 'AndroidDevice';
static final String defaultDeviceID = 'default'; static final String defaultDeviceID = 'default';
...@@ -232,6 +233,31 @@ class AndroidDevice extends _Device { ...@@ -232,6 +233,31 @@ class AndroidDevice extends _Device {
return true; return true;
} }
bool stop(AndroidApk apk) {
// Turn off reverse port forwarding
try {
runCheckedSync([adbPath, 'reverse', '--remove', 'tcp:$_serverPort']);
} catch (e) {}
// Stop the app
runCheckedSync([adbPath, 'shell', 'am', 'force-stop', apk.appPackageID]);
// Kill the server
try {
if (Platform.isMacOS) {
String pid = runCheckedSync(['lsof', '-i', ':$_serverPort', '-t']);
// Killing a pid with a shell command from within dart is hard,
// so use a library command, but it's still nice to give the
// equivalent command when doing verbose logging.
_logging.info('kill $pid');
Process.killPid(int.parse(pid));
} else {
runCheckedSync(['fuser', '-k', '$_serverPort/tcp']);
}
} catch (e) {}
return true;
}
@override @override
bool isConnected() => _hasValidAndroid; bool isConnected() => _hasValidAndroid;
} }
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
library sky_tools.stop;
import 'dart:async';
import 'package:args/command_runner.dart';
import 'package:logging/logging.dart';
import 'package:sky_tools/src/application_package.dart';
import 'package:sky_tools/src/device.dart';
final Logger _logging = new Logger('sky_tools.stop');
class StopCommand extends Command {
final name = 'stop';
final description = 'Stop your Flutter app on all attached devices.';
AndroidDevice android = null;
StopCommand([this.android]) {
if (android == null) {
android = new AndroidDevice();
}
}
@override
Future<int> run() async {
if (stop()) {
return 0;
} else {
return 2;
}
}
bool stop() {
bool stoppedSomething = false;
if (android.isConnected()) {
Map<BuildPlatform, ApplicationPackage> packages =
ApplicationPackageFactory.getAvailableApplicationPackages();
ApplicationPackage androidApp = packages[BuildPlatform.android];
stoppedSomething = android.stop(androidApp) || stoppedSomething;
}
return stoppedSomething;
}
}
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
library stop_test;
import 'package:args/command_runner.dart';
import 'package:mockito/mockito.dart';
import 'package:sky_tools/src/application_package.dart';
import 'package:sky_tools/src/stop.dart';
import 'package:test/test.dart';
import 'src/common.dart';
main() => defineTests();
defineTests() {
group('stop', () {
test('returns 0 when Android is connected and ready to be stopped', () {
ApplicationPackageFactory.srcPath = './';
ApplicationPackageFactory.setBuildPath(
BuildType.prebuilt, BuildPlatform.android, './');
MockAndroidDevice android = new MockAndroidDevice();
when(android.isConnected()).thenReturn(true);
when(android.stop(any)).thenReturn(true);
StopCommand command = new StopCommand(android);
CommandRunner runner = new CommandRunner('test_flutter', '')
..addCommand(command);
runner.run(['stop']).then((int code) => expect(code, equals(0)));
});
});
}
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