Merge pull request #2920 from devoncarew/screenshot

add a screenshot command
This commit is contained in:
Devon Carew 2016-03-26 11:04:57 -07:00
commit 1c474c6eee
7 changed files with 157 additions and 14 deletions

View file

@ -12,6 +12,7 @@ import 'package:stack_trace/stack_trace.dart';
import 'src/base/context.dart';
import 'src/base/logger.dart';
import 'src/base/process.dart';
import 'src/base/utils.dart';
import 'src/commands/analyze.dart';
import 'src/commands/build.dart';
import 'src/commands/create.dart';
@ -25,6 +26,7 @@ import 'src/commands/logs.dart';
import 'src/commands/refresh.dart';
import 'src/commands/run.dart';
import 'src/commands/run_mojo.dart';
import 'src/commands/screenshot.dart';
import 'src/commands/stop.dart';
import 'src/commands/test.dart';
import 'src/commands/trace.dart';
@ -63,6 +65,7 @@ Future<Null> main(List<String> args) async {
..addCommand(new RefreshCommand())
..addCommand(new RunCommand())
..addCommand(new RunMojoCommand(hidden: !verboseHelp))
..addCommand(new ScreenshotCommand())
..addCommand(new StopCommand())
..addCommand(new TestCommand())
..addCommand(new TraceCommand())
@ -93,7 +96,7 @@ Future<Null> main(List<String> args) async {
} else {
// We've crashed; emit a log report.
stderr.writeln();
stderr.writeln('Oops; flutter has crashed: "$error"');
stderr.writeln('Oops; flutter has exited unexpectedly: "$error"');
File file = _createCrashReport(args, error, chain);
@ -106,7 +109,7 @@ Future<Null> main(List<String> args) async {
}
File _createCrashReport(List<String> args, dynamic error, Chain chain) {
File crashFile = _createCrashFileName(Directory.current, 'flutter');
File crashFile = getUniqueFile(Directory.current, 'flutter', 'log');
StringBuffer buf = new StringBuffer();
@ -127,18 +130,6 @@ File _createCrashReport(List<String> args, dynamic error, Chain chain) {
return crashFile;
}
File _createCrashFileName(Directory dir, String baseName) {
int i = 1;
while (true) {
String name = '${baseName}_${i.toString().padLeft(2, '0')}.log';
File file = new File(path.join(dir.path, name));
if (!file.existsSync())
return file;
i++;
}
}
String _doctorText() {
try {
BufferLogger logger = new BufferLogger();

View file

@ -436,6 +436,20 @@ class AndroidDevice extends Device {
runCheckedSync(cmd);
return true;
}
@override
bool get supportsScreenshot => true;
@override
Future<bool> takeScreenshot(File outputFile) {
const String remotePath = '/data/local/tmp/flutter_screenshot.png';
runCheckedSync(adbCommandForDevice(<String>['shell', 'screencap', '-p', remotePath]));
runCheckedSync(adbCommandForDevice(<String>['pull', remotePath, outputFile.path]));
runCheckedSync(adbCommandForDevice(<String>['shell', 'rm', remotePath]));
return new Future<bool>.value(true);
}
}
List<AndroidDevice> getAdbDevices() {

View file

@ -6,6 +6,7 @@ import 'dart:async';
import 'dart:io';
import 'package:crypto/crypto.dart';
import 'package:path/path.dart' as path;
String hex(List<int> bytes) {
StringBuffer result = new StringBuffer();
@ -33,6 +34,18 @@ String camelCase(String str) {
/// Return the plural of the given word (`cat(s)`).
String pluralize(String word, int count) => count == 1 ? word : word + 's';
File getUniqueFile(Directory dir, String baseName, String ext) {
int i = 1;
while (true) {
String name = '${baseName}_${i.toString().padLeft(2, '0')}.$ext';
File file = new File(path.join(dir.path, name));
if (!file.existsSync())
return file;
i++;
}
}
/// A class to maintain a list of items, fire events when items are added or
/// removed, and calculate a diff of changes when a new list of items is
/// available.

View file

@ -0,0 +1,65 @@
// Copyright 2016 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.
import 'dart:async';
import 'dart:io';
import 'package:path/path.dart' as path;
import '../base/utils.dart';
import '../globals.dart';
import '../runner/flutter_command.dart';
class ScreenshotCommand extends FlutterCommand {
ScreenshotCommand() {
argParser.addOption('out',
abbr: 'o',
help: 'Location to write the screenshot.');
}
@override
String get name => 'screenshot';
@override
String get description => 'Take a screenshot from a connected device.';
@override
final List<String> aliases = <String>['pic'];
@override
bool get requiresProjectRoot => false;
@override
bool get requiresDevice => true;
@override
Future<int> runInProject() async {
if (!deviceForCommand.supportsScreenshot) {
printError('Screenshot not supported for ${deviceForCommand.name}.');
return 1;
}
File outputFile;
if (argResults.wasParsed('out')) {
outputFile = new File(argResults['out']);
} else {
outputFile = getUniqueFile(Directory.current, 'flutter', 'png');
}
try {
bool result = await deviceForCommand.takeScreenshot(outputFile);
if (result) {
int sizeKB = outputFile.lengthSync() ~/ 1000;
printStatus('Screenshot written to ${path.relative(outputFile.path)} (${sizeKB}kb).');
return 0;
}
} catch (error) {
printError('Error taking screenshot: $error');
}
return 1;
}
}

View file

@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import 'dart:math' as math;
import 'android/android_device.dart';
@ -191,6 +192,10 @@ abstract class Device {
/// Stop an app package on the current device.
Future<bool> stopApp(ApplicationPackage app);
bool get supportsScreenshot => false;
Future<bool> takeScreenshot(File outputFile) => new Future<bool>.error('unimplemented');
@override
int get hashCode => id.hashCode;

View file

@ -250,6 +250,18 @@ class IOSDevice extends Device {
@override
void clearLogs() {
}
@override
bool get supportsScreenshot => false;
@override
Future<bool> takeScreenshot(File outputFile) {
// We could use idevicescreenshot here (installed along with the brew
// ideviceinstaller tools). It however requires a developer disk image on
// the device.
return new Future<bool>.value(false);
}
}
class _IOSDeviceLogReader extends DeviceLogReader {

View file

@ -615,6 +615,49 @@ class IOSSimulator extends Device {
if (!logFile.existsSync())
logFile.writeAsBytesSync(<int>[]);
}
@override
bool get supportsScreenshot => true;
@override
Future<bool> takeScreenshot(File outputFile) async {
String homeDirPath = Platform.environment['HOME'] ?? Platform.environment['USERPROFILE'];
Directory desktopDir = new Directory(path.join(homeDirPath, 'Desktop'));
// 'Simulator Screen Shot Mar 25, 2016, 2.59.43 PM.png'
Set<File> getScreenshots() {
return new Set<File>.from(desktopDir.listSync().where((FileSystemEntity entity) {
String name = path.basename(entity.path);
return entity is File && name.startsWith('Simulator') && name.endsWith('.png');
}));
};
Set<File> existingScreenshots = getScreenshots();
runSync(<String>[
'osascript',
'-e',
'activate application "Simulator"\n'
'tell application "System Events" to keystroke "s" using command down'
]);
// There is some latency here from the applescript call.
await new Future<Null>.delayed(new Duration(seconds: 1));
Set<File> shots = getScreenshots().difference(existingScreenshots);
if (shots.isEmpty) {
printError('Unable to locate the screenshot file.');
return false;
}
File shot = shots.first;
outputFile.writeAsBytesSync(shot.readAsBytesSync());
shot.delete();
return true;
}
}
class _IOSSimulatorLogReader extends DeviceLogReader {