Skip to content

Show file operation progress on Taskbar & continue operations when minimized #6273

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

Merged
merged 4 commits into from
Oct 5, 2021
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
175 changes: 141 additions & 34 deletions Files.Launcher/MessageHandlers/FileOperationsHandler.cs

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions Files.Launcher/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ private static async Task Main(string[] args)

// Wait until the connection gets closed
appServiceExit.WaitOne();

// Wait for ongoing file operations
messageHandlers.OfType<FileOperationsHandler>().Single().WaitForCompletion();
}
finally
{
Expand Down
18 changes: 14 additions & 4 deletions Files.Launcher/Win32API.cs
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,13 @@ public static Bitmap GetBitmapFromHBitmap(HBITMAP hBitmap)
}
}

public static Shell32.ITaskbarList4 CreateTaskbarObject()
{
var taskbar2 = new Shell32.ITaskbarList2();
taskbar2.HrInit();
return taskbar2 as Shell32.ITaskbarList4;
}

private static Bitmap GetAlphaBitmapFromBitmapData(BitmapData bmpData)
{
using var tmp = new Bitmap(bmpData.Width, bmpData.Height, bmpData.Stride, PixelFormat.Format32bppArgb, bmpData.Scan0);
Expand Down Expand Up @@ -344,10 +351,13 @@ private static bool IsAlphaBitmap(BitmapData bmpData)

public static async Task SendMessageAsync(NamedPipeServerStream pipe, ValueSet valueSet, string requestID = null)
{
var message = new Dictionary<string, object>(valueSet);
message.Add("RequestID", requestID);
var serialized = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message));
await pipe.WriteAsync(serialized, 0, serialized.Length);
await Extensions.IgnoreExceptions(async () =>
{
var message = new Dictionary<string, object>(valueSet);
message.Add("RequestID", requestID);
var serialized = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message));
await pipe.WriteAsync(serialized, 0, serialized.Length);
});
}

// There is usually no need to define Win32 COM interfaces/P-Invoke methods here.
Expand Down
1 change: 1 addition & 0 deletions Files/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public App()
InitializeComponent();
Suspending += OnSuspending;
LeavingBackground += OnLeavingBackground;
AppServiceConnectionHelper.Register();
}

private static async Task EnsureSettingsAndConfigurationAreBootstrapped()
Expand Down
12 changes: 3 additions & 9 deletions Files/Filesystem/FilesystemOperations/FilesystemOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,6 @@ await DialogDisplayHelper.ShowDialogAsync(
{
{ "Arguments", "FileOperation" },
{ "fileop", "CopyItem" },
{ "operationID", Guid.NewGuid().ToString() },
{ "filepath", source.Path },
{ "destpath", destination },
{ "overwrite", collision == NameCollisionOption.ReplaceExisting }
Expand Down Expand Up @@ -321,7 +320,6 @@ await DialogDisplayHelper.ShowDialogAsync(
{
{ "Arguments", "FileOperation" },
{ "fileop", "CopyItem" },
{ "operationID", Guid.NewGuid().ToString() },
{ "filepath", source.Path },
{ "destpath", destination },
{ "overwrite", collision == NameCollisionOption.ReplaceExisting }
Expand Down Expand Up @@ -486,7 +484,6 @@ await DialogDisplayHelper.ShowDialogAsync(
{
{ "Arguments", "FileOperation" },
{ "fileop", "MoveItem" },
{ "operationID", Guid.NewGuid().ToString() },
{ "filepath", source.Path },
{ "destpath", destination },
{ "overwrite", collision == NameCollisionOption.ReplaceExisting }
Expand Down Expand Up @@ -532,7 +529,6 @@ await DialogDisplayHelper.ShowDialogAsync(
{
{ "Arguments", "FileOperation" },
{ "fileop", "MoveItem" },
{ "operationID", Guid.NewGuid().ToString() },
{ "filepath", source.Path },
{ "destpath", destination },
{ "overwrite", collision == NameCollisionOption.ReplaceExisting }
Expand Down Expand Up @@ -626,10 +622,8 @@ public async Task<IStorageHistory> DeleteAsync(IStorageItemWithPath source,
{
{ "Arguments", "FileOperation" },
{ "fileop", "DeleteItem" },
{ "operationID", Guid.NewGuid().ToString() },
{ "filepath", source.Path },
{ "permanently", permanently },
{ "HWND", NativeWinApiHelper.CoreWindowHandle.ToInt64() }
{ "permanently", permanently }
});
}
}
Expand Down Expand Up @@ -742,7 +736,6 @@ public async Task<IStorageHistory> RenameAsync(IStorageItemWithPath source,
{
{ "Arguments", "FileOperation" },
{ "fileop", "RenameItem" },
{ "operationID", Guid.NewGuid().ToString() },
{ "filepath", source.Path },
{ "newName", newName },
{ "overwrite", collision == NameCollisionOption.ReplaceExisting }
Expand Down Expand Up @@ -853,7 +846,6 @@ public async Task<IStorageHistory> RestoreFromTrashAsync(IStorageItemWithPath so
{
{ "Arguments", "FileOperation" },
{ "fileop", "MoveItem" },
{ "operationID", Guid.NewGuid().ToString() },
{ "filepath", source.Path },
{ "destpath", destination },
{ "overwrite", false }
Expand Down Expand Up @@ -947,6 +939,8 @@ private static async Task<BaseStorageFolder> MoveDirectoryAsync(BaseStorageFolde
connection = await AppServiceConnectionHelper.Instance;
if (connection != null)
{
operation.Add("operationID", Guid.NewGuid().ToString());
operation.Add("HWND", NativeWinApiHelper.CoreWindowHandle.ToInt64());
var (status, response) = await connection.SendMessageForResponseAsync(operation);
var fsResult = (FilesystemResult)(status == AppServiceResponseStatus.Success
&& response.Get("Success", false));
Expand Down
33 changes: 21 additions & 12 deletions Files/Filesystem/FilesystemOperations/ShellFilesystemOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public async Task<IStorageHistory> CopyItemsAsync(IEnumerable<IStorageItemWithPa
var operationID = Guid.NewGuid().ToString();
using var r = cancellationToken.Register(CancelOperation, operationID, false);

EventHandler<Dictionary<string, object>> handler = (s, e) => OnProgressUpdated(s, e, progress);
EventHandler<Dictionary<string, object>> handler = (s, e) => OnProgressUpdated(s, e, operationID, progress);
connection.RequestReceived += handler;

var sourceReplace = source.Where((src, index) => collisions.ElementAt(index) == FileNameConflictResolveOptionType.ReplaceExisting);
Expand All @@ -97,7 +97,8 @@ public async Task<IStorageHistory> CopyItemsAsync(IEnumerable<IStorageItemWithPa
{ "operationID", operationID },
{ "filepath", string.Join('|', sourceRename.Select(s => s.Path)) },
{ "destpath", string.Join('|', destinationRename) },
{ "overwrite", false }
{ "overwrite", false },
{ "HWND", NativeWinApiHelper.CoreWindowHandle.ToInt64() }
});
result &= (FilesystemResult)(status == AppServiceResponseStatus.Success
&& response.Get("Success", false));
Expand All @@ -113,7 +114,8 @@ public async Task<IStorageHistory> CopyItemsAsync(IEnumerable<IStorageItemWithPa
{ "operationID", operationID },
{ "filepath", string.Join('|', sourceReplace.Select(s => s.Path)) },
{ "destpath", string.Join('|', destinationReplace) },
{ "overwrite", true }
{ "overwrite", true },
{ "HWND", NativeWinApiHelper.CoreWindowHandle.ToInt64() }
});
result &= (FilesystemResult)(status == AppServiceResponseStatus.Success
&& response.Get("Success", false));
Expand Down Expand Up @@ -236,7 +238,7 @@ public async Task<IStorageHistory> DeleteItemsAsync(IEnumerable<IStorageItemWith
var operationID = Guid.NewGuid().ToString();
using var r = cancellationToken.Register(CancelOperation, operationID, false);

EventHandler<Dictionary<string, object>> handler = (s, e) => OnProgressUpdated(s, e, progress);
EventHandler<Dictionary<string, object>> handler = (s, e) => OnProgressUpdated(s, e, operationID, progress);
connection.RequestReceived += handler;

var deleteResult = new ShellOperationResult();
Expand Down Expand Up @@ -323,7 +325,7 @@ public async Task<IStorageHistory> MoveItemsAsync(IEnumerable<IStorageItemWithPa
var operationID = Guid.NewGuid().ToString();
using var r = cancellationToken.Register(CancelOperation, operationID, false);

EventHandler<Dictionary<string, object>> handler = (s, e) => OnProgressUpdated(s, e, progress);
EventHandler<Dictionary<string, object>> handler = (s, e) => OnProgressUpdated(s, e, operationID, progress);
connection.RequestReceived += handler;

var sourceReplace = source.Where((src, index) => collisions.ElementAt(index) == FileNameConflictResolveOptionType.ReplaceExisting);
Expand All @@ -342,7 +344,8 @@ public async Task<IStorageHistory> MoveItemsAsync(IEnumerable<IStorageItemWithPa
{ "operationID", operationID },
{ "filepath", string.Join('|', sourceRename.Select(s => s.Path)) },
{ "destpath", string.Join('|', destinationRename) },
{ "overwrite", false }
{ "overwrite", false },
{ "HWND", NativeWinApiHelper.CoreWindowHandle.ToInt64() }
});
result &= (FilesystemResult)(status == AppServiceResponseStatus.Success
&& response.Get("Success", false));
Expand All @@ -358,7 +361,8 @@ public async Task<IStorageHistory> MoveItemsAsync(IEnumerable<IStorageItemWithPa
{ "operationID", operationID },
{ "filepath", string.Join('|', sourceReplace.Select(s => s.Path)) },
{ "destpath", string.Join('|', destinationReplace) },
{ "overwrite", true }
{ "overwrite", true },
{ "HWND", NativeWinApiHelper.CoreWindowHandle.ToInt64() }
});
result &= (FilesystemResult)(status == AppServiceResponseStatus.Success
&& response.Get("Success", false));
Expand Down Expand Up @@ -458,7 +462,7 @@ public async Task<IStorageHistory> RestoreFromTrashAsync(IStorageItemWithPath so
var operationID = Guid.NewGuid().ToString();
using var r = cancellationToken.Register(CancelOperation, operationID, false);

EventHandler<Dictionary<string, object>> handler = (s, e) => OnProgressUpdated(s, e, progress);
EventHandler<Dictionary<string, object>> handler = (s, e) => OnProgressUpdated(s, e, operationID, progress);
connection.RequestReceived += handler;

var moveResult = new ShellOperationResult();
Expand All @@ -469,7 +473,8 @@ public async Task<IStorageHistory> RestoreFromTrashAsync(IStorageItemWithPath so
{ "operationID", operationID },
{ "filepath", source.Path },
{ "destpath", destination },
{ "overwrite", false }
{ "overwrite", false },
{ "HWND", NativeWinApiHelper.CoreWindowHandle.ToInt64() }
});
var result = (FilesystemResult)(status == AppServiceResponseStatus.Success
&& response.Get("Success", false));
Expand Down Expand Up @@ -506,12 +511,16 @@ await DeleteItemsAsync(movedSources.Select(src => StorageItemHelpers.FromPathAnd
}
}

private void OnProgressUpdated(object sender, Dictionary<string, object> message, IProgress<float> progress)
private void OnProgressUpdated(object sender, Dictionary<string, object> message, string currentOperation, IProgress<float> progress)
{
if (message.ContainsKey("OperationID"))
{
var value = (long)message["Progress"];
progress?.Report(value);
var operationID = (string)message["OperationID"];
if (operationID == currentOperation)
{
var value = (long)message["Progress"];
progress?.Report(value);
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion Files/Helpers/AppServiceConnectionHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static class AppServiceConnectionHelper

public static event EventHandler<Task<NamedPipeAsAppServiceConnection>> ConnectionChanged;

static AppServiceConnectionHelper()
public static void Register()
{
App.Current.Suspending += OnSuspending;
App.Current.LeavingBackground += OnLeavingBackground;
Expand Down
1 change: 1 addition & 0 deletions Files/ViewModels/StatusCenterViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ public bool CloseBanner(StatusBanner banner)
}

StatusBannersSource.Remove(banner);
UpdateBanner(banner);
return true;
}

Expand Down
3 changes: 1 addition & 2 deletions Files/Views/ColumnShellPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -913,11 +913,10 @@ private void FilesystemViewModel_ItemLoadStatusChanged(object sender, ItemLoadSt
private void SetLoadingIndicatorForTabs(bool isLoading)
{
var multitaskingControls = ((Window.Current.Content as Frame).Content as MainPage).ViewModel.MultitaskingControls;
var tabItemControl = this.FindAscendant<TabItemControl>();

foreach (var x in multitaskingControls)
{
x.SetLoadingIndicatorStatus(x.Items.FirstOrDefault(x => x.Control == tabItemControl), isLoading);
x.SetLoadingIndicatorStatus(x.Items.FirstOrDefault(x => x.Control.TabItemContent == PaneHolder), isLoading);
}
}

Expand Down
3 changes: 1 addition & 2 deletions Files/Views/ModernShellPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1017,11 +1017,10 @@ private void FilesystemViewModel_ItemLoadStatusChanged(object sender, ItemLoadSt
private void SetLoadingIndicatorForTabs(bool isLoading)
{
var multitaskingControls = ((Window.Current.Content as Frame).Content as MainPage).ViewModel.MultitaskingControls;
var tabItemControl = this.FindAscendant<TabItemControl>();

foreach (var x in multitaskingControls)
{
x.SetLoadingIndicatorStatus(x.Items.FirstOrDefault(x => x.Control == tabItemControl), isLoading);
x.SetLoadingIndicatorStatus(x.Items.FirstOrDefault(x => x.Control.TabItemContent == PaneHolder), isLoading);
}
}

Expand Down