mirror of
https://github.com/flutter/flutter
synced 2024-10-13 11:42:54 +00:00
Merge pull request #2920 from devoncarew/screenshot
add a screenshot command
This commit is contained in:
commit
1c474c6eee
|
@ -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();
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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.
|
||||
|
|
65
packages/flutter_tools/lib/src/commands/screenshot.dart
Normal file
65
packages/flutter_tools/lib/src/commands/screenshot.dart
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue