Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Check Improvements #1447

Merged
7 changes: 7 additions & 0 deletions LenovoLegionToolkit.Lib/Enums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -639,3 +639,10 @@ public enum WinKeyState
}

public enum WinKeyChanged;

public enum UpdateCheckStatus
{
Success,
RateLimitReached,
Error
}
3 changes: 3 additions & 0 deletions LenovoLegionToolkit.Lib/Settings/ApplicationSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ public class ApplicationSettingsStore
public bool SynchronizeBrightnessToAllPowerPlans { get; set; }
public ModifierKey SmartFnLockFlags { get; set; }
public bool ResetBatteryOnSinceTimerOnReboot { get; set; }
public int UpdateCheckMiniumTimeSpanHours { get; set; }
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a bit much. I guess a drop down with some values like hourly, daily, weekly, monthly etc.
It would be also nice to store this as an actual TimeSpan instead of ints.

public int UpdateCheckMiniumTimeSpanMinutes { get; set; }
public int UpdateCheckMiniumTimeSpanSeconds { get; set; }
}

public ApplicationSettings() : base("settings.json")
Expand Down
72 changes: 66 additions & 6 deletions LenovoLegionToolkit.Lib/Utils/UpdateChecker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,38 @@
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 ApplicationSettings _settings = IoCContainer.Resolve<ApplicationSettings>();
private readonly AsyncLock _updateSemaphore = new();

private DateTime _lastUpdate = DateTime.MinValue;
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;

CheckUpdateCheckMiniumTimeSpanSettings();

_minimumTimeSpanForRefresh = new(_settings.Store.UpdateCheckMiniumTimeSpanHours,
_settings.Store.UpdateCheckMiniumTimeSpanMinutes,
_settings.Store.UpdateCheckMiniumTimeSpanSeconds);
}

public async Task<Version?> CheckAsync(bool forceCheck)
{
using (await _updateSemaphore.LockAsync().ConfigureAwait(false))
{
Expand All @@ -37,13 +52,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,13 +80,24 @@ 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
Expand Down Expand Up @@ -101,10 +127,44 @@ 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 SetMinimumTimeSpanForRefresh(int hours, int minutes, int seconds) => _minimumTimeSpanForRefresh = new(hours, minutes, seconds);

private void CheckUpdateCheckMiniumTimeSpanSettings()
{
bool changed = false;
if (_settings.Store.UpdateCheckMiniumTimeSpanHours < 3)
{
_settings.Store.UpdateCheckMiniumTimeSpanHours = 3;
changed = true;
}
if (_settings.Store.UpdateCheckMiniumTimeSpanMinutes < 0)
{
_settings.Store.UpdateCheckMiniumTimeSpanMinutes = 0;
changed = true;
}
if (_settings.Store.UpdateCheckMiniumTimeSpanMinutes > 59)
{
_settings.Store.UpdateCheckMiniumTimeSpanMinutes = 59;
changed = true;
}
if (_settings.Store.UpdateCheckMiniumTimeSpanSeconds < 0)
{
_settings.Store.UpdateCheckMiniumTimeSpanSeconds = 0;
changed = true;
}
if (_settings.Store.UpdateCheckMiniumTimeSpanSeconds > 59)
{
_settings.Store.UpdateCheckMiniumTimeSpanSeconds = 59;
changed = true;
}
if (changed)
_settings.SynchronizeStore();
}
}
83 changes: 83 additions & 0 deletions LenovoLegionToolkit.WPF/Pages/SettingsPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,89 @@
</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="_updateCheckMiniumTimeSpanCard" Margin="0,0,0,8">
<custom:CardControl.Header>
<controls:CardHeaderControl Title="{x:Static resources:Resource.SettingsPage_UpdateCheckMiniumTimeSpan_Title}" Subtitle="{x:Static resources:Resource.SettingsPage_UpdateCheckMiniumTimeSpan_Message}" />
</custom:CardControl.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
Margin="0,0,0,8"
VerticalAlignment="Center"
Text="{x:Static resources:Resource.SettingsPage_UpdateCheckMiniumTimeSpan_Picker_Hours}" />
<wpfui:NumberBox
Grid.Column="1"
x:Name="_updateCheckMiniumTimeSpanPickerHours"
Margin="8,0,0,8"
ClearButtonEnabled="False"
MaxDecimalPlaces="0"
Maximum="1440"
Minimum="3"
ValueChanged="UpdateCheckMiniumTimeSpanPickerHours_ValueChanged"
Visibility="Hidden" />
<TextBlock
Grid.Column="2"
Margin="8,0,0,8"
VerticalAlignment="Center"
Text="{x:Static resources:Resource.SettingsPage_UpdateCheckMiniumTimeSpan_Picker_Minutes}" />
<wpfui:NumberBox
Grid.Column="3"
x:Name="_updateCheckMiniumTimeSpanPickerMinutes"
Margin="8,0,0,8"
ClearButtonEnabled="False"
MaxDecimalPlaces="0"
Maximum="59"
Minimum="0"
ValueChanged="UpdateCheckMiniumTimeSpanPickerMinutes_ValueChanged"
Visibility="Hidden" />
<TextBlock
Grid.Column="4"
Margin="8,0,0,8"
VerticalAlignment="Center"
Text="{x:Static resources:Resource.SettingsPage_UpdateCheckMiniumTimeSpan_Picker_Seconds}" />
<wpfui:NumberBox
Grid.Column="5"
x:Name="_updateCheckMiniumTimeSpanPickerSeconds"
Margin="8,0,0,8"
ClearButtonEnabled="False"
MaxDecimalPlaces="0"
Maximum="59"
Minimum="0"
ValueChanged="UpdateCheckMiniumTimeSpanPickerSeconds_ValueChanged"
Visibility="Hidden" />
</Grid>
</custom:CardControl>

<TextBlock
Margin="0,16,0,24"
AutomationProperties.Name="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Text}"
Expand Down
67 changes: 67 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,7 @@ 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 bool _isRefreshing;

Expand Down Expand Up @@ -119,6 +121,23 @@ private async Task RefreshAsync()

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

if (_updateChecker.Disable)
{
_updateTextBlock.Visibility = Visibility.Collapsed;
_checkUpdatesCard.Visibility = Visibility.Collapsed;
_updateCheckMiniumTimeSpanCard.Visibility = Visibility.Collapsed;
}
else
{
_checkUpdatesButton.Visibility = Visibility.Visible;
_updateCheckMiniumTimeSpanPickerHours.Visibility = Visibility.Visible;
_updateCheckMiniumTimeSpanPickerHours.Value = _settings.Store.UpdateCheckMiniumTimeSpanHours;
_updateCheckMiniumTimeSpanPickerMinutes.Visibility = Visibility.Visible;
_updateCheckMiniumTimeSpanPickerMinutes.Value = _settings.Store.UpdateCheckMiniumTimeSpanMinutes;
_updateCheckMiniumTimeSpanPickerSeconds.Visibility = Visibility.Visible;
_updateCheckMiniumTimeSpanPickerSeconds.Value = _settings.Store.UpdateCheckMiniumTimeSpanSeconds;
}

try
{
var mi = await Compatibility.GetMachineInformationAsync();
Expand Down Expand Up @@ -250,6 +269,12 @@ private void UpdateAccentColorPicker()
_accentColorPicker.SelectedColor = _themeManager.GetAccentColor().ToColor();
}

private void RefreshUpdateCheckMiniumTimeSpan() => _updateChecker.SetMinimumTimeSpanForRefresh(
(int)(_updateCheckMiniumTimeSpanPickerHours.Value ?? 3),
(int)(_updateCheckMiniumTimeSpanPickerMinutes.Value ?? 0),
(int)(_updateCheckMiniumTimeSpanPickerSeconds.Value ?? 0)
);

private void AutorunComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (_isRefreshing)
Expand Down Expand Up @@ -555,6 +580,48 @@ 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 UpdateCheckMiniumTimeSpanPickerHours_ValueChanged(object sender, RoutedEventArgs e)
{
if (_isRefreshing)
return;

_settings.Store.UpdateCheckMiniumTimeSpanHours = (int)(_updateCheckMiniumTimeSpanPickerHours.Value ?? 3);
_settings.SynchronizeStore();
RefreshUpdateCheckMiniumTimeSpan();
}

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

_settings.Store.UpdateCheckMiniumTimeSpanMinutes = (int)(_updateCheckMiniumTimeSpanPickerMinutes.Value ?? 0);
_settings.SynchronizeStore();
RefreshUpdateCheckMiniumTimeSpan();
}

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

_settings.Store.UpdateCheckMiniumTimeSpanSeconds = (int)(_updateCheckMiniumTimeSpanPickerSeconds.Value ?? 0);
_settings.SynchronizeStore();
RefreshUpdateCheckMiniumTimeSpan();
}

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