Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform
import java.time.Year

plugins {
id 'java'
Expand All @@ -13,6 +14,7 @@ group 'com.codedead'
version '1.5.1'

def currentOS = DefaultNativePlatform.currentOperatingSystem
def currentYear = Year.now().getValue()

java {
toolchain {
Expand All @@ -28,6 +30,12 @@ tasks.named("updateDaemonJvm") {
}
}

tasks.named('processResources') {
filesMatching('version.properties') {
expand(version: project.version)
}
}

application {
mainModule = 'Opal'
mainClass = 'com.codedead.opal.OpalApplication'
Expand Down Expand Up @@ -60,7 +68,7 @@ jlink {
'--win-upgrade-uuid', '876c5464-9a66-4913-89a4-c63a4b8b4bb9',
'--win-help-url', 'https://codedead.com/contact',
'--win-dir-chooser',
'--copyright', 'Copyright (c) 2026 CodeDead',
'--copyright', "Copyright (c) ${currentYear} CodeDead",
'--description', 'Opal is a free and open-source JavaFX application that can play relaxing music in the background',
'--vendor', 'CodeDead',
'--license-file', 'LICENSE',
Expand All @@ -76,7 +84,7 @@ jlink {
'--linux-shortcut',
'--linux-package-name', 'opal-codedead',
'--linux-rpm-license-type', 'GPLv3',
'--copyright', 'Copyright (c) 2026 CodeDead',
'--copyright', "Copyright (c) ${currentYear} CodeDead",
'--description', 'Opal is a free and open-source JavaFX application that can play relaxing music in the background',
'--vendor', 'CodeDead',
'--license-file', 'LICENSE',
Expand All @@ -94,7 +102,7 @@ jlink {
installerOptions = [
'--mac-package-name', 'Opal',
'--mac-app-category', 'public.app-category.music',
'--copyright', 'Copyright (c) 2026 CodeDead',
'--copyright', "Copyright (c) ${currentYear} CodeDead",
'--description', 'Opal is a free and open-source JavaFX application that can play relaxing music in the background',
'--vendor', 'CodeDead',
'--license-file', 'LICENSE',
Expand Down
25 changes: 24 additions & 1 deletion src/main/java/com/codedead/opal/OpalApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,13 @@ public void start(final Stage primaryStage) {
logger.info("Creating the MainWindowController");

final MainWindowController mainWindowController = loader.getController();
final UpdateController updateController = new UpdateController(properties.getProperty("updateApi", "https://codedead.com/Software/Opal/version.json"), SharedVariables.CURRENT_VERSION);
final String configuredUpdateApi = properties.getProperty("updateApi", SharedVariables.UPDATE_API_URL);
final String updateApi = resolveUpdateApi(configuredUpdateApi);
if (!updateApi.equals(configuredUpdateApi)) {
properties.setProperty("updateApi", updateApi);
}

final UpdateController updateController = new UpdateController(updateApi, SharedVariables.CURRENT_VERSION);
mainWindowController.setControllers(settingsController, updateController);

final Scene scene = new Scene(root);
Expand All @@ -120,4 +126,21 @@ public void start(final Stage primaryStage) {
logger.info("Showing the MainWindow");
primaryStage.show();
}

/**
* Resolve the configured update API URL, migrating the legacy URL to the current API
*
* @param configuredUpdateApi The configured update API URL
* @return The update API URL that should be used
*/
static String resolveUpdateApi(final String configuredUpdateApi) {
if (configuredUpdateApi == null || configuredUpdateApi.isBlank())
return SharedVariables.UPDATE_API_URL;

final String updateApi = configuredUpdateApi.trim();
if (SharedVariables.LEGACY_UPDATE_API_URL.equalsIgnoreCase(updateApi))
return SharedVariables.UPDATE_API_URL;

return updateApi;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.stage.Stage;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.time.Year;
import java.util.ResourceBundle;

public final class AboutWindowController {

@FXML
private Label lblAbout;

private ResourceBundle translationBundle;
private final HelpUtils helpUtils;
private final Logger logger;
Expand All @@ -36,6 +41,10 @@ public void setResourceBundle(final ResourceBundle resourceBundle) {
throw new NullPointerException("ResourceBundle cannot be null");

this.translationBundle = resourceBundle;

lblAbout.setText(resourceBundle.getString("AboutText")
.replace("{version}", SharedVariables.APP_VERSION)
.replace("{year}", Integer.toString(Year.now().getValue())));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.GeneralSecurityException;
Expand Down Expand Up @@ -200,6 +201,7 @@ public final class MainWindowController implements IAudioTimer, TrayIconListener
private TimerTask timerTask;
private TimerTask countDownTask;
private final String platformName;
private final String architecture;
private final HelpUtils helpUtils;
private final PasswordCipher passwordCipher;
private final JsonMapper jsonMapper;
Expand All @@ -218,7 +220,8 @@ public MainWindowController() {
logger = LogManager.getLogger(MainWindowController.class);
logger.info("Initializing new MainWindowController object");

platformName = OsCheck.getOperatingSystemType().name();
platformName = OsCheck.getPlatformName();
architecture = OsCheck.getArchitecture();
helpUtils = new HelpUtils();
passwordCipher = new PasswordCipher();

Expand Down Expand Up @@ -414,22 +417,23 @@ private void checkForUpdates(final boolean showNoUpdates, final boolean showErro
try {
configureUpdateProxy();

final Optional<PlatformUpdate> platformUpdate = updateController.checkForUpdates(platformName, SharedVariables.PORTABLE);
final Optional<PlatformUpdate> platformUpdate = updateController.checkForUpdates(platformName, architecture, SharedVariables.PORTABLE);
if (platformUpdate.isPresent()) {
final PlatformUpdate update = platformUpdate.get();
final String displayVersion = update.getSemver().replaceFirst("^[vV]", "");

logger.info("Version {}.{}.{}.{} is available", update.getMajorVersion(), update.getMinorVersion(), update.getBuildVersion(), update.getRevisionVersion());
logger.info("Version {} is available for {}/{}", displayVersion, update.getPlatformName(), update.getArch());

if (FxUtils.showConfirmationAlert(resourceFactory
.getResources()
.getString("NewUpdateAvailable")
.replace("{v}", String.format("%1$s.%2$s.%3$s.%4$s", update.getMajorVersion(), update.getMinorVersion(), update.getBuildVersion(), update.getRevisionVersion())),
.replace("{v}", displayVersion),
getClass().getResourceAsStream(SharedVariables.ICON_URL))) {

final String extension = update.getDownloadUrl().substring(update.getDownloadUrl().lastIndexOf('.')).toLowerCase();
final String extension = getDownloadExtension(update.getDownloadUrl());
final FileChooser fileChooser = new FileChooser();
final FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter(
String.format("%1$s (*%2$s)", extension.substring(1).toUpperCase(), extension),
String.format("%1$s (*%2$s)", extension.substring(1).toUpperCase(Locale.ROOT), extension),
String.format("*%s", extension));

fileChooser.getExtensionFilters().add(extFilter);
Expand All @@ -438,11 +442,19 @@ private void checkForUpdates(final boolean showNoUpdates, final boolean showErro

if (file != null) {
String filePath = file.getAbsolutePath();
if (!filePath.toLowerCase().contains(extension)) {
if (!filePath.toLowerCase(Locale.ROOT).endsWith(extension.toLowerCase(Locale.ROOT))) {
filePath += extension;
}

updateController.downloadFile(update.getDownloadUrl(), filePath);

if (extension.equalsIgnoreCase(".AppImage")) {
final File downloadedFile = new File(filePath);
if (!downloadedFile.setExecutable(true)) {
logger.warn("Unable to mark {} as executable", filePath);
}
}

openFile(filePath);
exitAction();
}
Expand All @@ -464,6 +476,22 @@ private void checkForUpdates(final boolean showNoUpdates, final boolean showErro
}
}

/**
* Get the file extension from a download URL
*
* @param downloadUrl The download URL
* @return The file extension, including the leading dot
*/
private String getDownloadExtension(final String downloadUrl) {
final String path = URI.create(downloadUrl).getPath();
final int extensionIndex = path.lastIndexOf('.');

if (extensionIndex < 0 || extensionIndex == path.length() - 1)
throw new IllegalArgumentException("Unable to determine the file extension from the download URL!");

return path.substring(extensionIndex);
}

/**
* Get all {@link SoundPane} objects
*
Expand Down
Loading
Loading