Add a devicelab task that generates sample app screenshots, etc (#10303)

diff --git a/dev/devicelab/bin/tasks/android_sample_catalog_generator.dart b/dev/devicelab/bin/tasks/android_sample_catalog_generator.dart
new file mode 100644
index 0000000..b9550b3
--- /dev/null
+++ b/dev/devicelab/bin/tasks/android_sample_catalog_generator.dart
@@ -0,0 +1,14 @@
+// Copyright 2017 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 'package:flutter_devicelab/framework/adb.dart';
+import 'package:flutter_devicelab/framework/framework.dart';
+import 'package:flutter_devicelab/tasks/sample_catalog_generator.dart';
+
+Future<Null> main() async {
+  deviceOperatingSystem = DeviceOperatingSystem.android;
+  await task(samplePageCatalogGenerator);
+}
diff --git a/dev/devicelab/bin/tasks/ios_sample_catalog_generator.dart b/dev/devicelab/bin/tasks/ios_sample_catalog_generator.dart
new file mode 100644
index 0000000..94f1464
--- /dev/null
+++ b/dev/devicelab/bin/tasks/ios_sample_catalog_generator.dart
@@ -0,0 +1,14 @@
+// Copyright 2017 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 'package:flutter_devicelab/framework/adb.dart';
+import 'package:flutter_devicelab/framework/framework.dart';
+import 'package:flutter_devicelab/tasks/sample_catalog_generator.dart';
+
+Future<Null> main() async {
+  deviceOperatingSystem = DeviceOperatingSystem.ios;
+  await task(samplePageCatalogGenerator);
+}
diff --git a/dev/devicelab/lib/tasks/sample_catalog_generator.dart b/dev/devicelab/lib/tasks/sample_catalog_generator.dart
new file mode 100644
index 0000000..d0ea57a
--- /dev/null
+++ b/dev/devicelab/lib/tasks/sample_catalog_generator.dart
@@ -0,0 +1,38 @@
+// Copyright 2017 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 '../framework/adb.dart';
+import '../framework/framework.dart';
+import '../framework/ios.dart';
+import '../framework/utils.dart';
+
+Future<TaskResult> samplePageCatalogGenerator() async {
+  final Device device = await devices.workingDevice;
+  await device.unlock();
+  final String deviceId = device.deviceId;
+
+  final Directory catalogDirectory = dir('${flutterDirectory.path}/examples/catalog');
+  await inDirectory(catalogDirectory, () async {
+    await flutter('packages', options: <String>['get']);
+
+    if (deviceOperatingSystem == DeviceOperatingSystem.ios) {
+      await prepareProvisioningCertificates(catalogDirectory.path);
+      // This causes an Xcode project to be created.
+      await flutter('build', options: <String>['ios', '--profile']);
+    }
+
+    await dart(<String>['bin/sample_page.dart']);
+
+    await flutter('drive', options: <String>[
+      'test_driver/screenshot.dart',
+      '-d',
+      deviceId,
+    ]);
+  });
+
+  return new TaskResult.success(null);
+}
diff --git a/dev/devicelab/manifest.yaml b/dev/devicelab/manifest.yaml
index d98fa2a..914f4fa 100644
--- a/dev/devicelab/manifest.yaml
+++ b/dev/devicelab/manifest.yaml
@@ -171,6 +171,13 @@
     stage: devicelab
     required_agent_capabilities: ["has-android-device"]
 
+  android_sample_catalog_generator:
+    description: >
+      Builds sample catalog markdown pages and Android screenshots
+    stage: devicelab
+    required_agent_capabilities: ["has-android-device"]
+    flaky: true
+
   # iOS on-device tests
 
   channels_integration_test_ios:
@@ -240,6 +247,13 @@
     stage: devicelab_ios
     required_agent_capabilities: ["has-ios-device"]
 
+  ios_sample_catalog_generator:
+    description: >
+      Builds sample catalog markdown pages and iOS screenshots
+    stage: devicelab_ios
+    required_agent_capabilities: ["has-ios-device"]
+    flaky: true
+
   # Tests running on Windows host
 
   channels_integration_test_win:
diff --git a/examples/catalog/bin/sample_page.dart b/examples/catalog/bin/sample_page.dart
index cc28273..f556892 100644
--- a/examples/catalog/bin/sample_page.dart
+++ b/examples/catalog/bin/sample_page.dart
@@ -135,6 +135,8 @@
       );
       commentValues[keyword.toLowerCase()] = value.trim();
     }
+    commentValues['name'] = sourceName;
+    commentValues['path'] = 'examples/catalog/${sourceFile.path}';
     commentValues['source'] = sourceCode.trim();
 
     return true;
@@ -177,14 +179,12 @@
     screenshotDriverTemplate,
     <String, String>{
       'paths': samples.map((SampleGenerator sample) {
-        return "'${outputFile(sample.sourceName + '.png').path}'";
+        return "'${outputFile('\${prefix}' + sample.sourceName + '.png').path}'";
       }).toList().join(',\n'),
     },
   );
 
-  final List<String> flutterDriveArgs = <String>['drive', 'test_driver/screenshot.dart'];
-  logMessage('Generating screenshots with: flutter ${flutterDriveArgs.join(" ")}');
-  Process.runSync('flutter', flutterDriveArgs);
+  // To generate the screenshots: flutter drive test_driver/screenshot.dart
 }
 
 void main(List<String> args) {
diff --git a/examples/catalog/bin/sample_page.md.template b/examples/catalog/bin/sample_page.md.template
index cc937f1..c3ccc78 100644
--- a/examples/catalog/bin/sample_page.md.template
+++ b/examples/catalog/bin/sample_page.md.template
@@ -1,5 +1,9 @@
-@(title)
-=============
+---
+catalog: @(name)
+title: "@(title)"
+
+permalink: /catalog/@(name)/
+---
 
 @(summary)
 
@@ -8,6 +12,7 @@
 See also:
 @(see also)
 
-```
+```dart
 @(source)
 ```
+The source code is based on [@(path)](https://github.com/flutter/flutter/blob/master/@(path)).
diff --git a/examples/catalog/bin/screenshot.dart.template b/examples/catalog/bin/screenshot.dart.template
index b513f85..c939db7 100644
--- a/examples/catalog/bin/screenshot.dart.template
+++ b/examples/catalog/bin/screenshot.dart.template
@@ -36,5 +36,6 @@
 
 void main() {
   enableFlutterDriverExtension();
+  WidgetsApp.debugAllowBannerOverride = false; // No "slow mode" banner.
   runApp(new SampleScreenshots());
 }
diff --git a/examples/catalog/bin/screenshot_test.dart.template b/examples/catalog/bin/screenshot_test.dart.template
index d25ceae..4eb55bd 100644
--- a/examples/catalog/bin/screenshot_test.dart.template
+++ b/examples/catalog/bin/screenshot_test.dart.template
@@ -7,7 +7,7 @@
 import 'package:test/test.dart';
 
 void main() {
-  group('sample screenshots', () async {
+  group('sample screenshots', () {
     FlutterDriver driver;
 
     setUpAll(() async {
@@ -19,6 +19,7 @@
     });
 
     test('take sample screenshots', () async {
+      String prefix = Platform.isMacOS ? 'ios_' : "";
       final List<String> paths = <String>[
         @(paths)
       ];