diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b919d6..b8f9b3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ ## [Unreleased] +## [1.12.0] - 2026-03-08 + +### Changed + +- **Simplified Analytics** - Replaced granular per-action event tracking with a single periodic `plugin_active` heartbeat every 23 hours + +### Removed + +- Per-action analytics event types (widget clicks, popup actions, onboarding steps, goal events) + ## [1.11.0] - 2026-02-27 ### Added @@ -225,7 +235,8 @@ - Support IntelliJ Platform 2024.3.5 -[Unreleased]: https://github.com/codeclocker/codeclocker-intellij-plugin/compare/v1.11.0...HEAD +[Unreleased]: https://github.com/codeclocker/codeclocker-intellij-plugin/compare/v1.12.0...HEAD +[1.12.0]: https://github.com/codeclocker/codeclocker-intellij-plugin/compare/v1.11.0...v1.12.0 [1.11.0]: https://github.com/codeclocker/codeclocker-intellij-plugin/compare/v1.10.0...v1.11.0 [1.10.0]: https://github.com/codeclocker/codeclocker-intellij-plugin/compare/v1.9.0...v1.10.0 [1.9.0]: https://github.com/codeclocker/codeclocker-intellij-plugin/compare/v1.8.0...v1.9.0 diff --git a/build.gradle.kts b/build.gradle.kts index 243cd43..8edbeba 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -164,7 +164,7 @@ intellijPlatformTesting { spotless { java { - googleJavaFormat() + googleJavaFormat("1.25.2") importOrder() removeUnusedImports() targetExclude("build/**/*.java") diff --git a/gradle.properties b/gradle.properties index a64ff63..72deaad 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ pluginGroup = com.codeclocker pluginName = CodeClocker pluginRepositoryUrl = https://github.com/codeclocker/codeclocker-intellij-plugin # SemVer format -> https://semver.org -pluginVersion = 1.11.0 +pluginVersion = 1.12.0 # Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html pluginSinceBuild = 252 diff --git a/src/main/java/com/codeclocker/plugin/intellij/analytics/Analytics.java b/src/main/java/com/codeclocker/plugin/intellij/analytics/Analytics.java deleted file mode 100644 index 5a46cec..0000000 --- a/src/main/java/com/codeclocker/plugin/intellij/analytics/Analytics.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.codeclocker.plugin.intellij.analytics; - -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diagnostic.Logger; -import java.util.Map; -import org.jetbrains.annotations.NotNull; - -/** - * Simple facade for tracking analytics events from anywhere in the plugin. Usage: - * - *
- *   Analytics.track(AnalyticsEventType.STATUS_BAR_WIDGET_CLICK);
- *   Analytics.track(AnalyticsEventType.POPUP_SET_GOALS_CLICK);
- * 
- */ -public final class Analytics { - - private static final Logger LOG = Logger.getInstance(Analytics.class); - - private Analytics() {} - - /** - * Tracks an analytics event. - * - * @param eventType The type of event to track (use constants from {@link AnalyticsEventType}) - */ - public static void track(@NotNull String eventType) { - track(eventType, Map.of()); - } - - /** - * Tracks an analytics event with properties. - * - * @param eventType The type of event to track - * @param properties Additional event-specific properties - */ - public static void track(@NotNull String eventType, @NotNull Map properties) { - try { - AnalyticsReportingTask task = - ApplicationManager.getApplication().getService(AnalyticsReportingTask.class); - if (task != null) { - task.track(eventType, properties); - } else { - LOG.debug("AnalyticsReportingTask not available, event not tracked: " + eventType); - } - } catch (Exception ex) { - LOG.debug("Failed to track event: " + eventType, ex); - } - } -} diff --git a/src/main/java/com/codeclocker/plugin/intellij/analytics/AnalyticsEventType.java b/src/main/java/com/codeclocker/plugin/intellij/analytics/AnalyticsEventType.java index 179a9ed..3ce686d 100644 --- a/src/main/java/com/codeclocker/plugin/intellij/analytics/AnalyticsEventType.java +++ b/src/main/java/com/codeclocker/plugin/intellij/analytics/AnalyticsEventType.java @@ -4,36 +4,5 @@ public final class AnalyticsEventType { private AnalyticsEventType() {} - // Widget events - public static final String STATUS_BAR_WIDGET_CLICK = "status_bar_widget_click"; - - // Widget popup actions - public static final String POPUP_WEB_DASHBOARD_CLICK = "popup_web_dashboard_click"; - public static final String POPUP_SAVE_HISTORY_CLICK = "popup_save_history_click"; - public static final String POPUP_RENEW_SUBSCRIPTION_CLICK = "popup_renew_subscription_click"; - public static final String POPUP_SET_GOALS_CLICK = "popup_set_goals_click"; - public static final String POPUP_SET_PROJECT_GOALS_CLICK = "popup_set_project_goals_click"; - public static final String POPUP_AUTO_PAUSE_CLICK = "popup_auto_pause_click"; - public static final String POPUP_DASHBOARD_CLICK = "popup_dashboard_click"; - public static final String POPUP_ACTIVITY_REPORT_CLICK = "popup_activity_report_click"; - - // Plugin lifecycle events - public static final String PLUGIN_STARTED = "plugin_started"; - public static final String PLUGIN_STOPPED = "plugin_stopped"; - - // Onboarding tour events - public static final String TOUR_WELCOME_START = "tour_welcome_start"; - public static final String TOUR_WELCOME_SKIP = "tour_welcome_skip"; - public static final String TOUR_STATUS_BAR_NEXT = "tour_status_bar_next"; - public static final String TOUR_STATUS_BAR_SKIP = "tour_status_bar_skip"; - public static final String TOUR_ACTIVITY_POPUP_NEXT = "tour_activity_popup_next"; - public static final String TOUR_ACTIVITY_POPUP_SKIP = "tour_activity_popup_skip"; - public static final String TOUR_GOALS_SET = "tour_goals_set"; - public static final String TOUR_GOALS_LATER = "tour_goals_later"; - public static final String TOUR_GOALS_SKIP = "tour_goals_skip"; - public static final String TOUR_HUB_CONNECT = "tour_hub_connect"; - public static final String TOUR_HUB_SKIP = "tour_hub_skip"; - - // Goal events - public static final String SET_NEW_GOAL = "set_new_goal"; + public static final String PLUGIN_ACTIVE = "plugin_active"; } diff --git a/src/main/java/com/codeclocker/plugin/intellij/analytics/AnalyticsReportingTask.java b/src/main/java/com/codeclocker/plugin/intellij/analytics/AnalyticsReportingTask.java index 3c3bbcb..177ce24 100644 --- a/src/main/java/com/codeclocker/plugin/intellij/analytics/AnalyticsReportingTask.java +++ b/src/main/java/com/codeclocker/plugin/intellij/analytics/AnalyticsReportingTask.java @@ -9,7 +9,6 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import java.util.List; -import java.util.Map; import java.util.concurrent.ScheduledFuture; /** @@ -21,11 +20,13 @@ public class AnalyticsReportingTask implements Disposable { private static final Logger LOG = Logger.getInstance(AnalyticsReportingTask.class); private static final int FLUSH_INTERVAL_MINUTES = 5; + private static final int PLUGIN_ACTIVE_INTERVAL_MINUTES = 23 * 60; private final AnalyticsQueue analyticsQueue; private final AnalyticsHttpClient analyticsHttpClient; private ScheduledFuture task; + private ScheduledFuture pluginActiveTask; private IdeContext cachedIdeContext; public AnalyticsReportingTask() { @@ -40,8 +41,14 @@ public void schedule() { return; } - // Track plugin started event - track(AnalyticsEventType.PLUGIN_STARTED); + // Track plugin active event immediately and every 23 hours + trackPluginActive(); + pluginActiveTask = + EXECUTOR.scheduleWithFixedDelay( + this::trackPluginActive, + PLUGIN_ACTIVE_INTERVAL_MINUTES, + PLUGIN_ACTIVE_INTERVAL_MINUTES, + MINUTES); task = EXECUTOR.scheduleWithFixedDelay( @@ -59,14 +66,8 @@ public void track(String eventType) { analyticsQueue.track(eventType); } - /** - * Tracks an analytics event with properties. - * - * @param eventType The type of event to track - * @param properties Additional event properties - */ - public void track(String eventType, Map properties) { - analyticsQueue.track(eventType, properties); + private void trackPluginActive() { + track(AnalyticsEventType.PLUGIN_ACTIVE); } /** Flushes queued analytics events to the backend. */ @@ -111,9 +112,6 @@ private IdeContext getIdeContext() { public void dispose() { LOG.info("Disposing AnalyticsReportingTask - flushing remaining events"); - // Track plugin stopped event - track(AnalyticsEventType.PLUGIN_STOPPED); - try { // Perform final flush flushAnalytics(); @@ -122,6 +120,9 @@ public void dispose() { LOG.warn("Error during final analytics flush", e); } + if (pluginActiveTask != null) { + pluginActiveTask.cancel(false); + } if (task != null) { task.cancel(false); } diff --git a/src/main/java/com/codeclocker/plugin/intellij/goal/GoalNotificationService.java b/src/main/java/com/codeclocker/plugin/intellij/goal/GoalNotificationService.java index 3447dd5..556b105 100644 --- a/src/main/java/com/codeclocker/plugin/intellij/goal/GoalNotificationService.java +++ b/src/main/java/com/codeclocker/plugin/intellij/goal/GoalNotificationService.java @@ -2,8 +2,6 @@ import static com.intellij.notification.NotificationType.INFORMATION; -import com.codeclocker.plugin.intellij.analytics.Analytics; -import com.codeclocker.plugin.intellij.analytics.AnalyticsEventType; import com.intellij.ide.DataManager; import com.intellij.notification.NotificationAction; import com.intellij.notification.NotificationGroupManager; @@ -339,7 +337,6 @@ private static class SetNewGoalAction extends com.intellij.notification.Notifica public void actionPerformed( com.intellij.openapi.actionSystem.AnActionEvent e, com.intellij.notification.Notification notification) { - Analytics.track(AnalyticsEventType.SET_NEW_GOAL); notification.expire(); GoalSettingsDialog.showDialog(); } @@ -357,7 +354,6 @@ private static class SetProjectGoalAction extends com.intellij.notification.Noti public void actionPerformed( com.intellij.openapi.actionSystem.AnActionEvent e, com.intellij.notification.Notification notification) { - Analytics.track(AnalyticsEventType.SET_NEW_GOAL); notification.expire(); Project project = e.getProject(); if (project != null) { diff --git a/src/main/java/com/codeclocker/plugin/intellij/onboarding/OnboardingStepRenderer.java b/src/main/java/com/codeclocker/plugin/intellij/onboarding/OnboardingStepRenderer.java index be4cbe4..82bc476 100644 --- a/src/main/java/com/codeclocker/plugin/intellij/onboarding/OnboardingStepRenderer.java +++ b/src/main/java/com/codeclocker/plugin/intellij/onboarding/OnboardingStepRenderer.java @@ -2,8 +2,6 @@ import static com.intellij.notification.NotificationType.INFORMATION; -import com.codeclocker.plugin.intellij.analytics.Analytics; -import com.codeclocker.plugin.intellij.analytics.AnalyticsEventType; import com.codeclocker.plugin.intellij.apikey.EnterApiKeyAction; import com.codeclocker.plugin.intellij.goal.GoalSettingsDialog; import com.intellij.notification.Notification; @@ -40,19 +38,9 @@ private static void showWelcomeStep(Project project, OnboardingService service) INFORMATION); notification.addAction( - NotificationAction.createSimpleExpiring( - "Start tour", - () -> { - Analytics.track(AnalyticsEventType.TOUR_WELCOME_START); - service.nextStep(); - })); + NotificationAction.createSimpleExpiring("Start tour", service::nextStep)); notification.addAction( - NotificationAction.createSimpleExpiring( - "Skip tour", - () -> { - Analytics.track(AnalyticsEventType.TOUR_WELCOME_SKIP); - service.skipOnboarding(); - })); + NotificationAction.createSimpleExpiring("Skip tour", service::skipOnboarding)); notification.notify(project); } @@ -78,19 +66,9 @@ private static void showStatusBarWidgetStep(Project project, OnboardingService s INFORMATION); notification.addAction( - NotificationAction.createSimpleExpiring( - "Got it, Next", - () -> { - Analytics.track(AnalyticsEventType.TOUR_STATUS_BAR_NEXT); - service.nextStep(); - })); + NotificationAction.createSimpleExpiring("Got it, Next", service::nextStep)); notification.addAction( - NotificationAction.createSimpleExpiring( - "Skip tour", - () -> { - Analytics.track(AnalyticsEventType.TOUR_STATUS_BAR_SKIP); - service.skipOnboarding(); - })); + NotificationAction.createSimpleExpiring("Skip tour", service::skipOnboarding)); notification.notify(project); } @@ -110,19 +88,9 @@ private static void showActivityPopupStep(Project project, OnboardingService ser INFORMATION); notification.addAction( - NotificationAction.createSimpleExpiring( - "Got it, Next", - () -> { - Analytics.track(AnalyticsEventType.TOUR_ACTIVITY_POPUP_NEXT); - service.nextStep(); - })); + NotificationAction.createSimpleExpiring("Got it, Next", service::nextStep)); notification.addAction( - NotificationAction.createSimpleExpiring( - "Skip tour", - () -> { - Analytics.track(AnalyticsEventType.TOUR_ACTIVITY_POPUP_SKIP); - service.skipOnboarding(); - })); + NotificationAction.createSimpleExpiring("Skip tour", service::skipOnboarding)); notification.notify(project); } @@ -142,7 +110,6 @@ private static void showGoalsStep(Project project, OnboardingService service) { NotificationAction.createSimpleExpiring( "Set goals now", () -> { - Analytics.track(AnalyticsEventType.TOUR_GOALS_SET); ApplicationManager.getApplication() .invokeLater( () -> { @@ -151,19 +118,9 @@ private static void showGoalsStep(Project project, OnboardingService service) { }); })); notification.addAction( - NotificationAction.createSimpleExpiring( - "Maybe later", - () -> { - Analytics.track(AnalyticsEventType.TOUR_GOALS_LATER); - service.nextStep(); - })); + NotificationAction.createSimpleExpiring("Maybe later", service::nextStep)); notification.addAction( - NotificationAction.createSimpleExpiring( - "Skip tour", - () -> { - Analytics.track(AnalyticsEventType.TOUR_GOALS_SKIP); - service.skipOnboarding(); - })); + NotificationAction.createSimpleExpiring("Skip tour", service::skipOnboarding)); notification.notify(project); } @@ -186,7 +143,6 @@ private static void showHubConnectionStep(Project project, OnboardingService ser NotificationAction.createSimpleExpiring( "Connect to Hub", () -> { - Analytics.track(AnalyticsEventType.TOUR_HUB_CONNECT); ApplicationManager.getApplication() .invokeLater( () -> { @@ -195,12 +151,7 @@ private static void showHubConnectionStep(Project project, OnboardingService ser }); })); notification.addAction( - NotificationAction.createSimpleExpiring( - "Skip, finish tour", - () -> { - Analytics.track(AnalyticsEventType.TOUR_HUB_SKIP); - service.completeOnboarding(); - })); + NotificationAction.createSimpleExpiring("Skip, finish tour", service::completeOnboarding)); notification.notify(project); } diff --git a/src/main/java/com/codeclocker/plugin/intellij/widget/TimeTrackerPopup.java b/src/main/java/com/codeclocker/plugin/intellij/widget/TimeTrackerPopup.java index 670e053..2942f3f 100644 --- a/src/main/java/com/codeclocker/plugin/intellij/widget/TimeTrackerPopup.java +++ b/src/main/java/com/codeclocker/plugin/intellij/widget/TimeTrackerPopup.java @@ -5,8 +5,6 @@ import static com.codeclocker.plugin.intellij.services.vcs.ChangesActivityTracker.GLOBAL_REMOVALS; import static org.apache.commons.lang3.StringUtils.isNotBlank; -import com.codeclocker.plugin.intellij.analytics.Analytics; -import com.codeclocker.plugin.intellij.analytics.AnalyticsEventType; import com.codeclocker.plugin.intellij.apikey.ApiKeyLifecycle; import com.codeclocker.plugin.intellij.apikey.ApiKeyPersistence; import com.codeclocker.plugin.intellij.apikey.EnterApiKeyAction; @@ -120,28 +118,20 @@ public boolean isSelectable(String value) { @Override public PopupStep onChosen(String selectedValue, boolean finalChoice) { if (WEB_DASHBOARD.equals(selectedValue)) { - Analytics.track(AnalyticsEventType.POPUP_WEB_DASHBOARD_CLICK); BrowserUtil.browse(HUB_UI_HOST); } else if (SAVE_HISTORY.equals(selectedValue)) { - Analytics.track(AnalyticsEventType.POPUP_SAVE_HISTORY_CLICK); EnterApiKeyAction.showAction(); } else if (RENEW_SUBSCRIPTION.equals(selectedValue)) { - Analytics.track(AnalyticsEventType.POPUP_RENEW_SUBSCRIPTION_CLICK); BrowserUtil.browse(HUB_UI_HOST + "/payment"); } else if (SET_GOALS.equals(selectedValue)) { - Analytics.track(AnalyticsEventType.POPUP_SET_GOALS_CLICK); GoalSettingsDialog.showDialog(); } else if (SET_PROJECT_GOALS.equals(selectedValue)) { - Analytics.track(AnalyticsEventType.POPUP_SET_PROJECT_GOALS_CLICK); ProjectGoalSettingsDialog.showDialog(project); } else if (AUTO_PAUSE.equals(selectedValue)) { - Analytics.track(AnalyticsEventType.POPUP_AUTO_PAUSE_CLICK); TrackingSettingsDialog.showDialog(); } else if (DASHBOARD.equals(selectedValue)) { - Analytics.track(AnalyticsEventType.POPUP_DASHBOARD_CLICK); openToolWindowTab(project, "Dashboard"); } else if (ACTIVITY_REPORT.equals(selectedValue)) { - Analytics.track(AnalyticsEventType.POPUP_ACTIVITY_REPORT_CLICK); openToolWindowTab(project, "Activity"); } return FINAL_CHOICE; diff --git a/src/main/java/com/codeclocker/plugin/intellij/widget/TimeTrackerWidget.java b/src/main/java/com/codeclocker/plugin/intellij/widget/TimeTrackerWidget.java index 6802ee7..562992a 100644 --- a/src/main/java/com/codeclocker/plugin/intellij/widget/TimeTrackerWidget.java +++ b/src/main/java/com/codeclocker/plugin/intellij/widget/TimeTrackerWidget.java @@ -1,7 +1,5 @@ package com.codeclocker.plugin.intellij.widget; -import com.codeclocker.plugin.intellij.analytics.Analytics; -import com.codeclocker.plugin.intellij.analytics.AnalyticsEventType; import com.codeclocker.plugin.intellij.goal.GoalPersistence; import com.codeclocker.plugin.intellij.goal.GoalService; import com.codeclocker.plugin.intellij.services.TimeTrackerWidgetService; @@ -105,8 +103,6 @@ public String getTooltipText() { @Nullable @Override public ListPopup getPopup() { - Analytics.track(AnalyticsEventType.STATUS_BAR_WIDGET_CLICK); - String totalTime = service.getFormattedTotalTime(); String projectTime = service.getFormattedProjectTime();