[flutter_tool] Send analytics command before the command runs (#36490)

diff --git a/packages/flutter_tools/lib/src/commands/logs.dart b/packages/flutter_tools/lib/src/commands/logs.dart
index e3f8d4b..7eaf9cf 100644
--- a/packages/flutter_tools/lib/src/commands/logs.dart
+++ b/packages/flutter_tools/lib/src/commands/logs.dart
@@ -32,11 +32,12 @@
   Device device;
 
   @override
-  Future<FlutterCommandResult> verifyThenRunCommand() async {
+  Future<FlutterCommandResult> verifyThenRunCommand(String commandPath) async {
     device = await findTargetDevice();
-    if (device == null)
+    if (device == null) {
       throwToolExit(null);
-    return super.verifyThenRunCommand();
+    }
+    return super.verifyThenRunCommand(commandPath);
   }
 
   @override
diff --git a/packages/flutter_tools/lib/src/commands/screenshot.dart b/packages/flutter_tools/lib/src/commands/screenshot.dart
index 63e454b..e98f6ed 100644
--- a/packages/flutter_tools/lib/src/commands/screenshot.dart
+++ b/packages/flutter_tools/lib/src/commands/screenshot.dart
@@ -64,15 +64,18 @@
   Device device;
 
   @override
-  Future<FlutterCommandResult> verifyThenRunCommand() async {
+  Future<FlutterCommandResult> verifyThenRunCommand(String commandPath) async {
     device = await findTargetDevice();
-    if (device == null)
+    if (device == null) {
       throwToolExit('Must have a connected device');
-    if (argResults[_kType] == _kDeviceType && !device.supportsScreenshot)
+    }
+    if (argResults[_kType] == _kDeviceType && !device.supportsScreenshot) {
       throwToolExit('Screenshot not supported for ${device.name}.');
-    if (argResults[_kType] != _kDeviceType && argResults[_kObservatoryUri] == null)
+    }
+    if (argResults[_kType] != _kDeviceType && argResults[_kObservatoryUri] == null) {
       throwToolExit('Observatory URI must be specified for screenshot type ${argResults[_kType]}');
-    return super.verifyThenRunCommand();
+    }
+    return super.verifyThenRunCommand(commandPath);
   }
 
   @override
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart
index 04c5861..e9d9211 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -379,9 +379,10 @@
       body: () async {
         if (flutterUsage.isFirstRun)
           flutterUsage.printWelcome();
+        final String commandPath = await usagePath;
         FlutterCommandResult commandResult;
         try {
-          commandResult = await verifyThenRunCommand();
+          commandResult = await verifyThenRunCommand(commandPath);
         } on ToolExit {
           commandResult = const FlutterCommandResult(ExitStatus.fail);
           rethrow;
@@ -389,8 +390,7 @@
           final DateTime endTime = systemClock.now();
           printTrace(userMessages.flutterElapsedTime(name, getElapsedAsMilliseconds(endTime.difference(startTime))));
           printTrace('"flutter $name" took ${getElapsedAsMilliseconds(endTime.difference(startTime))}.');
-
-          await _sendUsage(commandResult, startTime, endTime);
+          _sendPostUsage(commandPath, commandResult, startTime, endTime);
         }
       },
     );
@@ -400,33 +400,28 @@
   ///
   /// For example, the command path (e.g. `build/apk`) and the result,
   /// as well as the time spent running it.
-  Future<void> _sendUsage(FlutterCommandResult commandResult, DateTime startTime, DateTime endTime) async {
-    final String commandPath = await usagePath;
-
+  void _sendPostUsage(String commandPath, FlutterCommandResult commandResult,
+                      DateTime startTime, DateTime endTime) {
     if (commandPath == null) {
       return;
     }
 
-    // Send screen.
-    final Map<String, String> currentUsageValues = await usageValues;
-    final Map<String, String> additionalUsageValues = <String, String>{
-      ...?currentUsageValues,
-    };
+    // Send command result.
+    String result = 'unspecified';
     if (commandResult != null) {
       switch (commandResult.exitStatus) {
         case ExitStatus.success:
-          additionalUsageValues[kCommandResult] = 'success';
+          result = 'success';
           break;
         case ExitStatus.warning:
-          additionalUsageValues[kCommandResult] = 'warning';
+          result = 'warning';
           break;
         case ExitStatus.fail:
-          additionalUsageValues[kCommandResult] = 'fail';
+          result = 'fail';
           break;
       }
     }
-    additionalUsageValues[kCommandHasTerminal] = io.stdout.hasTerminal ? 'true' : 'false';
-    flutterUsage.sendCommand(commandPath, parameters: additionalUsageValues);
+    flutterUsage.sendEvent(commandPath, result);
 
     // Send timing.
     final List<String> labels = <String>[
@@ -459,7 +454,7 @@
   /// then call this method to execute the command
   /// rather than calling [runCommand] directly.
   @mustCallSuper
-  Future<FlutterCommandResult> verifyThenRunCommand() async {
+  Future<FlutterCommandResult> verifyThenRunCommand(String commandPath) async {
     await validateCommand();
 
     // Populate the cache. We call this before pub get below so that the sky_engine
@@ -476,6 +471,15 @@
 
     setupApplicationPackages();
 
+    if (commandPath != null) {
+      final Map<String, String> additionalUsageValues = <String,String>{
+        ...?await usageValues,
+      };
+      additionalUsageValues[kCommandHasTerminal] =
+          io.stdout.hasTerminal ? 'true' : 'false';
+      flutterUsage.sendCommand(commandPath, parameters: additionalUsageValues);
+    }
+
     return await runCommand();
   }
 
diff --git a/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart b/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart
index ac9fc36..c781e3c 100644
--- a/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart
+++ b/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart
@@ -60,11 +60,8 @@
       );
       await flutterCommand.run();
 
-      expect(
-        verify(usage.sendCommand(captureAny,
-                parameters: captureAnyNamed('parameters'))).captured[1]['cd26'],
-        equals('success'),
-      );
+      verify(usage.sendCommand(captureAny, parameters: captureAnyNamed('parameters')));
+      verify(usage.sendEvent(captureAny, 'success'));
     },
     overrides: <Type, Generator>{
       SystemClock: () => clock,
@@ -82,11 +79,8 @@
       );
       await flutterCommand.run();
 
-      expect(
-        verify(usage.sendCommand(captureAny,
-                parameters: captureAnyNamed('parameters'))).captured[1]['cd26'],
-        equals('warning'),
-      );
+      verify(usage.sendCommand(captureAny, parameters: captureAnyNamed('parameters')));
+      verify(usage.sendEvent(captureAny, 'warning'));
     },
     overrides: <Type, Generator>{
       SystemClock: () => clock,
@@ -106,11 +100,8 @@
       try {
         await flutterCommand.run();
       } on ToolExit {
-        expect(
-          verify(usage.sendCommand(captureAny,
-                  parameters: captureAnyNamed('parameters'))).captured[1]['cd26'],
-          equals('fail'),
-        );
+        verify(usage.sendCommand(captureAny, parameters: captureAnyNamed('parameters')));
+        verify(usage.sendEvent(captureAny, 'fail'));
       }
     },
     overrides: <Type, Generator>{
@@ -133,11 +124,8 @@
         await flutterCommand.run();
         fail('Mock should make this fail');
       } on ToolExit {
-        expect(
-          verify(usage.sendCommand(captureAny,
-                  parameters: captureAnyNamed('parameters'))).captured[1]['cd26'],
-          equals('fail'),
-        );
+        verify(usage.sendCommand(captureAny, parameters: captureAnyNamed('parameters')));
+        verify(usage.sendEvent(captureAny, 'fail'));
       }
     },
     overrides: <Type, Generator>{