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();