From 81c9bad0cd6dcb91a3d0e2f363f9a7fc704f11be Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Thu, 9 Jan 2025 16:01:04 -0800 Subject: [PATCH] [Inspector V2] Fix inspector analytics (#8684) --- .../inspector/inspector_controller.dart | 10 ++++++++++ .../inspector/inspector_screen_body.dart | 13 ++++++++++-- .../inspector/inspector_tree_controller.dart | 15 ++++++++++++-- .../inspector_settings_dialog.dart | 2 +- .../inspector_v2/inspector_controller.dart | 14 ++++++++++++- .../inspector_v2/inspector_screen_body.dart | 7 ++++++- .../inspector_tree_controller.dart | 13 ++++++++++-- .../src/shared/analytics/_analytics_web.dart | 20 +++++++++++++------ .../lib/src/shared/analytics/constants.dart | 2 ++ .../lib/src/shared/analytics/metrics.dart | 12 +++++------ 10 files changed, 87 insertions(+), 21 deletions(-) diff --git a/packages/devtools_app/lib/src/screens/inspector/inspector_controller.dart b/packages/devtools_app/lib/src/screens/inspector/inspector_controller.dart index f9d1f866c1c..abbc177e71b 100644 --- a/packages/devtools_app/lib/src/screens/inspector/inspector_controller.dart +++ b/packages/devtools_app/lib/src/screens/inspector/inspector_controller.dart @@ -23,6 +23,9 @@ import 'package:logging/logging.dart'; import 'package:vm_service/vm_service.dart'; import '../../service/service_extensions.dart' as extensions; +import '../../shared/analytics/analytics.dart' as ga; +import '../../shared/analytics/constants.dart' as gac; +import '../../shared/analytics/metrics.dart'; import '../../shared/console/eval/inspector_tree.dart'; import '../../shared/console/primitives/simple_items.dart'; import '../../shared/diagnostics/diagnostics_node.dart'; @@ -666,6 +669,13 @@ class InspectorController extends DisposableController subtreeRoot = newSelection; applyNewSelection(newSelection, detailsSelection, true); + + // Send an event that a widget was selected on the device. + ga.select( + gac.inspector, + gac.onDeviceSelection, + screenMetricsProvider: () => InspectorScreenMetrics.legacy(), + ); } catch (error, st) { if (selectionGroups.next == group) { _log.shout(error, error, st); diff --git a/packages/devtools_app/lib/src/screens/inspector/inspector_screen_body.dart b/packages/devtools_app/lib/src/screens/inspector/inspector_screen_body.dart index bbf47db4dd7..c737f4e42c6 100644 --- a/packages/devtools_app/lib/src/screens/inspector/inspector_screen_body.dart +++ b/packages/devtools_app/lib/src/screens/inspector/inspector_screen_body.dart @@ -13,6 +13,7 @@ import '../../service/service_extension_widgets.dart'; import '../../service/service_extensions.dart' as extensions; import '../../shared/analytics/analytics.dart' as ga; import '../../shared/analytics/constants.dart' as gac; +import '../../shared/analytics/metrics.dart'; import '../../shared/console/eval/inspector_tree.dart'; import '../../shared/globals.dart'; import '../../shared/managers/error_badge_manager.dart'; @@ -265,7 +266,11 @@ class InspectorScreenBodyState extends State } void _refreshInspector() { - ga.select(gac.inspector, gac.refresh); + ga.select( + gac.inspector, + gac.refresh, + screenMetricsProvider: () => InspectorScreenMetrics.legacy(), + ); unawaited( blockWhileInProgress(() async { // If the user is force refreshing the inspector before the first load has @@ -275,7 +280,11 @@ class InspectorScreenBodyState extends State // We do not want to complete this timing operation because the force // refresh will skew the results. ga.cancelTimingOperation(InspectorScreen.id, gac.pageReady); - ga.select(gac.inspector, gac.refreshEmptyTree); + ga.select( + gac.inspector, + gac.refreshEmptyTree, + screenMetricsProvider: () => InspectorScreenMetrics.legacy(), + ); controller.firstInspectorTreeLoadCompleted = true; } await controller.onForceRefresh(); diff --git a/packages/devtools_app/lib/src/screens/inspector/inspector_tree_controller.dart b/packages/devtools_app/lib/src/screens/inspector/inspector_tree_controller.dart index 0448d09d33b..dcaaebe7148 100644 --- a/packages/devtools_app/lib/src/screens/inspector/inspector_tree_controller.dart +++ b/packages/devtools_app/lib/src/screens/inspector/inspector_tree_controller.dart @@ -447,7 +447,11 @@ class InspectorTreeController extends DisposableController void onSelectNode(InspectorTreeNode? node) { selection = node; - ga.select(gac.inspector, gac.treeNodeSelection); + ga.select( + gac.inspector, + gac.treeNodeSelection, + screenMetricsProvider: () => InspectorScreenMetrics.legacy(), + ); expandPath(node); } @@ -1002,7 +1006,14 @@ class _InspectorTreeState extends State if (!controller.firstInspectorTreeLoadCompleted && widget.isSummaryTree) { final screenId = widget.screenId; if (screenId != null) { - ga.timeEnd(screenId, gac.pageReady); + ga.timeEnd( + screenId, + gac.pageReady, + screenMetricsProvider: + () => InspectorScreenMetrics.legacy( + rowCount: treeControllerLocal.numRows, + ), + ); unawaited( serviceConnection.sendDwdsEvent( screen: screenId, diff --git a/packages/devtools_app/lib/src/screens/inspector_shared/inspector_settings_dialog.dart b/packages/devtools_app/lib/src/screens/inspector_shared/inspector_settings_dialog.dart index 11d8f50aa36..3c793b3903d 100644 --- a/packages/devtools_app/lib/src/screens/inspector_shared/inspector_settings_dialog.dart +++ b/packages/devtools_app/lib/src/screens/inspector_shared/inspector_settings_dialog.dart @@ -75,7 +75,7 @@ class FlutterInspectorSettingsDialog extends StatelessWidget { description: 'Disable the redesigned Flutter inspector. Please know that ' 'the legacy inspector may be removed in a future release.', - gaItem: gac.inspectorV2Enabled, + gaItem: gac.inspectorV2Disabled, ), ), const SizedBox(height: largeSpacing), diff --git a/packages/devtools_app/lib/src/screens/inspector_v2/inspector_controller.dart b/packages/devtools_app/lib/src/screens/inspector_v2/inspector_controller.dart index 61b9c127b08..0e7255b445e 100644 --- a/packages/devtools_app/lib/src/screens/inspector_v2/inspector_controller.dart +++ b/packages/devtools_app/lib/src/screens/inspector_v2/inspector_controller.dart @@ -25,6 +25,7 @@ import 'package:vm_service/vm_service.dart'; import '../../service/service_extensions.dart' as extensions; import '../../shared/analytics/analytics.dart' as ga; import '../../shared/analytics/constants.dart' as gac; +import '../../shared/analytics/metrics.dart'; import '../../shared/console/eval/inspector_tree_v2.dart'; import '../../shared/console/primitives/simple_items.dart'; import '../../shared/diagnostics/diagnostics_node.dart'; @@ -392,7 +393,11 @@ class InspectorController extends DisposableController // We do not want to complete this timing operation because the force // refresh will skew the results. ga.cancelTimingOperation(InspectorScreen.id, gac.pageReady); - ga.select(gac.inspector, gac.refreshEmptyTree); + ga.select( + gac.inspector, + gac.refreshEmptyTree, + screenMetricsProvider: () => InspectorScreenMetrics.v2(), + ); firstInspectorTreeLoadCompleted = true; } await onForceRefresh(); @@ -766,6 +771,13 @@ class InspectorController extends DisposableController selectedNode: newSelection, group: group, ); + + // Send an event that a widget was selected on the device. + ga.select( + gac.inspector, + gac.onDeviceSelection, + screenMetricsProvider: () => InspectorScreenMetrics.v2(), + ); } catch (error, st) { if (selectionGroups.next == group) { _log.shout(error, error, st); diff --git a/packages/devtools_app/lib/src/screens/inspector_v2/inspector_screen_body.dart b/packages/devtools_app/lib/src/screens/inspector_v2/inspector_screen_body.dart index 7d918ff0a89..233fe5871b4 100644 --- a/packages/devtools_app/lib/src/screens/inspector_v2/inspector_screen_body.dart +++ b/packages/devtools_app/lib/src/screens/inspector_v2/inspector_screen_body.dart @@ -11,6 +11,7 @@ import 'package:flutter/material.dart'; import '../../shared/analytics/analytics.dart' as ga; import '../../shared/analytics/constants.dart' as gac; +import '../../shared/analytics/metrics.dart'; import '../../shared/console/eval/inspector_tree_v2.dart'; import '../../shared/globals.dart'; import '../../shared/managers/banner_messages.dart'; @@ -219,7 +220,11 @@ class InspectorScreenBodyState extends State } void _refreshInspector() { - ga.select(gac.inspector, gac.refresh); + ga.select( + gac.inspector, + gac.refresh, + screenMetricsProvider: () => InspectorScreenMetrics.v2(), + ); unawaited( blockWhileInProgress(() async { await controller.refreshInspector(); diff --git a/packages/devtools_app/lib/src/screens/inspector_v2/inspector_tree_controller.dart b/packages/devtools_app/lib/src/screens/inspector_v2/inspector_tree_controller.dart index 56784743d5a..d1891329231 100644 --- a/packages/devtools_app/lib/src/screens/inspector_v2/inspector_tree_controller.dart +++ b/packages/devtools_app/lib/src/screens/inspector_v2/inspector_tree_controller.dart @@ -553,7 +553,11 @@ class InspectorTreeController extends DisposableController void onSelectNode(InspectorTreeNode? node) { setSelectedNode(node, notifyFlutterInspector: true); - ga.select(gac.inspector, gac.treeNodeSelection); + ga.select( + gac.inspector, + gac.treeNodeSelection, + screenMetricsProvider: () => InspectorScreenMetrics.v2(), + ); final diagnostic = node?.diagnostic; if (diagnostic != null && diagnostic.groupIsHidden) { diagnostic.hideableGroupLeader?.toggleHiddenGroup(); @@ -1136,7 +1140,12 @@ class _InspectorTreeState extends State if (!controller.firstInspectorTreeLoadCompleted) { final screenId = widget.screenId; if (screenId != null) { - ga.timeEnd(screenId, gac.pageReady); + ga.timeEnd( + screenId, + gac.pageReady, + screenMetricsProvider: + () => InspectorScreenMetrics.v2(rowCount: rows.length), + ); unawaited( serviceConnection.sendDwdsEvent( screen: screenId, diff --git a/packages/devtools_app/lib/src/shared/analytics/_analytics_web.dart b/packages/devtools_app/lib/src/shared/analytics/_analytics_web.dart index 178e0817816..2154cba2421 100644 --- a/packages/devtools_app/lib/src/shared/analytics/_analytics_web.dart +++ b/packages/devtools_app/lib/src/shared/analytics/_analytics_web.dart @@ -67,6 +67,8 @@ extension type GtagEventDevTools._(JSObject _) implements GtagEvent { // NOTE: Do not reorder any of these. Order here must match the order in the // Google Analytics console. + // IMPORTANT! Only string and int values are supported. All other value + // types will be ignored in GA4. String? user_app, // dimension1 (flutter or web) String? user_build, // dimension2 (debug or profile) String? user_platform, // dimension3 (android/ios/fuchsia/linux/mac/windows) @@ -112,7 +114,7 @@ extension type GtagEventDevTools._(JSObject _) implements GtagEvent { String? android_app_id, //metric13 String? ios_bundle_id, //metric14 // Inspector screen metrics. See [InspectorScreenMetrics]. - bool? is_v2_inspector, // metric15 + String? is_v2_inspector, // metric15 }); factory GtagEventDevTools._create({ @@ -207,7 +209,9 @@ extension type GtagEventDevTools._(JSObject _) implements GtagEvent { : null, // [InspectorScreenMetrics] is_v2_inspector: - screenMetrics is InspectorScreenMetrics ? screenMetrics.isV2 : null, + screenMetrics is InspectorScreenMetrics + ? screenMetrics.isV2.toString() + : null, ); } @@ -243,7 +247,7 @@ extension type GtagEventDevTools._(JSObject _) implements GtagEvent { external int? get inspector_tree_controller_id; external String? get android_app_id; external String? get ios_bundle_id; - external bool? get is_v2_inspector; + external String? get is_v2_inspector; } extension type GtagExceptionDevTools._(JSObject _) implements GtagException { @@ -254,6 +258,8 @@ extension type GtagExceptionDevTools._(JSObject _) implements GtagException { // NOTE: Do not reorder any of these. Order here must match the order in the // Google Analytics console. + // IMPORTANT! Only string and int values are supported. All other value + // types will be ignored in GA4. String? user_app, // dimension1 (flutter or web) String? user_build, // dimension2 (debug or profile) String? user_platform, // dimension3 (android or ios) @@ -298,7 +304,7 @@ extension type GtagExceptionDevTools._(JSObject _) implements GtagException { String? android_app_id, //metric13 String? ios_bundle_id, //metric14 // Inspector screen metrics. See [InspectorScreenMetrics]. - bool? is_v2_inspector, // metric15 + String? is_v2_inspector, // metric15 }); factory GtagExceptionDevTools._create( @@ -385,7 +391,9 @@ extension type GtagExceptionDevTools._(JSObject _) implements GtagException { : null, // [InspectorScreenMetrics] is_v2_inspector: - screenMetrics is InspectorScreenMetrics ? screenMetrics.isV2 : null, + screenMetrics is InspectorScreenMetrics + ? screenMetrics.isV2.toString() + : null, ); } @@ -1026,7 +1034,7 @@ final class _DevToolsEventMetrics extends ua.CustomMetrics { final int? rootSetCount; final int? rowCount; final int? inspectorTreeControllerId; - final bool? isV2Inspector; + final String? isV2Inspector; // [DeepLinkScreenMetrics] final String? androidAppId; diff --git a/packages/devtools_app/lib/src/shared/analytics/constants.dart b/packages/devtools_app/lib/src/shared/analytics/constants.dart index 83679f50f6b..a3a714ffaa1 100644 --- a/packages/devtools_app/lib/src/shared/analytics/constants.dart +++ b/packages/devtools_app/lib/src/shared/analytics/constants.dart @@ -84,6 +84,7 @@ const selectWidgetMode = 'selectWidgetMode'; const enableOnDeviceInspector = 'enableOnDeviceInspector'; const showOnDeviceInspector = 'showInspector'; const treeNodeSelection = 'treeNodeSelection'; +const onDeviceSelection = 'onDeviceSelection'; const inspectorSettings = 'inspectorSettings'; const loggingSettings = 'loggingSettings'; const refreshPubRoots = 'refreshPubRoots'; @@ -119,6 +120,7 @@ const wasm = 'wasm'; const verboseLogging = 'verboseLogging'; const inspectorHoverEvalMode = 'inspectorHoverEvalMode'; const inspectorV2Enabled = 'inspectorV2Enabled'; +const inspectorV2Disabled = 'inspectorV2Disabled'; const inspectorAutoRefreshEnabled = 'inspectorAutoRefreshEnabled'; const inspectorV2Docs = 'inspectorV2Docs'; const clearLogs = 'clearLogs'; diff --git a/packages/devtools_app/lib/src/shared/analytics/metrics.dart b/packages/devtools_app/lib/src/shared/analytics/metrics.dart index dc4ef4ed74b..a3e1b9d96ce 100644 --- a/packages/devtools_app/lib/src/shared/analytics/metrics.dart +++ b/packages/devtools_app/lib/src/shared/analytics/metrics.dart @@ -64,15 +64,15 @@ class ProfilerScreenMetrics extends ScreenAnalyticsMetrics { class InspectorScreenMetrics extends ScreenAnalyticsMetrics { InspectorScreenMetrics.legacy({ - required this.rootSetCount, - required this.rowCount, - required this.inspectorTreeControllerId, + this.rootSetCount, + this.rowCount, + this.inspectorTreeControllerId, }) : isV2 = false; InspectorScreenMetrics.v2({ - required this.rootSetCount, - required this.rowCount, - required this.inspectorTreeControllerId, + this.rootSetCount, + this.rowCount, + this.inspectorTreeControllerId, }) : isV2 = true; static const summaryTreeGaId = 0;