Skip to content

Commit 4678cbf

Browse files
committed
Add a download progress window to give feedback and allow cancelling downloads
1 parent 3349e90 commit 4678cbf

File tree

5 files changed

+151
-22
lines changed

5 files changed

+151
-22
lines changed
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace UnityLauncherPro
2+
{
3+
public readonly struct DownloadProgress
4+
{
5+
public long TotalRead { get; }
6+
public long TotalBytes { get; }
7+
8+
public DownloadProgress(long totalRead, long totalBytes)
9+
{
10+
TotalRead = totalRead;
11+
TotalBytes = totalBytes;
12+
}
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<Window x:Class="UnityLauncherPro.DownloadProgressWindow"
2+
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
Title="Download Progress" Height="70" Width="500"
5+
ResizeMode="NoResize"
6+
WindowStyle="None"
7+
PreviewLostKeyboardFocus="Window_PreviewLostKeyboardFocus"
8+
Background="{DynamicResource ThemeDarkestBackground}">
9+
<Grid>
10+
<Grid>
11+
<Label Content="{Binding Title, RelativeSource={RelativeSource AncestorType=Window}, FallbackValue=Title}" IsHitTestVisible="False" Margin="5,0,0,-5" Foreground="{DynamicResource ThemeMainTitle}" FontSize="12" HorizontalAlignment="Left" />
12+
<Button BorderThickness="0" Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" HorizontalAlignment="Right" VerticalAlignment="Top" Height="23" Width="23" Background="Transparent" Click="CancelDownloadClick" Padding="0,2" IsTabStop="False">
13+
<TextBlock Text="" FontSize="10" Foreground="{DynamicResource ThemeWindowMinClose}" Padding="5,3,4,4" HorizontalAlignment="Center" />
14+
</Button>
15+
</Grid>
16+
<Grid VerticalAlignment="Bottom" Background="{DynamicResource ThemeMainBackgroundColor}">
17+
<ProgressBar x:Name="ProgressBar" Height="23" VerticalAlignment="Center" Margin="10, 10, 70, 10"/>
18+
<TextBlock x:Name="ProgressText" Text="0%" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="10, 10, 70, 10"/>
19+
<Button Style="{StaticResource CustomButton}" Content="Cancel" Width="50" Height="23" HorizontalAlignment="Right" Margin="10" Click="CancelDownloadClick"/>
20+
</Grid>
21+
</Grid>
22+
</Window>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using System;
2+
using System.Windows;
3+
using System.Windows.Input;
4+
5+
namespace UnityLauncherPro
6+
{
7+
public partial class DownloadProgressWindow
8+
{
9+
private readonly Action _cancelAction;
10+
private readonly string _subjectName;
11+
private static MainWindow MainWindow => Tools.mainWindow;
12+
13+
public DownloadProgressWindow(string subjectName, Action cancelAction = null)
14+
{
15+
InitializeComponent();
16+
_subjectName = subjectName;
17+
Title = subjectName;
18+
_cancelAction = cancelAction;
19+
Topmost = true;
20+
Owner = MainWindow;
21+
WindowStartupLocation = WindowStartupLocation.CenterOwner;
22+
MainWindow.IsEnabled = false;
23+
}
24+
25+
public void UpdateProgress(DownloadProgress downloadProgress)
26+
{
27+
Title = $"Downloading {_subjectName} ({downloadProgress.TotalRead / 1024d / 1024d:F1} MB / {downloadProgress.TotalBytes / 1024d / 1024d:F1} MB)";
28+
var progress = downloadProgress.TotalBytes == 0 ? 0 : downloadProgress.TotalRead * 100d / downloadProgress.TotalBytes;
29+
ProgressBar.Value = progress;
30+
ProgressText.Text = $"{progress / 100:P1}";
31+
}
32+
33+
private void CancelDownloadClick(object sender, RoutedEventArgs e)
34+
{
35+
CancelDownload();
36+
}
37+
38+
private void CancelDownload()
39+
{
40+
_cancelAction?.Invoke();
41+
}
42+
43+
private void Window_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
44+
{
45+
var window = (Window)sender;
46+
window.Topmost = true;
47+
}
48+
49+
protected override void OnClosed(EventArgs e)
50+
{
51+
base.OnClosed(e);
52+
_cancelAction?.Invoke();
53+
MainWindow.IsEnabled = true;
54+
}
55+
}
56+
}

UnityLauncherPro/Tools.cs

+54-22
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
using System.IO;
66
using System.Linq;
77
using System.Net;
8+
using System.Net.Http;
89
using System.Net.NetworkInformation;
910
using System.Runtime.InteropServices;
1011
using System.Text;
1112
using System.Text.RegularExpressions;
13+
using System.Threading;
1214
using System.Threading.Tasks;
1315
using System.Windows;
1416
using System.Windows.Controls;
@@ -697,7 +699,7 @@ public static async void DownloadAndInstall(string version)
697699
if (File.Exists(tempFile) == true) File.Delete(tempFile);
698700

699701
// TODO make async
700-
if (DownloadFile(exeURL, tempFile) == true)
702+
if (await DownloadFileAsync(exeURL, tempFile))
701703
{
702704
// get base version, to use for install path
703705
// FIXME check if have any paths?
@@ -737,7 +739,7 @@ public static async void DownloadAndInstall(string version)
737739

738740
static readonly string initFileDefaultURL = "https://raw.githubusercontent.com/unitycoder/UnityInitializeProject/main/Assets/Editor/InitializeProject.cs";
739741

740-
public static void DownloadInitScript(string currentInitScriptFullPath, string currentInitScriptLocationOrURL)
742+
public static async Task DownloadInitScript(string currentInitScriptFullPath, string currentInitScriptLocationOrURL)
741743
{
742744
string currentInitScriptFolder = Path.GetDirectoryName(currentInitScriptFullPath);
743745
string currentInitScriptFile = Path.GetFileName(currentInitScriptFullPath);
@@ -750,7 +752,7 @@ public static void DownloadInitScript(string currentInitScriptFullPath, string c
750752
if (currentInitScriptLocationOrURL.ToLower().StartsWith("http") == true)
751753
{
752754
// download into temp first
753-
if (DownloadFile(currentInitScriptLocationOrURL, tempFile) == false) return;
755+
if (await DownloadFileAsync(currentInitScriptLocationOrURL, tempFile) == false) return;
754756
}
755757
else // file is in local folders/drives/projects
756758
{
@@ -819,25 +821,6 @@ static void DeleteTempFile(string path)
819821
}
820822
}
821823

822-
static bool DownloadFile(string url, string tempFile)
823-
{
824-
bool result = false;
825-
try
826-
{
827-
using (WebClient client = new WebClient())
828-
{
829-
client.DownloadFile(url, tempFile);
830-
// TODO check if actually exists
831-
result = true;
832-
}
833-
}
834-
catch (Exception e)
835-
{
836-
Console.WriteLine("Error> DownloadFile: " + e);
837-
}
838-
return result;
839-
}
840-
841824
public static string DownloadHTML(string url)
842825
{
843826
if (string.IsNullOrEmpty(url) == true) return null;
@@ -2228,6 +2211,55 @@ internal static void OpenCustomAssetPath()
22282211
}
22292212
}
22302213
}
2214+
2215+
private static async Task<bool> DownloadFileAsync(string fileUrl, string destinationPath)
2216+
{
2217+
var cancellationTokenSource = new CancellationTokenSource();
2218+
var fileName = Path.GetFileName(fileUrl);
2219+
var progressWindow = new DownloadProgressWindow(fileName, () => cancellationTokenSource.Cancel());
2220+
progressWindow.Show();
2221+
var result = false;
2222+
try
2223+
{
2224+
using (var client = new HttpClient())
2225+
using (var response = await client.GetAsync(fileUrl, HttpCompletionOption.ResponseHeadersRead, cancellationTokenSource.Token))
2226+
{
2227+
response.EnsureSuccessStatusCode();
2228+
2229+
var totalBytes = response.Content.Headers.ContentLength ?? 1;
2230+
var buffer = new byte[8192];
2231+
var totalRead = 0;
2232+
2233+
using (var contentStream = await response.Content.ReadAsStreamAsync())
2234+
using (var fileStream = new FileStream(destinationPath, FileMode.Create, FileAccess.Write,
2235+
FileShare.None, buffer.Length, true))
2236+
{
2237+
int bytesRead;
2238+
while ((bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length, cancellationTokenSource.Token)) > 0)
2239+
{
2240+
await fileStream.WriteAsync(buffer, 0, bytesRead, cancellationTokenSource.Token);
2241+
totalRead += bytesRead;
2242+
progressWindow.UpdateProgress(new DownloadProgress(totalRead, totalBytes));
2243+
}
2244+
result = true;
2245+
}
2246+
}
2247+
}
2248+
catch (TaskCanceledException)
2249+
{
2250+
Console.WriteLine("Download cancelled");
2251+
}
2252+
catch (Exception e)
2253+
{
2254+
Console.WriteLine(e);
2255+
}
2256+
finally
2257+
{
2258+
DeleteTempFile(destinationPath);
2259+
progressWindow.Close();
2260+
}
2261+
return result;
2262+
}
22312263
} // class
22322264

22332265
} // namespace

UnityLauncherPro/UnityLauncherPro.csproj

+5
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,16 @@
114114
<Compile Include="Converters\LastModifiedConverter.cs" />
115115
<Compile Include="Data\BuildReport.cs" />
116116
<Compile Include="Data\BuildReportItem.cs" />
117+
<Compile Include="Data\DownloadProgress.cs" />
117118
<Compile Include="Data\Platform.cs" />
118119
<Compile Include="Data\Tabs.cs" />
119120
<Compile Include="Data\ThemeColor.cs" />
120121
<Compile Include="Data\UnityVersion.cs" />
121122
<Compile Include="Data\UnityVersionResponse.cs" />
122123
<Compile Include="Data\UnityVersionStream.cs" />
124+
<Compile Include="DownloadProgressWindow.xaml.cs">
125+
<DependentUpon>DownloadProgressWindow.xaml</DependentUpon>
126+
</Compile>
123127
<Compile Include="GetProjects.cs" />
124128
<Compile Include="GetUnityInstallations.cs" />
125129
<Compile Include="GetUnityUpdates.cs" />
@@ -141,6 +145,7 @@
141145
<DependentUpon>UpgradeWindow.xaml</DependentUpon>
142146
</Compile>
143147
<Compile Include="Version.cs" />
148+
<Page Include="DownloadProgressWindow.xaml" />
144149
<Page Include="NewProject.xaml">
145150
<SubType>Designer</SubType>
146151
<Generator>MSBuild:Compile</Generator>

0 commit comments

Comments
 (0)