Skip to content

Commit

Permalink
Update Check Improvements (#1447)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ace-Radom authored Oct 16, 2024
1 parent 70e4624 commit d2959c5
Show file tree
Hide file tree
Showing 12 changed files with 422 additions and 15 deletions.
23 changes: 23 additions & 0 deletions LenovoLegionToolkit.Lib/Enums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,29 @@ public enum TouchpadLockState
On
}

public enum UpdateCheckFrequency
{
[Display(ResourceType = typeof(Resource), Name = "UpdateCheckFrequency_PerHour")]
PerHour,
[Display(ResourceType = typeof(Resource), Name = "UpdateCheckFrequency_PerThreeHours")]
PerThreeHours,
[Display(ResourceType = typeof(Resource), Name = "UpdateCheckFrequency_PerTwelveHours")]
PerTwelveHours,
[Display(ResourceType = typeof(Resource), Name = "UpdateCheckFrequency_PerDay")]
PerDay,
[Display(ResourceType = typeof(Resource), Name = "UpdateCheckFrequency_PerWeek")]
PerWeek,
[Display(ResourceType = typeof(Resource), Name = "UpdateCheckFrequency_PerMonth")]
PerMonth
}

public enum UpdateCheckStatus
{
Success,
RateLimitReached,
Error
}

public enum WhiteKeyboardBacklightState
{
[Display(ResourceType = typeof(Resource), Name = "WhiteKeyboardBacklightState_Off")]
Expand Down
1 change: 1 addition & 0 deletions LenovoLegionToolkit.Lib/IoCModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ protected override void Load(ContainerBuilder builder)
builder.Register<RGBKeyboardSettings>();
builder.Register<SpectrumKeyboardSettings>();
builder.Register<SunriseSunsetSettings>();
builder.Register<UpdateCheckSettings>();

builder.Register<AlwaysOnUSBFeature>();
builder.Register<BatteryFeature>();
Expand Down
54 changes: 54 additions & 0 deletions LenovoLegionToolkit.Lib/Resources/Resource.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 22 additions & 4 deletions LenovoLegionToolkit.Lib/Resources/Resource.resx
Original file line number Diff line number Diff line change
Expand Up @@ -467,15 +467,33 @@
<value>Unmute</value>
</data>
<data name="PowerModeMappingMode_Disabled" xml:space="preserve">
<value>Disabled</value>
<value>Disabled</value>
</data>
<data name="PowerModeMappingMode_WindowsPowerMode" xml:space="preserve">
<value>Windows Power Mode</value>
<value>Windows Power Mode</value>
</data>
<data name="PowerModeMappingMode_WindowsPowerPlan" xml:space="preserve">
<value>Windows Power Plan</value>
<value>Windows Power Plan</value>
</data>
<data name="RGBKeyboardBacklightPreset_Four" xml:space="preserve">
<value>Preset 4</value>
<value>Preset 4</value>
</data>
<data name="UpdateCheckFrequency_PerDay" xml:space="preserve">
<value>Once per day</value>
</data>
<data name="UpdateCheckFrequency_PerHour" xml:space="preserve">
<value>Once per hour</value>
</data>
<data name="UpdateCheckFrequency_PerMonth" xml:space="preserve">
<value>Once per month</value>
</data>
<data name="UpdateCheckFrequency_PerThreeHours" xml:space="preserve">
<value>Once per 3 hours</value>
</data>
<data name="UpdateCheckFrequency_PerTwelveHours" xml:space="preserve">
<value>Once per 12 hours</value>
</data>
<data name="UpdateCheckFrequency_PerWeek" xml:space="preserve">
<value>Once per week</value>
</data>
</root>
23 changes: 23 additions & 0 deletions LenovoLegionToolkit.Lib/Settings/UpdateCheckSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LenovoLegionToolkit.Lib.Settings;

public class UpdateCheckSettings()
: AbstractSettings<UpdateCheckSettings.UpdateCheckSettingsStore>("update_check.json")
{
public class UpdateCheckSettingsStore
{
public DateTime? LastUpdateCheckDateTime { get; set; }
public UpdateCheckFrequency UpdateCheckFrequency { get; set; }
}

protected override UpdateCheckSettingsStore Default => new()
{
LastUpdateCheckDateTime = null,
UpdateCheckFrequency = UpdateCheckFrequency.PerThreeHours
};
}
50 changes: 43 additions & 7 deletions LenovoLegionToolkit.Lib/Utils/UpdateChecker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,35 @@
using System.Threading;
using System.Threading.Tasks;
using LenovoLegionToolkit.Lib.Extensions;
using LenovoLegionToolkit.Lib.Settings;
using NeoSmart.AsyncLock;
using Octokit;
using Octokit.Internal;

namespace LenovoLegionToolkit.Lib.Utils;

public class UpdateChecker(HttpClientFactory httpClientFactory)
public class UpdateChecker
{
private readonly TimeSpan _minimumTimeSpanForRefresh = new(hours: 3, minutes: 0, seconds: 0);
private readonly HttpClientFactory _httpClientFactory;
private readonly UpdateCheckSettings _updateCheckSettings = IoCContainer.Resolve<UpdateCheckSettings>();
private readonly AsyncLock _updateSemaphore = new();

private DateTime _lastUpdate = DateTime.MinValue;
private DateTime _lastUpdate;
private TimeSpan _minimumTimeSpanForRefresh;
private Update[] _updates = [];

public bool Disable { get; set; }
public UpdateCheckStatus Status { get; set; }

public async Task<Version?> CheckAsync()
public UpdateChecker(HttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;

UpdateMiniumTimeSpanForRefresh();
_lastUpdate = _updateCheckSettings.Store.LastUpdateCheckDateTime ?? DateTime.MinValue;
}

public async Task<Version?> CheckAsync(bool forceCheck)
{
using (await _updateSemaphore.LockAsync().ConfigureAwait(false))
{
Expand All @@ -37,13 +49,13 @@ public class UpdateChecker(HttpClientFactory httpClientFactory)
var timeSpanSinceLastUpdate = DateTime.UtcNow - _lastUpdate;
var shouldCheck = timeSpanSinceLastUpdate > _minimumTimeSpanForRefresh;

if (!shouldCheck)
if (!forceCheck && !shouldCheck)
return _updates.Length != 0 ? _updates.First().Version : null;

if (Log.Instance.IsTraceEnabled)
Log.Instance.Trace($"Checking...");

var adapter = new HttpClientAdapter(httpClientFactory.CreateHandler);
var adapter = new HttpClientAdapter(_httpClientFactory.CreateHandler);
var productInformation = new ProductHeaderValue("LenovoLegionToolkit-UpdateChecker");
var connection = new Connection(productInformation, adapter);
var githubClient = new GitHubClient(connection);
Expand All @@ -65,18 +77,31 @@ public class UpdateChecker(HttpClientFactory httpClientFactory)
Log.Instance.Trace($"Checked [updates.Length={updates.Length}]");

_updates = updates;
Status = UpdateCheckStatus.Success;

return _updates.Length != 0 ? _updates.First().Version : null;
}
catch (RateLimitExceededException ex)
{
if (Log.Instance.IsTraceEnabled)
Log.Instance.Trace($"Reached API Rate Limitation.", ex);

Status = UpdateCheckStatus.RateLimitReached;
return null;
}
catch (Exception ex)
{
if (Log.Instance.IsTraceEnabled)
Log.Instance.Trace($"Error checking for updates.", ex);

Status = UpdateCheckStatus.Error;
return null;
}
finally
{
_lastUpdate = DateTime.UtcNow;
_updateCheckSettings.Store.LastUpdateCheckDateTime = _lastUpdate;
_updateCheckSettings.SynchronizeStore();
}
}
}
Expand All @@ -101,10 +126,21 @@ public async Task<string> DownloadLatestUpdateAsync(IProgress<float>? progress =
throw new InvalidOperationException("Setup file URL could not be found");

await using var fileStream = File.OpenWrite(tempPath);
using var httpClient = httpClientFactory.Create();
using var httpClient = _httpClientFactory.Create();
await httpClient.DownloadAsync(latestUpdate.Url, fileStream, progress, cancellationToken).ConfigureAwait(false);

return tempPath;
}
}

public void UpdateMiniumTimeSpanForRefresh() => _minimumTimeSpanForRefresh = _updateCheckSettings.Store.UpdateCheckFrequency switch
{
UpdateCheckFrequency.PerHour => new(hours: 1, minutes: 0, seconds: 0),
UpdateCheckFrequency.PerThreeHours => new(hours: 3, minutes: 0, seconds: 0),
UpdateCheckFrequency.PerTwelveHours => new(hours: 2, minutes: 0, seconds: 0),
UpdateCheckFrequency.PerDay => new(days: 1, hours: 0, minutes: 0, seconds: 0),
UpdateCheckFrequency.PerWeek => new(days: 7, hours: 0, minutes: 0, seconds: 0),
UpdateCheckFrequency.PerMonth => new(days: 30, hours: 0, minutes: 0, seconds: 0),
_ => throw new ArgumentException(nameof(_updateCheckSettings.Store.UpdateCheckFrequency))
};
}
36 changes: 36 additions & 0 deletions LenovoLegionToolkit.WPF/Pages/SettingsPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,42 @@
</custom:CardAction.Content>
</custom:CardAction>

<TextBlock x:Name="_updateTextBlock"
Margin="0,16,0,24"
AutomationProperties.Name="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Text}"
Focusable="True"
FontSize="24"
FontWeight="Medium"
Text="{x:Static resources:Resource.SettingsPage_Update_Title}" />

<custom:CardControl x:Name="_checkUpdatesCard" Margin="0,0,0,8">
<custom:CardControl.Header>
<controls:CardHeaderControl Title="{x:Static resources:Resource.SettingsPage_CheckUpdates_Title}" Subtitle="{x:Static resources:Resource.SettingsPage_CheckUpdates_Message}" />
</custom:CardControl.Header>
<wpfui:Button
x:Name="_checkUpdatesButton"
Margin="0,0,0,8"
MinWidth="100"
AutomationProperties.Name="{x:Static resources:Resource.SettingsPage_CheckUpdates_Title}"
Click="CheckUpdates_Click"
Content="{x:Static resources:Resource.SettingsPage_CheckUpdatesButton_Content}"
Visibility="Hidden" />
</custom:CardControl>

<custom:CardControl x:Name="_updateCheckFrequencyCard" Margin="0,0,0,8">
<custom:CardControl.Header>
<controls:CardHeaderControl Title="{x:Static resources:Resource.SettingsPage_UpdateCheckFrequency_Title}" Subtitle="{x:Static resources:Resource.SettingsPage_UpdateCheckFrequency_Message}" />
</custom:CardControl.Header>
<ComboBox
x:Name="_updateCheckFrequencyComboBox"
MinWidth="160"
Margin="0,0,0,8"
AutomationProperties.Name="{x:Static resources:Resource.SettingsPage_UpdateCheckFrequency_Title}"
MaxDropDownHeight="Auto"
SelectionChanged="UpdateCheckFrequencyComboBox_SelectionChanged"
Visibility="Hidden" />
</custom:CardControl>

<TextBlock
Margin="0,16,0,24"
AutomationProperties.Name="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Text}"
Expand Down
41 changes: 41 additions & 0 deletions LenovoLegionToolkit.WPF/Pages/SettingsPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using LenovoLegionToolkit.WPF.Extensions;
using LenovoLegionToolkit.WPF.Resources;
using LenovoLegionToolkit.WPF.Utils;
using LenovoLegionToolkit.WPF.Windows;
using LenovoLegionToolkit.WPF.Windows.Settings;

namespace LenovoLegionToolkit.WPF.Pages;
Expand All @@ -36,6 +37,8 @@ public partial class SettingsPage
private readonly ThemeManager _themeManager = IoCContainer.Resolve<ThemeManager>();
private readonly HWiNFOIntegration _hwinfoIntegration = IoCContainer.Resolve<HWiNFOIntegration>();
private readonly IpcServer _ipcServer = IoCContainer.Resolve<IpcServer>();
private readonly UpdateChecker _updateChecker = IoCContainer.Resolve<UpdateChecker>();
private readonly UpdateCheckSettings _updateCheckSettings = IoCContainer.Resolve<UpdateCheckSettings>();

private bool _isRefreshing;

Expand Down Expand Up @@ -119,6 +122,19 @@ private async Task RefreshAsync()

_bootLogoCard.Visibility = await BootLogo.IsSupportedAsync() ? Visibility.Visible : Visibility.Collapsed;

if (_updateChecker.Disable)
{
_updateTextBlock.Visibility = Visibility.Collapsed;
_checkUpdatesCard.Visibility = Visibility.Collapsed;
_updateCheckFrequencyCard.Visibility = Visibility.Collapsed;
}
else
{
_checkUpdatesButton.Visibility = Visibility.Visible;
_updateCheckFrequencyComboBox.Visibility = Visibility.Visible;
_updateCheckFrequencyComboBox.SetItems(Enum.GetValues<UpdateCheckFrequency>(), _updateCheckSettings.Store.UpdateCheckFrequency, t => t.GetDisplayName());
}

try
{
var mi = await Compatibility.GetMachineInformationAsync();
Expand Down Expand Up @@ -555,6 +571,31 @@ private void BootLogo_Click(object sender, RoutedEventArgs e)
window.ShowDialog();
}

private void CheckUpdates_Click(object sender, RoutedEventArgs e)
{
if (_isRefreshing)
return;

if (App.Current.MainWindow is not MainWindow mainWindow)
return;

mainWindow.CheckForUpdates(true);
SnackbarHelper.Show(Resource.SettingsPage_CheckUpdates_Started_Title, Resource.SettingsPage_CheckUpdates_Started_Message);
}

private void UpdateCheckFrequencyComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (_isRefreshing)
return;

if (!_updateCheckFrequencyComboBox.TryGetSelectedItem(out UpdateCheckFrequency frequency))
return;

_updateCheckSettings.Store.UpdateCheckFrequency = frequency;
_updateCheckSettings.SynchronizeStore();
_updateChecker.UpdateMiniumTimeSpanForRefresh();
}

private async void GodModeFnQSwitchableToggle_Click(object sender, RoutedEventArgs e)
{
if (_isRefreshing)
Expand Down
Loading

0 comments on commit d2959c5

Please sign in to comment.