Unverified Commit a32f0bb7 authored by Elias Yishak's avatar Elias Yishak Committed by GitHub

Add analytics package + setTelemetry method attached (#124015)

The first of many PRs for transitioning to `package:unified_analytics`.
This PR is only focused on disabling and enabling telemetry via the
`flutter config --[no-]analytics` flag

Fixes: https://github.com/flutter/flutter/issues/121617

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#overview
[Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene
[test-exempt]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes
[Discord]: https://github.com/flutter/flutter/wiki/Chat

---------
Co-authored-by: 's avatarChristopher Fujino <fujino@google.com>
Co-authored-by: 's avatarChristopher Fujino <christopherfujino@gmail.com>
parent eca86e8c
...@@ -22,6 +22,7 @@ import 'src/context_runner.dart'; ...@@ -22,6 +22,7 @@ import 'src/context_runner.dart';
import 'src/doctor.dart'; import 'src/doctor.dart';
import 'src/globals.dart' as globals; import 'src/globals.dart' as globals;
import 'src/reporting/crash_reporting.dart'; import 'src/reporting/crash_reporting.dart';
import 'src/reporting/reporting.dart';
import 'src/runner/flutter_command.dart'; import 'src/runner/flutter_command.dart';
import 'src/runner/flutter_command_runner.dart'; import 'src/runner/flutter_command_runner.dart';
...@@ -61,6 +62,44 @@ Future<int> run( ...@@ -61,6 +62,44 @@ Future<int> run(
StackTrace? firstStackTrace; StackTrace? firstStackTrace;
return runZoned<Future<int>>(() async { return runZoned<Future<int>>(() async {
try { try {
// Ensure that the consent message has been displayed
if (globals.analytics.shouldShowMessage) {
globals.logger.printStatus(globals.analytics.getConsentMessage);
// Invoking this will onboard the flutter tool onto
// the package on the developer's machine and will
// allow for events to be sent to Google Analytics
// on subsequent runs of the flutter tool (ie. no events
// will be sent on the first run to allow developers to
// opt out of collection)
globals.analytics.clientShowedMessage();
}
// Disable analytics if user passes in the `--disable-telemetry` option
// `flutter --disable-telemetry`
//
// Same functionality as `flutter config --no-analytics` for disabling
// except with the `value` hard coded as false
if (args.contains('--disable-telemetry')) {
const bool value = false;
// The tool sends the analytics event *before* toggling the flag
// intentionally to be sure that opt-out events are sent correctly.
AnalyticsConfigEvent(enabled: value).send();
if (!value) {
// Normally, the tool waits for the analytics to all send before the
// tool exits, but only when analytics are enabled. When reporting that
// analytics have been disable, the wait must be done here instead.
await globals.flutterUsage.ensureAnalyticsSent();
}
globals.flutterUsage.enabled = value;
globals.printStatus('Analytics reporting disabled.');
// TODO(eliasyishak): Set the telemetry for the unified_analytics
// package as well, the above will be removed once we have
// fully transitioned to using the new package
await globals.analytics.setTelemetry(value);
}
await runner.run(args); await runner.run(args);
// Triggering [runZoned]'s error callback does not necessarily mean that // Triggering [runZoned]'s error callback does not necessarily mean that
......
...@@ -139,6 +139,11 @@ class ConfigCommand extends FlutterCommand { ...@@ -139,6 +139,11 @@ class ConfigCommand extends FlutterCommand {
} }
globals.flutterUsage.enabled = value; globals.flutterUsage.enabled = value;
globals.printStatus('Analytics reporting ${value ? 'enabled' : 'disabled'}.'); globals.printStatus('Analytics reporting ${value ? 'enabled' : 'disabled'}.');
// TODO(eliasyishak): Set the telemetry for the unified_analytics
// package as well, the above will be removed once we have
// fully transitioned to using the new package
await globals.analytics.setTelemetry(value);
} }
if (argResults?.wasParsed('android-sdk') ?? false) { if (argResults?.wasParsed('android-sdk') ?? false) {
......
...@@ -2,7 +2,9 @@ ...@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:intl/date_symbol_data_local.dart';
import 'package:process/process.dart'; import 'package:process/process.dart';
import 'package:unified_analytics/unified_analytics.dart';
import 'android/android_sdk.dart'; import 'android/android_sdk.dart';
import 'android/android_studio.dart'; import 'android/android_studio.dart';
...@@ -86,6 +88,22 @@ final BotDetector _defaultBotDetector = BotDetector( ...@@ -86,6 +88,22 @@ final BotDetector _defaultBotDetector = BotDetector(
); );
Future<bool> get isRunningOnBot => botDetector.isRunningOnBot; Future<bool> get isRunningOnBot => botDetector.isRunningOnBot;
// Analytics instance for package:unified_analytics for telemetry
// reporting for all Flutter and Dart related tooling
Analytics get analytics => context.get<Analytics>() ?? getDefaultAnalytics();
Analytics getDefaultAnalytics() {
initializeDateFormatting();
final Analytics defaultAnalytics = Analytics(
tool: DashTool.flutterTool,
flutterChannel: flutterVersion.channel,
flutterVersion: flutterVersion.frameworkVersion,
dartVersion: flutterVersion.dartSdkVersion,
);
return defaultAnalytics;
}
/// Currently active implementation of the file system. /// Currently active implementation of the file system.
/// ///
/// By default it uses local disk-based implementation. Override this in tests /// By default it uses local disk-based implementation. Override this in tests
......
...@@ -78,6 +78,9 @@ class FlutterCommandRunner extends CommandRunner<void> { ...@@ -78,6 +78,9 @@ class FlutterCommandRunner extends CommandRunner<void> {
argParser.addFlag('suppress-analytics', argParser.addFlag('suppress-analytics',
negatable: false, negatable: false,
help: 'Suppress analytics reporting when this command runs.'); help: 'Suppress analytics reporting when this command runs.');
argParser.addFlag('disable-telemetry',
negatable: false,
help: 'Disable telemetry reporting when this command runs.');
argParser.addOption('packages', argParser.addOption('packages',
hide: !verboseHelp, hide: !verboseHelp,
help: 'Path to your "package_config.json" file.'); help: 'Path to your "package_config.json" file.');
...@@ -185,6 +188,11 @@ class FlutterCommandRunner extends CommandRunner<void> { ...@@ -185,6 +188,11 @@ class FlutterCommandRunner extends CommandRunner<void> {
Future<void> runCommand(ArgResults topLevelResults) async { Future<void> runCommand(ArgResults topLevelResults) async {
final Map<Type, Object?> contextOverrides = <Type, Object?>{}; final Map<Type, Object?> contextOverrides = <Type, Object?>{};
// If the disable-telemetry flag has been passed, return out
if (topLevelResults.wasParsed('disable-telemetry')) {
return;
}
// Don't set wrapColumns unless the user said to: if it's set, then all // Don't set wrapColumns unless the user said to: if it's set, then all
// wrapping will occur at this width explicitly, and won't adapt if the // wrapping will occur at this width explicitly, and won't adapt if the
// terminal size changes during a run. // terminal size changes during a run.
......
...@@ -48,6 +48,7 @@ dependencies: ...@@ -48,6 +48,7 @@ dependencies:
http_multi_server: 3.2.1 http_multi_server: 3.2.1
convert: 3.1.1 convert: 3.1.1
async: 2.11.0 async: 2.11.0
unified_analytics: 1.0.1
# We depend on very specific internal implementation details of the # We depend on very specific internal implementation details of the
# 'test' package, which change between versions, so when upgrading # 'test' package, which change between versions, so when upgrading
...@@ -104,4 +105,4 @@ dartdoc: ...@@ -104,4 +105,4 @@ dartdoc:
# Exclude this package from the hosted API docs. # Exclude this package from the hosted API docs.
nodoc: true nodoc: true
# PUBSPEC CHECKSUM: 7ab7 # PUBSPEC CHECKSUM: 8913
...@@ -18,6 +18,8 @@ import 'package:flutter_tools/src/globals.dart' as globals; ...@@ -18,6 +18,8 @@ import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/reporting/crash_reporting.dart'; import 'package:flutter_tools/src/reporting/crash_reporting.dart';
import 'package:flutter_tools/src/reporting/reporting.dart'; import 'package:flutter_tools/src/reporting/reporting.dart';
import 'package:flutter_tools/src/runner/flutter_command.dart'; import 'package:flutter_tools/src/runner/flutter_command.dart';
import 'package:test/fake.dart';
import 'package:unified_analytics/unified_analytics.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
...@@ -313,6 +315,29 @@ void main() { ...@@ -313,6 +315,29 @@ void main() {
}); });
}); });
}); });
testUsingContext('runner disable telemetry with flag', () async {
io.setExitFunctionForTests((int exitCode) {});
expect(globals.analytics.telemetryEnabled, true);
expect(globals.analytics.shouldShowMessage, true);
await runner.run(
<String>['--disable-telemetry'],
() => <FlutterCommand>[],
// This flutterVersion disables crash reporting.
flutterVersion: '[user-branch]/',
shutdownHooks: ShutdownHooks(),
);
expect(globals.analytics.telemetryEnabled, false);
},
overrides: <Type, Generator>{
Analytics: () => FakeAnalytics(),
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
},
);
} }
class CrashingFlutterCommand extends FlutterCommand { class CrashingFlutterCommand extends FlutterCommand {
...@@ -451,3 +476,30 @@ class WaitingCrashReporter implements CrashReporter { ...@@ -451,3 +476,30 @@ class WaitingCrashReporter implements CrashReporter {
return _future; return _future;
} }
} }
/// A fake [Analytics] that will be used to test
/// the --disable-telemetry flag
class FakeAnalytics extends Fake implements Analytics {
bool _fakeTelemetryStatus = true;
bool _fakeShowMessage = true;
@override
String get getConsentMessage => 'message';
@override
bool get shouldShowMessage => _fakeShowMessage;
@override
void clientShowedMessage() {
_fakeShowMessage = false;
}
@override
Future<void> setTelemetry(bool reportingBool) {
_fakeTelemetryStatus = reportingBool;
return Future<void>.value();
}
@override
bool get telemetryEnabled => _fakeTelemetryStatus;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment