From 4a2d1cae50e29099bf3b1380a92d6c49f1939b44 Mon Sep 17 00:00:00 2001 From: pq Date: Mon, 3 Nov 2025 19:34:50 -0800 Subject: [PATCH 1/2] [analytics] introduces constants --- src/io/flutter/actions/FlutterSdkAction.java | 7 +- src/io/flutter/actions/ReloadFlutterApp.java | 7 +- src/io/flutter/actions/RestartFlutterApp.java | 5 +- src/io/flutter/analytics/Analytics.kt | 95 ++++++++++++++++--- 4 files changed, 95 insertions(+), 19 deletions(-) diff --git a/src/io/flutter/actions/FlutterSdkAction.java b/src/io/flutter/actions/FlutterSdkAction.java index db64abf3d..ae6dbc13d 100644 --- a/src/io/flutter/actions/FlutterSdkAction.java +++ b/src/io/flutter/actions/FlutterSdkAction.java @@ -14,6 +14,7 @@ import io.flutter.FlutterMessages; import io.flutter.FlutterUtils; import io.flutter.analytics.Analytics; +import io.flutter.analytics.AnalyticsConstants; import io.flutter.analytics.AnalyticsData; import io.flutter.bazel.Workspace; import io.flutter.pub.PubRoot; @@ -42,7 +43,7 @@ public void actionPerformed(@NotNull AnActionEvent event) { if (workspace != null) { FileDocumentManager.getInstance().saveAllDocuments(); startCommandInBazelContext(project, workspace, event); - analyticsData.add("inBazelContext", true); + analyticsData.add(AnalyticsConstants.IN_BAZEL_CONTEXT, true); Analytics.report(analyticsData); return; } @@ -51,7 +52,7 @@ public void actionPerformed(@NotNull AnActionEvent event) { final FlutterSdk sdk = project != null ? FlutterSdk.getFlutterSdk(project) : null; if (sdk == null) { showMissingSdkDialog(project); - analyticsData.add("missingSdk", true); + analyticsData.add(AnalyticsConstants.MISSING_SDK, true); Analytics.report(analyticsData); return; } @@ -100,4 +101,4 @@ public static void showMissingSdkDialog(@Nullable Project project) { FlutterUtils.openFlutterSettings(project); } } -} +} \ No newline at end of file diff --git a/src/io/flutter/actions/ReloadFlutterApp.java b/src/io/flutter/actions/ReloadFlutterApp.java index 6ec637b91..8a6c3ef6f 100644 --- a/src/io/flutter/actions/ReloadFlutterApp.java +++ b/src/io/flutter/actions/ReloadFlutterApp.java @@ -13,6 +13,7 @@ import io.flutter.FlutterBundle; import io.flutter.FlutterConstants; import io.flutter.analytics.Analytics; +import io.flutter.analytics.AnalyticsConstants; import io.flutter.analytics.AnalyticsData; import io.flutter.run.FlutterReloadManager; import io.flutter.run.daemon.FlutterApp; @@ -49,7 +50,7 @@ public void actionPerformed(@NotNull AnActionEvent 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); + analyticsData.add(AnalyticsConstants.REQUIRES_RESTART, shouldRestart); var reloadManager = FlutterReloadManager.getInstance(project); if (reloadManager == null) return; @@ -61,7 +62,7 @@ public void actionPerformed(@NotNull AnActionEvent e) { // Else perform a hot reload. reloadManager.saveAllAndReload(getApp(), FlutterConstants.RELOAD_REASON_MANUAL); } - + Analytics.report(analyticsData); } @@ -74,4 +75,4 @@ public void update(@NotNull AnActionEvent e) { e.getPresentation().setEnabled(false); } } -} +} \ No newline at end of file diff --git a/src/io/flutter/actions/RestartFlutterApp.java b/src/io/flutter/actions/RestartFlutterApp.java index 10aa03643..8499c4bac 100644 --- a/src/io/flutter/actions/RestartFlutterApp.java +++ b/src/io/flutter/actions/RestartFlutterApp.java @@ -17,6 +17,7 @@ import io.flutter.FlutterConstants; import io.flutter.FlutterMessages; import io.flutter.analytics.Analytics; +import io.flutter.analytics.AnalyticsConstants; import io.flutter.analytics.AnalyticsData; import io.flutter.bazel.WorkspaceCache; import io.flutter.run.FlutterReloadManager; @@ -65,7 +66,7 @@ public void actionPerformed(@NotNull AnActionEvent e) { NotificationType.INFORMATION); Notifications.Bus.notify(notification, project); - analyticsData.add("google3", true); + analyticsData.add(AnalyticsConstants.GOOGLE3, true); // We only want to show this notification once. FlutterSettings.getInstance().setShowBazelHotRestartWarning(false); @@ -73,4 +74,4 @@ public void actionPerformed(@NotNull AnActionEvent e) { Analytics.report(analyticsData); } -} +} \ No newline at end of file diff --git a/src/io/flutter/analytics/Analytics.kt b/src/io/flutter/analytics/Analytics.kt index 7f9c37fb1..f7ce54e98 100644 --- a/src/io/flutter/analytics/Analytics.kt +++ b/src/io/flutter/analytics/Analytics.kt @@ -14,19 +14,15 @@ object Analytics { private val reporter = NoOpReporter @JvmStatic - fun report(data: AnalyticsData) = reporter.report(data) + fun report(data: AnalyticsData) = data.reportTo(reporter) } 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() { @@ -37,7 +33,7 @@ abstract class AnalyticsData(type: String) { val data = mutableMapOf() init { - add("type", type) + add(AnalyticsConstants.TYPE, type) } companion object { @@ -50,15 +46,17 @@ abstract class AnalyticsData(type: String) { ) } - fun add(key: String, value: Boolean) { + fun add(key: DataValue, value: T) = key.addTo(this, value) + + internal operator fun set(key: String, value: Boolean) { data[key] = value } - fun add(key: String, value: Int) { + internal operator fun set(key: String, value: Int) { data[key] = value } - fun add(key: String, value: String) { + internal operator fun set(key: String, value: String) { data[key] = value } @@ -75,8 +73,8 @@ abstract class AnalyticsData(type: String) { class ActionData(private val id: String?, private val place: String) : AnalyticsData("action") { init { - id?.let { add("id", it) } - add("place", place) + id?.let { add(AnalyticsConstants.ID, it) } + add(AnalyticsConstants.PLACE, place) } override fun reportTo(reporter: AnalyticsReporter) { @@ -85,3 +83,78 @@ class ActionData(private val id: String?, private val place: String) : Analytics super.reportTo(reporter) } } + +/** + * Defines standard keys for analytics data properties. + * + * The properties are exposed as `@JvmField`s to be easily accessible as static + * fields from Java. + */ +object AnalyticsConstants { + /** + * Indicates if the project is a Google3 project. + */ + @JvmField + val GOOGLE3 = BooleanValue("google3") + + /** + * The unique identifier for an action or event. + */ + @JvmField + val ID = StringValue("id") + + /** + * Indicates if the project is in a Bazel context. + */ + @JvmField + val IN_BAZEL_CONTEXT = BooleanValue("inBazelContext") + + /** + * Indicates if the Flutter SDK is missing. + */ + @JvmField + val MISSING_SDK = BooleanValue("missingSdk") + + /** + * The UI location where an action was invoked, as provided by + * [com.intellij.openapi.actionSystem.PlaceProvider.getPlace] (for example, "MainMenu", + * "MainToolbar", "EditorPopup", "GoToAction", etc). + */ + @JvmField + val PLACE = StringValue("place") + + /** + * Indicates if a restart is required for a hot reload request. + */ + @JvmField + val REQUIRES_RESTART = BooleanValue("requiresRestart") + + /** + * The type of the analytics event (e.g., "action", ...). + */ + @JvmField + val TYPE = StringValue("type") +} + + +sealed class DataValue(val name: String) { + abstract fun addTo(data: AnalyticsData, value: T); +} + +class StringValue(name: String) : DataValue(name) { + override fun addTo(data: AnalyticsData, value: String) { + data[name] = value + } +} + +class IntValue(name: String) : DataValue(name) { + override fun addTo(data: AnalyticsData, value: Int) { + data[name] = value + } +} + +class BooleanValue(name: String) : DataValue(name) { + override fun addTo(data: AnalyticsData, value: Boolean) { + data[name] = value + } +} \ No newline at end of file From 51258af47e8c43fecad86fa2fa7dc6cf37afcfc7 Mon Sep 17 00:00:00 2001 From: pq Date: Mon, 3 Nov 2025 19:36:41 -0800 Subject: [PATCH 2/2] ++ --- src/io/flutter/actions/FlutterSdkAction.java | 2 +- src/io/flutter/actions/ReloadFlutterApp.java | 2 +- src/io/flutter/actions/RestartFlutterApp.java | 2 +- src/io/flutter/analytics/Analytics.kt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/io/flutter/actions/FlutterSdkAction.java b/src/io/flutter/actions/FlutterSdkAction.java index ae6dbc13d..5babc1bc5 100644 --- a/src/io/flutter/actions/FlutterSdkAction.java +++ b/src/io/flutter/actions/FlutterSdkAction.java @@ -101,4 +101,4 @@ public static void showMissingSdkDialog(@Nullable Project project) { FlutterUtils.openFlutterSettings(project); } } -} \ No newline at end of file +} diff --git a/src/io/flutter/actions/ReloadFlutterApp.java b/src/io/flutter/actions/ReloadFlutterApp.java index 8a6c3ef6f..c3b3f47ff 100644 --- a/src/io/flutter/actions/ReloadFlutterApp.java +++ b/src/io/flutter/actions/ReloadFlutterApp.java @@ -75,4 +75,4 @@ public void update(@NotNull AnActionEvent e) { e.getPresentation().setEnabled(false); } } -} \ No newline at end of file +} diff --git a/src/io/flutter/actions/RestartFlutterApp.java b/src/io/flutter/actions/RestartFlutterApp.java index 8499c4bac..1d258925b 100644 --- a/src/io/flutter/actions/RestartFlutterApp.java +++ b/src/io/flutter/actions/RestartFlutterApp.java @@ -74,4 +74,4 @@ public void actionPerformed(@NotNull AnActionEvent e) { Analytics.report(analyticsData); } -} \ No newline at end of file +} diff --git a/src/io/flutter/analytics/Analytics.kt b/src/io/flutter/analytics/Analytics.kt index f7ce54e98..cb0103fdc 100644 --- a/src/io/flutter/analytics/Analytics.kt +++ b/src/io/flutter/analytics/Analytics.kt @@ -157,4 +157,4 @@ class BooleanValue(name: String) : DataValue(name) { override fun addTo(data: AnalyticsData, value: Boolean) { data[name] = value } -} \ No newline at end of file +}