add google analytics to flutter_tools (#3523)

* add google analytics

* send in the run target type

* track device type targets

* use the real GA code

* review comments

* rev to usage 2.0

* rev to 2.2.0 of usage; add tests

* review comments
diff --git a/packages/flutter_tools/lib/src/commands/config.dart b/packages/flutter_tools/lib/src/commands/config.dart
new file mode 100644
index 0000000..b789955
--- /dev/null
+++ b/packages/flutter_tools/lib/src/commands/config.dart
@@ -0,0 +1,50 @@
+// 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 '../globals.dart';
+import '../runner/flutter_command.dart';
+
+class ConfigCommand extends FlutterCommand {
+  ConfigCommand() {
+    String usageStatus = flutterUsage.enabled ? 'enabled' : 'disabled';
+
+    argParser.addFlag('analytics',
+      negatable: true,
+      help: 'Enable or disable reporting anonymously tool usage statistics and crash reports.\n(currently $usageStatus)');
+  }
+
+  @override
+  final String name = 'config';
+
+  @override
+  final String description =
+    'Configure Flutter settings.\n\n'
+    'The Flutter tool anonymously reports feature usage statistics and basic crash reports to help improve\n'
+    'Flutter tools over time. See Google\'s privacy policy: www.google.com/intl/en/policies/privacy';
+
+  @override
+  final List<String> aliases = <String>['configure'];
+
+  @override
+  bool get requiresProjectRoot => false;
+
+  /// Return `null` to disable tracking of the `config` command.
+  @override
+  String get usagePath => null;
+
+  @override
+  Future<int> runInProject() async {
+    if (argResults.wasParsed('analytics')) {
+      bool value = argResults['analytics'];
+      flutterUsage.enabled = value;
+      printStatus('Analytics reporting ${value ? 'enabled' : 'disabled'}.');
+    } else {
+      printStatus(usage);
+    }
+
+    return 0;
+  }
+}
diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart
index 6a2269a..122294c 100644
--- a/packages/flutter_tools/lib/src/commands/create.dart
+++ b/packages/flutter_tools/lib/src/commands/create.dart
@@ -5,7 +5,6 @@
 import 'dart:async';
 import 'dart:io';
 
-import 'package:args/command_runner.dart';
 import 'package:path/path.dart' as path;
 
 import '../android/android.dart' as android;
@@ -14,19 +13,10 @@
 import '../cache.dart';
 import '../dart/pub.dart';
 import '../globals.dart';
+import '../runner/flutter_command.dart';
 import '../template.dart';
 
-class CreateCommand extends Command {
-  @override
-  final String name = 'create';
-
-  @override
-  final String description = 'Create a new Flutter project.\n\n'
-    'If run on a project that already exists, this will repair the project, recreating any files that are missing.';
-
-  @override
-  final List<String> aliases = <String>['init'];
-
+class CreateCommand extends FlutterCommand {
   CreateCommand() {
     argParser.addFlag('pub',
       defaultsTo: true,
@@ -46,10 +36,23 @@
   }
 
   @override
+  final String name = 'create';
+
+  @override
+  final String description = 'Create a new Flutter project.\n\n'
+    'If run on a project that already exists, this will repair the project, recreating any files that are missing.';
+
+  @override
+  final List<String> aliases = <String>['init'];
+
+  @override
+  bool get requiresProjectRoot => false;
+
+  @override
   String get invocation => "${runner.executableName} $name <output directory>";
 
   @override
-  Future<int> run() async {
+  Future<int> runInProject() async {
     if (argResults.rest.isEmpty) {
       printStatus('No option specified for the output directory.');
       printStatus(usage);
diff --git a/packages/flutter_tools/lib/src/commands/precache.dart b/packages/flutter_tools/lib/src/commands/precache.dart
index 6b950c2..192b055 100644
--- a/packages/flutter_tools/lib/src/commands/precache.dart
+++ b/packages/flutter_tools/lib/src/commands/precache.dart
@@ -4,11 +4,10 @@
 
 import 'dart:async';
 
-import 'package:args/command_runner.dart';
-
 import '../globals.dart';
+import '../runner/flutter_command.dart';
 
-class PrecacheCommand extends Command {
+class PrecacheCommand extends FlutterCommand {
   @override
   final String name = 'precache';
 
@@ -16,7 +15,10 @@
   final String description = 'Populates the Flutter tool\'s cache of binary artifacts.';
 
   @override
-  Future<int> run() async {
+  bool get requiresProjectRoot => false;
+
+  @override
+  Future<int> runInProject() async {
     if (cache.isUpToDate())
       printStatus('Already up-to-date.');
     else
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart
index c45110b..7911eb8 100644
--- a/packages/flutter_tools/lib/src/commands/run.dart
+++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -83,6 +83,17 @@
   bool get requiresDevice => true;
 
   @override
+  String get usagePath {
+    Device device = deviceForCommand;
+
+    if (device == null)
+      return name;
+
+    // Return 'run/ios'.
+    return '$name/${getNameForTargetPlatform(device.platform)}';
+  }
+
+  @override
   Future<int> runInProject() async {
     bool clearLogs = argResults['clear-logs'];
 
diff --git a/packages/flutter_tools/lib/src/commands/skia.dart b/packages/flutter_tools/lib/src/commands/skia.dart
index 3e5f0f4..1f1bb8c 100644
--- a/packages/flutter_tools/lib/src/commands/skia.dart
+++ b/packages/flutter_tools/lib/src/commands/skia.dart
@@ -12,12 +12,6 @@
 import '../runner/flutter_command.dart';
 
 class SkiaCommand extends FlutterCommand {
-  @override
-  final String name = 'skia';
-
-  @override
-  final String description = 'Retrieve the last frame rendered by a Flutter app as a Skia picture.';
-
   SkiaCommand() {
     argParser.addOption('output-file', help: 'Write the Skia picture file to this path.');
     argParser.addOption('skiaserve', help: 'Post the picture to a skiaserve debugger at this URL.');
@@ -27,6 +21,12 @@
   }
 
   @override
+  final String name = 'skia';
+
+  @override
+  final String description = 'Retrieve the last frame rendered by a Flutter app as a Skia picture.';
+
+  @override
   Future<int> runInProject() async {
     File outputFile;
     Uri skiaserveUri;