From bd68da0704bf4e83a9c85418fa1743ecc536a744 Mon Sep 17 00:00:00 2001 From: 0x5BFA Date: Mon, 6 May 2024 18:10:07 +0900 Subject: [PATCH 1/8] Init --- .../Compress/CompressIntoArchiveAction.cs | 17 +++- .../Compress/CompressIntoSevenZipAction.cs | 17 +++- .../Compress/CompressIntoZipAction.cs | 17 +++- .../Data/Contracts/IStorageArchiveService.cs | 9 +++ .../Services/Storage/StorageArchiveService.cs | 78 +++++++++++++++++++ .../{ => Storage}/StorageCacheService.cs | 0 6 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 src/Files.App/Data/Contracts/IStorageArchiveService.cs create mode 100644 src/Files.App/Services/Storage/StorageArchiveService.cs rename src/Files.App/Services/{ => Storage}/StorageCacheService.cs (100%) diff --git a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoArchiveAction.cs b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoArchiveAction.cs index ba5c2c77d73a..41989d23b935 100644 --- a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoArchiveAction.cs +++ b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoArchiveAction.cs @@ -24,7 +24,22 @@ public override async Task ExecuteAsync() if (context.ShellPage is null) return; - var (sources, directory, fileName) = CompressHelper.GetCompressDestination(context.ShellPage); + string[] sources = context.SelectedItems.Select(item => item.ItemPath).ToArray(); + string directory = string.Empty; + string fileName = string.Empty; + + if (sources.Length is not 0) + { + // Get the current directory path + directory = context.ShellPage.FilesystemViewModel.WorkingDirectory.Normalize(); + + // Get the library save folder if the folder is library item + if (App.LibraryManager.TryGetLibrary(directory, out var library) && !library.IsEmpty) + directory = library.DefaultSaveFolder; + + // Gets the file name from the directory path + fileName = SystemIO.Path.GetFileName(sources.Length is 1 ? sources[0] : directory); + } var dialog = new CreateArchiveDialog { diff --git a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoSevenZipAction.cs b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoSevenZipAction.cs index 003b5af8921a..e29e04ddf247 100644 --- a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoSevenZipAction.cs +++ b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoSevenZipAction.cs @@ -20,7 +20,22 @@ public override Task ExecuteAsync() if (context.ShellPage is null) return Task.CompletedTask; - var (sources, directory, fileName) = CompressHelper.GetCompressDestination(context.ShellPage); + string[] sources = context.SelectedItems.Select(item => item.ItemPath).ToArray(); + string directory = string.Empty; + string fileName = string.Empty; + + if (sources.Length is not 0) + { + // Get the current directory path + directory = context.ShellPage.FilesystemViewModel.WorkingDirectory.Normalize(); + + // Get the library save folder if the folder is library item + if (App.LibraryManager.TryGetLibrary(directory, out var library) && !library.IsEmpty) + directory = library.DefaultSaveFolder; + + // Gets the file name from the directory path + fileName = SystemIO.Path.GetFileName(sources.Length is 1 ? sources[0] : directory); + } ICompressArchiveModel creator = new CompressArchiveModel( sources, diff --git a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoZipAction.cs b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoZipAction.cs index db7e603aa817..55467065ae62 100644 --- a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoZipAction.cs +++ b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoZipAction.cs @@ -20,7 +20,22 @@ public override Task ExecuteAsync() if (context.ShellPage is null) return Task.CompletedTask; - var (sources, directory, fileName) = CompressHelper.GetCompressDestination(context.ShellPage); + string[] sources = context.SelectedItems.Select(item => item.ItemPath).ToArray(); + string directory = string.Empty; + string fileName = string.Empty; + + if (sources.Length is not 0) + { + // Get the current directory path + directory = context.ShellPage.FilesystemViewModel.WorkingDirectory.Normalize(); + + // Get the library save folder if the folder is library item + if (App.LibraryManager.TryGetLibrary(directory, out var library) && !library.IsEmpty) + directory = library.DefaultSaveFolder; + + // Gets the file name from the directory path + fileName = SystemIO.Path.GetFileName(sources.Length is 1 ? sources[0] : directory); + } ICompressArchiveModel creator = new CompressArchiveModel( sources, diff --git a/src/Files.App/Data/Contracts/IStorageArchiveService.cs b/src/Files.App/Data/Contracts/IStorageArchiveService.cs new file mode 100644 index 000000000000..70668b3bcacd --- /dev/null +++ b/src/Files.App/Data/Contracts/IStorageArchiveService.cs @@ -0,0 +1,9 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +namespace Files.App.Data.Contracts +{ + internal interface IStorageArchiveService + { + } +} diff --git a/src/Files.App/Services/Storage/StorageArchiveService.cs b/src/Files.App/Services/Storage/StorageArchiveService.cs new file mode 100644 index 000000000000..09ac26ce7892 --- /dev/null +++ b/src/Files.App/Services/Storage/StorageArchiveService.cs @@ -0,0 +1,78 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +using Files.Shared.Helpers; +using System.IO; +using Windows.Storage; +using Windows.Win32; + +namespace Files.App.Services +{ + internal class StorageArchiveService + { + private StatusCenterViewModel StatusCenterViewModel { get; } = Ioc.Default.GetRequiredService(); + + public bool CanCompress(IReadOnlyList items) + { + return + CanDecompress(items) is false || + items.Count > 1; + } + + public bool CanDecompress(IReadOnlyList items) + { + return + items.Any() && + (items.All(x => x.IsArchive) || + items.All(x => + x.PrimaryItemAttribute == StorageItemTypes.File && + FileExtensionHelpers.IsZipFile(x.FileExtension))); + } + + public async Task CompressAsync(ICompressArchiveModel creator) + { + var archivePath = creator.GetArchivePath(); + + int index = 1; + + while (File.Exists(archivePath) || Directory.Exists(archivePath)) + archivePath = creator.GetArchivePath($" ({++index})"); + + creator.ArchivePath = archivePath; + + var banner = StatusCenterHelper.AddCard_Compress( + creator.Sources, + archivePath.CreateEnumerable(), + ReturnResult.InProgress, + creator.Sources.Count()); + + creator.Progress = banner.ProgressEventSource; + creator.CancellationToken = banner.CancellationToken; + + bool isSuccess = await creator.RunCreationAsync(); + + StatusCenterViewModel.RemoveItem(banner); + + if (isSuccess) + { + StatusCenterHelper.AddCard_Compress( + creator.Sources, + archivePath.CreateEnumerable(), + ReturnResult.Success, + creator.Sources.Count()); + } + else + { + PInvoke.DeleteFileFromApp(archivePath); + + StatusCenterHelper.AddCard_Compress( + creator.Sources, + archivePath.CreateEnumerable(), + creator.CancellationToken.IsCancellationRequested + ? ReturnResult.Cancelled + : ReturnResult.Failed, + creator.Sources.Count()); + } + } + } +} diff --git a/src/Files.App/Services/StorageCacheService.cs b/src/Files.App/Services/Storage/StorageCacheService.cs similarity index 100% rename from src/Files.App/Services/StorageCacheService.cs rename to src/Files.App/Services/Storage/StorageCacheService.cs From 8baf8e180814a0e6e940d20080964c23452eedc6 Mon Sep 17 00:00:00 2001 From: 0x5BFA Date: Mon, 6 May 2024 19:59:14 +0900 Subject: [PATCH 2/8] Init --- .../Compress/BaseCompressArchiveAction.cs | 3 +- .../Compress/CompressIntoArchiveAction.cs | 2 +- .../Compress/CompressIntoSevenZipAction.cs | 4 +- .../Compress/CompressIntoZipAction.cs | 4 +- .../Decompress/BaseDecompressArchiveAction.cs | 78 ++++- .../Archives/Decompress/DecompressArchive.cs | 58 +++- .../Decompress/DecompressArchiveHere.cs | 2 +- .../Decompress/DecompressArchiveHereSmart.cs | 2 +- .../DecompressArchiveToChildFolderAction.cs | 50 ++- .../Data/Contracts/IStorageArchiveService.cs | 17 +- .../ContentPageContextFlyoutFactory.cs | 5 +- .../Helpers/Application/AppLifecycleHelper.cs | 1 + .../Services/Storage/StorageArchiveService.cs | 143 ++++++++- .../Utils/Archives/CompressHelper.cs | 108 ------- .../Utils/Archives/DecompressHelper.cs | 300 ------------------ 15 files changed, 350 insertions(+), 427 deletions(-) delete mode 100644 src/Files.App/Utils/Archives/CompressHelper.cs delete mode 100644 src/Files.App/Utils/Archives/DecompressHelper.cs diff --git a/src/Files.App/Actions/Content/Archives/Compress/BaseCompressArchiveAction.cs b/src/Files.App/Actions/Content/Archives/Compress/BaseCompressArchiveAction.cs index a4dbfbe4e2ad..42d1edd4733e 100644 --- a/src/Files.App/Actions/Content/Archives/Compress/BaseCompressArchiveAction.cs +++ b/src/Files.App/Actions/Content/Archives/Compress/BaseCompressArchiveAction.cs @@ -6,6 +6,7 @@ namespace Files.App.Actions internal abstract class BaseCompressArchiveAction : BaseUIAction, IAction { protected readonly IContentPageContext context; + protected IStorageArchiveService StorageArchiveService { get; } = Ioc.Default.GetRequiredService(); public abstract string Label { get; } @@ -13,7 +14,7 @@ internal abstract class BaseCompressArchiveAction : BaseUIAction, IAction public override bool IsExecutable => IsContextPageTypeAdaptedToCommand() && - CompressHelper.CanCompress(context.SelectedItems) && + StorageArchiveService.CanCompress(context.SelectedItems) && UIHelpers.CanShowDialog; public BaseCompressArchiveAction() diff --git a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoArchiveAction.cs b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoArchiveAction.cs index 41989d23b935..2f35b2bb6323 100644 --- a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoArchiveAction.cs +++ b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoArchiveAction.cs @@ -63,7 +63,7 @@ public override async Task ExecuteAsync() dialog.CompressionLevel, dialog.SplittingSize); - await CompressHelper.CompressArchiveAsync(creator); + await StorageArchiveService.CompressAsync(creator); } } } diff --git a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoSevenZipAction.cs b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoSevenZipAction.cs index e29e04ddf247..333f2b736276 100644 --- a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoSevenZipAction.cs +++ b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoSevenZipAction.cs @@ -6,7 +6,7 @@ namespace Files.App.Actions internal sealed class CompressIntoSevenZipAction : BaseCompressArchiveAction { public override string Label - => string.Format("CreateNamedArchive".GetLocalizedResource(), $"{CompressHelper.DetermineArchiveNameFromSelection(context.SelectedItems)}.7z"); + => string.Format("CreateNamedArchive".GetLocalizedResource(), $"{StorageArchiveService.GenerateArchiveNameFromItems(context.SelectedItems)}.7z"); public override string Description => "CompressIntoSevenZipDescription".GetLocalizedResource(); @@ -43,7 +43,7 @@ public override Task ExecuteAsync() fileName, fileFormat: ArchiveFormats.SevenZip); - return CompressHelper.CompressArchiveAsync(creator); + return StorageArchiveService.CompressAsync(creator); } } } diff --git a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoZipAction.cs b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoZipAction.cs index 55467065ae62..d34acbc58945 100644 --- a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoZipAction.cs +++ b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoZipAction.cs @@ -6,7 +6,7 @@ namespace Files.App.Actions internal sealed class CompressIntoZipAction : BaseCompressArchiveAction { public override string Label - => string.Format("CreateNamedArchive".GetLocalizedResource(), $"{CompressHelper.DetermineArchiveNameFromSelection(context.SelectedItems)}.zip"); + => string.Format("CreateNamedArchive".GetLocalizedResource(), $"{StorageArchiveService.GenerateArchiveNameFromItems(context.SelectedItems)}.zip"); public override string Description => "CompressIntoZipDescription".GetLocalizedResource(); @@ -43,7 +43,7 @@ public override Task ExecuteAsync() fileName, fileFormat: ArchiveFormats.Zip); - return CompressHelper.CompressArchiveAsync(creator); + return StorageArchiveService.CompressAsync(creator); } } } diff --git a/src/Files.App/Actions/Content/Archives/Decompress/BaseDecompressArchiveAction.cs b/src/Files.App/Actions/Content/Archives/Decompress/BaseDecompressArchiveAction.cs index 3c19254326f2..b8661a244c0d 100644 --- a/src/Files.App/Actions/Content/Archives/Decompress/BaseDecompressArchiveAction.cs +++ b/src/Files.App/Actions/Content/Archives/Decompress/BaseDecompressArchiveAction.cs @@ -1,11 +1,18 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Files.App.Dialogs; +using Microsoft.UI.Xaml.Controls; +using SevenZip; +using System.Text; +using Windows.Foundation.Metadata; + namespace Files.App.Actions { internal abstract class BaseDecompressArchiveAction : BaseUIAction, IAction { protected readonly IContentPageContext context; + protected IStorageArchiveService StorageArchiveService { get; } = Ioc.Default.GetRequiredService(); public abstract string Label { get; } @@ -16,7 +23,7 @@ public virtual HotKey HotKey public override bool IsExecutable => (IsContextPageTypeAdaptedToCommand() && - CompressHelper.CanDecompress(context.SelectedItems) || + StorageArchiveService.CanDecompress(context.SelectedItems) || CanDecompressInsideArchive()) && UIHelpers.CanShowDialog; @@ -37,6 +44,75 @@ protected bool IsContextPageTypeAdaptedToCommand() context.PageType != ContentPageTypes.None; } + protected async Task DecompressArchiveHereAsync(bool smart = false) + { + if (context.SelectedItems.Count is 0) + return; + + foreach (var selectedItem in context.SelectedItems) + { + var password = string.Empty; + BaseStorageFile archive = await StorageHelpers.ToStorageItem(selectedItem.ItemPath); + BaseStorageFolder currentFolder = await StorageHelpers.ToStorageItem(context.ShellPage?.FilesystemViewModel.CurrentFolder.ItemPath); + + if (archive?.Path is null) + return; + + if (await FilesystemTasks.Wrap(() => StorageArchiveService.IsEncryptedAsync(archive))) + { + DecompressArchiveDialog decompressArchiveDialog = new(); + DecompressArchiveDialogViewModel decompressArchiveViewModel = new(archive) + { + IsArchiveEncrypted = true, + ShowPathSelection = false + }; + + decompressArchiveDialog.ViewModel = decompressArchiveViewModel; + + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + decompressArchiveDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; + + ContentDialogResult option = await decompressArchiveDialog.TryShowAsync(); + if (option != ContentDialogResult.Primary) + return; + + password = Encoding.UTF8.GetString(decompressArchiveViewModel.Password); + } + + BaseStorageFolder? destinationFolder = null; + + var isMultipleItems = await FilesystemTasks.Wrap(async () => + { + using SevenZipExtractor? zipFile = await StorageArchiveService.GetSevenZipExtractorAsync(archive); + if (zipFile is null) + return true; + + return zipFile.ArchiveFileData.Select(file => + { + var pathCharIndex = file.FileName.IndexOfAny(['/', '\\']); + if (pathCharIndex == -1) + return file.FileName; + else + return file.FileName.Substring(0, pathCharIndex); + }) + .Distinct().Count() > 1; + }); + + if (smart && currentFolder is not null && isMultipleItems) + { + destinationFolder = await FilesystemTasks.Wrap(() => currentFolder.CreateFolderAsync(SystemIO.Path.GetFileNameWithoutExtension(archive.Path), CreationCollisionOption.GenerateUniqueName).AsTask()); + } + else + { + destinationFolder = currentFolder; + } + + // Operate decompress + var result = await FilesystemTasks.Wrap(() => + StorageArchiveService.DecompressAsync(archive, destinationFolder, password)); + } + } + protected virtual bool CanDecompressInsideArchive() { return false; diff --git a/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchive.cs b/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchive.cs index 6af438b131c2..516e66b140bd 100644 --- a/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchive.cs +++ b/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchive.cs @@ -1,8 +1,13 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Files.App.Dialogs; using Files.Shared.Helpers; +using Microsoft.UI.Xaml.Controls; using System.IO; +using System.Text; +using Windows.Foundation.Metadata; +using Windows.Storage; namespace Files.App.Actions { @@ -21,12 +26,59 @@ public DecompressArchive() { } - public override Task ExecuteAsync() + public override async Task ExecuteAsync() { if (context.ShellPage is null) - return Task.CompletedTask; + return; - return DecompressHelper.DecompressArchiveAsync(context.ShellPage); + BaseStorageFile archive = await StorageHelpers.ToStorageItem( + context.SelectedItems.Count is 0 + ? context.ShellPage.FilesystemViewModel.WorkingDirectory + : context.SelectedItem?.ItemPath ?? string.Empty); + + if (archive?.Path is null) + return; + + var isArchiveEncrypted = await FilesystemTasks.Wrap(() => StorageArchiveService.IsEncryptedAsync(archive)); + var password = string.Empty; + + DecompressArchiveDialog decompressArchiveDialog = new(); + DecompressArchiveDialogViewModel decompressArchiveViewModel = new(archive) + { + IsArchiveEncrypted = isArchiveEncrypted, + ShowPathSelection = true + }; + decompressArchiveDialog.ViewModel = decompressArchiveViewModel; + + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + decompressArchiveDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; + + ContentDialogResult option = await decompressArchiveDialog.TryShowAsync(); + if (option != ContentDialogResult.Primary) + return; + + if (isArchiveEncrypted && decompressArchiveViewModel.Password is not null) + password = Encoding.UTF8.GetString(decompressArchiveViewModel.Password); + + // Check if archive still exists + if (!StorageHelpers.Exists(archive.Path)) + return; + + BaseStorageFolder destinationFolder = decompressArchiveViewModel.DestinationFolder; + string destinationFolderPath = decompressArchiveViewModel.DestinationFolderPath; + + if (destinationFolder is null) + { + BaseStorageFolder parentFolder = await StorageHelpers.ToStorageItem(Path.GetDirectoryName(archive.Path)); + destinationFolder = await FilesystemTasks.Wrap(() => parentFolder.CreateFolderAsync(Path.GetFileName(destinationFolderPath), CreationCollisionOption.GenerateUniqueName).AsTask()); + } + + // Operate decompress + var result = await FilesystemTasks.Wrap(() => + StorageArchiveService.DecompressAsync(archive, destinationFolder, password)); + + if (decompressArchiveViewModel.OpenDestinationFolderOnCompletion) + await NavigationHelpers.OpenPath(destinationFolderPath, context.ShellPage, FilesystemItemType.Directory); } protected override bool CanDecompressInsideArchive() diff --git a/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveHere.cs b/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveHere.cs index e1c1e4e12e1f..9d3983ee405c 100644 --- a/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveHere.cs +++ b/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveHere.cs @@ -17,7 +17,7 @@ public DecompressArchiveHere() public override Task ExecuteAsync() { - return DecompressHelper.DecompressArchiveHereAsync(context.ShellPage); + return DecompressArchiveHereAsync(); } } } diff --git a/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveHereSmart.cs b/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveHereSmart.cs index 78741b386666..281c4f52600a 100644 --- a/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveHereSmart.cs +++ b/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveHereSmart.cs @@ -20,7 +20,7 @@ public DecompressArchiveHereSmart() public override Task ExecuteAsync() { - return DecompressHelper.DecompressArchiveHereAsync(context.ShellPage, true); + return DecompressArchiveHereAsync(true); } } } diff --git a/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveToChildFolderAction.cs b/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveToChildFolderAction.cs index 7c087e0a0a30..c28835094624 100644 --- a/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveToChildFolderAction.cs +++ b/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveToChildFolderAction.cs @@ -1,6 +1,12 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Files.App.Dialogs; +using Microsoft.UI.Xaml.Controls; +using System.Text; +using Windows.Foundation.Metadata; +using Windows.Storage; + namespace Files.App.Actions { internal sealed class DecompressArchiveToChildFolderAction : BaseDecompressArchiveAction @@ -15,9 +21,49 @@ public DecompressArchiveToChildFolderAction() { } - public override Task ExecuteAsync() + public override async Task ExecuteAsync() { - return DecompressHelper.DecompressArchiveToChildFolderAsync(context.ShellPage); + if (context.SelectedItems.Count is 0) + return; + + foreach (var selectedItem in context.SelectedItems) + { + var password = string.Empty; + + BaseStorageFile archive = await StorageHelpers.ToStorageItem(selectedItem.ItemPath); + BaseStorageFolder currentFolder = await StorageHelpers.ToStorageItem(context.ShellPage?.FilesystemViewModel.CurrentFolder.ItemPath); + BaseStorageFolder destinationFolder = null; + + if (archive?.Path is null) + return; + + if (await FilesystemTasks.Wrap(() => StorageArchiveService.IsEncryptedAsync(archive))) + { + DecompressArchiveDialog decompressArchiveDialog = new(); + DecompressArchiveDialogViewModel decompressArchiveViewModel = new(archive) + { + IsArchiveEncrypted = true, + ShowPathSelection = false + }; + decompressArchiveDialog.ViewModel = decompressArchiveViewModel; + + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + decompressArchiveDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; + + ContentDialogResult option = await decompressArchiveDialog.TryShowAsync(); + if (option != ContentDialogResult.Primary) + return; + + password = Encoding.UTF8.GetString(decompressArchiveViewModel.Password); + } + + if (currentFolder is not null) + destinationFolder = await FilesystemTasks.Wrap(() => currentFolder.CreateFolderAsync(SystemIO.Path.GetFileNameWithoutExtension(archive.Path), CreationCollisionOption.GenerateUniqueName).AsTask()); + + // Operate decompress + var result = await FilesystemTasks.Wrap(() => + StorageArchiveService.DecompressAsync(archive, destinationFolder, password)); + } } protected override void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) diff --git a/src/Files.App/Data/Contracts/IStorageArchiveService.cs b/src/Files.App/Data/Contracts/IStorageArchiveService.cs index 70668b3bcacd..cbe2f197fd49 100644 --- a/src/Files.App/Data/Contracts/IStorageArchiveService.cs +++ b/src/Files.App/Data/Contracts/IStorageArchiveService.cs @@ -1,9 +1,24 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using SevenZip; + namespace Files.App.Data.Contracts { - internal interface IStorageArchiveService + public interface IStorageArchiveService { + bool CanCompress(IReadOnlyList items); + + bool CanDecompress(IReadOnlyList items); + + Task CompressAsync(ICompressArchiveModel creator); + + Task DecompressAsync(BaseStorageFile archive, BaseStorageFolder destinationFolder, string password); + + string GenerateArchiveNameFromItems(IReadOnlyList items); + + Task IsEncryptedAsync(BaseStorageFile archive); + + Task GetSevenZipExtractorAsync(BaseStorageFile archive, string password = ""); } } diff --git a/src/Files.App/Data/Factories/ContentPageContextFlyoutFactory.cs b/src/Files.App/Data/Factories/ContentPageContextFlyoutFactory.cs index f5e172cdab2a..9322d19c8782 100644 --- a/src/Files.App/Data/Factories/ContentPageContextFlyoutFactory.cs +++ b/src/Files.App/Data/Factories/ContentPageContextFlyoutFactory.cs @@ -22,6 +22,7 @@ public static class ContentPageContextFlyoutFactory private static readonly IModifiableCommandManager ModifiableCommands = Ioc.Default.GetRequiredService(); private static readonly IAddItemService AddItemService = Ioc.Default.GetRequiredService(); private static readonly ICommandManager Commands = Ioc.Default.GetRequiredService(); + private static IStorageArchiveService StorageArchiveService { get; } = Ioc.Default.GetRequiredService(); public static List GetItemContextCommandsWithoutShellItems(CurrentInstanceViewModel currentInstanceViewModel, List selectedItems, BaseLayoutViewModel commandsViewModel, bool shiftPressed, SelectedItemsPropertiesViewModel? selectedItemsPropertiesViewModel, ItemViewModel? itemViewModel = null) { @@ -517,7 +518,7 @@ public static List GetBaseItemMenuItems( new ContextMenuFlyoutItemViewModelBuilder(Commands.CompressIntoZip).Build(), new ContextMenuFlyoutItemViewModelBuilder(Commands.CompressIntoSevenZip).Build(), ], - ShowItem = UserSettingsService.GeneralSettingsService.ShowCompressionOptions && itemsSelected && CompressHelper.CanCompress(selectedItems) + ShowItem = UserSettingsService.GeneralSettingsService.ShowCompressionOptions && itemsSelected && StorageArchiveService.CanCompress(selectedItems) }, new ContextMenuFlyoutItemViewModel { @@ -534,7 +535,7 @@ public static List GetBaseItemMenuItems( new ContextMenuFlyoutItemViewModelBuilder(Commands.DecompressArchiveHere).Build(), new ContextMenuFlyoutItemViewModelBuilder(Commands.DecompressArchiveToChildFolder).Build(), ], - ShowItem = UserSettingsService.GeneralSettingsService.ShowCompressionOptions && CompressHelper.CanDecompress(selectedItems) + ShowItem = UserSettingsService.GeneralSettingsService.ShowCompressionOptions && StorageArchiveService.CanDecompress(selectedItems) }, new ContextMenuFlyoutItemViewModel() { diff --git a/src/Files.App/Helpers/Application/AppLifecycleHelper.cs b/src/Files.App/Helpers/Application/AppLifecycleHelper.cs index 67fb24dc9542..833ed8e10d72 100644 --- a/src/Files.App/Helpers/Application/AppLifecycleHelper.cs +++ b/src/Files.App/Helpers/Application/AppLifecycleHelper.cs @@ -190,6 +190,7 @@ public static IHost ConfigureHost() .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() .AddSingleton() // ViewModels .AddSingleton() diff --git a/src/Files.App/Services/Storage/StorageArchiveService.cs b/src/Files.App/Services/Storage/StorageArchiveService.cs index 09ac26ce7892..85865d031ef6 100644 --- a/src/Files.App/Services/Storage/StorageArchiveService.cs +++ b/src/Files.App/Services/Storage/StorageArchiveService.cs @@ -2,16 +2,20 @@ // Licensed under the MIT License. See the LICENSE. using Files.Shared.Helpers; +using Microsoft.Extensions.Logging; +using SevenZip; using System.IO; using Windows.Storage; using Windows.Win32; namespace Files.App.Services { - internal class StorageArchiveService + public class StorageArchiveService : IStorageArchiveService { private StatusCenterViewModel StatusCenterViewModel { get; } = Ioc.Default.GetRequiredService(); + private IThreadingService ThreadingService { get; } = Ioc.Default.GetRequiredService(); + /// public bool CanCompress(IReadOnlyList items) { return @@ -19,6 +23,7 @@ public bool CanCompress(IReadOnlyList items) items.Count > 1; } + /// public bool CanDecompress(IReadOnlyList items) { return @@ -29,13 +34,14 @@ public bool CanDecompress(IReadOnlyList items) FileExtensionHelpers.IsZipFile(x.FileExtension))); } + /// public async Task CompressAsync(ICompressArchiveModel creator) { var archivePath = creator.GetArchivePath(); int index = 1; - while (File.Exists(archivePath) || Directory.Exists(archivePath)) + while (SystemIO.File.Exists(archivePath) || SystemIO.Directory.Exists(archivePath)) archivePath = creator.GetArchivePath($" ({++index})"); creator.ArchivePath = archivePath; @@ -74,5 +80,138 @@ public async Task CompressAsync(ICompressArchiveModel creator) creator.Sources.Count()); } } + + /// + public async Task DecompressAsync(BaseStorageFile archive, BaseStorageFolder destinationFolder, string password) + { + using var zipFile = await GetSevenZipExtractorAsync(archive, password); + if (zipFile is null) + return false; + + // Initialize a new in-progress status card + var statusCard = StatusCenterHelper.AddCard_Decompress( + archive.Path.CreateEnumerable(), + destinationFolder!.Path.CreateEnumerable(), + ReturnResult.InProgress); + + // Check if the decompress operation canceled + if (statusCard.CancellationToken.IsCancellationRequested) + return false; + + StatusCenterItemProgressModel fsProgress = new( + statusCard.ProgressEventSource, + enumerationCompleted: true, + FileSystemStatusCode.InProgress, + zipFile.ArchiveFileData.Count(x => !x.IsDirectory)); + + fsProgress.TotalSize = zipFile.ArchiveFileData.Select(x => (long)x.Size).Sum(); + fsProgress.Report(); + + bool isSuccess = false; + + try + { + // TODO: Get this method return result + await zipFile.ExtractArchiveAsync(destinationFolder.Path); + + isSuccess = true; + } + catch + { + isSuccess = false; + } + finally + { + // Remove the in-progress status card + StatusCenterViewModel.RemoveItem(statusCard); + + if (isSuccess) + { + // Successful + StatusCenterHelper.AddCard_Decompress( + archive.Path.CreateEnumerable(), + destinationFolder.Path.CreateEnumerable(), + ReturnResult.Success); + } + else + { + // Error + StatusCenterHelper.AddCard_Decompress( + archive.Path.CreateEnumerable(), + destinationFolder.Path.CreateEnumerable(), + statusCard.CancellationToken.IsCancellationRequested + ? ReturnResult.Cancelled + : ReturnResult.Failed); + } + } + + zipFile.Extracting += (s, e) => + { + if (fsProgress.TotalSize > 0) + fsProgress.Report(e.BytesProcessed / (double)fsProgress.TotalSize * 100); + }; + + zipFile.FileExtractionStarted += (s, e) => + { + if (cancellationToken.IsCancellationRequested) + e.Cancel = true; + + if (!e.FileInfo.IsDirectory) + { + ThreadingService.ExecuteOnUiThreadAsync(() => + { + fsProgress.FileName = e.FileInfo.FileName; + fsProgress.Report(); + }); + } + }; + + zipFile.FileExtractionFinished += (s, e) => + { + if (!e.FileInfo.IsDirectory) + { + fsProgress.AddProcessedItemsCount(1); + fsProgress.Report(); + } + }; + + return isSuccess; + } + + /// + public string GenerateArchiveNameFromItems(IReadOnlyList items) + { + if (!items.Any()) + return string.Empty; + + return + SystemIO.Path.GetFileName( + items.Count is 1 + ? items[0].ItemPath + : SystemIO.Path.GetDirectoryName(items[0].ItemPath)) + ?? string.Empty; + } + + /// + public async Task IsEncryptedAsync(BaseStorageFile archive) + { + using SevenZipExtractor? zipFile = await GetSevenZipExtractorAsync(archive); + if (zipFile is null) + return true; + + return zipFile.ArchiveFileData.Any(file => file.Encrypted || file.Method.Contains("Crypto") || file.Method.Contains("AES")); + } + + /// + public async Task GetSevenZipExtractorAsync(BaseStorageFile archive, string password = "") + { + return await FilesystemTasks.Wrap(async () => + { + var arch = new SevenZipExtractor(await archive.OpenStreamForReadAsync(), password); + + // Force to load archive (1665013614u) + return arch?.ArchiveFileData is null ? null : arch; + }); + } } } diff --git a/src/Files.App/Utils/Archives/CompressHelper.cs b/src/Files.App/Utils/Archives/CompressHelper.cs deleted file mode 100644 index 800e044ffb6e..000000000000 --- a/src/Files.App/Utils/Archives/CompressHelper.cs +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.Shared.Helpers; -using System.IO; -using Windows.Storage; -using Windows.Win32; - -namespace Files.App.Utils.Archives -{ - /// - /// Provides static helper for compressing archive. - /// - public static class CompressHelper - { - private readonly static StatusCenterViewModel _statusCenterViewModel = Ioc.Default.GetRequiredService(); - - public static bool CanDecompress(IReadOnlyList selectedItems) - { - return selectedItems.Any() && - (selectedItems.All(x => x.IsArchive) - || selectedItems.All(x => x.PrimaryItemAttribute == StorageItemTypes.File && FileExtensionHelpers.IsZipFile(x.FileExtension))); - } - - public static bool CanCompress(IReadOnlyList selectedItems) - { - return !CanDecompress(selectedItems) || selectedItems.Count > 1; - } - - public static string DetermineArchiveNameFromSelection(IReadOnlyList selectedItems) - { - if (!selectedItems.Any()) - return string.Empty; - - return Path.GetFileName( - selectedItems.Count is 1 - ? selectedItems[0].ItemPath - : Path.GetDirectoryName(selectedItems[0].ItemPath - )) ?? string.Empty; - } - - public static (string[] Sources, string directory, string fileName) GetCompressDestination(IShellPage associatedInstance) - { - string[] sources = associatedInstance.SlimContentPage.SelectedItems - .Select(item => item.ItemPath) - .ToArray(); - - if (sources.Length is 0) - return (sources, string.Empty, string.Empty); - - string directory = associatedInstance.FilesystemViewModel.WorkingDirectory.Normalize(); - - - if (App.LibraryManager.TryGetLibrary(directory, out var library) && !library.IsEmpty) - directory = library.DefaultSaveFolder; - - string fileName = Path.GetFileName(sources.Length is 1 ? sources[0] : directory); - - return (sources, directory, fileName); - } - - public static async Task CompressArchiveAsync(ICompressArchiveModel creator) - { - var archivePath = creator.GetArchivePath(); - - int index = 1; - - while (File.Exists(archivePath) || Directory.Exists(archivePath)) - archivePath = creator.GetArchivePath($" ({++index})"); - - creator.ArchivePath = archivePath; - - var banner = StatusCenterHelper.AddCard_Compress( - creator.Sources, - archivePath.CreateEnumerable(), - ReturnResult.InProgress, - creator.Sources.Count()); - - creator.Progress = banner.ProgressEventSource; - creator.CancellationToken = banner.CancellationToken; - - bool isSuccess = await creator.RunCreationAsync(); - - _statusCenterViewModel.RemoveItem(banner); - - if (isSuccess) - { - StatusCenterHelper.AddCard_Compress( - creator.Sources, - archivePath.CreateEnumerable(), - ReturnResult.Success, - creator.Sources.Count()); - } - else - { - PInvoke.DeleteFileFromApp(archivePath); - - StatusCenterHelper.AddCard_Compress( - creator.Sources, - archivePath.CreateEnumerable(), - creator.CancellationToken.IsCancellationRequested - ? ReturnResult.Cancelled - : ReturnResult.Failed, - creator.Sources.Count()); - } - } - } -} diff --git a/src/Files.App/Utils/Archives/DecompressHelper.cs b/src/Files.App/Utils/Archives/DecompressHelper.cs deleted file mode 100644 index 90503bd6bea1..000000000000 --- a/src/Files.App/Utils/Archives/DecompressHelper.cs +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Files.App.Dialogs; -using Files.App.ViewModels.Dialogs; -using Microsoft.Extensions.Logging; -using Microsoft.UI.Xaml.Controls; -using SevenZip; -using System.IO; -using System.Text; -using Windows.Foundation.Metadata; -using Windows.Storage; - -namespace Files.App.Utils.Archives -{ - public static class DecompressHelper - { - private readonly static StatusCenterViewModel _statusCenterViewModel = Ioc.Default.GetRequiredService(); - - private static IThreadingService _threadingService = Ioc.Default.GetRequiredService(); - - public static async Task DecompressArchiveAsync(BaseStorageFile archive, BaseStorageFolder destinationFolder, string password, IProgress progress, CancellationToken cancellationToken) - { - using SevenZipExtractor? zipFile = await GetZipFile(archive, password); - if (zipFile is null) - return false; - - // Check if the decompress operation canceled - if (cancellationToken.IsCancellationRequested) - return false; - - // Fill files - - byte[] buffer = new byte[4096]; - int entriesAmount = zipFile.ArchiveFileData.Count(x => !x.IsDirectory); - - StatusCenterItemProgressModel fsProgress = new( - progress, - enumerationCompleted: true, - FileSystemStatusCode.InProgress, - entriesAmount); - - fsProgress.TotalSize = zipFile.ArchiveFileData.Select(x => (long)x.Size).Sum(); - fsProgress.Report(); - - zipFile.Extracting += (s, e) => - { - if (fsProgress.TotalSize > 0) - fsProgress.Report(e.BytesProcessed / (double)fsProgress.TotalSize * 100); - }; - zipFile.FileExtractionStarted += (s, e) => - { - if (cancellationToken.IsCancellationRequested) - e.Cancel = true; - if (!e.FileInfo.IsDirectory) - { - _threadingService.ExecuteOnUiThreadAsync(() => - { - fsProgress.FileName = e.FileInfo.FileName; - fsProgress.Report(); - }); - } - }; - zipFile.FileExtractionFinished += (s, e) => - { - if (!e.FileInfo.IsDirectory) - { - fsProgress.AddProcessedItemsCount(1); - fsProgress.Report(); - } - }; - - try - { - // TODO: Get this method return result - await zipFile.ExtractArchiveAsync(destinationFolder.Path); - } - catch (Exception ex) - { - App.Logger.LogWarning(ex, $"Error extracting file: {archive.Name}"); - - return false; - } - - return true; - } - - private static async Task DecompressArchiveAsync(BaseStorageFile archive, BaseStorageFolder? destinationFolder, string password) - { - if (archive is null || destinationFolder is null) - return; - - // Initialize a new in-progress status card - var statusCard = StatusCenterHelper.AddCard_Decompress( - archive.Path.CreateEnumerable(), - destinationFolder.Path.CreateEnumerable(), - ReturnResult.InProgress); - - // Operate decompress - var result = await FilesystemTasks.Wrap(() => - DecompressArchiveAsync(archive, destinationFolder, password, statusCard.ProgressEventSource, statusCard.CancellationToken)); - - // Remove the in-progress status card - _statusCenterViewModel.RemoveItem(statusCard); - - if (result.Result) - { - // Successful - StatusCenterHelper.AddCard_Decompress( - archive.Path.CreateEnumerable(), - destinationFolder.Path.CreateEnumerable(), - ReturnResult.Success); - } - else - { - // Error - StatusCenterHelper.AddCard_Decompress( - archive.Path.CreateEnumerable(), - destinationFolder.Path.CreateEnumerable(), - statusCard.CancellationToken.IsCancellationRequested - ? ReturnResult.Cancelled - : ReturnResult.Failed); - } - } - - public static async Task DecompressArchiveAsync(IShellPage associatedInstance) - { - if (associatedInstance == null) - return; - - BaseStorageFile archive = await StorageHelpers.ToStorageItem(associatedInstance.SlimContentPage?.SelectedItems?.Count is null or 0 - ? associatedInstance.FilesystemViewModel.WorkingDirectory - : associatedInstance.SlimContentPage.SelectedItem.ItemPath); - - if (archive?.Path is null) - return; - - var isArchiveEncrypted = await FilesystemTasks.Wrap(() => DecompressHelper.IsArchiveEncrypted(archive)); - var password = string.Empty; - - DecompressArchiveDialog decompressArchiveDialog = new(); - DecompressArchiveDialogViewModel decompressArchiveViewModel = new(archive) - { - IsArchiveEncrypted = isArchiveEncrypted, - ShowPathSelection = true - }; - decompressArchiveDialog.ViewModel = decompressArchiveViewModel; - - if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - decompressArchiveDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; - - ContentDialogResult option = await decompressArchiveDialog.TryShowAsync(); - if (option != ContentDialogResult.Primary) - return; - - if (isArchiveEncrypted) - password = Encoding.UTF8.GetString(decompressArchiveViewModel.Password); - - // Check if archive still exists - if (!StorageHelpers.Exists(archive.Path)) - return; - - BaseStorageFolder destinationFolder = decompressArchiveViewModel.DestinationFolder; - string destinationFolderPath = decompressArchiveViewModel.DestinationFolderPath; - - if (destinationFolder is null) - { - BaseStorageFolder parentFolder = await StorageHelpers.ToStorageItem(Path.GetDirectoryName(archive.Path)); - destinationFolder = await FilesystemTasks.Wrap(() => parentFolder.CreateFolderAsync(Path.GetFileName(destinationFolderPath), CreationCollisionOption.GenerateUniqueName).AsTask()); - } - - await DecompressArchiveAsync(archive, destinationFolder, password); - - if (decompressArchiveViewModel.OpenDestinationFolderOnCompletion) - await NavigationHelpers.OpenPath(destinationFolderPath, associatedInstance, FilesystemItemType.Directory); - } - - public static async Task DecompressArchiveHereAsync(IShellPage associatedInstance, bool smart = false) - { - if (associatedInstance?.SlimContentPage?.SelectedItems == null) - return; - - foreach (var selectedItem in associatedInstance.SlimContentPage.SelectedItems) - { - var password = string.Empty; - BaseStorageFile archive = await StorageHelpers.ToStorageItem(selectedItem.ItemPath); - BaseStorageFolder currentFolder = await StorageHelpers.ToStorageItem(associatedInstance.FilesystemViewModel.CurrentFolder.ItemPath); - - if (archive?.Path is null) - return; - - if (await FilesystemTasks.Wrap(() => IsArchiveEncrypted(archive))) - { - DecompressArchiveDialog decompressArchiveDialog = new(); - DecompressArchiveDialogViewModel decompressArchiveViewModel = new(archive) - { - IsArchiveEncrypted = true, - ShowPathSelection = false - }; - - decompressArchiveDialog.ViewModel = decompressArchiveViewModel; - - if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - decompressArchiveDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; - - ContentDialogResult option = await decompressArchiveDialog.TryShowAsync(); - if (option != ContentDialogResult.Primary) - return; - - password = Encoding.UTF8.GetString(decompressArchiveViewModel.Password); - } - - if (smart && currentFolder is not null && await FilesystemTasks.Wrap(() => IsMultipleItems(archive))) - { - var destinationFolder = await FilesystemTasks.Wrap(() => currentFolder.CreateFolderAsync(Path.GetFileNameWithoutExtension(archive.Path), CreationCollisionOption.GenerateUniqueName).AsTask()); - await DecompressArchiveAsync(archive, destinationFolder, password); - } - else - await DecompressArchiveAsync(archive, currentFolder, password); - } - } - - public static async Task DecompressArchiveToChildFolderAsync(IShellPage associatedInstance) - { - if (associatedInstance?.SlimContentPage?.SelectedItems == null) - return; - - foreach (var selectedItem in associatedInstance.SlimContentPage.SelectedItems) - { - var password = string.Empty; - - BaseStorageFile archive = await StorageHelpers.ToStorageItem(selectedItem.ItemPath); - BaseStorageFolder currentFolder = await StorageHelpers.ToStorageItem(associatedInstance.FilesystemViewModel.CurrentFolder.ItemPath); - BaseStorageFolder destinationFolder = null; - - if (archive?.Path is null) - return; - - if (await FilesystemTasks.Wrap(() => DecompressHelper.IsArchiveEncrypted(archive))) - { - DecompressArchiveDialog decompressArchiveDialog = new(); - DecompressArchiveDialogViewModel decompressArchiveViewModel = new(archive) - { - IsArchiveEncrypted = true, - ShowPathSelection = false - }; - decompressArchiveDialog.ViewModel = decompressArchiveViewModel; - - if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - decompressArchiveDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; - - ContentDialogResult option = await decompressArchiveDialog.TryShowAsync(); - if (option != ContentDialogResult.Primary) - return; - - password = Encoding.UTF8.GetString(decompressArchiveViewModel.Password); - } - - if (currentFolder is not null) - destinationFolder = await FilesystemTasks.Wrap(() => currentFolder.CreateFolderAsync(Path.GetFileNameWithoutExtension(archive.Path), CreationCollisionOption.GenerateUniqueName).AsTask()); - - await DecompressArchiveAsync(archive, destinationFolder, password); - } - } - - private static async Task GetZipFile(BaseStorageFile archive, string password = "") - { - return await FilesystemTasks.Wrap(async () => - { - var arch = new SevenZipExtractor(await archive.OpenStreamForReadAsync(), password); - return arch?.ArchiveFileData is null ? null : arch; // Force load archive (1665013614u) - }); - } - - private static async Task IsArchiveEncrypted(BaseStorageFile archive) - { - using SevenZipExtractor? zipFile = await GetZipFile(archive); - if (zipFile is null) - return true; - - return zipFile.ArchiveFileData.Any(file => file.Encrypted || file.Method.Contains("Crypto") || file.Method.Contains("AES")); - } - - private static async Task IsMultipleItems(BaseStorageFile archive) - { - using SevenZipExtractor? zipFile = await GetZipFile(archive); - if (zipFile is null) - return true; - - return zipFile.ArchiveFileData.Select(file => - { - var pathCharIndex = file.FileName.IndexOfAny(['/', '\\']); - if (pathCharIndex == -1) - return file.FileName; - else - return file.FileName.Substring(0, pathCharIndex); - }).Distinct().Count() > 1; - } - } -} From 23402f1f233b612d7191d6942401d8d2ce533c47 Mon Sep 17 00:00:00 2001 From: 0x5BFA Date: Mon, 6 May 2024 20:26:00 +0900 Subject: [PATCH 3/8] Added comments --- .../Decompress/BaseDecompressArchiveAction.cs | 19 ++++--- .../Archives/Decompress/DecompressArchive.cs | 11 ++--- .../DecompressArchiveToChildFolderAction.cs | 4 +- .../Data/Contracts/IStorageArchiveService.cs | 49 +++++++++++++++++-- .../Services/Storage/StorageArchiveService.cs | 35 +++++++------ 5 files changed, 85 insertions(+), 33 deletions(-) diff --git a/src/Files.App/Actions/Content/Archives/Decompress/BaseDecompressArchiveAction.cs b/src/Files.App/Actions/Content/Archives/Decompress/BaseDecompressArchiveAction.cs index b8661a244c0d..6791cfef3bbf 100644 --- a/src/Files.App/Actions/Content/Archives/Decompress/BaseDecompressArchiveAction.cs +++ b/src/Files.App/Actions/Content/Archives/Decompress/BaseDecompressArchiveAction.cs @@ -4,8 +4,10 @@ using Files.App.Dialogs; using Microsoft.UI.Xaml.Controls; using SevenZip; +using System.Diagnostics.CodeAnalysis; using System.Text; using Windows.Foundation.Metadata; +using Windows.Storage; namespace Files.App.Actions { @@ -53,12 +55,12 @@ protected async Task DecompressArchiveHereAsync(bool smart = false) { var password = string.Empty; BaseStorageFile archive = await StorageHelpers.ToStorageItem(selectedItem.ItemPath); - BaseStorageFolder currentFolder = await StorageHelpers.ToStorageItem(context.ShellPage?.FilesystemViewModel.CurrentFolder.ItemPath); + BaseStorageFolder currentFolder = await StorageHelpers.ToStorageItem(context.ShellPage?.FilesystemViewModel.CurrentFolder?.ItemPath ?? string.Empty); if (archive?.Path is null) return; - if (await FilesystemTasks.Wrap(() => StorageArchiveService.IsEncryptedAsync(archive))) + if (await FilesystemTasks.Wrap(() => StorageArchiveService.IsEncryptedAsync(archive.Path))) { DecompressArchiveDialog decompressArchiveDialog = new(); DecompressArchiveDialogViewModel decompressArchiveViewModel = new(archive) @@ -76,14 +78,15 @@ protected async Task DecompressArchiveHereAsync(bool smart = false) if (option != ContentDialogResult.Primary) return; - password = Encoding.UTF8.GetString(decompressArchiveViewModel.Password); + if (decompressArchiveViewModel.Password is not null) + password = Encoding.UTF8.GetString(decompressArchiveViewModel.Password); } BaseStorageFolder? destinationFolder = null; var isMultipleItems = await FilesystemTasks.Wrap(async () => { - using SevenZipExtractor? zipFile = await StorageArchiveService.GetSevenZipExtractorAsync(archive); + using SevenZipExtractor? zipFile = await StorageArchiveService.GetSevenZipExtractorAsync(archive.Path); if (zipFile is null) return true; @@ -100,7 +103,11 @@ protected async Task DecompressArchiveHereAsync(bool smart = false) if (smart && currentFolder is not null && isMultipleItems) { - destinationFolder = await FilesystemTasks.Wrap(() => currentFolder.CreateFolderAsync(SystemIO.Path.GetFileNameWithoutExtension(archive.Path), CreationCollisionOption.GenerateUniqueName).AsTask()); + destinationFolder = + await FilesystemTasks.Wrap(() => + currentFolder.CreateFolderAsync( + SystemIO.Path.GetFileNameWithoutExtension(archive.Path), + CreationCollisionOption.GenerateUniqueName).AsTask()); } else { @@ -109,7 +116,7 @@ protected async Task DecompressArchiveHereAsync(bool smart = false) // Operate decompress var result = await FilesystemTasks.Wrap(() => - StorageArchiveService.DecompressAsync(archive, destinationFolder, password)); + StorageArchiveService.DecompressAsync(selectedItem.ItemPath, destinationFolder?.Path ?? string.Empty, password)); } } diff --git a/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchive.cs b/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchive.cs index 516e66b140bd..e523cbb3e6ee 100644 --- a/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchive.cs +++ b/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchive.cs @@ -31,15 +31,12 @@ public override async Task ExecuteAsync() if (context.ShellPage is null) return; - BaseStorageFile archive = await StorageHelpers.ToStorageItem( - context.SelectedItems.Count is 0 - ? context.ShellPage.FilesystemViewModel.WorkingDirectory - : context.SelectedItem?.ItemPath ?? string.Empty); + BaseStorageFile archive = await StorageHelpers.ToStorageItem(context.SelectedItem?.ItemPath ?? string.Empty); if (archive?.Path is null) return; - var isArchiveEncrypted = await FilesystemTasks.Wrap(() => StorageArchiveService.IsEncryptedAsync(archive)); + var isArchiveEncrypted = await FilesystemTasks.Wrap(() => StorageArchiveService.IsEncryptedAsync(archive.Path)); var password = string.Empty; DecompressArchiveDialog decompressArchiveDialog = new(); @@ -69,13 +66,13 @@ context.SelectedItems.Count is 0 if (destinationFolder is null) { - BaseStorageFolder parentFolder = await StorageHelpers.ToStorageItem(Path.GetDirectoryName(archive.Path)); + BaseStorageFolder parentFolder = await StorageHelpers.ToStorageItem(Path.GetDirectoryName(archive.Path) ?? string.Empty); destinationFolder = await FilesystemTasks.Wrap(() => parentFolder.CreateFolderAsync(Path.GetFileName(destinationFolderPath), CreationCollisionOption.GenerateUniqueName).AsTask()); } // Operate decompress var result = await FilesystemTasks.Wrap(() => - StorageArchiveService.DecompressAsync(archive, destinationFolder, password)); + StorageArchiveService.DecompressAsync(archive?.Path ?? string.Empty, destinationFolder?.Path ?? string.Empty, password)); if (decompressArchiveViewModel.OpenDestinationFolderOnCompletion) await NavigationHelpers.OpenPath(destinationFolderPath, context.ShellPage, FilesystemItemType.Directory); diff --git a/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveToChildFolderAction.cs b/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveToChildFolderAction.cs index c28835094624..c49b0cb02664 100644 --- a/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveToChildFolderAction.cs +++ b/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveToChildFolderAction.cs @@ -37,7 +37,7 @@ public override async Task ExecuteAsync() if (archive?.Path is null) return; - if (await FilesystemTasks.Wrap(() => StorageArchiveService.IsEncryptedAsync(archive))) + if (await FilesystemTasks.Wrap(() => StorageArchiveService.IsEncryptedAsync(archive.Path))) { DecompressArchiveDialog decompressArchiveDialog = new(); DecompressArchiveDialogViewModel decompressArchiveViewModel = new(archive) @@ -62,7 +62,7 @@ public override async Task ExecuteAsync() // Operate decompress var result = await FilesystemTasks.Wrap(() => - StorageArchiveService.DecompressAsync(archive, destinationFolder, password)); + StorageArchiveService.DecompressAsync(selectedItem.ItemPath, destinationFolder?.Path ?? string.Empty, password)); } } diff --git a/src/Files.App/Data/Contracts/IStorageArchiveService.cs b/src/Files.App/Data/Contracts/IStorageArchiveService.cs index cbe2f197fd49..245ebb86d05b 100644 --- a/src/Files.App/Data/Contracts/IStorageArchiveService.cs +++ b/src/Files.App/Data/Contracts/IStorageArchiveService.cs @@ -5,20 +5,61 @@ namespace Files.App.Data.Contracts { + /// + /// Represents a service to manage storage archives, powered by 7zip and its C# wrapper SevenZipSharp. + /// public interface IStorageArchiveService { + /// + /// Gets the value that indicates whether specified items can be compressed. + /// + /// Items to check if they can be compressed. + /// True if can be compressed; otherwise, false. bool CanCompress(IReadOnlyList items); + /// + /// Gets the value that indicates whether specified items can be decompressed. + /// + /// Items to check if they can be decompressed. + /// True if can be decompressed; otherwise, false. bool CanDecompress(IReadOnlyList items); - Task CompressAsync(ICompressArchiveModel creator); + /// + /// Compresses the specified items. + /// + /// A valid instance of . + /// True if the compression has done successfully; otherwise, false. + Task CompressAsync(ICompressArchiveModel creator); - Task DecompressAsync(BaseStorageFile archive, BaseStorageFolder destinationFolder, string password); + /// + /// Decompresses the archive file specified by the path to the path specified by the path with password if applicable. + /// + /// The archive file path to decompress. + /// The destination folder path which the archive file will be decompressed to. + /// The password to decrypt the archive file if applicable. + /// True if the decompression has done successfully; otherwise, false. + Task DecompressAsync(string archiveFilePath, string destinationFolderPath, string password = ""); + /// + /// Generates the archive file name from item names. + /// + /// Item names to generate archive file name. + /// string GenerateArchiveNameFromItems(IReadOnlyList items); - Task IsEncryptedAsync(BaseStorageFile archive); + /// + /// Gets the value that indicates whether the archive file is encrypted. + /// + /// The archive file path to check if the item is encrypted. + /// True if the archive file is encrypted; otherwise, false. + Task IsEncryptedAsync(string archiveFilePath); - Task GetSevenZipExtractorAsync(BaseStorageFile archive, string password = ""); + /// + /// Gets the instance from the archive file path. + /// + /// The archive file path to generate an instance. + /// The password to decrypt the archive file if applicable. + /// An instance of if the specified item is archive; otherwise null. + Task GetSevenZipExtractorAsync(string archiveFilePath, string password = ""); } } diff --git a/src/Files.App/Services/Storage/StorageArchiveService.cs b/src/Files.App/Services/Storage/StorageArchiveService.cs index 85865d031ef6..7fdca86a3398 100644 --- a/src/Files.App/Services/Storage/StorageArchiveService.cs +++ b/src/Files.App/Services/Storage/StorageArchiveService.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. See the LICENSE. using Files.Shared.Helpers; -using Microsoft.Extensions.Logging; using SevenZip; using System.IO; using Windows.Storage; @@ -35,7 +34,7 @@ public bool CanDecompress(IReadOnlyList items) } /// - public async Task CompressAsync(ICompressArchiveModel creator) + public async Task CompressAsync(ICompressArchiveModel creator) { var archivePath = creator.GetArchivePath(); @@ -79,19 +78,25 @@ public async Task CompressAsync(ICompressArchiveModel creator) : ReturnResult.Failed, creator.Sources.Count()); } + + return isSuccess; } /// - public async Task DecompressAsync(BaseStorageFile archive, BaseStorageFolder destinationFolder, string password) + public async Task DecompressAsync(string archiveFilePath, string destinationFolderPath, string password = "") { - using var zipFile = await GetSevenZipExtractorAsync(archive, password); + if (string.IsNullOrEmpty(archiveFilePath) || + string.IsNullOrEmpty(destinationFolderPath)) + return false; + + using var zipFile = await GetSevenZipExtractorAsync(archiveFilePath, password); if (zipFile is null) return false; // Initialize a new in-progress status card var statusCard = StatusCenterHelper.AddCard_Decompress( - archive.Path.CreateEnumerable(), - destinationFolder!.Path.CreateEnumerable(), + archiveFilePath.CreateEnumerable(), + destinationFolderPath.CreateEnumerable(), ReturnResult.InProgress); // Check if the decompress operation canceled @@ -112,7 +117,7 @@ public async Task DecompressAsync(BaseStorageFile archive, BaseStorageFold try { // TODO: Get this method return result - await zipFile.ExtractArchiveAsync(destinationFolder.Path); + await zipFile.ExtractArchiveAsync(destinationFolderPath); isSuccess = true; } @@ -129,16 +134,16 @@ public async Task DecompressAsync(BaseStorageFile archive, BaseStorageFold { // Successful StatusCenterHelper.AddCard_Decompress( - archive.Path.CreateEnumerable(), - destinationFolder.Path.CreateEnumerable(), + archiveFilePath.CreateEnumerable(), + destinationFolderPath.CreateEnumerable(), ReturnResult.Success); } else { // Error StatusCenterHelper.AddCard_Decompress( - archive.Path.CreateEnumerable(), - destinationFolder.Path.CreateEnumerable(), + archiveFilePath.CreateEnumerable(), + destinationFolderPath.CreateEnumerable(), statusCard.CancellationToken.IsCancellationRequested ? ReturnResult.Cancelled : ReturnResult.Failed); @@ -193,9 +198,9 @@ items.Count is 1 } /// - public async Task IsEncryptedAsync(BaseStorageFile archive) + public async Task IsEncryptedAsync(string archiveFilePath) { - using SevenZipExtractor? zipFile = await GetSevenZipExtractorAsync(archive); + using SevenZipExtractor? zipFile = await GetSevenZipExtractorAsync(archiveFilePath); if (zipFile is null) return true; @@ -203,10 +208,12 @@ public async Task IsEncryptedAsync(BaseStorageFile archive) } /// - public async Task GetSevenZipExtractorAsync(BaseStorageFile archive, string password = "") + public async Task GetSevenZipExtractorAsync(string archiveFilePath, string password = "") { return await FilesystemTasks.Wrap(async () => { + BaseStorageFile archive = await StorageHelpers.ToStorageItem(archiveFilePath); + var arch = new SevenZipExtractor(await archive.OpenStreamForReadAsync(), password); // Force to load archive (1665013614u) From 2582a2a4a0f38be42d19962de31963fb5a12727c Mon Sep 17 00:00:00 2001 From: 0x5BFA Date: Mon, 6 May 2024 20:35:32 +0900 Subject: [PATCH 4/8] Update the decompress event code --- src/Files.App/Services/Storage/StorageArchiveService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Files.App/Services/Storage/StorageArchiveService.cs b/src/Files.App/Services/Storage/StorageArchiveService.cs index 7fdca86a3398..3adcd2826fb3 100644 --- a/src/Files.App/Services/Storage/StorageArchiveService.cs +++ b/src/Files.App/Services/Storage/StorageArchiveService.cs @@ -158,7 +158,7 @@ public async Task DecompressAsync(string archiveFilePath, string destinati zipFile.FileExtractionStarted += (s, e) => { - if (cancellationToken.IsCancellationRequested) + if (statusCard.CancellationToken.IsCancellationRequested) e.Cancel = true; if (!e.FileInfo.IsDirectory) From 42a61b3b808a6786a87c8254f6e4f6ddad05507f Mon Sep 17 00:00:00 2001 From: 0x5BFA Date: Mon, 6 May 2024 20:38:10 +0900 Subject: [PATCH 5/8] Added xml comment to the class --- src/Files.App/Services/Storage/StorageArchiveService.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Files.App/Services/Storage/StorageArchiveService.cs b/src/Files.App/Services/Storage/StorageArchiveService.cs index 3adcd2826fb3..27d94e08bca3 100644 --- a/src/Files.App/Services/Storage/StorageArchiveService.cs +++ b/src/Files.App/Services/Storage/StorageArchiveService.cs @@ -9,6 +9,7 @@ namespace Files.App.Services { + /// public class StorageArchiveService : IStorageArchiveService { private StatusCenterViewModel StatusCenterViewModel { get; } = Ioc.Default.GetRequiredService(); @@ -214,10 +215,10 @@ public async Task IsEncryptedAsync(string archiveFilePath) { BaseStorageFile archive = await StorageHelpers.ToStorageItem(archiveFilePath); - var arch = new SevenZipExtractor(await archive.OpenStreamForReadAsync(), password); + var extractor = new SevenZipExtractor(await archive.OpenStreamForReadAsync(), password); // Force to load archive (1665013614u) - return arch?.ArchiveFileData is null ? null : arch; + return extractor?.ArchiveFileData is null ? null : extractor; }); } } From 731efa371cb1dbab233e721988e7ac4c9b9531ed Mon Sep 17 00:00:00 2001 From: 0x5BFA Date: Mon, 6 May 2024 20:39:31 +0900 Subject: [PATCH 6/8] Move the compression model classes --- .../{Utils/Archives => Data/Contracts}/ICompressArchiveModel.cs | 2 +- .../{Utils/Archives => Data/Models}/CompressArchiveModel.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/Files.App/{Utils/Archives => Data/Contracts}/ICompressArchiveModel.cs (97%) rename src/Files.App/{Utils/Archives => Data/Models}/CompressArchiveModel.cs (99%) diff --git a/src/Files.App/Utils/Archives/ICompressArchiveModel.cs b/src/Files.App/Data/Contracts/ICompressArchiveModel.cs similarity index 97% rename from src/Files.App/Utils/Archives/ICompressArchiveModel.cs rename to src/Files.App/Data/Contracts/ICompressArchiveModel.cs index ccdebd7e0a9a..5d11ea5a85c0 100644 --- a/src/Files.App/Utils/Archives/ICompressArchiveModel.cs +++ b/src/Files.App/Data/Contracts/ICompressArchiveModel.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.App.Utils.Archives +namespace Files.App.Data.Contracts { /// /// Represents an interface for archive creation support. diff --git a/src/Files.App/Utils/Archives/CompressArchiveModel.cs b/src/Files.App/Data/Models/CompressArchiveModel.cs similarity index 99% rename from src/Files.App/Utils/Archives/CompressArchiveModel.cs rename to src/Files.App/Data/Models/CompressArchiveModel.cs index ef89b15644ea..8e4ff0ac45f6 100644 --- a/src/Files.App/Utils/Archives/CompressArchiveModel.cs +++ b/src/Files.App/Data/Models/CompressArchiveModel.cs @@ -6,7 +6,7 @@ using SevenZip; using System.IO; -namespace Files.App.Utils.Archives +namespace Files.App.Data.Models { /// /// Provides an archive creation support. From c35b72bf9d726ca9bd9f6ca57264bfaf82666cf6 Mon Sep 17 00:00:00 2001 From: 0x5bfa <62196528+0x5bfa@users.noreply.github.com> Date: Thu, 9 May 2024 09:11:03 +0900 Subject: [PATCH 7/8] Fix build issue --- src/Files.App/GlobalUsings.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Files.App/GlobalUsings.cs b/src/Files.App/GlobalUsings.cs index 90ba38009f1d..ee75e573644e 100644 --- a/src/Files.App/GlobalUsings.cs +++ b/src/Files.App/GlobalUsings.cs @@ -25,7 +25,6 @@ global using global::Files.App.Helpers; global using global::Files.App.Extensions; global using global::Files.App.Utils; -global using global::Files.App.Utils.Archives; global using global::Files.App.Utils.Cloud; global using global::Files.App.Utils.FileTags; global using global::Files.App.Utils.Git; From d49cddefb522a2ebce5e2e7ff953266068bc35f4 Mon Sep 17 00:00:00 2001 From: 0x5bfa <62196528+0x5bfa@users.noreply.github.com> Date: Sat, 11 May 2024 16:45:30 +0900 Subject: [PATCH 8/8] Update --- .../Compress/BaseCompressArchiveAction.cs | 20 +++++++++++++ .../Compress/CompressIntoArchiveAction.cs | 26 +++-------------- .../Compress/CompressIntoSevenZipAction.cs | 21 ++------------ .../Compress/CompressIntoZipAction.cs | 21 ++------------ .../Data/Contracts/IStorageArchiveService.cs | 2 +- .../Services/Storage/StorageArchiveService.cs | 28 +++++++++---------- 6 files changed, 45 insertions(+), 73 deletions(-) diff --git a/src/Files.App/Actions/Content/Archives/Compress/BaseCompressArchiveAction.cs b/src/Files.App/Actions/Content/Archives/Compress/BaseCompressArchiveAction.cs index cbcde19a059c..3831bca97af6 100644 --- a/src/Files.App/Actions/Content/Archives/Compress/BaseCompressArchiveAction.cs +++ b/src/Files.App/Actions/Content/Archives/Compress/BaseCompressArchiveAction.cs @@ -26,6 +26,26 @@ public BaseCompressArchiveAction() public abstract Task ExecuteAsync(object? parameter = null); + protected void GetDestination(out string[] sources, out string directory, out string fileName) + { + sources = context.SelectedItems.Select(item => item.ItemPath).ToArray(); + directory = string.Empty; + fileName = string.Empty; + + if (sources.Length is not 0) + { + // Get the current directory path + directory = context.ShellPage.FilesystemViewModel.WorkingDirectory.Normalize(); + + // Get the library save folder if the folder is library item + if (App.LibraryManager.TryGetLibrary(directory, out var library) && !library.IsEmpty) + directory = library.DefaultSaveFolder; + + // Gets the file name from the directory path + fileName = SystemIO.Path.GetFileName(sources.Length is 1 ? sources[0] : directory); + } + } + private bool IsContextPageTypeAdaptedToCommand() { return diff --git a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoArchiveAction.cs b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoArchiveAction.cs index 6a0b89dcec43..99524e36fdea 100644 --- a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoArchiveAction.cs +++ b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoArchiveAction.cs @@ -24,27 +24,9 @@ public override async Task ExecuteAsync(object? parameter = null) if (context.ShellPage is null) return; - string[] sources = context.SelectedItems.Select(item => item.ItemPath).ToArray(); - string directory = string.Empty; - string fileName = string.Empty; + GetDestination(out var sources, out var directory, out var fileName); - if (sources.Length is not 0) - { - // Get the current directory path - directory = context.ShellPage.FilesystemViewModel.WorkingDirectory.Normalize(); - - // Get the library save folder if the folder is library item - if (App.LibraryManager.TryGetLibrary(directory, out var library) && !library.IsEmpty) - directory = library.DefaultSaveFolder; - - // Gets the file name from the directory path - fileName = SystemIO.Path.GetFileName(sources.Length is 1 ? sources[0] : directory); - } - - var dialog = new CreateArchiveDialog - { - FileName = fileName, - }; + var dialog = new CreateArchiveDialog() { FileName = fileName }; if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) dialog.XamlRoot = MainWindow.Instance.Content.XamlRoot; @@ -54,7 +36,7 @@ public override async Task ExecuteAsync(object? parameter = null) if (!dialog.CanCreate || result != ContentDialogResult.Primary) return; - ICompressArchiveModel creator = new CompressArchiveModel( + ICompressArchiveModel compressionModel = new CompressArchiveModel( sources, directory, dialog.FileName, @@ -63,7 +45,7 @@ public override async Task ExecuteAsync(object? parameter = null) dialog.CompressionLevel, dialog.SplittingSize); - await StorageArchiveService.CompressAsync(creator); + await StorageArchiveService.CompressAsync(compressionModel); } } } diff --git a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoSevenZipAction.cs b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoSevenZipAction.cs index f788ffb8a9dd..1d3414ab34c1 100644 --- a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoSevenZipAction.cs +++ b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoSevenZipAction.cs @@ -20,30 +20,15 @@ public override Task ExecuteAsync(object? parameter = null) if (context.ShellPage is null) return Task.CompletedTask; - string[] sources = context.SelectedItems.Select(item => item.ItemPath).ToArray(); - string directory = string.Empty; - string fileName = string.Empty; + GetDestination(out var sources, out var directory, out var fileName); - if (sources.Length is not 0) - { - // Get the current directory path - directory = context.ShellPage.FilesystemViewModel.WorkingDirectory.Normalize(); - - // Get the library save folder if the folder is library item - if (App.LibraryManager.TryGetLibrary(directory, out var library) && !library.IsEmpty) - directory = library.DefaultSaveFolder; - - // Gets the file name from the directory path - fileName = SystemIO.Path.GetFileName(sources.Length is 1 ? sources[0] : directory); - } - - ICompressArchiveModel creator = new CompressArchiveModel( + ICompressArchiveModel compressionModel = new CompressArchiveModel( sources, directory, fileName, fileFormat: ArchiveFormats.SevenZip); - return StorageArchiveService.CompressAsync(creator); + return StorageArchiveService.CompressAsync(compressionModel); } } } diff --git a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoZipAction.cs b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoZipAction.cs index 7ea14bb90396..72e5ded7b1eb 100644 --- a/src/Files.App/Actions/Content/Archives/Compress/CompressIntoZipAction.cs +++ b/src/Files.App/Actions/Content/Archives/Compress/CompressIntoZipAction.cs @@ -20,30 +20,15 @@ public override Task ExecuteAsync(object? parameter = null) if (context.ShellPage is null) return Task.CompletedTask; - string[] sources = context.SelectedItems.Select(item => item.ItemPath).ToArray(); - string directory = string.Empty; - string fileName = string.Empty; + GetDestination(out var sources, out var directory, out var fileName); - if (sources.Length is not 0) - { - // Get the current directory path - directory = context.ShellPage.FilesystemViewModel.WorkingDirectory.Normalize(); - - // Get the library save folder if the folder is library item - if (App.LibraryManager.TryGetLibrary(directory, out var library) && !library.IsEmpty) - directory = library.DefaultSaveFolder; - - // Gets the file name from the directory path - fileName = SystemIO.Path.GetFileName(sources.Length is 1 ? sources[0] : directory); - } - - ICompressArchiveModel creator = new CompressArchiveModel( + ICompressArchiveModel compressionModel = new CompressArchiveModel( sources, directory, fileName, fileFormat: ArchiveFormats.Zip); - return StorageArchiveService.CompressAsync(creator); + return StorageArchiveService.CompressAsync(compressionModel); } } } diff --git a/src/Files.App/Data/Contracts/IStorageArchiveService.cs b/src/Files.App/Data/Contracts/IStorageArchiveService.cs index 245ebb86d05b..97b31b86176b 100644 --- a/src/Files.App/Data/Contracts/IStorageArchiveService.cs +++ b/src/Files.App/Data/Contracts/IStorageArchiveService.cs @@ -29,7 +29,7 @@ public interface IStorageArchiveService /// /// A valid instance of . /// True if the compression has done successfully; otherwise, false. - Task CompressAsync(ICompressArchiveModel creator); + Task CompressAsync(ICompressArchiveModel compressionModel); /// /// Decompresses the archive file specified by the path to the path specified by the path with password if applicable. diff --git a/src/Files.App/Services/Storage/StorageArchiveService.cs b/src/Files.App/Services/Storage/StorageArchiveService.cs index 27d94e08bca3..8b705f05acc3 100644 --- a/src/Files.App/Services/Storage/StorageArchiveService.cs +++ b/src/Files.App/Services/Storage/StorageArchiveService.cs @@ -35,49 +35,49 @@ public bool CanDecompress(IReadOnlyList items) } /// - public async Task CompressAsync(ICompressArchiveModel creator) + public async Task CompressAsync(ICompressArchiveModel compressionModel) { - var archivePath = creator.GetArchivePath(); + var archivePath = compressionModel.GetArchivePath(); int index = 1; while (SystemIO.File.Exists(archivePath) || SystemIO.Directory.Exists(archivePath)) - archivePath = creator.GetArchivePath($" ({++index})"); + archivePath = compressionModel.GetArchivePath($" ({++index})"); - creator.ArchivePath = archivePath; + compressionModel.ArchivePath = archivePath; var banner = StatusCenterHelper.AddCard_Compress( - creator.Sources, + compressionModel.Sources, archivePath.CreateEnumerable(), ReturnResult.InProgress, - creator.Sources.Count()); + compressionModel.Sources.Count()); - creator.Progress = banner.ProgressEventSource; - creator.CancellationToken = banner.CancellationToken; + compressionModel.Progress = banner.ProgressEventSource; + compressionModel.CancellationToken = banner.CancellationToken; - bool isSuccess = await creator.RunCreationAsync(); + bool isSuccess = await compressionModel.RunCreationAsync(); StatusCenterViewModel.RemoveItem(banner); if (isSuccess) { StatusCenterHelper.AddCard_Compress( - creator.Sources, + compressionModel.Sources, archivePath.CreateEnumerable(), ReturnResult.Success, - creator.Sources.Count()); + compressionModel.Sources.Count()); } else { PInvoke.DeleteFileFromApp(archivePath); StatusCenterHelper.AddCard_Compress( - creator.Sources, + compressionModel.Sources, archivePath.CreateEnumerable(), - creator.CancellationToken.IsCancellationRequested + compressionModel.CancellationToken.IsCancellationRequested ? ReturnResult.Cancelled : ReturnResult.Failed, - creator.Sources.Count()); + compressionModel.Sources.Count()); } return isSuccess;