Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/io/flutter/actions/FlutterAppAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,8 @@ public void update(@NotNull final AnActionEvent e) {
public FlutterApp getApp() {
return myApp;
}

public @NotNull String getId() {
return myActionId;
}
}
10 changes: 10 additions & 0 deletions src/io/flutter/actions/FlutterSdkAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import io.flutter.FlutterBundle;
import io.flutter.FlutterMessages;
import io.flutter.FlutterUtils;
import io.flutter.analytics.Analytics;
import io.flutter.analytics.AnalyticsData;
import io.flutter.bazel.Workspace;
import io.flutter.pub.PubRoot;
import io.flutter.pub.PubRoots;
Expand All @@ -32,19 +34,25 @@ public abstract class FlutterSdkAction extends DumbAwareAction {
public void actionPerformed(@NotNull AnActionEvent event) {
final Project project = DumbAwareAction.getEventProject(event);

AnalyticsData analyticsData = AnalyticsData.forAction(this, event);

if (enableActionInBazelContext()) {
// See if the Bazel workspace exists for this project.
final Workspace workspace = FlutterModuleUtils.getFlutterBazelWorkspace(project);
if (workspace != null) {
FileDocumentManager.getInstance().saveAllDocuments();
startCommandInBazelContext(project, workspace, event);
analyticsData.add("inBazelContext", true);
Analytics.report(analyticsData);
return;
}
}

final FlutterSdk sdk = project != null ? FlutterSdk.getFlutterSdk(project) : null;
if (sdk == null) {
showMissingSdkDialog(project);
analyticsData.add("missingSdk", true);
Analytics.report(analyticsData);
return;
}

Expand All @@ -60,6 +68,8 @@ public void actionPerformed(@NotNull AnActionEvent event) {
startCommand(project, sdk, sub, context);
}
}

Analytics.report(analyticsData);
}

public abstract void startCommand(@NotNull Project project,
Expand Down
7 changes: 7 additions & 0 deletions src/io/flutter/actions/ReloadFlutterApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import icons.FlutterIcons;
import io.flutter.FlutterBundle;
import io.flutter.FlutterConstants;
import io.flutter.analytics.Analytics;
import io.flutter.analytics.AnalyticsData;
import io.flutter.run.FlutterReloadManager;
import io.flutter.run.daemon.FlutterApp;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -42,9 +44,12 @@ public void actionPerformed(@NotNull AnActionEvent e) {
return;
}

var analyticsData = AnalyticsData.forAction(this, e);

// If the shift key is held down, perform a restart. We check to see if we're being invoked from the
// 'GoToAction' dialog. If so, the modifiers are for the command that opened the go-to action dialog.
final boolean shouldRestart = (e.getModifiers() & InputEvent.SHIFT_MASK) != 0 && !"GoToAction".equals(e.getPlace());
analyticsData.add("requiresRestart", shouldRestart);

var reloadManager = FlutterReloadManager.getInstance(project);
if (reloadManager == null) return;
Expand All @@ -56,6 +61,8 @@ public void actionPerformed(@NotNull AnActionEvent e) {
// Else perform a hot reload.
reloadManager.saveAllAndReload(getApp(), FlutterConstants.RELOAD_REASON_MANUAL);
}

Analytics.report(analyticsData);
}

// Override to disable the hot reload action when running flutter web apps.
Expand Down
8 changes: 8 additions & 0 deletions src/io/flutter/actions/RestartFlutterApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import io.flutter.FlutterBundle;
import io.flutter.FlutterConstants;
import io.flutter.FlutterMessages;
import io.flutter.analytics.Analytics;
import io.flutter.analytics.AnalyticsData;
import io.flutter.bazel.WorkspaceCache;
import io.flutter.run.FlutterReloadManager;
import io.flutter.run.daemon.FlutterApp;
Expand Down Expand Up @@ -50,6 +52,8 @@ public void actionPerformed(@NotNull AnActionEvent e) {
reloadManager.saveAllAndRestart(getApp(), FlutterConstants.RELOAD_REASON_MANUAL);
}

var analyticsData = AnalyticsData.forAction(this, e);

if (WorkspaceCache.getInstance(project).isBazel() &&
FlutterSettings.getInstance().isShowBazelHotRestartWarning() &&
!FlutterSettings.getInstance().isEnableBazelHotRestart()) {
Expand All @@ -61,8 +65,12 @@ public void actionPerformed(@NotNull AnActionEvent e) {
NotificationType.INFORMATION);
Notifications.Bus.notify(notification, project);

analyticsData.add("google3", true);

// We only want to show this notification once.
FlutterSettings.getInstance().setShowBazelHotRestartWarning(false);
}

Analytics.report(analyticsData);
}
}
76 changes: 76 additions & 0 deletions src/io/flutter/analytics/Analytics.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright 2025 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.
*/

package io.flutter.analytics

import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import io.flutter.actions.FlutterAppAction

object Analytics {
private val reporter = NoOpReporter

@JvmStatic
fun report(data: AnalyticsData) = reporter.report(data)
}

abstract class AnalyticsReporter {

fun report(data: AnalyticsData) = data.reportTo(this)

internal abstract fun process(data: AnalyticsData)
}

internal object PrintingReporter : AnalyticsReporter() {
override fun process(data: AnalyticsData) = println(data.data)

}

internal object NoOpReporter : AnalyticsReporter() {
override fun process(data: AnalyticsData) = Unit
}

abstract class AnalyticsData {
val data = mutableMapOf<String, Any>()

companion object {
@JvmStatic
fun forAction(action: AnAction, event: AnActionEvent): ActionData = ActionData(
event.actionManager.getId(action)
// `FlutterAppAction`s aren't registered so ask them directly.
?: (action as? FlutterAppAction)?.id,
event.place
)
}

fun add(key: String, value: Boolean) {
data[key] = value
}

fun add(key: String, value: Int) {
data[key] = value
}

fun add(key: String, value: String) {
data[key] = value
}

open fun reportTo(reporter: AnalyticsReporter) = reporter.process(this)
}

class ActionData(private val id: String?, private val place: String) : AnalyticsData() {

init {
id?.let { add("id", it) }
add("place", place)
}

override fun reportTo(reporter: AnalyticsReporter) {
// We only report if we have an id for the event.
if (id == null) return
super.reportTo(reporter)
}
}