From 8f7abe2dc532902b76b126aae7999b9b7ab871d2 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Mon, 5 Aug 2024 00:01:38 +0400 Subject: [PATCH] GUI: added old what's new page (build-in window on startup); --- Mage.Client/pom.xml | 18 + .../src/main/java/mage/client/MageFrame.java | 27 +- .../java/mage/client/dialog/AboutDialog.java | 2 +- .../mage/client/dialog/ConnectDialog.java | 5 +- .../java/mage/client/dialog/ErrorDialog.java | 2 +- .../mage/client/dialog/WhatsNewDialog.form | 73 ++++ .../mage/client/dialog/WhatsNewDialog.java | 411 ++++++++++++++++++ .../client/remote/XmageURLConnection.java | 7 +- .../java/mage/client/table/TablesPanel.java | 2 +- .../main/java/mage/client/util/AppUtil.java | 2 +- .../java/mage/client/util/URLHandler.java | 13 +- pom.xml | 2 +- 12 files changed, 540 insertions(+), 24 deletions(-) create mode 100644 Mage.Client/src/main/java/mage/client/dialog/WhatsNewDialog.form create mode 100644 Mage.Client/src/main/java/mage/client/dialog/WhatsNewDialog.java diff --git a/Mage.Client/pom.xml b/Mage.Client/pom.xml index e5445fb2a68..afe8ef85ddc 100644 --- a/Mage.Client/pom.xml +++ b/Mage.Client/pom.xml @@ -144,6 +144,24 @@ 1.17 + + + + org.openjfx + javafx-controls + 11.0.2 + + + org.openjfx + javafx-swing + 11.0.2 + + + org.openjfx + javafx-web + 11.0.2 + + diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index 6700d99e241..73e50b20320 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -72,6 +72,8 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.SocketException; +import java.net.URI; +import java.net.URISyntaxException; import java.nio.charset.Charset; import java.util.*; import java.util.concurrent.Executors; @@ -109,6 +111,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { private static CallbackClient callbackClient; private static final Preferences PREFS = Preferences.userNodeForPackage(MageFrame.class); private final JPanel fakeTopPanel; + private WhatsNewDialog whatsNewDialog; // can be null private JLabel title; private Rectangle titleRectangle; private static final MageVersion VERSION = new MageVersion(MageFrame.class); @@ -306,6 +309,15 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { errorDialog.setLocation(100, 100); desktopPane.add(errorDialog, JLayeredPane.MODAL_LAYER); + try { + this.whatsNewDialog = new WhatsNewDialog(); + } catch (Throwable e) { + // example: JavaFX is not supported on old MacOS with OpenJDK + // https://bugs.openjdk.java.net/browse/JDK-8202132 + LOGGER.error("JavaFX is not supported by your system. What's new page will be disabled.", e); + this.whatsNewDialog = null; + } + PING_SENDER_EXECUTOR.scheduleAtFixedRate(SessionHandler::ping, TablesPanel.PING_SERVER_SECS, TablesPanel.PING_SERVER_SECS, TimeUnit.SECONDS); updateMemUsageTask = new UpdateMemUsageTask(jMemUsageLabel); @@ -384,6 +396,11 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { } setWindowTitle(); }); + + // run what's new checks (loading in background) + SwingUtilities.invokeLater(() -> { + showWhatsNewDialog(false); + }); } private void bootstrapSetsAndFormats() { @@ -1861,8 +1878,14 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { updateTooltipContainerSizes(); } - public static void showWhatsNewDialog() { - AppUtil.openUrlInBrowser("https://jaydi85.github.io/xmage-web-news/news.html"); + public void showWhatsNewDialog(boolean forceToShowPage) { + if (whatsNewDialog != null) { + // build-in browser + whatsNewDialog.checkUpdatesAndShow(forceToShowPage); + } else { + // system browser + AppUtil.openUrlInSystemBrowser(WhatsNewDialog.WHATS_NEW_PAGE); + } } public boolean isGameFrameActive(UUID gameId) { diff --git a/Mage.Client/src/main/java/mage/client/dialog/AboutDialog.java b/Mage.Client/src/main/java/mage/client/dialog/AboutDialog.java index a5e4a0032fe..4e1ac407315 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/AboutDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/AboutDialog.java @@ -147,7 +147,7 @@ public class AboutDialog extends MageDialog { }//GEN-LAST:event_btnOkActionPerformed private void btnWhatsNewActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnWhatsNewActionPerformed - MageFrame.showWhatsNewDialog(); + MageFrame.getInstance().showWhatsNewDialog(true); }//GEN-LAST:event_btnWhatsNewActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables diff --git a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java index 9ed7e23ded8..dece061fa10 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java @@ -13,7 +13,6 @@ import mage.utils.StreamUtils; import org.apache.log4j.Logger; import javax.swing.*; -import java.awt.*; import java.awt.event.ActionListener; import java.io.*; import java.net.*; @@ -739,11 +738,11 @@ public class ConnectDialog extends MageDialog { }//GEN-LAST:event_btnFlagSearchActionPerformed private void btnCheckStatusActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCheckStatusActionPerformed - AppUtil.openUrlInBrowser("http://xmage.today/servers/"); + AppUtil.openUrlInSystemBrowser("http://xmage.today/servers/"); }//GEN-LAST:event_btnCheckStatusActionPerformed private void btnWhatsNewActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnWhatsNewActionPerformed - MageFrame.showWhatsNewDialog(); + MageFrame.getInstance().showWhatsNewDialog(true); }//GEN-LAST:event_btnWhatsNewActionPerformed private void btnFindMainActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnFindMainActionPerformed diff --git a/Mage.Client/src/main/java/mage/client/dialog/ErrorDialog.java b/Mage.Client/src/main/java/mage/client/dialog/ErrorDialog.java index c19038184c9..80952993e75 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/ErrorDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/ErrorDialog.java @@ -64,7 +64,7 @@ public class ErrorDialog extends MageDialog { CardUtil.urlEncode(title), CardUtil.urlEncode(body) ); - AppUtil.openUrlInBrowser(url); + AppUtil.openUrlInSystemBrowser(url); } /** This method is called from within the constructor to diff --git a/Mage.Client/src/main/java/mage/client/dialog/WhatsNewDialog.form b/Mage.Client/src/main/java/mage/client/dialog/WhatsNewDialog.form new file mode 100644 index 00000000000..af26b1a5408 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/dialog/WhatsNewDialog.form @@ -0,0 +1,73 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Mage.Client/src/main/java/mage/client/dialog/WhatsNewDialog.java b/Mage.Client/src/main/java/mage/client/dialog/WhatsNewDialog.java new file mode 100644 index 00000000000..ea8265924ff --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/dialog/WhatsNewDialog.java @@ -0,0 +1,411 @@ +package mage.client.dialog; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import javafx.application.Platform; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.concurrent.Worker; +import javafx.embed.swing.JFXPanel; +import javafx.scene.Scene; +import javafx.scene.web.WebEngine; +import javafx.scene.web.WebView; +import mage.client.MageFrame; +import mage.client.remote.XmageURLConnection; +import mage.client.util.AppUtil; +import mage.client.util.GUISizeHelper; +import org.apache.log4j.Logger; +import org.w3c.dom.events.EventListener; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.lang.reflect.Type; +import java.net.*; +import java.util.List; +import java.util.stream.Collectors; + +/** + * App GUI: what's new dialog with latest news. + * Uses system browser. Shows on app's start after page ready. + * + * @author JayDi85 + */ +public class WhatsNewDialog extends MageDialog { + + private static final Logger LOGGER = Logger.getLogger(WhatsNewDialog.class); + + // cookies store tester: https://setcookie.net/ + public static final String WHATS_NEW_PAGE = "https://jaydi85.github.io/xmage-web-news/news.html"; + private static final String WHATS_NEW_VERSION_PAGE = "https://jaydi85.github.io/xmage-web-news/news_version.html"; // increment version=123 to auto-shown for all users + private static final int WHATS_NEW_MAX_LOAD_TIMEOUT_SECS = 20; // timeout for page loading (example: no network) + private static final boolean WHATS_NEW_DEBUG_ENABLE_CONTROLS = false; // default: false, enable it for debug/test + + private final JFXPanel fxPanel; + private WebView webView; + private WebEngine engine; + private boolean isPageReady = false; + + private SwingWorker lastWaitingWorker = null; + + public WhatsNewDialog() { + initComponents(); + this.setDefaultCloseOperation(HIDE_ON_CLOSE); + + fxPanel = new JFXPanel(); + panelData.add(fxPanel); + webView = null; + engine = null; + + createWebView(); + } + + private void showDialog() { + this.setModal(true); + this.setResizable(true); + + getRootPane().setDefaultButton(buttonCancel); + this.setSize(GUISizeHelper.dialogGuiScaleSize(new Dimension(800, 600))); + + // windows settings + MageFrame.getDesktop().remove(this); + if (this.isModal()) { + MageFrame.getDesktop().add(this, JLayeredPane.MODAL_LAYER); + } else { + MageFrame.getDesktop().add(this, JLayeredPane.PALETTE_LAYER); + } + this.makeWindowCentered(); + + // Close on "ESC" + registerKeyboardAction(e -> onCancel(), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + + this.setVisible(true); + } + + private final SwingWorker checkUpdatesWorker = new SwingWorker() { + private String newVersion = ""; + + @Override + public Void doInBackground() { + // download version + String newsVersion = XmageURLConnection.downloadText(WHATS_NEW_VERSION_PAGE); + if (newsVersion.startsWith("version=")) { + newVersion = newsVersion.substring("version=".length()); + } + return null; + } + + @Override + public void done() { + SwingUtilities.invokeLater(() -> { + String oldVersion = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEWS_PAGE_LAST_VERSION, "1"); + + boolean isHaveUpdates = newVersion.isEmpty() || !newVersion.equals(oldVersion); + if (isHaveUpdates) { + PreferencesDialog.saveValue(PreferencesDialog.KEY_NEWS_PAGE_LAST_VERSION, newVersion); + startBrowser(WHATS_NEW_PAGE); + startWaitingWorker(); + } + }); + } + }; + + private void startWaitingWorker() { + // wait page ready and open it on complete + if (this.lastWaitingWorker != null) { + this.lastWaitingWorker.cancel(true); + } + + this.lastWaitingWorker = new SwingWorker() { + @Override + public Void doInBackground() { + // waiting page loading + int waitedSecs = 0; + while (!isPageReady && waitedSecs <= WHATS_NEW_MAX_LOAD_TIMEOUT_SECS) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return null; + } + + waitedSecs++; + } + return null; + } + + @Override + public void done() { + if (isPageReady) { + SwingUtilities.invokeLater(() -> { + showDialog(); + }); + } + } + }; + this.lastWaitingWorker.execute(); + } + + public void checkUpdatesAndShow(boolean forceToShowPage) { + // lazy loading in background + // shows it on page ready or by force + + if (!forceToShowPage) { + // checks version -> start loading -> show on isPageReady + checkUpdatesWorker.execute(); + return; + } + + // direct open + if (isPageReady) { + SwingUtilities.invokeLater(() -> { + showDialog(); + }); + } else { + checkUpdatesWorker.cancel(true); + startBrowser(WHATS_NEW_PAGE); + startWaitingWorker(); + } + } + + + /** + * Store cookies in preferences + */ + private static class PersistentCookieStore implements CookieStore, Runnable { + private final CookieStore store; + + public PersistentCookieStore() { + // improved store with save/load feature + store = new CookieManager().getCookieStore(); + + // load on startup + loadFromPrefs(); + + // save on app close + Runtime.getRuntime().addShutdownHook(new Thread(this)); + } + + private void saveToPrefs() { + // convert cookie to version 1, so it will get full data before save + // example: xxx + List v1Cookies = store.getCookies().stream() + .peek(c -> c.setVersion(1)) + .map(HttpCookie::toString) + .collect(Collectors.toList()); + PreferencesDialog.saveValue(PreferencesDialog.KEY_NEWS_PAGE_COOKIES, new Gson().toJson(v1Cookies)); + } + + private void loadFromPrefs() { + Type type = new TypeToken>() { + }.getType(); + try { + String savedData = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEWS_PAGE_COOKIES, ""); + List savedCookies = new Gson().fromJson(savedData, type); + if (savedCookies != null) { + savedCookies.forEach(savedCookie -> { + // load as version 1, but convert back to version 0 for compatibility + List v1Cookies = HttpCookie.parse("set-cookie2:" + savedCookie.replace("$", "")); + v1Cookies.forEach(realCookie -> { + realCookie.setVersion(0); + store.add(URI.create(realCookie.getDomain()), realCookie); + }); + }); + } + } catch (Exception e) { + LOGGER.error("News page: catch broken cookies", e); + } + } + + @Override + public void run() { + saveToPrefs(); + } + + @Override + public void add(URI uri, HttpCookie cookie) { + store.add(uri, cookie); + } + + @Override + public List get(URI uri) { + return store.get(uri); + } + + @Override + public List getCookies() { + return store.getCookies(); + } + + @Override + public List getURIs() { + return store.getURIs(); + } + + @Override + public boolean remove(URI uri, HttpCookie cookie) { + return store.remove(uri, cookie); + } + + @Override + public boolean removeAll() { + return store.removeAll(); + } + } + + private void createWebView() { + + // init web engine and events + // https://docs.oracle.com/javafx/2/swing/swing-fx-interoperability.htm + + // workaround for empty dialog on 2+ opens - keep jfx thread alive (by default it exits on parent window close) + // see https://stackoverflow.com/a/32104851 + Platform.setImplicitExit(false); + + Platform.runLater(() -> { + webView = new WebView(); + engine = webView.getEngine(); + engine.setJavaScriptEnabled(true); + engine.setUserAgent(engine.getUserAgent() + " " + XmageURLConnection.getDefaultUserAgent()); // keep system user-agent too + if (!WHATS_NEW_DEBUG_ENABLE_CONTROLS) { + webView.contextMenuEnabledProperty().setValue(false); + } + + CookieManager cookieManager = new CookieManager(new PersistentCookieStore(), CookiePolicy.ACCEPT_ALL); + CookieHandler.setDefault(cookieManager); + + // on error + engine.getLoadWorker().exceptionProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue o, Throwable old, final Throwable value) { + if (engine.getLoadWorker().getState() == Worker.State.FAILED + || engine.getLoadWorker().getState() == Worker.State.CANCELLED) { + LOGGER.error("News page: can't load page", value); + } + } + }); + + // on completed + engine.getLoadWorker().stateProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue ov, Worker.State oldState, Worker.State newState) { + if (newState != Worker.State.SUCCEEDED) { + return; + } + + if (!WHATS_NEW_DEBUG_ENABLE_CONTROLS) { + // 1. open all page links in real browser, not build-in window + EventListener listener = new EventListener() { + @Override + public void handleEvent(org.w3c.dom.events.Event ev) { + String href = ((org.w3c.dom.Element) ev.getTarget()).getAttribute("href"); + ev.preventDefault(); + + // open browser (must check href on null anyway) + if (href != null && href.startsWith("http")) { + SwingUtilities.invokeLater(() -> AppUtil.openUrlInSystemBrowser(href)); + } + } + }; + org.w3c.dom.Document doc = engine.getDocument(); + org.w3c.dom.NodeList listA = doc.getElementsByTagName("a"); + for (int i = 0; i < listA.getLength(); i++) { + ((org.w3c.dom.events.EventTarget) listA.item(i)).addEventListener("click", listener, false); + } + } + + // 2. all done, build-in browser ready to show + isPageReady = true; + } + }); + + fxPanel.setScene(new Scene(webView)); + }); + } + + public void startBrowser(final String startingUrl) { + Platform.runLater(() -> { + String link = startingUrl; + if (!link.startsWith("http")) { + link = "http://" + link; + } + engine.load(link); + }); + } + + private void onCancel() { + this.hideDialog(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + buttonCancel = new JButton(); + buttonRefresh = new JButton(); + panelData = new JPanel(); + + buttonCancel.setText("Close"); + buttonCancel.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + buttonCancelActionPerformed(evt); + } + }); + + buttonRefresh.setText("Refresh"); + buttonRefresh.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + buttonRefreshActionPerformed(evt); + } + }); + + panelData.setLayout(new BorderLayout()); + + GroupLayout layout = new GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addComponent(panelData, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addComponent(buttonRefresh, GroupLayout.PREFERRED_SIZE, 100, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(buttonCancel, GroupLayout.PREFERRED_SIZE, 100, GroupLayout.PREFERRED_SIZE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(panelData, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(buttonCancel, GroupLayout.PREFERRED_SIZE, 30, GroupLayout.PREFERRED_SIZE) + .addComponent(buttonRefresh, GroupLayout.PREFERRED_SIZE, 30, GroupLayout.PREFERRED_SIZE)) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + private void buttonCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonCancelActionPerformed + onCancel(); + }//GEN-LAST:event_buttonCancelActionPerformed + + private void buttonRefreshActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonRefreshActionPerformed + startBrowser(WHATS_NEW_PAGE); + }//GEN-LAST:event_buttonRefreshActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private JButton buttonCancel; + private JButton buttonRefresh; + private JPanel panelData; + // End of variables declaration//GEN-END:variables +} diff --git a/Mage.Client/src/main/java/mage/client/remote/XmageURLConnection.java b/Mage.Client/src/main/java/mage/client/remote/XmageURLConnection.java index 007339e1708..2c9e7443be8 100644 --- a/Mage.Client/src/main/java/mage/client/remote/XmageURLConnection.java +++ b/Mage.Client/src/main/java/mage/client/remote/XmageURLConnection.java @@ -141,10 +141,13 @@ public class XmageURLConnection { this.connection.setRequestProperty("Accept-Encoding", "gzip"); } + this.connection.setRequestProperty("User-Agent", getDefaultUserAgent()); + } + + public static String getDefaultUserAgent() { // user agent due standard notation User-Agent: / // warning, dot not add os, language and other details - this.connection.setRequestProperty("User-Agent", String.format("XMage/%s build: %s", - version.toString(false), version.getBuildTime())); + return String.format("XMage/%s build: %s", version.toString(false), version.getBuildTime()); } /** diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java index be2c2e85cd2..06777f2680b 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -1752,7 +1752,7 @@ public class TablesPanel extends javax.swing.JPanel { }//GEN-LAST:event_btnStateFinishedActionPerformed private void buttonWhatsNewActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_buttonWhatsNewActionPerformed - MageFrame.showWhatsNewDialog(); + MageFrame.getInstance().showWhatsNewDialog(true); }//GEN-LAST:event_buttonWhatsNewActionPerformed private void btnQuickStart2PlayerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnQuickStartDuelActionPerformed diff --git a/Mage.Client/src/main/java/mage/client/util/AppUtil.java b/Mage.Client/src/main/java/mage/client/util/AppUtil.java index cd6a72f0e1b..dcfccf156bf 100644 --- a/Mage.Client/src/main/java/mage/client/util/AppUtil.java +++ b/Mage.Client/src/main/java/mage/client/util/AppUtil.java @@ -47,7 +47,7 @@ public class AppUtil { } } - public static void openUrlInBrowser(String url) { + public static void openUrlInSystemBrowser(String url) { Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null; if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) { try { diff --git a/Mage.Client/src/main/java/mage/client/util/URLHandler.java b/Mage.Client/src/main/java/mage/client/util/URLHandler.java index 1153b8063e9..096edc41ffc 100644 --- a/Mage.Client/src/main/java/mage/client/util/URLHandler.java +++ b/Mage.Client/src/main/java/mage/client/util/URLHandler.java @@ -1,12 +1,8 @@ package mage.client.util; -import java.awt.Desktop; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; -import java.io.IOException; import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; import java.net.URL; import javax.swing.*; @@ -48,14 +44,7 @@ public class URLHandler { return; } if (e.getClickCount() > 0) { - if (Desktop.isDesktopSupported()) { - Desktop desktop = Desktop.getDesktop(); - try { - URI uri = new URI(url); - desktop.browse(uri); - } catch (IOException | URISyntaxException ignore) { - } - } + AppUtil.openUrlInSystemBrowser(url); } } }; diff --git a/pom.xml b/pom.xml index cabed2ccfab..6e346f2e552 100644 --- a/pom.xml +++ b/pom.xml @@ -343,7 +343,7 @@ com.google.code.gson gson - 2.8.8 + 2.11.0