Clean up 'flutter run' output. (#6610)

Fixes #4166.
Fixes #5120.
Fixes #5399.
Fixes #6507.
diff --git a/packages/flutter_tools/lib/src/android/android_device.dart b/packages/flutter_tools/lib/src/android/android_device.dart
index 8a84e6a..ab108ae 100644
--- a/packages/flutter_tools/lib/src/android/android_device.dart
+++ b/packages/flutter_tools/lib/src/android/android_device.dart
@@ -269,14 +269,16 @@
     return true;
   }
 
-  Future<Null> _forwardPort(String service, int devicePort, int port) async {
+  Future<int> _forwardPort(String service, int devicePort, int port) async {
     try {
       // Set up port forwarding for observatory.
       port = await portForwarder.forward(devicePort, hostPort: port);
-      printStatus('$service listening on http://127.0.0.1:$port');
+      printTrace('$service listening on http://127.0.0.1:$port');
+      return port;
     } catch (e) {
       printError('Unable to forward port $port: $e');
     }
+    return null;
   }
 
   Future<LaunchResult> startBundle(AndroidApk apk, String bundlePath, {
@@ -363,17 +365,17 @@
           observatoryDevicePort = await observatoryDiscovery.nextPort().timeout(new Duration(seconds: 20));
         }
 
-        printTrace('observatory port = $observatoryDevicePort');
+        printTrace('observatory port on device: $observatoryDevicePort');
         int observatoryLocalPort = await options.findBestObservatoryPort();
         // TODO(devoncarew): Remember the forwarding information (so we can later remove the
         // port forwarding).
-        await _forwardPort(ProtocolDiscovery.kObservatoryService, observatoryDevicePort, observatoryLocalPort);
+        observatoryLocalPort = await _forwardPort(ProtocolDiscovery.kObservatoryService, observatoryDevicePort, observatoryLocalPort);
 
         int diagnosticLocalPort;
         if (diagnosticDevicePort != null) {
-          printTrace('diagnostic port = $diagnosticDevicePort');
+          printTrace('diagnostic port on device: $diagnosticDevicePort');
           diagnosticLocalPort = await options.findBestDiagnosticPort();
-          await _forwardPort(ProtocolDiscovery.kDiagnosticService, diagnosticDevicePort, diagnosticLocalPort);
+          diagnosticLocalPort = await _forwardPort(ProtocolDiscovery.kDiagnosticService, diagnosticDevicePort, diagnosticLocalPort);
         }
 
         return new LaunchResult.succeeded(
@@ -715,8 +717,6 @@
 
   final AndroidDevice device;
 
-  bool _lastWasFiltered = false;
-
   StreamController<String> _linesController;
   Process _process;
 
@@ -757,38 +757,44 @@
   }
 
   // 'W/ActivityManager: '
-  static final RegExp _logFormat = new RegExp(r'^[VDIWEF]\/[^:]+:\s+');
+  static final RegExp _logFormat = new RegExp(r'^[VDIWEF]\/.{8,}:\s');
 
   static final List<RegExp> _whitelistedTags = <RegExp>[
     new RegExp(r'^[VDIWEF]\/flutter[^:]*:\s+', caseSensitive: false),
     new RegExp(r'^[IE]\/DartVM[^:]*:\s+'),
     new RegExp(r'^[WEF]\/AndroidRuntime:\s+'),
-    new RegExp(r'^[WEF]\/ActivityManager:\s+'),
+    new RegExp(r'^[WEF]\/ActivityManager:\s+.*(\bflutter\b|\bdomokit\b|\bsky\b)'),
     new RegExp(r'^[WEF]\/System\.err:\s+'),
     new RegExp(r'^[F]\/[\S^:]+:\s+')
   ];
 
+  // we default to true in case none of the log lines match
+  bool _acceptedLastLine = true;
+
   void _onLine(String line) {
     if (_logFormat.hasMatch(line)) {
-      // Filter out some noisy ActivityManager notifications.
-      if (line.startsWith('W/ActivityManager: getRunningAppProcesses'))
-        return;
-
       // Filter on approved names and levels.
       for (RegExp regex in _whitelistedTags) {
         if (regex.hasMatch(line)) {
-          _lastWasFiltered = false;
+          _acceptedLastLine = true;
           _linesController.add(line);
           return;
         }
       }
-
-      _lastWasFiltered = true;
+      _acceptedLastLine = false;
+    } else if (line == '--------- beginning of system' ||
+               line == '--------- beginning of main' ) {
+      // hide the ugly adb logcat log boundaries at the start
+      _acceptedLastLine = false;
     } else {
-      // If it doesn't match the log pattern at all, pass it through.
-      if (!_lastWasFiltered)
+      // If it doesn't match the log pattern at all, then pass it through if we
+      // passed the last matching line through. It might be a multiline message.
+      if (_acceptedLastLine) {
         _linesController.add(line);
+        return;
+      }
     }
+    printTrace('skipped log line: $line');
   }
 
   void _stop() {
diff --git a/packages/flutter_tools/lib/src/commands/build_apk.dart b/packages/flutter_tools/lib/src/commands/build_apk.dart
index 6ac544b..6f0cec8 100644
--- a/packages/flutter_tools/lib/src/commands/build_apk.dart
+++ b/packages/flutter_tools/lib/src/commands/build_apk.dart
@@ -378,7 +378,7 @@
       artifactBuilder.directory, components.resources, buildMode
     );
 
-    int signResult = _signApk(builder, components, unalignedApk, keystore);
+    int signResult = _signApk(builder, components, unalignedApk, keystore, buildMode);
     if (signResult != 0)
       return signResult;
 
@@ -397,7 +397,11 @@
 }
 
 int _signApk(
-  _ApkBuilder builder, _ApkComponents components, File apk, ApkKeystoreInfo keystoreInfo
+  _ApkBuilder builder,
+  _ApkComponents components,
+  File apk,
+  ApkKeystoreInfo keystoreInfo,
+  BuildMode buildMode,
 ) {
   File keystore;
   String keystorePassword;
@@ -405,7 +409,12 @@
   String keyPassword;
 
   if (keystoreInfo == null) {
-    printStatus('Warning: signing the APK using the debug keystore.');
+    if (buildMode == BuildMode.release) {
+      printStatus('Warning! Signing the APK using the debug keystore.');
+      printStatus('You will need a real keystore to distribute your application.');
+    } else {
+      printTrace('Signing the APK using the debug keystore.');
+    }
     keystore = components.debugKeystore;
     keystorePassword = _kDebugKeystorePassword;
     keyAlias = _kDebugKeystoreKeyAlias;
@@ -415,7 +424,7 @@
     keystorePassword = keystoreInfo.password ?? '';
     keyAlias = keystoreInfo.keyAlias ?? '';
     if (keystorePassword.isEmpty || keyAlias.isEmpty) {
-      printError('Must provide a keystore password and a key alias.');
+      printError('You must provide a keystore password and a key alias.');
       return 1;
     }
     keyPassword = keystoreInfo.keyPassword ?? '';
@@ -586,7 +595,7 @@
 
   if (result == 0) {
     File apkFile = new File(outputFile);
-    printStatus('Built $outputFile (${getSizeAsMB(apkFile.lengthSync())}).');
+    printTrace('Built $outputFile (${getSizeAsMB(apkFile.lengthSync())}).');
 
     _writeBuildMetaEntry(
       path.dirname(outputFile),
diff --git a/packages/flutter_tools/lib/src/hot.dart b/packages/flutter_tools/lib/src/hot.dart
index 1d27d12..4e4f4a0 100644
--- a/packages/flutter_tools/lib/src/hot.dart
+++ b/packages/flutter_tools/lib/src/hot.dart
@@ -6,6 +6,7 @@
 import 'dart:convert';
 import 'dart:io';
 
+import 'package:meta/meta.dart';
 import 'package:path/path.dart' as path;
 import 'package:stack_trace/stack_trace.dart';
 
@@ -135,6 +136,7 @@
   String _mainPath;
   String _projectRootPath;
   Set<String> _startupDependencies;
+  int _observatoryPort;
   final AssetBundle bundle = new AssetBundle();
   final bool benchmarkMode;
   final Map<String, int> benchmarkData = new Map<String, int>();
@@ -217,7 +219,7 @@
 
     await startEchoingDeviceLog();
 
-    printStatus('Launching loader on ${device.name}...');
+    printTrace('Launching loader on ${device.name}...');
 
     // Start the loader.
     Future<LaunchResult> futureResult = device.startApp(
@@ -246,13 +248,14 @@
       return 2;
     }
 
-    await connectToServiceProtocol(result.observatoryPort);
+    _observatoryPort = result.observatoryPort;
+    await connectToServiceProtocol(_observatoryPort);
 
     try {
       Uri baseUri = await _initDevFS();
       if (connectionInfoCompleter != null) {
         connectionInfoCompleter.complete(
-          new DebugConnectionInfo(result.observatoryPort, baseUri: baseUri.toString())
+          new DebugConnectionInfo(_observatoryPort, baseUri: baseUri.toString())
         );
       }
     } catch (error) {
@@ -273,13 +276,13 @@
     }
 
     await vmService.vm.refreshViews();
-    printStatus('Connected to ${vmService.vm.mainView}.');
+    printTrace('Connected to ${vmService.vm.mainView}.');
 
     printStatus('Running ${getDisplayPath(_mainPath)} on ${device.name}...');
     _loaderShowMessage('Launching...');
     await _launchFromDevFS(_package, _mainPath);
 
-    printStatus('Application running.');
+    printTrace('Application running.');
 
     setupTerminal();
 
@@ -361,10 +364,7 @@
     devFSStatus.stop(showElapsedTime: true);
     // Clear the minimal set after the first sync.
     _startupDependencies = null;
-    if (progressReporter != null)
-      printStatus('Synced ${getSizeAsMB(_devFS.bytes)}.');
-    else
-      printTrace('Synced ${getSizeAsMB(_devFS.bytes)}.');
+    printTrace('Synced ${getSizeAsMB(_devFS.bytes)}.');
     return true;
   }
 
@@ -537,10 +537,16 @@
   }
 
   @override
-  void printHelp() {
-    printStatus('Type "h" or F1 for this help message; type "q", F10, or ctrl-c to quit.', emphasis: true);
-    printStatus('Type "r" or F5 to perform a hot reload of the app, and "R" to restart the app.', emphasis: true);
-    printStatus('Type "w" to print the widget hierarchy of the app, and "t" for the render tree.', emphasis: true);
+  void printHelp({ @required bool details }) {
+    printStatus('To hot reload your app on the fly, press "r" or F5. To restart the app entirely, press "R".', emphasis: true);
+    printStatus('The Observatory debugger and profiler is available at: http://127.0.0.1:$_observatoryPort/');
+    if (details) {
+      printStatus('To dump the widget hierarchy of the app (debugDumpApp), press "w".');
+      printStatus('To dump the rendering tree of the app (debugDumpRenderTree), press "r".');
+      printStatus('To repeat this help message, press "h" or F1. To quit, press "q", F10, or Ctrl-C.');
+    } else {
+      printStatus('For a more detailed help message, press "h" or F1. To quit, press "q", F10, or Ctrl-C.');
+    }
   }
 
   @override
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart
index a9524b2..f8d9fb6 100644
--- a/packages/flutter_tools/lib/src/resident_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -5,6 +5,7 @@
 import 'dart:async';
 import 'dart:io';
 
+import 'package:meta/meta.dart';
 import 'package:path/path.dart' as path;
 
 import 'base/logger.dart';
@@ -126,7 +127,7 @@
 
     if (lower == 'h' || lower == '?' || character == AnsiTerminal.KEY_F1) {
       // F1, help
-      printHelp();
+      printHelp(details: true);
       return true;
     } else if (lower == 'w') {
       await _debugDumpApp();
@@ -165,7 +166,7 @@
   void setupTerminal() {
     if (usesTerminalUI) {
       if (!logger.quiet)
-        printHelp();
+        printHelp(details: false);
 
       terminal.singleCharMode = true;
       terminal.onCharInput.listen((String code) {
@@ -198,7 +199,7 @@
   /// Called right before we exit.
   Future<Null> cleanupAtFinish();
   /// Called to print help to the terminal.
-  void printHelp();
+  void printHelp({ @required bool details });
   /// Called when the runner should handle a terminal command.
   Future<Null> handleTerminalCommand(String code);
 }
diff --git a/packages/flutter_tools/lib/src/run.dart b/packages/flutter_tools/lib/src/run.dart
index 2fd0ea5..216253f 100644
--- a/packages/flutter_tools/lib/src/run.dart
+++ b/packages/flutter_tools/lib/src/run.dart
@@ -5,6 +5,7 @@
 import 'dart:async';
 import 'dart:io';
 
+import 'package:meta/meta.dart';
 import 'package:stack_trace/stack_trace.dart';
 
 import 'application_package.dart';
@@ -197,13 +198,13 @@
       }
     }
 
-    printStatus('Application running.');
+    printTrace('Application running.');
     if (debuggingOptions.buildMode == BuildMode.release)
       return 0;
 
     if (vmService != null) {
       await vmService.vm.refreshViews();
-      printStatus('Connected to ${vmService.vm.mainView}\.');
+      printTrace('Connected to ${vmService.vm.mainView}\.');
     }
 
     if (vmService != null && traceStartup) {
@@ -261,11 +262,18 @@
   }
 
   @override
-  void printHelp() {
-    final bool showRestartText = !prebuiltMode && device.supportsRestart;
-    String restartText = showRestartText ? ', "r" or F5 to restart the app,' : '';
-    printStatus('Type "h" or F1 for help$restartText and "q", F10, or ctrl-c to quit.');
-    printStatus('Type "w" to print the widget hierarchy of the app, and "t" for the render tree.');
+  void printHelp({ @required bool details }) {
+    if (!prebuiltMode && device.supportsRestart)
+      printStatus('To restart the app, press "r" or F5.');
+    if (_result.hasObservatory)
+      printStatus('The Observatory debugger and profiler is available at: http://127.0.0.1:${_result.observatoryPort}/');
+    if (details) {
+      printStatus('To dump the widget hierarchy of the app (debugDumpApp), press "w".');
+      printStatus('To dump the rendering tree of the app (debugDumpRenderTree), press "r".');
+      printStatus('To repeat this help message, press "h" or F1. To quit, press "q", F10, or Ctrl-C.');
+    } else {
+      printStatus('For a more detailed help message, press "h" or F1. To quit, press "q", F10, or Ctrl-C.');
+    }
   }
 }