version checking Fanfix -> Utils
authorNiki Roo <niki@nikiroo.be>
Thu, 7 May 2020 09:37:10 +0000 (11:37 +0200)
committerNiki Roo <niki@nikiroo.be>
Thu, 7 May 2020 09:37:10 +0000 (11:37 +0200)
src/be/nikiroo/utils/VersionCheck.java [new file with mode: 0644]
src/be/nikiroo/utils/ui/UIUtils.java

diff --git a/src/be/nikiroo/utils/VersionCheck.java b/src/be/nikiroo/utils/VersionCheck.java
new file mode 100644 (file)
index 0000000..0e16c2b
--- /dev/null
@@ -0,0 +1,172 @@
+package be.nikiroo.utils;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Version checker: can check the current version of the program against a
+ * remote changelog, and list the missed updates and their description.
+ * 
+ * @author niki
+ */
+public class VersionCheck {
+       private static final String base = "https://github.com/${PROJECT}/raw/master/changelog${LANG}.md";
+       private static Downloader downloader = new Downloader(null);
+
+       private Version current;
+       private List<Version> newer;
+       private Map<Version, List<String>> changes;
+
+       /**
+        * Create a new {@link VersionCheck}.
+        * 
+        * @param current
+        *            the current version of the program
+        * @param newer
+        *            the list of available {@link Version}s newer the current one
+        * @param changes
+        *            the list of changes
+        */
+       private VersionCheck(Version current, List<Version> newer,
+                       Map<Version, List<String>> changes) {
+               this.current = current;
+               this.newer = newer;
+               this.changes = changes;
+       }
+
+       /**
+        * Check if there are more recent {@link Version}s of this program
+        * available.
+        * 
+        * @return TRUE if there is at least one
+        */
+       public boolean isNewVersionAvailable() {
+               return !newer.isEmpty();
+       }
+
+       /**
+        * The current {@link Version} of the program.
+        * 
+        * @return the current {@link Version}
+        */
+       public Version getCurrentVersion() {
+               return current;
+       }
+
+       /**
+        * The list of available {@link Version}s newer than the current one.
+        * 
+        * @return the newer {@link Version}s
+        */
+       public List<Version> getNewer() {
+               return newer;
+       }
+
+       /**
+        * The list of changes for each available {@link Version} newer than the
+        * current one.
+        * 
+        * @return the list of changes
+        */
+       public Map<Version, List<String>> getChanges() {
+               return changes;
+       }
+
+       /**
+        * Check if there are available {@link Version}s of this program more recent
+        * than the current one.
+        * 
+        * @param githubProject
+        *            the GitHub project to check on, for instance "nikiroo/fanfix"
+        * @param lang
+        *            the current locale, so we can try to get the changelog in the
+        *            correct language (can be NULL, will fetch the default
+        *            changelog)
+        * 
+        * @return a {@link VersionCheck}
+        * 
+        * @throws IOException
+        *             in case of I/O error
+        */
+       public static VersionCheck check(String githubProject, Locale lang)
+                       throws IOException {
+               Version current = Version.getCurrentVersion();
+               List<Version> newer = new ArrayList<Version>();
+               Map<Version, List<String>> changes = new HashMap<Version, List<String>>();
+
+               // Use the right project:
+               String base = VersionCheck.base.replace("${PROJECT}", githubProject);
+
+               // Prepare the URLs according to the user's language (we take here
+               // "-fr_BE" as an example):
+               String fr = lang == null ? "" : "-" + lang.getLanguage();
+               String BE = lang == null ? ""
+                               : "_" + lang.getCountry().replace(".UTF8", "");
+               String urlFrBE = base.replace("${LANG}", fr + BE);
+               String urlFr = base.replace("${LANG}", "-" + fr);
+               String urlDefault = base.replace("${LANG}", "");
+
+               InputStream in = null;
+               for (String url : new String[] { urlFrBE, urlFr, urlDefault }) {
+                       try {
+                               in = downloader.open(new URL(url), false);
+                               break;
+                       } catch (IOException e) {
+                       }
+               }
+
+               if (in == null) {
+                       throw new IOException("No changelog found");
+               }
+
+               BufferedReader reader = new BufferedReader(
+                               new InputStreamReader(in, "UTF-8"));
+               try {
+                       Version version = new Version();
+                       for (String line = reader.readLine(); line != null; line = reader
+                                       .readLine()) {
+                               if (line.startsWith("## Version ")) {
+                                       version = new Version(
+                                                       line.substring("## Version ".length()));
+                                       if (version.isNewerThan(current)) {
+                                               newer.add(version);
+                                               changes.put(version, new ArrayList<String>());
+                                       } else {
+                                               version = new Version();
+                                       }
+                               } else if (!version.isEmpty() && !newer.isEmpty()
+                                               && !line.isEmpty()) {
+                                       List<String> ch = changes.get(newer.get(newer.size() - 1));
+                                       if (!ch.isEmpty() && !line.startsWith("- ")) {
+                                               int i = ch.size() - 1;
+                                               ch.set(i, ch.get(i) + " " + line.trim());
+                                       } else {
+                                               ch.add(line.substring("- ".length()).trim());
+                                       }
+                               }
+                       }
+               } finally {
+                       reader.close();
+               }
+
+               return new VersionCheck(current, newer, changes);
+       }
+       
+       @Override
+       public String toString() {
+               return String.format(
+                               "Version checker: version [%s], %d releases behind latest version [%s]", //
+                               current, //
+                               newer.size(), //
+                               newer.isEmpty() ? current : newer.get(newer.size() - 1)//
+               );
+       }
+}
index 7d78d1e8c38669b118dab12828da99c351091b8c..c7fcf623cd01274d60716658eabcc27a7b46a626 100644 (file)
@@ -1,17 +1,30 @@
 package be.nikiroo.utils.ui;
 
 import java.awt.Color;
+import java.awt.Component;
+import java.awt.Desktop;
 import java.awt.GradientPaint;
 import java.awt.Graphics;
 import java.awt.Graphics2D;
 import java.awt.Paint;
 import java.awt.RadialGradientPaint;
 import java.awt.RenderingHints;
+import java.io.IOException;
+import java.net.URISyntaxException;
 
 import javax.swing.JComponent;
+import javax.swing.JEditorPane;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
 import javax.swing.JScrollPane;
 import javax.swing.UIManager;
 import javax.swing.UnsupportedLookAndFeelException;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.event.HyperlinkListener;
+
+import be.nikiroo.fanfix.Instance;
+import be.nikiroo.utils.Version;
+import be.nikiroo.utils.VersionCheck;
 
 /**
  * Some Java Swing utilities.
@@ -234,4 +247,89 @@ public class UIUtils {
 
                return scroll;
        }
+
+       /**
+        * Show a confirmation message to the user to show him the changes since
+        * last version.
+        * <p>
+        * HTML 3.2 supported, links included (the user browser will be launched if
+        * possible).
+        * <p>
+        * If this is already the latest version, a message will still be displayed.
+        * 
+        * @param parentComponent
+        *            determines the {@link java.awt.Frame} in which the dialog is
+        *            displayed; if <code>null</code>, or if the
+        *            <code>parentComponent</code> has no {@link java.awt.Frame}, a
+        *            default {@link java.awt.Frame} is used
+        * @param updates
+        *            the new version
+        * @param introText
+        *            an introduction text before the list of changes
+        * @param title
+        *            the title of the dialog
+        * 
+        * @return TRUE if the user clicked on OK, false if the dialog was dismissed
+        */
+       static public boolean showUpdatedDialog(Component parentComponent,
+                       VersionCheck updates, String introText, String title) {
+               
+               StringBuilder builder = new StringBuilder();
+               final JEditorPane updateMessage = new JEditorPane("text/html", "");
+               if (introText != null && !introText.isEmpty()) {
+                       builder.append(introText);
+                       builder.append("<br>");
+                       builder.append("<br>");
+               }
+               for (Version v : updates.getNewer()) {
+                       builder.append("\t<b>" //
+                                       + "Version " + v.toString() //
+                                       + "</b>");
+                       builder.append("<br>");
+                       builder.append("<ul>");
+                       for (String item : updates.getChanges().get(v)) {
+                               builder.append("<li>" + item + "</li>");
+                       }
+                       builder.append("</ul>");
+               }
+
+               // html content
+               updateMessage.setText("<html><body>" //
+                               + builder//
+                               + "</body></html>");
+
+               // handle link events
+               updateMessage.addHyperlinkListener(new HyperlinkListener() {
+                       @Override
+                       public void hyperlinkUpdate(HyperlinkEvent e) {
+                               if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED))
+                                       try {
+                                               Desktop.getDesktop().browse(e.getURL().toURI());
+                                       } catch (IOException ee) {
+                                               Instance.getInstance().getTraceHandler().error(ee);
+                                       } catch (URISyntaxException ee) {
+                                               Instance.getInstance().getTraceHandler().error(ee);
+                                       }
+                       }
+               });
+               updateMessage.setEditable(false);
+               updateMessage.setBackground(new JLabel().getBackground());
+               updateMessage.addHyperlinkListener(new HyperlinkListener() {
+                       @Override
+                       public void hyperlinkUpdate(HyperlinkEvent evn) {
+                               if (evn.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
+                                       if (Desktop.isDesktopSupported()) {
+                                               try {
+                                                       Desktop.getDesktop().browse(evn.getURL().toURI());
+                                               } catch (IOException e) {
+                                               } catch (URISyntaxException e) {
+                                               }
+                                       }
+                               }
+                       }
+               });
+
+               return JOptionPane.showConfirmDialog(parentComponent, updateMessage,
+                               title, JOptionPane.DEFAULT_OPTION) == JOptionPane.OK_OPTION;
+       }
 }