6
6
package io .flutter ;
7
7
8
8
import com .google .common .annotations .VisibleForTesting ;
9
+ import com .google .gson .JsonObject ;
10
+ import com .google .gson .JsonPrimitive ;
9
11
import com .intellij .ProjectTopics ;
10
12
import com .intellij .ide .BrowserUtil ;
11
13
import com .intellij .ide .plugins .IdeaPluginDescriptor ;
12
14
import com .intellij .ide .plugins .PluginManagerCore ;
15
+ import com .intellij .ide .ui .UISettingsListener ;
13
16
import com .intellij .ide .util .PropertiesComponent ;
14
17
import com .intellij .notification .*;
15
18
import com .intellij .openapi .actionSystem .AnAction ;
16
19
import com .intellij .openapi .actionSystem .AnActionEvent ;
17
20
import com .intellij .openapi .application .ApplicationInfo ;
18
21
import com .intellij .openapi .application .ApplicationManager ;
22
+ import com .intellij .openapi .diagnostic .Logger ;
23
+ import com .intellij .openapi .editor .colors .EditorColorsListener ;
24
+ import com .intellij .openapi .editor .colors .EditorColorsManager ;
19
25
import com .intellij .openapi .extensions .PluginId ;
20
26
import com .intellij .openapi .fileEditor .FileEditorManager ;
21
27
import com .intellij .openapi .module .Module ;
25
31
import com .intellij .openapi .roots .ProjectRootManager ;
26
32
import com .intellij .openapi .startup .StartupActivity ;
27
33
import com .intellij .openapi .vfs .VirtualFile ;
34
+ import com .intellij .util .messages .MessageBusConnection ;
28
35
import com .jetbrains .lang .dart .analyzer .DartAnalysisServerService ;
36
+ import com .jetbrains .lang .dart .ide .toolingDaemon .DartToolingDaemonService ;
37
+ import de .roderick .weberknecht .WebSocketException ;
29
38
import io .flutter .analytics .Analytics ;
30
39
import io .flutter .analytics .FlutterAnalysisServerListener ;
31
40
import io .flutter .analytics .ToolWindowTracker ;
32
41
import io .flutter .analytics .UnifiedAnalytics ;
33
42
import io .flutter .android .IntelliJAndroidSdk ;
34
43
import io .flutter .bazel .WorkspaceCache ;
35
- import io .flutter .deeplinks . DeepLinksViewFactory ;
44
+ import io .flutter .dart . DtdUtils ;
36
45
import io .flutter .devtools .DevToolsExtensionsViewFactory ;
37
- import io .flutter .devtools .DevToolsExtensionsViewService ;
46
+ import io .flutter .devtools .DevToolsUtils ;
38
47
import io .flutter .devtools .RemainingDevToolsViewFactory ;
39
48
import io .flutter .editor .FlutterSaveActionsManager ;
40
49
import io .flutter .logging .FlutterConsoleLogManager ;
59
68
import javax .swing .event .HyperlinkEvent ;
60
69
import java .util .List ;
61
70
import java .util .UUID ;
71
+ import java .util .concurrent .ExecutionException ;
72
+ import java .util .concurrent .Executors ;
73
+ import java .util .concurrent .TimeUnit ;
74
+ import java .util .concurrent .atomic .AtomicLong ;
62
75
63
76
/**
64
77
* Runs actions after the project has started up and the index is up to date.
68
81
* may run when a project is being imported.
69
82
*/
70
83
public class FlutterInitializer implements StartupActivity {
84
+ private static final Logger LOG = Logger .getInstance (FlutterInitializer .class );
71
85
private static final String analyticsClientIdKey = "io.flutter.analytics.clientId" ;
72
86
private static final String analyticsOptOutKey = "io.flutter.analytics.optOut" ;
73
87
private static final String analyticsToastShown = "io.flutter.analytics.toastShown" ;
@@ -76,6 +90,10 @@ public class FlutterInitializer implements StartupActivity {
76
90
77
91
private boolean toolWindowsInitialized = false ;
78
92
93
+ private boolean busSubscribed = false ;
94
+
95
+ private @ NotNull AtomicLong lastScheduledThemeChangeTime = new AtomicLong ();
96
+
79
97
@ Override
80
98
public void runActivity (@ NotNull Project project ) {
81
99
// Convert all modules of deprecated type FlutterModuleType.
@@ -194,6 +212,9 @@ public void moduleAdded(@NotNull Project project, @NotNull Module module) {
194
212
// Set our preferred settings for the run console.
195
213
FlutterConsoleLogManager .initConsolePreferences ();
196
214
215
+ // Initialize notifications for theme changes.
216
+ setUpThemeChangeNotifications (project );
217
+
197
218
setUpDtdAnalytics (project );
198
219
199
220
// Initialize analytics.
@@ -255,6 +276,80 @@ private void setUpDtdAnalytics(Project project) {
255
276
t1 .start ();
256
277
}
257
278
279
+ private void setUpThemeChangeNotifications (Project project ) {
280
+ if (project == null ) return ;
281
+ FlutterSdk sdk = FlutterSdk .getFlutterSdk (project );
282
+ if (sdk == null || !sdk .getVersion ().canUseDtd ()) return ;
283
+ Thread t1 = new Thread (() -> {
284
+ if (busSubscribed ) return ;
285
+ final MessageBusConnection connection = ApplicationManager .getApplication ().getMessageBus ().connect ();
286
+ connection .subscribe (EditorColorsManager .TOPIC , (EditorColorsListener )scheme -> {
287
+ sendThemeChangedEvent (project );
288
+ });
289
+ connection .subscribe (UISettingsListener .TOPIC , (UISettingsListener )scheme -> {
290
+ sendThemeChangedEvent (project );
291
+ });
292
+ busSubscribed = true ;
293
+ });
294
+ t1 .start ();
295
+ }
296
+
297
+ private void sendThemeChangedEvent (@ NotNull Project project ) {
298
+ // Debounce this request because the topic subscriptions can trigger multiple times (potentially from initial notification of change and
299
+ // also from application of change)
300
+
301
+ // Set the current time of this request
302
+ final long requestTime = System .currentTimeMillis ();
303
+ lastScheduledThemeChangeTime .set (requestTime );
304
+
305
+ // Schedule event to be sent in a second if nothing more recent has come in.
306
+ Executors .newSingleThreadScheduledExecutor ().schedule (() -> {
307
+ if (lastScheduledThemeChangeTime .get () != requestTime ) {
308
+ // A more recent request has been set, so drop this request.
309
+ return ;
310
+ }
311
+
312
+ final JsonObject params = new JsonObject ();
313
+ params .addProperty ("eventKind" , "themeChanged" );
314
+ params .addProperty ("streamId" , "Editor" );
315
+ final JsonObject eventData = new JsonObject ();
316
+ final DevToolsUtils utils = new DevToolsUtils ();
317
+ eventData .addProperty ("isDarkMode" , Boolean .FALSE .equals (utils .getIsBackgroundBright ()));
318
+ eventData .addProperty ("backgroundColor" , utils .getColorHexCode ());
319
+ eventData .addProperty ("fontSize" , utils .getFontSize ().intValue ());
320
+ params .add ("eventData" , eventData );
321
+
322
+ try {
323
+ final DtdUtils dtdUtils = new DtdUtils ();
324
+ final DartToolingDaemonService dtdService = dtdUtils .readyDtdService (project ).get ();
325
+ if (dtdService == null ) {
326
+ LOG .error ("Unable to send theme changed event because DTD service is null" );
327
+ return ;
328
+ }
329
+
330
+ dtdService .sendRequest ("postEvent" , params , false , object -> {
331
+ JsonObject result = object .getAsJsonObject ("result" );
332
+ if (result == null ) {
333
+ LOG .error ("Theme changed event returned null result" );
334
+ return ;
335
+ }
336
+ JsonPrimitive type = result .getAsJsonPrimitive ("type" );
337
+ if (type == null ) {
338
+ LOG .error ("Theme changed event result type is null" );
339
+ return ;
340
+ }
341
+ if (!"Success" .equals (type .getAsString ())) {
342
+ LOG .error ("Theme changed event result: " + type .getAsString ());
343
+ }
344
+ }
345
+ );
346
+ }
347
+ catch (WebSocketException | InterruptedException | ExecutionException e ) {
348
+ LOG .error ("Unable to send theme changed event" , e );
349
+ }
350
+ }, 1 , TimeUnit .SECONDS );
351
+ }
352
+
258
353
private static void enableAnalytics (@ NotNull Project project ) {
259
354
ToolWindowTracker .track (project , getAnalytics ());
260
355
DartAnalysisServerService .getInstance (project ).addAnalysisServerListener (FlutterAnalysisServerListener .getInstance (project ));
0 commit comments