Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
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
102 changes: 100 additions & 2 deletions flutter-idea/src/io/flutter/FlutterInitializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,22 @@
package io.flutter;

import com.google.common.annotations.VisibleForTesting;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.intellij.ProjectTopics;
import com.intellij.ide.BrowserUtil;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.PluginManagerCore;
import com.intellij.ide.ui.UISettingsListener;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.notification.*;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.application.ApplicationInfo;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.colors.EditorColorsListener;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.module.Module;
Expand All @@ -25,16 +31,19 @@
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.startup.StartupActivity;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.messages.MessageBusConnection;
import com.jetbrains.lang.dart.analyzer.DartAnalysisServerService;
import com.jetbrains.lang.dart.ide.toolingDaemon.DartToolingDaemonService;
import de.roderick.weberknecht.WebSocketException;
import io.flutter.analytics.Analytics;
import io.flutter.analytics.FlutterAnalysisServerListener;
import io.flutter.analytics.ToolWindowTracker;
import io.flutter.analytics.UnifiedAnalytics;
import io.flutter.android.IntelliJAndroidSdk;
import io.flutter.bazel.WorkspaceCache;
import io.flutter.deeplinks.DeepLinksViewFactory;
import io.flutter.dart.DtdUtils;
import io.flutter.devtools.DevToolsExtensionsViewFactory;
import io.flutter.devtools.DevToolsExtensionsViewService;
import io.flutter.devtools.DevToolsUtils;
import io.flutter.devtools.RemainingDevToolsViewFactory;
import io.flutter.editor.FlutterSaveActionsManager;
import io.flutter.logging.FlutterConsoleLogManager;
Expand All @@ -59,6 +68,10 @@
import javax.swing.event.HyperlinkEvent;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
* Runs actions after the project has started up and the index is up to date.
Expand All @@ -68,6 +81,7 @@
* may run when a project is being imported.
*/
public class FlutterInitializer implements StartupActivity {
private static final Logger LOG = Logger.getInstance(FlutterInitializer.class);
private static final String analyticsClientIdKey = "io.flutter.analytics.clientId";
private static final String analyticsOptOutKey = "io.flutter.analytics.optOut";
private static final String analyticsToastShown = "io.flutter.analytics.toastShown";
Expand All @@ -76,6 +90,10 @@ public class FlutterInitializer implements StartupActivity {

private boolean toolWindowsInitialized = false;

private boolean busSubscribed = false;

private @NotNull AtomicLong lastScheduledThemeChangeTime = new AtomicLong();

@Override
public void runActivity(@NotNull Project project) {
// Convert all modules of deprecated type FlutterModuleType.
Expand Down Expand Up @@ -194,6 +212,9 @@ public void moduleAdded(@NotNull Project project, @NotNull Module module) {
// Set our preferred settings for the run console.
FlutterConsoleLogManager.initConsolePreferences();

// Initialize notifications for theme changes.
setUpThemeChangeNotifications(project);

setUpDtdAnalytics(project);

// Initialize analytics.
Expand Down Expand Up @@ -255,6 +276,83 @@ private void setUpDtdAnalytics(Project project) {
t1.start();
}

private void setUpThemeChangeNotifications(Project project) {
if (project == null) return;
FlutterSdk sdk = FlutterSdk.getFlutterSdk(project);
if (sdk == null || !sdk.getVersion().canUseDtd()) return;
Thread t1 = new Thread(() -> {
if (busSubscribed) return;
final MessageBusConnection connection = ApplicationManager.getApplication().getMessageBus().connect();
connection.subscribe(EditorColorsManager.TOPIC, (EditorColorsListener)scheme -> {
sendThemeChangedEvent(project);
});
connection.subscribe(UISettingsListener.TOPIC, (UISettingsListener)scheme -> {
sendThemeChangedEvent(project);
});
busSubscribed = true;
});
t1.start();
}

private void sendThemeChangedEvent(@NotNull Project project) {
// Debounce this request because the topic subscriptions can trigger multiple times (potentially from initial notification of change and
// also from application of change)

// Set the current time of this request
final long requestTime = System.currentTimeMillis();
lastScheduledThemeChangeTime.set(requestTime);

// Schedule event to be sent in a second if nothing more recent has come in.
Executors.newSingleThreadScheduledExecutor().schedule(() -> {
if (lastScheduledThemeChangeTime.get() != requestTime) {
System.out.println("this request is being dropped: " + requestTime);
System.out.println("last scheduled time is: " + lastScheduledThemeChangeTime.get());
// A more recent request has been set, so drop this request.
return;
}

System.out.println("this request is being sent: " + requestTime);
final JsonObject params = new JsonObject();
params.addProperty("eventKind", "themeChanged");
params.addProperty("streamId", "Editor");
final JsonObject eventData = new JsonObject();
final DevToolsUtils utils = new DevToolsUtils();
eventData.addProperty("isDarkMode", Boolean.FALSE.equals(utils.getIsBackgroundBright()));
eventData.addProperty("backgroundColor", utils.getColorHexCode());
eventData.addProperty("fontSize", utils.getFontSize().intValue());
params.add("eventData", eventData);

try {
final DtdUtils dtdUtils = new DtdUtils();
final DartToolingDaemonService dtdService = dtdUtils.readyDtdService(project).get();
if (dtdService == null) {
LOG.error("Unable to send theme changed event because DTD service is null");
return;
}

dtdService.sendRequest("postEvent", params, false, object -> {
JsonObject result = object.getAsJsonObject("result");
if (result == null) {
LOG.error("Theme changed event returned null result");
return;
}
JsonPrimitive type = result.getAsJsonPrimitive("type");
if (type == null) {
LOG.error("Theme changed event result type is null");
return;
}
if (!"Success".equals(type.getAsString())) {
LOG.error("Theme changed event result: " + type.getAsString());
}
}
);
}
catch (WebSocketException | InterruptedException | ExecutionException e) {
LOG.error("Unable to send theme changed event", e);
}
}, 1, TimeUnit.SECONDS);
}

private static void enableAnalytics(@NotNull Project project) {
ToolWindowTracker.track(project, getAnalytics());
DartAnalysisServerService.getInstance(project).addAnalysisServerListener(FlutterAnalysisServerListener.getInstance(project));
Expand Down
31 changes: 4 additions & 27 deletions flutter-idea/src/io/flutter/analytics/UnifiedAnalytics.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.intellij.openapi.project.Project;
import com.jetbrains.lang.dart.ide.toolingDaemon.DartToolingDaemonService;
import de.roderick.weberknecht.WebSocketException;
import io.flutter.dart.DtdUtils;
import io.flutter.sdk.FlutterSdkUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand All @@ -31,19 +32,19 @@ public class UnifiedAnalytics {

@Nullable Boolean enabled = null;
final Project project;
final DartToolingDaemonService dtdService;
final DtdUtils dtdUtils;
@NotNull final FlutterSdkUtil flutterSdkUtil;


public UnifiedAnalytics(@NotNull Project project) {
this.project = project;
this.dtdService = DartToolingDaemonService.getInstance(project);
this.dtdUtils = new DtdUtils();
this.flutterSdkUtil = new FlutterSdkUtil();
}

public void manageConsent() {
try {
DartToolingDaemonService service = readyDtdService().get();
DartToolingDaemonService service = dtdUtils.readyDtdService(project).get();
if (service != null) {
final JsonObject params = new JsonObject();
params.addProperty("tool", getToolName());
Expand Down Expand Up @@ -201,28 +202,4 @@ else if ("Android-Studio".equals(ideValue)) {
return innerResult;
});
}

private CompletableFuture<DartToolingDaemonService> readyDtdService() {
CompletableFuture<DartToolingDaemonService> readyService = new CompletableFuture<>();
int attemptsRemaining = 10;
final int TIME_IN_BETWEEN = 2;
while (attemptsRemaining > 0) {
attemptsRemaining--;
if (dtdService != null && dtdService.getWebSocketReady()) {
readyService.complete(dtdService);
break;
}
try {
Thread.sleep(TIME_IN_BETWEEN * 1000);
}
catch (InterruptedException e) {
readyService.completeExceptionally(e);
break;
}
}
if (!readyService.isDone()) {
readyService.completeExceptionally(new Exception("Timed out waiting for DTD websocket to start"));
}
return readyService;
}
}
39 changes: 39 additions & 0 deletions flutter-idea/src/io/flutter/dart/DtdUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2024 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.dart;

import com.intellij.openapi.project.Project;
import com.jetbrains.lang.dart.ide.toolingDaemon.DartToolingDaemonService;
import org.jetbrains.annotations.NotNull;

import java.util.concurrent.CompletableFuture;

public class DtdUtils {
public CompletableFuture<DartToolingDaemonService> readyDtdService(@NotNull Project project) {
final DartToolingDaemonService dtdService = DartToolingDaemonService.getInstance(project);
CompletableFuture<DartToolingDaemonService> readyService = new CompletableFuture<>();
int attemptsRemaining = 10;
final int TIME_IN_BETWEEN = 2;
while (attemptsRemaining > 0) {
attemptsRemaining--;
if (dtdService.getWebSocketReady()) {
readyService.complete(dtdService);
break;
}
try {
Thread.sleep(TIME_IN_BETWEEN * 1000);
}
catch (InterruptedException e) {
readyService.completeExceptionally(e);
break;
}
}
if (!readyService.isDone()) {
readyService.completeExceptionally(new Exception("Timed out waiting for DTD websocket to start"));
}
return readyService;
}
}
8 changes: 4 additions & 4 deletions flutter-idea/src/io/flutter/devtools/DevToolsUrl.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class DevToolsUrl {
private String devToolsHost;
Expand All @@ -38,7 +39,7 @@ public class DevToolsUrl {

public final DevToolsIdeFeature ideFeature;

private final DevToolsUtils devToolsUtils;
@NotNull private final DevToolsUtils devToolsUtils;

public static class Builder {
private String devToolsHost;
Expand Down Expand Up @@ -222,7 +223,7 @@ public String getUrlString() {

public void maybeUpdateColor() {
final String newColor = devToolsUtils.getColorHexCode();
if (colorHexCode == newColor) {
if (Objects.equals(colorHexCode, newColor)) {
return;
}

Expand All @@ -231,8 +232,7 @@ public void maybeUpdateColor() {
}

public void maybeUpdateFontSize() {
EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
final Float newFontSize = (float) scheme.getEditorFontSize();
final Float newFontSize = devToolsUtils.getFontSize();
if (fontSize == null || !fontSize.equals(newFontSize)) {
fontSize = newFontSize;
}
Expand Down
12 changes: 11 additions & 1 deletion flutter-idea/src/io/flutter/devtools/DevToolsUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
*/
package io.flutter.devtools;

import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.ui.ColorUtil;
import com.intellij.ui.JBColor;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;

public class DevToolsUtils {
public static String findWidgetId(String url) {
Expand All @@ -29,5 +32,12 @@ public Boolean getIsBackgroundBright() {
return JBColor.isBright();
}

public Float getFontSize() {return UIUtil.getFontSize(UIUtil.FontSize.NORMAL);}
public @NotNull Float getFontSize() {
EditorColorsManager manager = EditorColorsManager.getInstance();
if (manager == null) {
// Return the default normal font size if editor manager is not found.
return UIUtil.getFontSize(UIUtil.FontSize.NORMAL);
}
return (float) manager.getGlobalScheme().getEditorFontSize();
}
}