From 0db78d8ea427fff065d145d35d9680bad3cb4cb0 Mon Sep 17 00:00:00 2001 From: "Luiz C. M. de Aquino" Date: Sun, 15 Jun 2025 13:18:31 -0300 Subject: [PATCH 1/5] Add files via upload. --- PostCodeSerialMonitor/Models/ReleaseDefinition.cs | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 PostCodeSerialMonitor/Models/ReleaseDefinition.cs diff --git a/PostCodeSerialMonitor/Models/ReleaseDefinition.cs b/PostCodeSerialMonitor/Models/ReleaseDefinition.cs new file mode 100644 index 0000000..2298b2a --- /dev/null +++ b/PostCodeSerialMonitor/Models/ReleaseDefinition.cs @@ -0,0 +1,5 @@ +namespace PostCodeSerialMonitor.Models; +public class ReleaseDefinition +{ + public string tag_name { get; set; } = string.Empty; +} \ No newline at end of file From 287ef33ea8da6c207a7e281d6d5d5a91f26970ed Mon Sep 17 00:00:00 2001 From: "Luiz C. M. de Aquino" Date: Sun, 15 Jun 2025 13:22:05 -0300 Subject: [PATCH 2/5] Add files via upload. Check for app and firmware update. --- .../Services/MetaUpdateService.cs | 72 +++++++++++++++++-- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/PostCodeSerialMonitor/Services/MetaUpdateService.cs b/PostCodeSerialMonitor/Services/MetaUpdateService.cs index 0204cd9..70338ec 100644 --- a/PostCodeSerialMonitor/Services/MetaUpdateService.cs +++ b/PostCodeSerialMonitor/Services/MetaUpdateService.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Net.Http; +using System.Reflection; using System.Text.Json; using System.Threading.Tasks; using System.Collections.Generic; @@ -8,6 +9,7 @@ using Microsoft.Extensions.Logging; namespace PostCodeSerialMonitor.Services; + public class MetaUpdateService { private readonly ConfigurationService _configurationService; @@ -24,8 +26,8 @@ public class MetaUpdateService public AppConfiguration Config => _configurationService.Config; public MetaUpdateService( - ConfigurationService configurationService, - JsonSerializerOptions jsonOptions, + ConfigurationService configurationService, + JsonSerializerOptions jsonOptions, ILogger logger) { _configurationService = configurationService ?? throw new ArgumentNullException(nameof(configurationService)); @@ -34,6 +36,10 @@ public MetaUpdateService( ?? throw new ArgumentNullException(nameof(_configurationService.Config.MetaStoragePath)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _httpClient = new HttpClient(); + + //All GitHub's API requests must include a valid User-Agent header. + //@see https://docs.github.com/en/rest/using-the-rest-api/getting-started-with-the-rest-api?apiVersion=2022-11-28#user-agent + _httpClient.DefaultRequestHeaders.Add("User-Agent", "XboxPostcodeMonitor"); } public async Task TryLoadLocalDefinition() @@ -46,11 +52,29 @@ public async Task TryLoadLocalDefinition() return true; } + public async Task CheckForAppUpdatesAsync(string currentVersion) + { + // Get the latest release from GitHub repo. + var remoteRelease = await GetRemoteAppReleaseAsync(); + var remoteVersion = (remoteRelease == null) ? string.Empty : remoteRelease.tag_name; + + return string.Compare($"{remoteVersion}", $"{currentVersion}") > 0; + } + + public async Task CheckForFirmwareUpdatesAsync(string currentVersion) + { + // Get the latest release from GitHub repo. + var remoteRelease = await GetRemoteFirmwareReleaseAsync(); + var remoteVersion = (remoteRelease == null) ? string.Empty : remoteRelease.tag_name; + + return string.Compare($"{remoteVersion}", $"{currentVersion}") > 0; + } + public async Task CheckForMetaDefinitionUpdatesAsync() { var localMeta = await GetLocalMetaDefinitionAsync(); var remoteMeta = await GetRemoteMetaDefinitionAsync(); - + if (localMeta == null || remoteMeta == null) { // Update required @@ -75,7 +99,7 @@ public async Task UpdateMetaDefinitionAsync() // Ensure directory exists Directory.CreateDirectory(_localPath); - + // Save the new meta definition await File.WriteAllTextAsync(LocalMetaPath, metaContentStr); @@ -143,7 +167,7 @@ private async Task DownloadMetaFilesAsync() { var response = await _httpClient.GetAsync(Config.MetaJsonUrl); response.EnsureSuccessStatusCode(); - + var json = await response.Content.ReadAsStringAsync(); return JsonSerializer.Deserialize(json, _jsonSerializeOptions); } @@ -153,4 +177,42 @@ private async Task DownloadMetaFilesAsync() return null; } } + + private async Task GetRemoteAppReleaseAsync() + { + var gitHubApiReleasesLatest = new Uri("https://api.github.com/repos/xboxoneresearch/XboxPostcodeMonitor/releases/latest"); + + try + { + var response = await _httpClient.GetAsync(gitHubApiReleasesLatest); + response.EnsureSuccessStatusCode(); + + var json = await response.Content.ReadAsStringAsync(); + return JsonSerializer.Deserialize(json, _jsonSerializeOptions); + } + catch (Exception ex) + { + _logger.LogError(ex, Assets.Resources.FailedDownloadReleaseDefinition, gitHubApiReleasesLatest); + return null; + } + } + + private async Task GetRemoteFirmwareReleaseAsync() + { + var gitHubApiReleasesLatest = new Uri("https://api.github.com/repos/xboxoneresearch/PicoDurangoPOST/releases/latest"); + + try + { + var response = await _httpClient.GetAsync(gitHubApiReleasesLatest); + response.EnsureSuccessStatusCode(); + + var json = await response.Content.ReadAsStringAsync(); + return JsonSerializer.Deserialize(json, _jsonSerializeOptions); + } + catch (Exception ex) + { + _logger.LogError(ex, Assets.Resources.FailedDownloadReleaseDefinition, gitHubApiReleasesLatest); + return null; + } + } } \ No newline at end of file From 2ae8c4ceb8348dfd5000510c1222677de8ce1e6b Mon Sep 17 00:00:00 2001 From: "Luiz C. M. de Aquino" Date: Sun, 15 Jun 2025 13:23:47 -0300 Subject: [PATCH 3/5] Add files via upload. Check for app and firmware update. --- .../ViewModels/MainWindowViewModel.cs | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/PostCodeSerialMonitor/ViewModels/MainWindowViewModel.cs b/PostCodeSerialMonitor/ViewModels/MainWindowViewModel.cs index aa01310..3f61d45 100644 --- a/PostCodeSerialMonitor/ViewModels/MainWindowViewModel.cs +++ b/PostCodeSerialMonitor/ViewModels/MainWindowViewModel.cs @@ -174,6 +174,19 @@ await MessageBoxManager ButtonEnum.Ok) .ShowAsync(); } + + if (_configurationService.Config.CheckForAppUpdates) + { + updateAvailable = await _metaUpdateService.CheckForAppUpdatesAsync($"v{AppVersion}"); + if (updateAvailable) + { + var box = MessageBoxManager + .GetMessageBoxStandard(Assets.Resources.Warning, + string.Format(Assets.Resources.NewAppReleaseAvailable, "https://github.com/xboxoneresearch/XboxPostcodeMonitor/releases"), ButtonEnum.Ok); + + await box.ShowAsync(); + } + } } [RelayCommand] @@ -267,10 +280,23 @@ private async Task ConnectAsync() { _logger.LogError(ex, Assets.Resources.ErrorConection); await MessageBoxManager - .GetMessageBoxStandard(Assets.Resources.Error, string.Format(Assets.Resources.ErrorConectionMessageBoxError,ex.Message), + .GetMessageBoxStandard(Assets.Resources.Error, string.Format(Assets.Resources.ErrorConectionMessageBoxError, ex.Message), ButtonEnum.Ok) .ShowAsync(); } + + if (IsConnected && _configurationService.Config.CheckForFwUpdates) + { + var updateAvailable = await _metaUpdateService.CheckForFirmwareUpdatesAsync(_serialService.FirmwareVersion); + if (updateAvailable) + { + var box = MessageBoxManager + .GetMessageBoxStandard(Assets.Resources.Warning, + string.Format(Assets.Resources.NewFirmwareReleaseAvailable, "https://github.com/xboxoneresearch/PicoDurangoPOST/releases"), ButtonEnum.Ok); + + await box.ShowAsync(); + } + } } } From 8051005fac724dc30ac9fcdceddce5ec82ac41c5 Mon Sep 17 00:00:00 2001 From: "Luiz C. M. de Aquino" Date: Sun, 15 Jun 2025 13:24:33 -0300 Subject: [PATCH 4/5] Add files via upload. --- .../Assets/Resources.Designer.cs | 38 ++++++++++++++++++- .../Assets/Resources.pt-BR.resx | 16 ++++++++ PostCodeSerialMonitor/Assets/Resources.resx | 16 ++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/PostCodeSerialMonitor/Assets/Resources.Designer.cs b/PostCodeSerialMonitor/Assets/Resources.Designer.cs index 755ef01..03b363c 100644 --- a/PostCodeSerialMonitor/Assets/Resources.Designer.cs +++ b/PostCodeSerialMonitor/Assets/Resources.Designer.cs @@ -176,7 +176,25 @@ public static string FailedDownloadMetaDefinition { return ResourceManager.GetString("FailedDownloadMetaDefinition", resourceCulture); } } + + /// + /// In Services/MetaUpdateService.cs + /// + public static string FailedDeserializingReleaseDefinition { + get { + return ResourceManager.GetString("FailedDeserializingReleaseDefinition", resourceCulture); + } + } + /// + /// In Services/MetaUpdateService.cs + /// + public static string FailedDownloadReleaseDefinition { + get { + return ResourceManager.GetString("FailedDownloadReleaseDefinition", resourceCulture); + } + } + /// /// In Services/SerialLineDecoder.cs /// @@ -302,7 +320,25 @@ public static string FailedUpdateMetadata { return ResourceManager.GetString("FailedUpdateMetadata", resourceCulture); } } - + + /// + /// In ViewModels/MainWindowViewModel.cs + /// + public static string NewAppReleaseAvailable { + get { + return ResourceManager.GetString("NewAppReleaseAvailable", resourceCulture); + } + } + + /// + /// In ViewModels/MainWindowViewModel.cs + /// + public static string NewFirmwareReleaseAvailable { + get { + return ResourceManager.GetString("NewFirmwareReleaseAvailable", resourceCulture); + } + } + /// /// In ViewModels/MainWindowViewModel.cs /// diff --git a/PostCodeSerialMonitor/Assets/Resources.pt-BR.resx b/PostCodeSerialMonitor/Assets/Resources.pt-BR.resx index 8195cca..9afb5d7 100644 --- a/PostCodeSerialMonitor/Assets/Resources.pt-BR.resx +++ b/PostCodeSerialMonitor/Assets/Resources.pt-BR.resx @@ -169,6 +169,14 @@ Falha ao baixar MetaDefinition de {0} In Services/MetaUpdateService.cs + + Falha ao deserializar ReleaseDefinition + In Services/MetaUpdateService.cs + + + Falha ao baixar o último lançamento de {0} + In Services/MetaUpdateService.cs + Decodificador: Ignorando linha {0} In Services/SerialLineDecoder.cs @@ -225,6 +233,14 @@ Falha ao ataulizar o metadata In ViewModels/MainWindowViewModel.cs + + Uma nova versão do aplicativo está disponível em {0}. + In ViewModels/MainWindowViewModel.cs + + + Uma nova versão do firmware está disponível em {0}. + In ViewModels/MainWindowViewModel.cs + Erro In ViewModels/MainWindowViewModel.cs diff --git a/PostCodeSerialMonitor/Assets/Resources.resx b/PostCodeSerialMonitor/Assets/Resources.resx index 0336470..ca22351 100644 --- a/PostCodeSerialMonitor/Assets/Resources.resx +++ b/PostCodeSerialMonitor/Assets/Resources.resx @@ -169,6 +169,14 @@ Failed to download MetaDefinition from {0} In Services/MetaUpdateService.cs + + Failed deserializing ReleaseDefinition + In Services/MetaUpdateService.cs + + + Failed to download latest release from {0} + In Services/MetaUpdateService.cs + Decoder: Ignoring line {0} In Services/SerialLineDecoder.cs @@ -225,6 +233,14 @@ Failed to update metadata In ViewModels/MainWindowViewModel.cs + + A new app release is available at {0}. + In ViewModels/MainWindowViewModel.cs + + + A new firmware release is available at {0}. + In ViewModels/MainWindowViewModel.cs + Error In ViewModels/MainWindowViewModel.cs From 3ebf98de6c225f0fd6bb4d267d0fae6093b2b78e Mon Sep 17 00:00:00 2001 From: "Luiz C. M. de Aquino" Date: Mon, 16 Jun 2025 17:29:58 -0300 Subject: [PATCH 5/5] Adds Utils/SemanticVersionUtils.cs --- .../Services/MetaUpdateService.cs | 42 +++++++------------ .../Utils/SemanticVersionUtils.cs | 34 +++++++++++++++ .../ViewModels/MainWindowViewModel.cs | 2 +- 3 files changed, 50 insertions(+), 28 deletions(-) create mode 100644 PostCodeSerialMonitor/Utils/SemanticVersionUtils.cs diff --git a/PostCodeSerialMonitor/Services/MetaUpdateService.cs b/PostCodeSerialMonitor/Services/MetaUpdateService.cs index 70338ec..ab2e516 100644 --- a/PostCodeSerialMonitor/Services/MetaUpdateService.cs +++ b/PostCodeSerialMonitor/Services/MetaUpdateService.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using System.Collections.Generic; using PostCodeSerialMonitor.Models; +using PostCodeSerialMonitor.Utils; using Microsoft.Extensions.Logging; namespace PostCodeSerialMonitor.Services; @@ -52,22 +53,28 @@ public async Task TryLoadLocalDefinition() return true; } - public async Task CheckForAppUpdatesAsync(string currentVersion) + public async Task CheckForAppUpdatesAsync(string localVersion) { // Get the latest release from GitHub repo. - var remoteRelease = await GetRemoteAppReleaseAsync(); + var remoteRelease = await GetRepositoryLatestReleaseAsync("xboxoneresearch", "XboxPostcodeMonitor"); var remoteVersion = (remoteRelease == null) ? string.Empty : remoteRelease.tag_name; - return string.Compare($"{remoteVersion}", $"{currentVersion}") > 0; + SemanticVersionUtils local = new SemanticVersionUtils(localVersion); + SemanticVersionUtils remote = new SemanticVersionUtils(remoteVersion); + + return remote > local; } - public async Task CheckForFirmwareUpdatesAsync(string currentVersion) + public async Task CheckForFirmwareUpdatesAsync(string localVersion) { // Get the latest release from GitHub repo. - var remoteRelease = await GetRemoteFirmwareReleaseAsync(); + var remoteRelease = await GetRepositoryLatestReleaseAsync("xboxoneresearch", "PicoDurangoPOST"); var remoteVersion = (remoteRelease == null) ? string.Empty : remoteRelease.tag_name; - return string.Compare($"{remoteVersion}", $"{currentVersion}") > 0; + SemanticVersionUtils local = new SemanticVersionUtils(localVersion); + SemanticVersionUtils remote = new SemanticVersionUtils(remoteVersion); + + return remote > local; } public async Task CheckForMetaDefinitionUpdatesAsync() @@ -178,28 +185,9 @@ private async Task DownloadMetaFilesAsync() } } - private async Task GetRemoteAppReleaseAsync() - { - var gitHubApiReleasesLatest = new Uri("https://api.github.com/repos/xboxoneresearch/XboxPostcodeMonitor/releases/latest"); - - try - { - var response = await _httpClient.GetAsync(gitHubApiReleasesLatest); - response.EnsureSuccessStatusCode(); - - var json = await response.Content.ReadAsStringAsync(); - return JsonSerializer.Deserialize(json, _jsonSerializeOptions); - } - catch (Exception ex) - { - _logger.LogError(ex, Assets.Resources.FailedDownloadReleaseDefinition, gitHubApiReleasesLatest); - return null; - } - } - - private async Task GetRemoteFirmwareReleaseAsync() + private async Task GetRepositoryLatestReleaseAsync(string owner, string repo) { - var gitHubApiReleasesLatest = new Uri("https://api.github.com/repos/xboxoneresearch/PicoDurangoPOST/releases/latest"); + var gitHubApiReleasesLatest = new Uri($"https://api.github.com/repos/{owner}/{repo}/releases/latest"); try { diff --git a/PostCodeSerialMonitor/Utils/SemanticVersionUtils.cs b/PostCodeSerialMonitor/Utils/SemanticVersionUtils.cs new file mode 100644 index 0000000..aa700a8 --- /dev/null +++ b/PostCodeSerialMonitor/Utils/SemanticVersionUtils.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using Avalonia.X11.Interop; + +namespace PostCodeSerialMonitor.Utils; + +public class SemanticVersionUtils +{ + private string _version { get; set; } = string.Empty; + + public SemanticVersionUtils(string version) + { + //Ignore the 'v' at the beginning of the version string + if (version.StartsWith("v")) + { + _version = version.Substring(1); + } + else + { + _version = version; + } + } + + /// Override greater-than operator for SemanticVersionUtils + public static bool operator >(SemanticVersionUtils left, SemanticVersionUtils right) + { + return string.Compare(left._version, right._version) > 0; + } + + /// Override less-than operator for SemanticVersionUtils + public static bool operator <(SemanticVersionUtils left, SemanticVersionUtils right) + { + return string.Compare(left._version, right._version) < 0; + } +} \ No newline at end of file diff --git a/PostCodeSerialMonitor/ViewModels/MainWindowViewModel.cs b/PostCodeSerialMonitor/ViewModels/MainWindowViewModel.cs index 3f61d45..3c02046 100644 --- a/PostCodeSerialMonitor/ViewModels/MainWindowViewModel.cs +++ b/PostCodeSerialMonitor/ViewModels/MainWindowViewModel.cs @@ -177,7 +177,7 @@ await MessageBoxManager if (_configurationService.Config.CheckForAppUpdates) { - updateAvailable = await _metaUpdateService.CheckForAppUpdatesAsync($"v{AppVersion}"); + updateAvailable = await _metaUpdateService.CheckForAppUpdatesAsync(AppVersion); if (updateAvailable) { var box = MessageBoxManager