-
Notifications
You must be signed in to change notification settings - Fork 1
/
App.java
192 lines (178 loc) · 8.1 KB
/
App.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
package com.osiris.desku;
import com.osiris.desku.swing.LoadingWindow;
import com.osiris.desku.swing.NativeWindow;
import com.osiris.jlib.Stream;
import com.osiris.jlib.logger.AL;
import me.friwi.jcefmaven.CefAppBuilder;
import me.friwi.jcefmaven.MavenCefAppHandlerAdapter;
import org.cef.CefApp;
import org.cef.CefClient;
import org.cef.browser.CefMessageRouter;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
public class App {
/**
* Should be the directory in which this application was started. <br>
* Can be used to store information that is not specific to an user. <br>
*/
public static final File workingDir = new File(System.getProperty("user.dir"));
public static final CefApp cef;
public static final CefClient cefClient;
public static final CefMessageRouter cefMessageRouter;
public static final AtomicInteger cefMessageRouterRequestId = new AtomicInteger();
public static String name = "My Todo";
/**
* Should get cleared by the operating system on reboots. <br>
* Example on Windows: <br>
* C:\Users\UserName\AppData\Local\Temp\AppName
*/
public static final File tempDir = new File(System.getProperty("java.io.tmpdir") + "/" + name);
public static final File styles = new File(tempDir + "/global-styles.css");
/**
* Can be used to store user-specific data. <br>
* Example on Windows: <br>
* C:\Users\UserName\AppName
*/
public static final File userDir = new File(System.getProperty("user.home") + "/" + name);
public static CopyOnWriteArrayList<Route> routes = new CopyOnWriteArrayList<>();
public static CopyOnWriteArrayList<NativeWindow> windows = new CopyOnWriteArrayList<>();
static {
try {
Logger.getGlobal().setLevel(Level.SEVERE);
if (!AL.isStarted) {
AL.start("Logger", true, new File(workingDir + "/latest.log"), false);
AL.mirrorSystemStreams(new File(workingDir + "/mirror-out.log"), new File(workingDir + "/mirror-err.log"));
}
AL.info("Starting application...");
AL.info("workingDir = " + workingDir);
AL.info("tempDir = " + tempDir);
AL.info("userDir = " + userDir);
// Create styles file
styles.getParentFile().mkdirs();
if (styles.exists()) styles.delete();
styles.createNewFile();
// (0) Initialize CEF using the maven loader
CefAppBuilder builder = new CefAppBuilder();
try{
builder.setProgressHandler(new LoadingWindow().getProgressHandler());
} catch (Exception e) {
// Expected to fail on Android/iOS
AL.warn("Failed to open startup loading window, thus not displaying/logging JCEF load status.", e);
}
builder.getCefSettings().windowless_rendering_enabled = false;
// USE builder.setAppHandler INSTEAD OF CefApp.addAppHandler!
// Fixes compatibility issues with MacOSX
builder.setAppHandler(new MavenCefAppHandlerAdapter() {
@Override
public void stateHasChanged(org.cef.CefApp.CefAppState state) {
// Shutdown the app if the native CEF part is terminated
if (state == CefApp.CefAppState.TERMINATED) System.exit(0);
}
});
// (1) The entry point to JCEF is always the class CefApp.
cef = builder.build();
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
CefApp.getInstance().dispose();
}));
// (2) JCEF can handle one to many browser instances simultaneous.
cefClient = cef.createClient();
// (3) Create a simple message router to receive messages from CEF.
CefMessageRouter.CefMessageRouterConfig config = new CefMessageRouter.CefMessageRouterConfig();
config.jsQueryFunction = "cefQuery";
config.jsCancelFunction = "cefQueryCancel";
cefMessageRouter = CefMessageRouter.create(config);
cefClient.addMessageRouter(cefMessageRouter);
AL.info("Started application successfully!");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static Image getIcon() throws IOException {
try {
return getResourceImage("/icon.png");
} catch (Exception e) {
AL.warn("Failed to load /icon.png from current jar! Set default icon.", e);
return null; // TODO
}
}
/**
* @param path expected relative path to a file inside the current jar. Example: icon.png or /icon.png
*/
public static Image getResourceImage(String path) throws IOException {
String fullPath = (path.startsWith("/") ? path : "/" + path);
return Toolkit.getDefaultToolkit().getImage(getResourceURL(fullPath));
}
/**
* If the provided resource cannot be found it also checks these directories: <br>
* - App.workingDir <br>
* - App.workingDir + "/src/main/java" <br>
* - App.workingDir + "/src/test/java" <br>
*
* @param path expected relative path to a file inside the current jar. Example: help.txt or /help.txt
*/
public static InputStream getResource(String path) throws IOException {
String fullPath = (path.startsWith("/") ? path : "/" + path);
InputStream in = App.class.getResourceAsStream(fullPath);
if (in != null) return in;
File f = new File(App.workingDir + fullPath);
if (f.exists()) return Files.newInputStream(f.toPath());
f = new File(App.workingDir + "/src/main/java" + fullPath);
if (f.exists()) return Files.newInputStream(f.toPath());
f = new File(App.workingDir + "/src/test/java" + fullPath); // Support JUnit tests
if (f.exists()) return Files.newInputStream(f.toPath());
return null;
}
/**
* If the provided resource cannot be found it also checks these directories: <br>
* - App.workingDir <br>
* - App.workingDir + "/src/main/java" <br>
* - App.workingDir + "/src/test/java" <br>
*
* @param path expected relative path to a file inside the current jar. Example: help.txt or /help.txt
*/
public static URL getResourceURL(String path) throws IOException {
String fullPath = (path.startsWith("/") ? path : "/" + path);
URL url = App.class.getResource(fullPath);
if (url != null) return url;
File f = new File(App.workingDir + fullPath);
if (f.exists()) return f.toURI().toURL();
f = new File(App.workingDir + "/src/main/java" + fullPath);
if (f.exists()) return f.toURI().toURL();
f = new File(App.workingDir + "/src/test/java" + fullPath); // Support JUnit tests
if (f.exists()) return f.toURI().toURL();
return null;
}
public static void appendToGlobalStyles(String s) {
synchronized (styles) {
try {
Files.write(styles.toPath(), s.getBytes(StandardCharsets.UTF_8), StandardOpenOption.APPEND);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
/**
* Returns the .css file content as string.
* The css file is expected to have the same name as the class and be in the same package.
*/
public static String getCSS(Class<?> clazz) throws IOException {
return Stream.toString(App.getResource(clazz.getName().replace(".","/") + ".css"));
}
/**
* Returns the .js file content as string.
* The js file is expected to have the same name as the class and be in the same package.
*/
public static String getJS(Class<?> clazz) throws IOException {
return Stream.toString(App.getResource(clazz.getName().replace(".","/") + ".js"));
}
}