Skip to content

Commit 97a2e5d

Browse files
feat: added update manager [macata#17] (#27)
1 parent 026df19 commit 97a2e5d

File tree

12 files changed

+454
-13
lines changed

12 files changed

+454
-13
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Just put the `macatalauncher.jar` in some folder of your choice and double click
4141
- No more having to move folders between `Cataclysm.app`s on each Experimental upgrade!
4242
- This shared data directory is located at `/<macata_launcher_folder>/userdir`
4343
- [x] Soundpack management
44+
- [x] Launcher autoupdater
4445

4546
### Planned
4647

@@ -54,6 +55,10 @@ Just put the `macatalauncher.jar` in some folder of your choice and double click
5455
- I want to focus mainly on macOS since there are already some excellent launchers for Windows and Linux already.
5556
- Having said that, given the current launcher architecture, I would like to think it wouldn't be terribly hard to support Linux and/or Windows. Just a couple of changes to the [launcher class](src/main/java/com/dazednconfused/catalauncher/launcher/CDDALauncherManager.java) maybe? PRs welcome!
5657

58+
## Build
59+
60+
Just run `mvn package` and a `macatalauncher-x.x.x.jar` binary will be built in the `target` directory.
61+
5762
## FAQ
5863

5964
### Why Yet Another Launcher?

pom.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>com.dazednconfused</groupId>
88
<artifactId>macatalauncher</artifactId>
9-
<version>0.1</version>
9+
<version>0.2.0</version>
1010

1111
<properties>
1212
<maven.compiler.source>11</maven.compiler.source>
@@ -79,7 +79,7 @@
7979
<archive>
8080
<manifest>
8181
<addClasspath>true</addClasspath>
82-
<mainClass>com.dazednconfused.catalauncher.gui.MainWindow</mainClass>
82+
<mainClass>com.dazednconfused.catalauncher.Application</mainClass>
8383
</manifest>
8484
</archive>
8585
</configuration>
@@ -90,7 +90,7 @@
9090
<configuration>
9191
<archive>
9292
<manifest>
93-
<mainClass>com.dazednconfused.catalauncher.gui.MainWindow</mainClass>
93+
<mainClass>com.dazednconfused.catalauncher.Application</mainClass>
9494
</manifest>
9595
</archive>
9696
<descriptorRefs>

src/main/java/com/dazednconfused/catalauncher/configuration/Configuration.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public class Configuration {
99
private String cddaPath;
1010
private boolean backupOnExit;
1111
private boolean debug;
12+
private boolean shouldLookForUpdates;
1213

1314
public Configuration() {
1415
}
@@ -37,6 +38,14 @@ public void setDebug(boolean debug) {
3738
this.debug = debug;
3839
}
3940

41+
public boolean isShouldLookForUpdates() {
42+
return shouldLookForUpdates;
43+
}
44+
45+
public void setShouldLookForUpdates(boolean shouldLookForUpdates) {
46+
this.shouldLookForUpdates = shouldLookForUpdates;
47+
}
48+
4049
@Override
4150
public String toString() {
4251
return Try.of(() -> new ObjectMapper().writeValueAsString(this)).getOrElse(super::toString);

src/main/java/com/dazednconfused/catalauncher/configuration/ConfigurationManager.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,15 @@ public void setDebug(boolean debug) {
8686
save(this.configuration);
8787
}
8888

89+
public boolean isShouldLookForUpdates() {
90+
return this.configuration.isShouldLookForUpdates();
91+
}
92+
93+
public void setShouldLookForUpdates(boolean shouldLookForUpdates) {
94+
this.configuration.setShouldLookForUpdates(shouldLookForUpdates);
95+
save(this.configuration);
96+
}
97+
8998
/**
9099
* Saves the given {@link Configuration} to disk.
91100
* */

src/main/java/com/dazednconfused/catalauncher/gui/ConfirmDialog.java

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
public class ConfirmDialog extends JDialog {
2323

24+
public static final Consumer<Boolean> DO_NOTHING_ACTION = bool -> { }; // does nothing - represents an empty action
25+
2426
private static final String ERROR_ICON = extractIconFrom(ICONS_PATH + "/" + "errorDialog.svg");
2527
private static final String INFO_ICON = extractIconFrom(ICONS_PATH + "/" + "informationDialog.svg");
2628
private static final String WARN_ICON = extractIconFrom(ICONS_PATH + "/" + "warningDialog.svg");
@@ -32,13 +34,30 @@ public class ConfirmDialog extends JDialog {
3234
private JSVGCanvas iconSvg;
3335

3436
/**
35-
* Constructor.
37+
* Shows a confirmation dialog, which is basically a {@link JDialog} composed of a {@link String} {@code message}, a {@link ConfirmDialogType}
38+
* which gives visual feedback of the severity of the message by means of different icons, and a {@link Consumer} action
39+
* to be triggered upon confirmation or cancellation of the dialog.
40+
*
41+
* @param message The message to show in the dialog.
42+
* @param dialogType The type of dialog, representing its severity. Applicable types are {@link ConfirmDialogType#INFO},
43+
* {@link ConfirmDialogType#WARNING} and {@link ConfirmDialogType#ERROR}. {@link ConfirmDialogType#NONE}
44+
* is equivalent to using {@link ConfirmDialogType#INFO}.
45+
* @param doOnResult The action to be executed upon <i>confirmation</i> (user clicks the {@code OK} button) or <i>cancellation</i>
46+
* (user clicks the {@code Cancel} button and/or exits the {@link JDialog} by simply closing it).
47+
* <ul>
48+
* <li>A <i>confirmation</i> will pass a {@code true} to the provided consumer.</li>
49+
* <li>A <i>cancellation</i> will pass a {@code false} to the provided consumer.</li>
50+
* </ul>
51+
* If <i>confirmation</i> or <i>cancellation</i> is irrelevant for the desired {@link ConfirmDialog}
52+
* (ie: we just want to show a non-actionable message to the user), then a {@link #DO_NOTHING_ACTION}
53+
* may be passed to this parameter. This will make the resulting {@link JDialog} to only have an {@code OK}
54+
* button that will just close the dialog and nothing else.
3655
* */
3756
public ConfirmDialog(String message, ConfirmDialogType dialogType, Consumer<Boolean> doOnResult) {
3857

3958
// initialize dialog ---
4059
setContentPane(contentPane);
41-
setModal(true);
60+
setModalityType(ModalityType.APPLICATION_MODAL);
4261

4362
// set default button ---
4463
getRootPane().setDefaultButton(buttonCancel);
@@ -76,6 +95,16 @@ public void windowClosing(WindowEvent e) {
7695

7796
// call onCancel() on ESCAPE ---
7897
contentPane.registerKeyboardAction(e -> onCancel(doOnResult), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
98+
99+
// if defined action is DO_NOTHING_ACTION, then the OK/Cancel buttons do virtually the same thing: nothing
100+
// hide the OK button from view, there is no need for redundancy
101+
// also, make the Cancel feel like an 'OK' button from a user's perspective
102+
if (doOnResult == DO_NOTHING_ACTION) {
103+
buttonOK.setVisible(false);
104+
buttonCancel.setText("OK");
105+
}
106+
107+
this.spawnOnTopOfParentComponent(); // without this, dialog gets created _behind_ parent and doesn't come on top until Swing redraws, for some reason
79108
}
80109

81110
/**
@@ -88,11 +117,31 @@ public void packCenterAndShow(JPanel parent) {
88117
this.setVisible(true);
89118
}
90119

120+
/**
121+
* Forces this {@link JDialog} to always spawn on top of its parent.
122+
* */
123+
private void spawnOnTopOfParentComponent() {
124+
final JDialog thisDialog = this;
125+
this.addWindowListener(new java.awt.event.WindowAdapter() {
126+
@Override
127+
public void windowOpened(java.awt.event.WindowEvent evt) {
128+
thisDialog.toFront();
129+
thisDialog.requestFocus();
130+
}
131+
});
132+
}
133+
134+
/**
135+
* Represents the action to be taken when the {@code OK} button is clicked.
136+
* */
91137
private void onOK(Consumer<Boolean> doOnResult) {
92138
doOnResult.accept(true);
93139
dispose();
94140
}
95141

142+
/**
143+
* Represents the action to be taken when the {@code Cancel} button is clicked.
144+
* */
96145
private void onCancel(Consumer<Boolean> doOnResult) {
97146
doOnResult.accept(false);
98147
dispose();

src/main/java/com/dazednconfused/catalauncher/gui/MainWindow.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ public MainWindow() {
139139
};
140140

141141
this.refreshGuiElements();
142+
143+
new Thread(this::checkForUpdates).start(); // check for updates on a background thread, to not slow down application's startup
142144
}
143145

144146
/**
@@ -639,8 +641,8 @@ private static JMenuBar buildMenuBarFor(Component parent) {
639641
about.addActionListener(e -> {
640642
LOGGER.trace("About button clicked");
641643

642-
AboutDialog aboutDialog = new AboutDialog();
643-
aboutDialog.packCenterAndShow(parent);
644+
VersionManagerWindow versionManagerWindow = new VersionManagerWindow();
645+
versionManagerWindow.packCenterAndShow(parent);
644646
});
645647
helpMenu.add(about);
646648

@@ -730,4 +732,13 @@ public boolean isCellEditable(int row, int column) {
730732
};
731733
this.soundpacksTable.setModel(tableModel);
732734
}
735+
736+
/**
737+
* Checks for new software releases if the {@link ConfigurationManager} dictates said process should be carried out.
738+
* */
739+
private void checkForUpdates() {
740+
if (ConfigurationManager.getInstance().isShouldLookForUpdates()) {
741+
VersionManagerWindow.checkForUpdates(this.mainPanel, false);
742+
}
743+
}
733744
}

src/main/java/com/dazednconfused/catalauncher/gui/AboutDialog.form renamed to src/main/java/com/dazednconfused/catalauncher/gui/VersionManagerWindow.form

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.dazednconfused.catalauncher.gui.AboutDialog">
3-
<grid id="cbd77" binding="contentPane" layout-manager="GridLayoutManager" row-count="6" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
2+
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.dazednconfused.catalauncher.gui.VersionManagerWindow">
3+
<grid id="cbd77" binding="contentPane" layout-manager="GridLayoutManager" row-count="7" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
44
<margin top="10" left="10" bottom="10" right="10"/>
55
<constraints>
6-
<xy x="48" y="54" width="436" height="393"/>
6+
<xy x="48" y="54" width="465" height="393"/>
77
</constraints>
88
<properties/>
99
<border type="none"/>
@@ -173,6 +173,38 @@
173173
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
174174
</constraints>
175175
</vspacer>
176+
<grid id="afc62" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
177+
<margin top="0" left="0" bottom="0" right="0"/>
178+
<constraints>
179+
<grid row="6" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
180+
</constraints>
181+
<properties/>
182+
<border type="none"/>
183+
<children>
184+
<component id="4383c" class="javax.swing.JCheckBox" binding="autoUpdaterCheckbox">
185+
<constraints>
186+
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
187+
</constraints>
188+
<properties>
189+
<selected value="false"/>
190+
<text value="Automatically check for updates at startup"/>
191+
</properties>
192+
</component>
193+
<hspacer id="e225b">
194+
<constraints>
195+
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
196+
</constraints>
197+
</hspacer>
198+
<component id="6930e" class="javax.swing.JButton" binding="updateNowButton">
199+
<constraints>
200+
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
201+
</constraints>
202+
<properties>
203+
<text value="Check Updates"/>
204+
</properties>
205+
</component>
206+
</children>
207+
</grid>
176208
</children>
177209
</grid>
178210
</form>

src/main/java/com/dazednconfused/catalauncher/gui/AboutDialog.java renamed to src/main/java/com/dazednconfused/catalauncher/gui/VersionManagerWindow.java

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
import static com.dazednconfused.catalauncher.helper.Constants.APP_NAME;
44

5+
import com.dazednconfused.catalauncher.configuration.ConfigurationManager;
56
import com.dazednconfused.catalauncher.helper.GitInfoManager;
7+
import com.dazednconfused.catalauncher.update.UpdateManager;
68

79
import io.vavr.control.Try;
810

@@ -15,6 +17,8 @@
1517

1618
import javax.imageio.ImageIO;
1719
import javax.swing.ImageIcon;
20+
import javax.swing.JButton;
21+
import javax.swing.JCheckBox;
1822
import javax.swing.JComponent;
1923
import javax.swing.JDialog;
2024
import javax.swing.JLabel;
@@ -24,9 +28,9 @@
2428
import org.slf4j.Logger;
2529
import org.slf4j.LoggerFactory;
2630

27-
public class AboutDialog extends JDialog {
31+
public class VersionManagerWindow extends JDialog {
2832

29-
private static final Logger LOGGER = LoggerFactory.getLogger(AboutDialog.class);
33+
private static final Logger LOGGER = LoggerFactory.getLogger(VersionManagerWindow.class);
3034

3135
private static final String LOGO_PATH = "logo/logo.png";
3236

@@ -36,11 +40,13 @@ public class AboutDialog extends JDialog {
3640
private JLabel buildCommitLabel;
3741
private JLabel buildTimeLabel;
3842
private JLabel logoLabel;
43+
private JCheckBox autoUpdaterCheckbox;
44+
private JButton updateNowButton;
3945

4046
/**
4147
* Constructor.
4248
* */
43-
public AboutDialog() {
49+
public VersionManagerWindow() {
4450

4551
// initialize dialog ---
4652
setContentPane(contentPane);
@@ -58,6 +64,19 @@ public AboutDialog() {
5864
buildCommitLabel.setText("Build " + GitInfoManager.getInstance().getCommitIdFull());
5965
buildTimeLabel.setText(GitInfoManager.getInstance().getBuildTime());
6066

67+
// configure update now button ---
68+
updateNowButton.addActionListener(e -> {
69+
LOGGER.trace("Update now button clicked");
70+
checkForUpdates(contentPane, true);
71+
});
72+
73+
// configure auto-update checkbox ---
74+
this.autoUpdaterCheckbox.setSelected(ConfigurationManager.getInstance().isShouldLookForUpdates());
75+
autoUpdaterCheckbox.addActionListener(e -> {
76+
LOGGER.trace("Auto updater checkbox changed to [{}]", autoUpdaterCheckbox.isSelected());
77+
ConfigurationManager.getInstance().setShouldLookForUpdates(autoUpdaterCheckbox.isSelected());
78+
});
79+
6180
// call onOK() when cross is clicked ---
6281
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
6382
addWindowListener(new WindowAdapter() {
@@ -70,6 +89,46 @@ public void windowClosing(WindowEvent e) {
7089
contentPane.registerKeyboardAction(e -> dispose(), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_FOCUSED);
7190
}
7291

92+
/**
93+
* Checks for new binary releases and prompts the user in case an update is available.
94+
* */
95+
public static void checkForUpdates(JPanel parent, boolean showDialogIfNoUpdateAvailable) {
96+
LOGGER.info("Checking for updates...");
97+
98+
boolean updateAvailable = UpdateManager.isUpdateAvailable().orElse(false);
99+
100+
if (!updateAvailable) {
101+
LOGGER.debug("No update is available");
102+
103+
if (!showDialogIfNoUpdateAvailable) {
104+
LOGGER.trace("Skipping showing 'no update available' dialog because showDialogIfNoUpdateAvailable is false'");
105+
return;
106+
}
107+
108+
ConfirmDialog confirmDialog = new ConfirmDialog(
109+
"There were no updates found",
110+
ConfirmDialog.ConfirmDialogType.NONE,
111+
ConfirmDialog.DO_NOTHING_ACTION
112+
);
113+
114+
confirmDialog.packCenterAndShow(parent);
115+
} else {
116+
LOGGER.debug("Update is available");
117+
ConfirmDialog confirmDialog = new ConfirmDialog(
118+
"A new version is available! Check the releases page now?",
119+
ConfirmDialog.ConfirmDialogType.INFO,
120+
confirmed -> {
121+
if (confirmed) {
122+
UpdateManager.openLatestReleaseInDefaultBrowser();
123+
} else {
124+
LOGGER.debug("Aborting update process due to user input");
125+
}
126+
}
127+
);
128+
confirmDialog.packCenterAndShow(parent);
129+
}
130+
}
131+
73132
/**
74133
* Packs ({@link #pack()}), centers ({@link #setLocationRelativeTo(Component)}) and sets the current dialog as visible
75134
* ({@link #setVisible(boolean)}).

src/main/java/com/dazednconfused/catalauncher/helper/Constants.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ public class Constants {
1212
public static final String CUSTOM_MODS_DIR = CUSTOM_USER_DIR + "mods/";
1313
public static final String CUSTOM_TRASHED_SAVE_PATH = LAUNCHER_ROOT_FOLDER + "/trashed/saves/";
1414
public static final String SAVE_BACKUP_PATH = LAUNCHER_ROOT_FOLDER + "/backups";
15+
public static final String GITHUB_REPOSITORY_OWNER = "DazedNConfused-";
16+
public static final String GITHUB_REPOSITORY_NAME = "macata-launcher";
17+
1518

1619
// resources
1720
public static final String ICONS_PATH = "icon/svg";

0 commit comments

Comments
 (0)