Open your own links and files - fakeivchenko/lwjwae GitHub Wiki
An application can own a scheme of links, such as notes://today, and the types of files that it
opens, such as .note. A browser, a chat, or the file manager then hands them to the application.
In this guide, a notes application hears of a link and of a file, both in the window that already
runs.

Register the scheme and the type
The Gradle plugin registers both in every package that it builds:
lwjwae {
displayName = "Notes"
urlScheme("notes")
fileType("note")
packaging.all()
}
| Line | What it registers |
|---|---|
urlScheme("notes") |
Links that start with notes:. A scheme is a lowercase letter, then letters, digits, +, -, or .. The schemes of the system, such as https and mailto, are refused. |
fileType("note") |
Files that end with .note, as the media type application/x-IMAGE-note, where IMAGE is the name of the executable. The file manager calls the type by the display name and "document", such as "Notes document". |
fileType("md", "text/markdown") |
The same, with a media type of your own. |
fileType("note", "application/x-notes", "Note") |
The same, with what the file manager calls the type. |
| Package | Where the registration goes |
|---|---|
.deb, Arch package, AppImage |
The MimeType line of the desktop entry, with x-scheme-handler/notes for the scheme, and a shared-mime-info type in /usr/share/mime/packages for each type of file. |
.app and .dmg |
CFBundleURLTypes and CFBundleDocumentTypes in Info.plist. |
.msi |
The classes of the user in the registry, under HKCU\Software\Classes, without administrator rights. |
The registration comes with the installation, so ./gradlew run doesn't register anything. To try
the code under run, pass a link or a file as an argument, as the system would.
Hear what the system opens
application.onOpen hears every request. With a single instance, a link that the user opens while
the application runs reaches the running window instead of a second one:
public class OpenLinks {
public static void main(String[] args) {
ApplicationParameters parameters = ApplicationParameters.builder().name("Notes").build();
Optional<Application> instance = Application.createSingleInstance(parameters, args);
if (instance.isEmpty()) {
return; // The running instance hears of the links and the files.
}
try (Application application = instance.get()) {
Window window =
application.show(
WindowParameters.builder()
.title("Notes")
.size(new WindowSize(560, 360))
.resource("open/index.html")
.build());
application.onOpen(
event -> {
event.urls().forEach(url -> window.emit("link", url.toString()));
event.files().forEach(file -> window.emit("file", file.getFileName().toString()));
});
application.run();
}
}
}
An OpenEvent has two lists: urls, the links as URIs, and files, the files and folders as
absolute Paths. The page shows what arrives:
lwjwae.listen("link", ({ payload }) => add(`Link: ${payload}`));
lwjwae.listen("file", ({ payload }) => add(`File: ${payload}`));
What counts as a link or a file
On Windows and Linux, the system starts the executable with the link or the file as an argument. The arguments also carry what the program needs for itself, so only what is clearly a link or a file counts:
- A URI with a scheme of two letters or more is a link. A
file:URI is a file. A Windows drive, such asC:, isn't a scheme. - A path that exists, relative to the working directory, is a file.
- A flag, which starts with
-, and anything else are left out.
Application.create(parameters, args) and Application.createSingleInstance(parameters, args)
read the arguments of the first start. A request that comes before any listener waits for the first
one, so onOpen after show misses nothing.
Platforms
| Platform | How a request arrives |
|---|---|
| Windows | A new process with the link or the file as an argument. A running application hears of it only as a single instance. |
| macOS | AppKit hands the links and the files to the running process through application:openURLs:, and starts it first when none runs. No second process starts, with or without a single instance. |
| Linux | As on Windows, through the Exec line of the desktop entry. |
Good to know
- Treat a link like any input from outside: a page of the web can open
notes://with whatever follows. Check it before you act on it. - The
nameof the application is what the processes of a single instance meet under, so keep it the same from one version to the next. See Run a single instance.