usage.dart 5.46 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/os.dart';
12
import 'base/utils.dart';
13
import 'globals.dart';
14
import 'version.dart';
15 16 17

// TODO(devoncarew): We'll want to find a way to send (sanitized) command parameters.

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] is used for testing.
  Usage({ String settingsName: 'flutter', String versionOverride }) {
25
    final String version = versionOverride ?? FlutterVersion.getVersionString(whitelistBranchName: true);
26
    _analytics = new AnalyticsIO(_kFlutterUA, settingsName, version);
27

28 29 30 31
    // Report a more detailed OS version string than package:usage does by
    // default as custom dimension 1 (configured in our analytics account).
    _analytics.setSessionValue('dimension1', os.name);

32 33 34
    bool runningOnCI = false;

    // Many CI systems don't do a full git checkout.
35
    if (version.endsWith('/unknown'))
36 37 38 39 40 41 42 43
      runningOnCI = true;

    // Check for common CI systems.
    if (isRunningOnBot)
      runningOnCI = true;

    // If we think we're running on a CI system, default to not sending analytics.
    _analytics.analyticsOpt = runningOnCI ? AnalyticsOpt.optIn : AnalyticsOpt.optOut;
44 45 46
  }

  /// Returns [Usage] active in the current app context.
47
  static Usage get instance => context.putIfAbsent(Usage, () => new Usage());
48 49 50

  Analytics _analytics;

51
  bool _printedWelcome = false;
52
  bool _suppressAnalytics = false;
53

54 55 56 57
  bool get isFirstRun => _analytics.firstRun;

  bool get enabled => _analytics.enabled;

58 59 60 61 62 63 64
  bool get suppressAnalytics => _suppressAnalytics || _analytics.firstRun;

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

65 66 67 68 69
  /// Enable or disable reporting analytics.
  set enabled(bool value) {
    _analytics.enabled = value;
  }

70 71 72 73
  /// A stable randomly generated UUID used to deduplicate multiple identical
  /// reports coming from the same computer.
  String get clientId => _analytics.clientId;

74
  void sendCommand(String command) {
75
    if (!suppressAnalytics)
76 77 78 79
      _analytics.sendScreenView(command);
  }

  void sendEvent(String category, String parameter) {
80
    if (!suppressAnalytics)
81 82 83
      _analytics.sendEvent(category, parameter);
  }

84 85 86 87 88 89 90 91 92 93 94 95 96 97
  void sendTiming(
    String category, 
    String variableName, 
    Duration duration, {
    String label,
    }) {
    if (!suppressAnalytics) {
      _analytics.sendTiming(
        variableName, 
        duration.inMilliseconds, 
        category: category,
        label: label,
      );
    }
98 99 100
  }

  void sendException(dynamic exception, StackTrace trace) {
101
    if (!suppressAnalytics)
102
      _analytics.sendException('${exception.runtimeType}\n${sanitizeStacktrace(trace)}');
103 104
  }

105 106
  /// Fires whenever analytics data is sent over the network.
  @visibleForTesting
107 108 109 110
  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.
111
  Future<Null> ensureAnalyticsSent() async {
112 113 114
    // 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?
115
    await _analytics.waitForLastPing(timeout: const Duration(milliseconds: 250));
116
  }
117

118 119 120 121
  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)
122
      return;
123
    _printedWelcome = true;
124 125 126

    printStatus('');
    printStatus('''
Seth Ladd's avatar
Seth Ladd committed
127 128 129
  ╔════════════════════════════════════════════════════════════════════════════╗
  ║                 Welcome to Flutter! - https://flutter.io                   ║
  ║                                                                            ║
130 131 132 133 134 135 136 137
  ║ 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
138 139
   https://www.google.com/intl/en/policies/privacy/                           ║
                                                                              
140 141
   Use "flutter config --no-analytics" to disable analytics and crash         
   reporting.                                                                 
Seth Ladd's avatar
Seth Ladd committed
142
  ╚════════════════════════════════════════════════════════════════════════════╝
143 144
  ''', emphasis: true);
  }
145
}