Updated Desktop utility class to use JNA native access for

browseURL/email/browseDirectory methods. This also fixed issues where
xdg-open could cause strange problems with Chrome on Linux (but not as
the default browser).
This commit is contained in:
nathan 2017-07-16 02:08:15 +02:00
parent dd7892483e
commit 0ecc75aa7d
1 changed files with 82 additions and 22 deletions

View File

@ -24,11 +24,35 @@ import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import dorkbox.util.jna.linux.Gtk2;
import dorkbox.util.jna.linux.GtkEventDispatch;
import dorkbox.util.process.ShellExecutor; import dorkbox.util.process.ShellExecutor;
@SuppressWarnings("WeakerAccess") @SuppressWarnings({"WeakerAccess", "Convert2Lambda"})
public public
class Desktop { class Desktop {
/**
* 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}. * Launches the default browser to display a {@code URI}.
* *
@ -37,15 +61,24 @@ class Desktop {
* defined by the {@code URI} class. * defined by the {@code URI} class.
* *
* @param uri the URL to browse/open * @param uri the URL to browse/open
*
* @throws IOException
*/ */
public static void browseURL(URI uri) throws IOException { public static void browseURL(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) // Prevent GTK2/3 conflict caused by Desktop.getDesktop(), which is GTK2 only (via AWT)
if ((OS.isUnix() || OS.isLinux()) && OSUtil.DesktopEnv.isGtkLoaded && OSUtil.DesktopEnv.isGtk3) { // Prefer JNA method over AWT, since there are fewer chances for JNA to fail (even though they call the same method)
if (!ShellExecutor.run("xdg-open", uri.toString())) { // Additionally, xdg-open can cause problems in Linux with Chrome installed but not the default browser. It will crash Chrome
throw new IOException("Error running xdg-open for " + uri.toString()); // if Chrome was open before this app opened a URL
} if ((OS.isUnix() || OS.isLinux()) && OSUtil.DesktopEnv.isGtkLoaded) {
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
Gtk2.Gtk2.gtk_show_uri(Gtk2.Gtk2.gdk_screen_get_default(), uri.toString(), 0, null);
}
});
} }
else { else {
if (java.awt.Desktop.isDesktopSupported() && java.awt.Desktop.getDesktop().isSupported(java.awt.Desktop.Action.BROWSE)) { if (java.awt.Desktop.isDesktopSupported() && java.awt.Desktop.getDesktop().isSupported(java.awt.Desktop.Action.BROWSE)) {
@ -60,12 +93,18 @@ class Desktop {
* Launches the mail composing window of the user default mail client for the specified address. * Launches the mail composing window of the user default mail client for the specified address.
* *
* @param address who the email goes to * @param address who the email goes to
*
* @throws IOException
*/ */
public static void launchEmail(String address) throws IOException { public static void launchEmail(String address) throws IOException {
URI uri = null; if (address == null || address.isEmpty()) {
throw new IOException("Address must not be null or empty.");
}
URI uri;
try { try {
if (!address.startsWith("mailto:")) {
address = "mailto:" + address;
}
uri = new URI(address); uri = new URI(address);
launchEmail(uri); launchEmail(uri);
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
@ -81,15 +120,21 @@ class Desktop {
* details. * details.
* *
* @param uri the specified {@code mailto:} URI * @param uri the specified {@code mailto:} URI
*
* @throws IOException
*/ */
public static void launchEmail(final URI uri) throws IOException { 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) // Prevent GTK2/3 conflict caused by Desktop.getDesktop(), which is GTK2 only (via AWT)
if ((OS.isUnix() || OS.isLinux()) && OSUtil.DesktopEnv.isGtkLoaded && OSUtil.DesktopEnv.isGtk3) { if ((OS.isUnix() || OS.isLinux()) && OSUtil.DesktopEnv.isGtkLoaded) {
if (!ShellExecutor.run("xdg-email", uri.toString())) { GtkEventDispatch.dispatch(new Runnable() {
throw new IOException("Error running xdg-email for " + uri.toString()); @Override
} public
void run() {
Gtk2.Gtk2.gtk_show_uri(Gtk2.Gtk2.gdk_screen_get_default(), uri.toString(), 0, null);
}
});
} }
else { else {
if (java.awt.Desktop.isDesktopSupported() && java.awt.Desktop.getDesktop().isSupported(java.awt.Desktop.Action.MAIL)) { if (java.awt.Desktop.isDesktopSupported() && java.awt.Desktop.getDesktop().isSupported(java.awt.Desktop.Action.MAIL)) {
@ -105,13 +150,15 @@ class Desktop {
* *
* Works around several OS limitations: * Works around several OS limitations:
* - Apple tries to launch <code>.app</code> bundle directories as applications rather than browsing contents * - Apple tries to launch <code>.app</code> bundle directories as applications rather than browsing contents
* - Linux has mixed support for <code>Desktop.getDesktop()</code>. Uses the <code>xdg-open</code> fallback. * - Linux has mixed support for <code>Desktop.getDesktop()</code>. Uses <code>JNA</code> instead.
* *
* @param path The directory to browse * @param path The directory to browse
*
* @throws IOException
*/ */
public static void browseDirectory(String path) throws IOException { 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()) { if (OS.isMacOsX()) {
File directory = new File(path); File directory = new File(path);
@ -123,13 +170,26 @@ class Desktop {
if (!ShellExecutor.run("open", "-R", child.getCanonicalPath())) { if (!ShellExecutor.run("open", "-R", child.getCanonicalPath())) {
throw new IOException("Error opening the directory for " + path); throw new IOException("Error opening the directory for " + path);
} }
return;
} }
} else { } else {
// Prevent GTK2/3 conflict caused by Desktop.getDesktop(), which is GTK2 only (via AWT) // Prevent GTK2/3 conflict caused by Desktop.getDesktop(), which is GTK2 only (via AWT)
if ((OS.isUnix() || OS.isLinux()) && OSUtil.DesktopEnv.isGtkLoaded && OSUtil.DesktopEnv.isGtk3) { // Prefer JNA method over AWT, since there are fewer chances for JNA to fail (even though they call the same method)
if (!ShellExecutor.run("xdg-open", path)) { if ((OS.isUnix() || OS.isLinux()) && OSUtil.DesktopEnv.isGtkLoaded) {
throw new IOException("Error running xdg-open for " + path); // it can actually be MORE that just "file://" (ie, "ftp://" is legit as well)
if (!path.contains("://")) {
path = "file://" + path;
} }
final String finalPath = path;
GtkEventDispatch.dispatch(new Runnable() {
@Override
public
void run() {
Gtk2.Gtk2.gtk_show_uri(Gtk2.Gtk2.gdk_screen_get_default(), finalPath, 0, null);
}
});
return;
} }
else { else {
if (java.awt.Desktop.isDesktopSupported() && java.awt.Desktop.getDesktop().isSupported(java.awt.Desktop.Action.OPEN)) { if (java.awt.Desktop.isDesktopSupported() && java.awt.Desktop.getDesktop().isSupported(java.awt.Desktop.Action.OPEN)) {