@@ -6,17 +6,37 @@ import 'dart:async';
6
6
import 'dart:io' ;
7
7
8
8
import 'package:flutter_tools/src/device.dart' ;
9
+ import 'package:http/http.dart' as http;
9
10
import 'package:path/path.dart' as path;
10
11
11
12
import '../base/utils.dart' ;
12
13
import '../globals.dart' ;
13
14
import '../runner/flutter_command.dart' ;
14
15
16
+ const String _kOut = 'out' ;
17
+ const String _kSkia = 'skia' ;
18
+ const String _kSkiaServe = 'skiaserve' ;
19
+
15
20
class ScreenshotCommand extends FlutterCommand {
16
21
ScreenshotCommand () {
17
- argParser.addOption ('out' ,
22
+ argParser.addOption (
23
+ _kOut,
18
24
abbr: 'o' ,
19
- help: 'Location to write the screenshot.' );
25
+ help: 'Location to write the screenshot.' ,
26
+ );
27
+ argParser.addOption (
28
+ _kSkia,
29
+ valueHelp: 'port' ,
30
+ help: 'Retrieve the last frame rendered by a Flutter app as a Skia picture\n '
31
+ 'using the specified diagnostic server port.\n '
32
+ 'To find the diagnostic server port number, use "flutter run --verbose"\n '
33
+ 'and look for "Diagnostic server listening on" in the output.'
34
+ );
35
+ argParser.addOption (
36
+ _kSkiaServe,
37
+ valueHelp: 'url' ,
38
+ help: 'Post the picture to a skiaserve debugger at this URL.' ,
39
+ );
20
40
}
21
41
22
42
@override
@@ -32,39 +52,116 @@ class ScreenshotCommand extends FlutterCommand {
32
52
33
53
@override
34
54
Future <int > verifyThenRunCommand () async {
35
- device = await findTargetDevice ();
36
- if (device == null )
37
- return 1 ;
55
+ if (argResults[_kSkia] != null ) {
56
+ if (argResults[_kOut] != null && argResults[_kSkiaServe] != null ) {
57
+ printError ('Cannot specify both --$_kOut and --$_kSkiaServe ' );
58
+ return 1 ;
59
+ }
60
+ } else {
61
+ if (argResults[_kSkiaServe] != null ) {
62
+ printError ('Must specify --$_kSkia with --$_kSkiaServe ' );
63
+ return 1 ;
64
+ }
65
+ device = await findTargetDevice ();
66
+ if (device == null ) {
67
+ printError ('Must specify --$_kSkia or have a connected device' );
68
+ return 1 ;
69
+ }
70
+ if (! device.supportsScreenshot && argResults[_kSkia] == null ) {
71
+ printError ('Screenshot not supported for ${device .name }.' );
72
+ return 1 ;
73
+ }
74
+ }
38
75
return super .verifyThenRunCommand ();
39
76
}
40
77
41
78
@override
42
79
Future <int > runCommand () async {
43
- if (! device.supportsScreenshot) {
44
- printError ('Screenshot not supported for ${device .name }.' );
45
- return 1 ;
46
- }
47
-
48
80
File outputFile;
81
+ if (argResults.wasParsed (_kOut))
82
+ outputFile = new File (argResults[_kOut]);
49
83
50
- if (argResults. wasParsed ( 'out' ) ) {
51
- outputFile = new File (argResults[ 'out' ] );
84
+ if (argResults[_kSkia] != null ) {
85
+ return runSkia (outputFile );
52
86
} else {
53
- outputFile = getUniqueFile ( Directory .current, 'flutter' , 'png' );
87
+ return runScreenshot (outputFile );
54
88
}
89
+ }
55
90
91
+ Future <int > runScreenshot (File outputFile) async {
92
+ outputFile ?? = getUniqueFile (Directory .current, 'flutter' , 'png' );
56
93
try {
57
- bool result = await device.takeScreenshot (outputFile);
58
-
59
- if (result) {
60
- int sizeKB = outputFile.lengthSync () ~ / 1000 ;
61
- printStatus ('Screenshot written to ${path .relative (outputFile .path )} (${sizeKB }kb).' );
94
+ if (await device.takeScreenshot (outputFile)) {
95
+ await showOutputFileInfo (outputFile);
62
96
return 0 ;
63
97
}
64
98
} catch (error) {
65
99
printError ('Error taking screenshot: $error ' );
66
100
}
67
-
68
101
return 1 ;
69
102
}
103
+
104
+ Future <int > runSkia (File outputFile) async {
105
+ Uri skpUri = new Uri (scheme: 'http' , host: '127.0.0.1' ,
106
+ port: int .parse (argResults[_kSkia]),
107
+ path: '/skp' );
108
+
109
+ void printErrorHelpText () {
110
+ printError ('' );
111
+ printError ('Be sure the --$_kSkia = option specifies the diagnostic server port, not the observatory port.' );
112
+ printError ('To find the diagnostic server port number, use "flutter run --verbose"' );
113
+ printError ('and look for "Diagnostic server listening on" in the output.' );
114
+ }
115
+
116
+ http.StreamedResponse skpResponse;
117
+ try {
118
+ skpResponse = await new http.Request ('GET' , skpUri).send ();
119
+ } on SocketException catch (e) {
120
+ printError ('Skia screenshot failed: $skpUri \n $e ' );
121
+ printErrorHelpText ();
122
+ return 1 ;
123
+ }
124
+ if (skpResponse.statusCode != HttpStatus .OK ) {
125
+ String error = await skpResponse.stream.toStringStream ().join ();
126
+ printError ('Error: $error ' );
127
+ printErrorHelpText ();
128
+ return 1 ;
129
+ }
130
+
131
+ if (argResults[_kSkiaServe] != null ) {
132
+ Uri skiaserveUri = Uri .parse (argResults[_kSkiaServe]);
133
+ Uri postUri = new Uri .http (skiaserveUri.authority, '/new' );
134
+ http.MultipartRequest postRequest = new http.MultipartRequest ('POST' , postUri);
135
+ postRequest.files.add (new http.MultipartFile (
136
+ 'file' , skpResponse.stream, skpResponse.contentLength));
137
+
138
+ http.StreamedResponse postResponse = await postRequest.send ();
139
+ if (postResponse.statusCode != HttpStatus .OK ) {
140
+ printError ('Failed to post Skia picture to skiaserve.' );
141
+ printErrorHelpText ();
142
+ return 1 ;
143
+ }
144
+ } else {
145
+ outputFile ?? = getUniqueFile (Directory .current, 'flutter' , 'skp' );
146
+ IOSink sink = outputFile.openWrite ();
147
+ await sink.addStream (skpResponse.stream);
148
+ await sink.close ();
149
+ await showOutputFileInfo (outputFile);
150
+ if (await outputFile.length () < 1000 ) {
151
+ String content = await outputFile.readAsString ();
152
+ if (content.startsWith ('{"jsonrpc":"2.0", "error"' )) {
153
+ printError ('' );
154
+ printError ('It appears the output file contains an error message, not valid skia output.' );
155
+ printErrorHelpText ();
156
+ return 1 ;
157
+ }
158
+ }
159
+ }
160
+ return 0 ;
161
+ }
162
+
163
+ Future <Null > showOutputFileInfo (File outputFile) async {
164
+ int sizeKB = (await outputFile.length ()) ~ / 1000 ;
165
+ printStatus ('Screenshot written to ${path .relative (outputFile .path )} (${sizeKB }kb).' );
166
+ }
70
167
}
0 commit comments