Skip to content

Commit 590d261

Browse files
authored
add google analytics (#578)
* add google analytics * review comments * review comments
1 parent adc142a commit 590d261

21 files changed

+540
-12
lines changed

src/io/flutter/FlutterBundle.properties

+8
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,11 @@ flutter.run.bazel.noLaunchingScript=no launching script specified
6363
flutter.run.bazel.launchingScriptNotFound=launching script not found: {0}
6464
flutter.run.bazel.noTargetSet=no Bazel target set
6565
flutter.run.bazel.noWorkingDirectorySet=no working directory set
66+
67+
flutter.analytics.notification.title=Welcome to Flutter!
68+
flutter.analytics.notification.content=The Flutter plugin anonymously reports feature usage statistics and crash reports to Google \
69+
in order to help Google contribute improvements to Flutter over time. See Google's privacy policy: \
70+
<a href="url">www.google.com/policies/privacy</a>.<br><br>Send anonymous usage statistics?
71+
flutter.analytics.notification.accept=Sounds good!
72+
flutter.analytics.notification.decline=No thanks
73+
flutter.analytics.privacyUrl=http://www.google.com/policies/privacy/

src/io/flutter/FlutterErrorReportSubmitter.java

+2
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ public void submitAsync(@NotNull IdeaLoggingEvent[] events,
112112
builder.append(event.getThrowableText().trim()).append("\n");
113113
builder.append("```\n");
114114
builder.append("\n");
115+
116+
FlutterInitializer.getAnalytics().sendException(event.getThrowable(), false);
115117
}
116118
}
117119

src/io/flutter/FlutterInitializer.java

+107
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,122 @@
55
*/
66
package io.flutter;
77

8+
import com.intellij.ide.BrowserUtil;
9+
import com.intellij.ide.plugins.IdeaPluginDescriptor;
10+
import com.intellij.ide.plugins.PluginManager;
11+
import com.intellij.ide.util.PropertiesComponent;
12+
import com.intellij.notification.Notification;
13+
import com.intellij.notification.NotificationType;
14+
import com.intellij.notification.Notifications;
15+
import com.intellij.openapi.actionSystem.AnAction;
16+
import com.intellij.openapi.actionSystem.AnActionEvent;
17+
import com.intellij.openapi.extensions.PluginId;
818
import com.intellij.openapi.project.Project;
919
import com.intellij.openapi.startup.StartupActivity;
20+
import io.flutter.analytics.Analytics;
1021
import io.flutter.run.daemon.FlutterDaemonService;
1122
import org.jetbrains.annotations.NotNull;
1223

24+
import javax.swing.event.HyperlinkEvent;
25+
import java.util.UUID;
26+
1327
public class FlutterInitializer implements StartupActivity {
28+
private static final String analyticsClientIdKey = "io.flutter.analytics.clientId";
29+
private static final String analyticsOptOutKey = "io.flutter.analytics.optOut";
30+
private static final String analyticsToastShown = "io.flutter.analytics.toastShown";
31+
32+
private static Analytics analytics;
33+
34+
@NotNull
35+
public static Analytics getAnalytics() {
36+
if (analytics == null) {
37+
final PropertiesComponent properties = PropertiesComponent.getInstance();
38+
39+
String clientId = properties.getValue(analyticsClientIdKey);
40+
if (clientId == null) {
41+
clientId = UUID.randomUUID().toString();
42+
properties.setValue(analyticsClientIdKey, clientId);
43+
}
44+
45+
final IdeaPluginDescriptor descriptor = PluginManager.getPlugin(PluginId.getId("io.flutter"));
46+
assert descriptor != null;
47+
analytics = new Analytics(clientId, descriptor.getVersion());
48+
49+
// Set up reporting prefs.
50+
analytics.setCanSend(getCanReportAnalytics());
51+
52+
// Send initial loading hit.
53+
analytics.sendScreenView("main");
54+
}
55+
56+
return analytics;
57+
}
58+
59+
public static void setCanReportAnalaytics(boolean canReportAnalaytics) {
60+
if (getCanReportAnalytics() != canReportAnalaytics) {
61+
final boolean wasReporting = getCanReportAnalytics();
62+
63+
final PropertiesComponent properties = PropertiesComponent.getInstance();
64+
properties.setValue(analyticsOptOutKey, !canReportAnalaytics);
65+
if (analytics != null) {
66+
analytics.setCanSend(getCanReportAnalytics());
67+
}
68+
69+
if (!wasReporting && canReportAnalaytics) {
70+
getAnalytics().sendScreenView("main");
71+
}
72+
}
73+
}
74+
75+
public static boolean getCanReportAnalytics() {
76+
final PropertiesComponent properties = PropertiesComponent.getInstance();
77+
return !properties.getBoolean(analyticsOptOutKey, false);
78+
}
79+
80+
public static void sendActionEvent(@NotNull AnAction action) {
81+
getAnalytics().sendEvent("intellij", action.getClass().getSimpleName());
82+
}
1483

1584
@Override
1685
public void runActivity(@NotNull Project project) {
86+
// Initialize the daemon service (this starts a device watcher).
1787
FlutterDaemonService.getInstance(project);
88+
89+
// Initialize analytics.
90+
final PropertiesComponent properties = PropertiesComponent.getInstance();
91+
if (!properties.getBoolean(analyticsToastShown)) {
92+
properties.setValue(analyticsToastShown, true);
93+
94+
final Notification notification = new Notification(
95+
FlutterErrors.FLUTTER_NOTIFICATION_GOUP_ID,
96+
FlutterBundle.message("flutter.analytics.notification.title"),
97+
FlutterBundle.message("flutter.analytics.notification.content"),
98+
NotificationType.INFORMATION,
99+
(notification1, event) -> {
100+
if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
101+
if ("url".equals(event.getDescription())) {
102+
BrowserUtil.browse("https://www.google.com/policies/privacy/");
103+
}
104+
}
105+
});
106+
notification.addAction(new AnAction(FlutterBundle.message("flutter.analytics.notification.accept")) {
107+
@Override
108+
public void actionPerformed(AnActionEvent event) {
109+
notification.expire();
110+
getAnalytics();
111+
}
112+
});
113+
notification.addAction(new AnAction(FlutterBundle.message("flutter.analytics.notification.decline")) {
114+
@Override
115+
public void actionPerformed(AnActionEvent event) {
116+
notification.expire();
117+
setCanReportAnalaytics(false);
118+
}
119+
});
120+
Notifications.Bus.notify(notification);
121+
}
122+
else {
123+
getAnalytics();
124+
}
18125
}
19126
}

src/io/flutter/actions/FlutterAppAction.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818
import javax.swing.*;
1919

2020
abstract public class FlutterAppAction extends DumbAwareAction {
21-
22-
2321
private static final Logger LOG = Logger.getInstance(FlutterAppAction.class);
2422

2523
private final ObservatoryConnector myConnector;
@@ -34,7 +32,12 @@ public void stateChanged(FlutterApp.State newState) {
3432
};
3533
private boolean myIsListening = false;
3634

37-
public FlutterAppAction(ObservatoryConnector connector, String text, String description, Icon icon, Computable<Boolean> isApplicable, @NotNull String actionId) {
35+
public FlutterAppAction(ObservatoryConnector connector,
36+
String text,
37+
String description,
38+
Icon icon,
39+
Computable<Boolean> isApplicable,
40+
@NotNull String actionId) {
3841
super(text, description, icon);
3942
myConnector = connector;
4043
myIsApplicable = isApplicable;

src/io/flutter/actions/FlutterGettingStarted.java

+3
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@
88
import com.intellij.ide.browsers.BrowserLauncher;
99
import com.intellij.openapi.actionSystem.AnActionEvent;
1010
import com.intellij.openapi.project.DumbAwareAction;
11+
import io.flutter.FlutterInitializer;
1112
import org.jetbrains.annotations.NotNull;
1213

1314
public class FlutterGettingStarted extends DumbAwareAction {
1415
@Override
1516
public void actionPerformed(@NotNull final AnActionEvent e) {
17+
FlutterInitializer.sendActionEvent(this);
18+
1619
final String url = "https://flutter.io/intellij-ide/";
1720
BrowserLauncher.getInstance().browse(url, null);
1821
}

src/io/flutter/actions/FlutterPackagesGetAction.java

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import com.intellij.openapi.vfs.VirtualFile;
1414
import io.flutter.FlutterBundle;
1515
import io.flutter.FlutterErrors;
16+
import io.flutter.FlutterInitializer;
1617
import io.flutter.sdk.FlutterSdk;
1718
import org.jetbrains.annotations.NotNull;
1819

@@ -22,6 +23,8 @@ public class FlutterPackagesGetAction extends FlutterSdkAction {
2223

2324
@Override
2425
public void perform(@NotNull FlutterSdk sdk, @NotNull Project project, AnActionEvent event) throws ExecutionException {
26+
FlutterInitializer.sendActionEvent(this);
27+
2528
final Pair<Module, VirtualFile> pair = getModuleAndPubspecYamlFile(project, event);
2629
if (pair != null) {
2730
sdk.run(COMMAND, pair.first, pair.second.getParent(), null);

src/io/flutter/actions/FlutterPackagesUpgradeAction.java

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import com.intellij.openapi.vfs.VirtualFile;
1515
import io.flutter.FlutterBundle;
1616
import io.flutter.FlutterErrors;
17+
import io.flutter.FlutterInitializer;
1718
import io.flutter.sdk.FlutterSdk;
1819
import org.jetbrains.annotations.NotNull;
1920

@@ -23,6 +24,8 @@ public class FlutterPackagesUpgradeAction extends FlutterSdkAction {
2324

2425
@Override
2526
public void perform(@NotNull FlutterSdk sdk, @NotNull Project project, AnActionEvent event) throws ExecutionException {
27+
FlutterInitializer.sendActionEvent(this);
28+
2629
final Pair<Module, VirtualFile> pair = getModuleAndPubspecYamlFile(project, event);
2730
if (pair != null) {
2831
sdk.run(COMMAND, pair.first, pair.second.getParent(), null);

src/io/flutter/actions/FlutterSubmitFeedback.java

+3
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@
88
import com.intellij.ide.browsers.BrowserLauncher;
99
import com.intellij.openapi.actionSystem.AnActionEvent;
1010
import com.intellij.openapi.project.DumbAwareAction;
11+
import io.flutter.FlutterInitializer;
1112
import org.jetbrains.annotations.NotNull;
1213

1314
public class FlutterSubmitFeedback extends DumbAwareAction {
1415
@Override
1516
public void actionPerformed(@NotNull final AnActionEvent e) {
17+
FlutterInitializer.sendActionEvent(this);
18+
1619
final String url = "https://github.com/flutter/flutter-intellij/issues/new";
1720
BrowserLauncher.getInstance().browse(url, null);
1821
}

src/io/flutter/actions/FlutterUpgradeAction.java

+3
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@
1111
import com.intellij.openapi.project.Project;
1212
import com.intellij.openapi.util.Pair;
1313
import com.intellij.openapi.vfs.VirtualFile;
14+
import io.flutter.FlutterInitializer;
1415
import io.flutter.sdk.FlutterSdk;
1516
import org.jetbrains.annotations.NotNull;
1617

1718
public class FlutterUpgradeAction extends FlutterSdkAction {
1819
@Override
1920
public void perform(@NotNull FlutterSdk sdk, @NotNull Project project, AnActionEvent event) throws ExecutionException {
21+
FlutterInitializer.sendActionEvent(this);
22+
2023
final Pair<Module, VirtualFile> pair = getModuleAndPubspecYamlFile(project, event);
2124
if (pair != null) {
2225
sdk.run(FlutterSdk.Command.UPGRADE, pair.first, pair.second.getParent(), null);

src/io/flutter/actions/HotReloadFlutterApp.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111
import com.jetbrains.lang.dart.ide.runner.ObservatoryConnector;
1212
import icons.FlutterIcons;
1313
import io.flutter.FlutterBundle;
14+
import io.flutter.FlutterInitializer;
1415

1516
import java.lang.reflect.Method;
1617

1718
@SuppressWarnings("ComponentNotRegistered")
1819
public class HotReloadFlutterApp extends FlutterAppAction {
19-
2020
public static final String ID = "Flutter.HotReloadFlutterApp"; //NON-NLS
2121

2222
public HotReloadFlutterApp(ObservatoryConnector connector, Computable<Boolean> isApplicable) {
@@ -26,6 +26,8 @@ public HotReloadFlutterApp(ObservatoryConnector connector, Computable<Boolean> i
2626

2727
@Override
2828
public void actionPerformed(AnActionEvent e) {
29+
FlutterInitializer.sendActionEvent(this);
30+
2931
ifReadyThen(() -> {
3032
FileDocumentManager.getInstance().saveAllDocuments();
3133
final boolean pauseAfterRestart = hasCapability("supports.pausePostRequest");

src/io/flutter/actions/OpenObservatoryAction.java

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import com.intellij.openapi.util.Computable;
1515
import icons.FlutterIcons;
1616
import io.flutter.FlutterBundle;
17+
import io.flutter.FlutterInitializer;
1718
import org.jetbrains.annotations.NotNull;
1819

1920
import java.util.List;
@@ -37,6 +38,8 @@ public void update(@NotNull final AnActionEvent e) {
3738

3839
@Override
3940
public void actionPerformed(@NotNull final AnActionEvent e) {
41+
FlutterInitializer.sendActionEvent(this);
42+
4043
openInAnyChromeFamilyBrowser(myUrl.compute());
4144
}
4245

src/io/flutter/actions/RestartFlutterApp.java

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import com.jetbrains.lang.dart.ide.runner.ObservatoryConnector;
1212
import icons.FlutterIcons;
1313
import io.flutter.FlutterBundle;
14+
import io.flutter.FlutterInitializer;
1415

1516
@SuppressWarnings("ComponentNotRegistered")
1617
public class RestartFlutterApp extends FlutterAppAction {
@@ -24,6 +25,8 @@ public RestartFlutterApp(ObservatoryConnector connector, Computable<Boolean> isA
2425

2526
@Override
2627
public void actionPerformed(AnActionEvent e) {
28+
FlutterInitializer.sendActionEvent(this);
29+
2730
ifReadyThen(() -> {
2831
FileDocumentManager.getInstance().saveAllDocuments();
2932
getApp().performRestartApp();

0 commit comments

Comments
 (0)