![JAR search and dependency download from the Maven repository](/logo.png)
dorkbox.util.Desktop Maven / Gradle / Ivy
/*
* Copyright 2017 dorkbox, llc
*
* Copyright (C) 2016 Tres Finocchiaro, QZ Industries, LLC
* Derivative code has been released as Apache 2.0, used with permission.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.util;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import dorkbox.executor.ShellExecutor;
import dorkbox.util.jna.linux.GnomeVFS;
import dorkbox.util.jna.linux.GtkCheck;
import dorkbox.util.jna.linux.GtkEventDispatch;
@SuppressWarnings({"WeakerAccess", "Convert2Lambda", "Duplicates"})
public
class Desktop {
// used for any linux system that has it...
private static final String GVFS = "/usr/bin/gvfs-open";
private static final boolean GVFS_VALID = new File(GVFS).canExecute();
/**
* Launches the default browser to display the specified HTTP address.
*
* If the default browser is not able to handle the specified address, the application registered for handling
* HTTP requests of the specified type is invoked.
*
* @param address the URL to browse/open
*/
public static
void browseURL(String address) throws IOException {
if (address == null || address.isEmpty()) {
throw new IOException("Address must not be null or empty.");
}
URI uri;
try {
uri = new URI(address);
browseURL(uri);
} catch (URISyntaxException e) {
throw new IOException("Invalid URI " + address);
}
}
/**
* Launches the default browser to display a {@code URI}.
*
* If the default browser is not able to handle the specified {@code URI}, the application registered for handling
* {@code URIs} of the specified type is invoked. The application is determined from the protocol and path of the {@code URI}, as
* defined by the {@code URI} class.
*
* @param uri the URL to browse/open
*/
public static
void browseURL(final URI uri) throws IOException {
if (uri == null) {
throw new IOException("URI must not be null.");
}
// Prevent GTK2/3 conflict caused by Desktop.getDesktop(), which is GTK2 only (via AWT)
// Prefer JNA method over AWT, since there are fewer chances for JNA to fail (even though they call the same method)
if ((OS.isUnix() || OS.isLinux()) && GtkCheck.isGtkLoaded) {
launchNix(uri.toString());
}
else if (java.awt.Desktop.isDesktopSupported() && java.awt.Desktop.getDesktop()
.isSupported(java.awt.Desktop.Action.BROWSE)) {
// make sure this doesn't block the current UI
SwingUtil.invokeLater(new Runnable() {
@Override
public
void run() {
try {
java.awt.Desktop.getDesktop()
.browse(uri);
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
else {
throw new IOException("Current OS and desktop configuration does not support browsing for a URL");
}
}
/**
* Launches the mail composing window of the user default mail client for the specified address.
*
* @param address who the email goes to
*/
public static
void launchEmail(String address) throws IOException {
if (address == null || address.isEmpty()) {
throw new IOException("Address must not be null or empty.");
}
URI uri;
try {
if (!address.startsWith("mailto:")) {
address = "mailto:" + address;
}
uri = new URI(address);
launchEmail(uri);
} catch (URISyntaxException e) {
throw new IOException("Invalid URI " + address);
}
}
/**
* Launches the mail composing window of the user default mail client, filling the message fields specified by a {@code mailto:} URI.
*
* A mailto:
URI can specify message fields including "to", "cc", "subject", "body", etc.
* See The mailto URL scheme (RFC 2368) for the {@code mailto:} URI specification
* details.
*
* @param uri the specified {@code mailto:} URI
*/
public static
void launchEmail(final URI uri) throws IOException {
if (uri == null) {
throw new IOException("URI must not be null.");
}
// Prevent GTK2/3 conflict caused by Desktop.getDesktop(), which is GTK2 only (via AWT)
// Prefer JNA method over AWT, since there are fewer chances for JNA to fail (even though they call the same method)
if ((OS.isUnix() || OS.isLinux()) && GtkCheck.isGtkLoaded) {
launchNix(uri.toString());
}
else if (java.awt.Desktop.isDesktopSupported() && java.awt.Desktop.getDesktop()
.isSupported(java.awt.Desktop.Action.MAIL)) {
// make sure this doesn't block the current UI
SwingUtil.invokeLater(new Runnable() {
@Override
public
void run() {
try {
java.awt.Desktop.getDesktop()
.mail(uri);
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
else {
throw new IOException("Current OS and desktop configuration does not support launching an email client");
}
}
/**
* Opens the specified path in the system-default file browser.
*
* Works around several OS limitations:
* - Apple tries to launch .app
bundle directories as applications rather than browsing contents
* - Linux has mixed support for Desktop.getDesktop()
. Uses JNA
instead.
*
* @param path The directory to browse
*/
public static
void browseDirectory(String path) throws IOException {
if (path == null || path.isEmpty()) {
throw new IOException("Path must not be null or empty.");
}
if (OS.isMacOsX()) {
File directory = new File(path);
// Mac tries to open the .app rather than browsing it. Instead, pass a child with -R to select it in finder
File[] files = directory.listFiles();
if (files != null && files.length > 0) {
// Get first child
File child = files[0];
if (!ShellExecutor.run("open", "-R", child.getCanonicalPath())) {
throw new IOException("Error opening the directory for " + path);
}
}
}
// Prevent GTK2/3 conflict caused by Desktop.getDesktop(), which is GTK2 only (via AWT)
// Prefer JNA method over AWT, since there are fewer chances for JNA to fail (even though they call the same method)
else if ((OS.isUnix() || OS.isLinux()) && GtkCheck.isGtkLoaded) {
// it can actually be MORE that just "file://" (ie, "ftp://" is legit as well)
if (!path.contains("://")) {
path = "file://" + path;
}
launchNix(path);
}
else if (java.awt.Desktop.isDesktopSupported() && java.awt.Desktop.getDesktop()
.isSupported(java.awt.Desktop.Action.OPEN)) {
final String finalPath = path;
// make sure this doesn't block the current UI
SwingUtil.invokeLater(new Runnable() {
@Override
public
void run() {
try {
java.awt.Desktop.getDesktop()
.open(new File(finalPath));
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
else {
throw new IOException("Current OS and desktop configuration does not support opening a directory to browse");
}
}
/**
* Only called when (OS.isUnix() || OS.isLinux()) && GtkCheck.isGtkLoaded
*
* Of important note, xdg-open can cause problems in Linux with Chrome installed but not the default browser. It will crash Chrome
* if Chrome was open before this app opened a URL
*
* There are a number of strange bugs with `xdg-open` and `gnome_vfs_url_show_with_env`, ubuntu, once again takes the cake for stupidity.
*
* @param path the path to open
*/
private static
void launchNix(final String path) {
if (GVFS_VALID) {
// ubuntu, fedora, etc MIGHT have access to gvfs-open. Ubuntu is also VERY buggy with xdg-open!!
ShellExecutor.run(GVFS, path);
}
else if (OSUtil.DesktopEnv.isGnome() && GnomeVFS.isInited) {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
// try to open the URL via gnome. This is exactly how (ultimately) java natively does this, but we do it via our own
// loaded version of GTK via JNA
int errorCode = GnomeVFS.gnome_vfs_url_show_with_env(path, null);
if (errorCode != 0) {
// if there are problems, use xdg-open
//
// there are problems with ubuntu and practically everything. Errors galore, and sometimes things don't even work.
// see: https://bugzilla.mozilla.org/show_bug.cgi?id=672671
// this can be really buggy ... you have been warned
ShellExecutor.run("xdg-open", path);
}
}
});
}
else {
// just use xdg-open, since it's not gnome.
// this can be really buggy ... you have been warned
ShellExecutor.run("xdg-open", path);
}
}
}