usage.dart 6.2 KB
Newer Older
1 2 3 4 5 6
// 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';

7
import 'package:meta/meta.dart';
8
import 'package:usage/usage_io.dart';
9 10

import 'base/context.dart';
11
import 'base/file_system.dart';
12
import 'base/os.dart';
13
import 'base/platform.dart';
14
import 'base/utils.dart';
15
import 'globals.dart';
16
import 'version.dart';
17

18
const String _kFlutterUA = 'UA-67589403-6';
19

20 21
Usage get flutterUsage => Usage.instance;

22
class Usage {
23 24
  /// Create a new Usage instance; [versionOverride] and [configDirOverride] are
  /// used for testing.
25
  Usage({ String settingsName = 'flutter', String versionOverride, String configDirOverride}) {
26
    final FlutterVersion flutterVersion = FlutterVersion.instance;
27
    final String version = versionOverride ?? flutterVersion.getVersionString(redactUnknownBranches: true);
28 29 30 31 32
    _analytics = new AnalyticsIO(_kFlutterUA, settingsName, version,
        // Analyzer doesn't recognize that [Directory] objects match up due to a
        // conditional import.
        // ignore: argument_type_not_assignable
        documentDirectory: configDirOverride != null ? fs.directory(configDirOverride) : null);
33

34
    // Report a more detailed OS version string than package:usage does by default.
35
    _analytics.setSessionValue('cd1', os.name);
36
    // Send the branch name as the "channel".
37
    _analytics.setSessionValue('cd2', flutterVersion.getBranchName(redactUnknownBranches: true));
38 39 40 41
    // Record the host as the application installer ID - the context that flutter_tools is running in.
    if (platform.environment.containsKey('FLUTTER_HOST')) {
      _analytics.setSessionValue('aiid', platform.environment['FLUTTER_HOST']);
    }
42
    _analytics.analyticsOpt = AnalyticsOpt.optOut;
43 44

    // Many CI systems don't do a full git checkout.
45 46 47 48
    if (version.endsWith('/unknown') || isRunningOnBot) {
      // If we think we're running on a CI system, suppress sending analytics.
      suppressAnalytics = true;
    }
49 50 51
  }

  /// Returns [Usage] active in the current app context.
52
  static Usage get instance => context[Usage];
53 54 55

  Analytics _analytics;

56
  bool _printedWelcome = false;
57
  bool _suppressAnalytics = false;
58

59 60 61 62
  bool get isFirstRun => _analytics.firstRun;

  bool get enabled => _analytics.enabled;

63 64 65 66 67 68 69
  bool get suppressAnalytics => _suppressAnalytics || _analytics.firstRun;

  /// Suppress analytics for this session.
  set suppressAnalytics(bool value) {
    _suppressAnalytics = value;
  }

70 71 72 73 74
  /// Enable or disable reporting analytics.
  set enabled(bool value) {
    _analytics.enabled = value;
  }

75 76 77 78
  /// A stable randomly generated UUID used to deduplicate multiple identical
  /// reports coming from the same computer.
  String get clientId => _analytics.clientId;

79 80 81 82 83 84 85
  void sendCommand(String command, { Map<String, String> parameters }) {
    if (suppressAnalytics)
      return;

    parameters ??= const <String, String>{};

    _analytics.sendScreenView(command, parameters: parameters);
86 87
  }

88 89 90 91 92 93 94 95
  void sendEvent(String category, String parameter,
      { Map<String, String> parameters }) {
    if (suppressAnalytics)
      return;

    parameters ??= const <String, String>{};

    _analytics.sendEvent(category, parameter, parameters: parameters);
96 97
  }

98
  void sendTiming(
99 100
    String category,
    String variableName,
101 102 103 104 105
    Duration duration, {
    String label,
    }) {
    if (!suppressAnalytics) {
      _analytics.sendTiming(
106 107
        variableName,
        duration.inMilliseconds,
108 109 110 111
        category: category,
        label: label,
      );
    }
112 113 114
  }

  void sendException(dynamic exception, StackTrace trace) {
115
    if (!suppressAnalytics)
116
      _analytics.sendException('${exception.runtimeType}\n${sanitizeStacktrace(trace)}');
117 118
  }

119 120
  /// Fires whenever analytics data is sent over the network.
  @visibleForTesting
121 122 123 124
  Stream<Map<String, dynamic>> get onSend => _analytics.onSend;

  /// Returns when the last analytics event has been sent, or after a fixed
  /// (short) delay, whichever is less.
125
  Future<Null> ensureAnalyticsSent() async {
126 127 128
    // TODO(devoncarew): This may delay tool exit and could cause some analytics
    // events to not be reported. Perhaps we could send the analytics pings
    // out-of-process from flutter_tools?
129
    await _analytics.waitForLastPing(timeout: const Duration(milliseconds: 250));
130
  }
131

132 133 134 135
  void printWelcome() {
    // This gets called if it's the first run by the selected command, if any,
    // and on exit, in case there was no command.
    if (_printedWelcome)
136
      return;
137
    _printedWelcome = true;
138 139 140

    printStatus('');
    printStatus('''
Seth Ladd's avatar
Seth Ladd committed
141 142 143
  ╔════════════════════════════════════════════════════════════════════════════╗
  ║                 Welcome to Flutter! - https://flutter.io                   ║
  ║                                                                            ║
144 145 146 147 148 149 150 151
  ║ The Flutter tool anonymously reports feature usage statistics and crash    ║
  ║ reports to Google in order to help Google contribute improvements to       ║
  ║ Flutter over time.                                                         ║
  ║                                                                            ║
  ║ Read about data we send with crash reports:                                ║
  ║ https://github.com/flutter/flutter/wiki/Flutter-CLI-crash-reporting        ║
  ║                                                                            ║
  ║ See Google's privacy policy:                                               
Seth Ladd's avatar
Seth Ladd committed
152 153
   https://www.google.com/intl/en/policies/privacy/                           ║
                                                                              
154 155
   Use "flutter config --no-analytics" to disable analytics and crash         
   reporting.                                                                 
Seth Ladd's avatar
Seth Ladd committed
156
  ╚════════════════════════════════════════════════════════════════════════════╝
157 158
  ''', emphasis: true);
  }
159
}