/// <summary> /// Starts resizing the image operation /// </summary> /// <param name="action">Determine whether the image files should be saved as, or updated or resize image is used from sharing</param> /// <param name="processedImageAction">Action method which retrievs the resized image. Can be used for sharing.</param> /// <returns>Task which indicates if the current resize operation was successfull</returns> public async Task <bool> ResizeImages(ImageAction action, Action <ImageFile, String> processedImageAction = null) { IProgressBarDialogService progressBarDialog = !IsShareTarget?_progressBarDialogService.ProgressBarDialogFactory() : null; try { Resizing = true; //if no file is selected open file picker if (ImageFiles == null || ImageFiles.Count == 0) { await OpenFilePicker(); } #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed progressBarDialog?.StartAsync(ImageFiles.Count); #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed String suggestedFileName = String.Empty; String targetStorageFolder = Settings.DefaultSaveAsTargetFolder; Exception lastException = null; if (ImageFiles != null) { foreach (ImageFile currentImage in ImageFiles) { try { SelectedFile = currentImage; suggestedFileName = _imageFileService.GenerateResizedFileName(currentImage, currentImage.NewWidth, currentImage.NewHeight); if (progressBarDialog != null) { progressBarDialog.CurrentItem = suggestedFileName; } //if the stream is disposed CanSeek and CanRead is false if (currentImage.Stream == null || !currentImage.Stream.CanSeek && !currentImage.Stream.CanRead) { var imageFile = (await _imageFileService.LoadImageFileAsync(currentImage.Path)); if (imageFile?.Stream != null) { currentImage.Stream = imageFile.Stream; } else { _loggerService.LogEvent("Could not load stream.", new Dictionary <string, string>() { { nameof(currentImage.Path), currentImage.Path } }); continue; } } TaskCompletionSource <MemoryStream> taskMemoryStreamCompletionSource = new TaskCompletionSource <MemoryStream>(); if (!IsShareTarget) { //do the resizing in a seperate thread using Task.Run try { var ms = await Task.Run(() => _resizeService.ResizeAsync(currentImage.Stream, currentImage.NewWidth, currentImage.NewHeight, suggestedFileName, _localSettings.Settings.ImageQuality)); taskMemoryStreamCompletionSource.SetResult(ms); } catch (Exception e) { taskMemoryStreamCompletionSource.SetException(e); } } else { //do the resizing on the main thread in share target to avoid issues see #53 var ms = await _resizeService.ResizeAsync(currentImage.Stream, currentImage.NewWidth, currentImage.NewHeight, suggestedFileName, _localSettings.Settings.ImageQuality); taskMemoryStreamCompletionSource.SetResult(ms); } using (MemoryStream resizedImageFileStream = await taskMemoryStreamCompletionSource.Task) { //log image size _loggerService?.LogEvent(nameof(IResizeService.ResizeAsync), new Dictionary <String, String>() { { $"{nameof(ImageFile)}{nameof(Width)}", $"{currentImage?.Width}" }, { $"{nameof(ImageFile)}{nameof(Height)}", $"{currentImage?.Height}" }, { nameof(Width), $"{currentImage?.NewHeight }" }, { nameof(Height), $"{currentImage?.NewHeight}" }, { nameof(ImageAction), $"{action}" } }); //overwrite current ImageStoreage if (action.Equals(ImageAction.Save)) { try { await _imageFileService.WriteBytesAsync(currentImage, resizedImageFileStream.ToArray()); LastFile = currentImage; } catch (Contract.Exceptions.UnauthorizedAccessException) { //log the UnauthorizedAccessException _loggerService?.LogEvent(nameof(ResizeImages), new Dictionary <String, String>() { { nameof(Contract.Exceptions.UnauthorizedAccessException), $"{true}" }, }); //we can't override the current file try for the next files saveAs action = ImageAction.SaveAs; await ShowPermissionDeniedDialog(progressBarDialog); ImageFile File = await _imageFileService.PickSaveFileAsync(currentImage.Path, suggestedFileName); if (null != File) { //try to apply the new storagefolder (if the user selected a new location) targetStorageFolder = Path.GetDirectoryName(File.Path); await _imageFileService.WriteBytesAsync(File, resizedImageFileStream.ToArray()); LastFile = File; } } } //create new ImageStoreage else if (action.Equals(ImageAction.SaveAs)) { ImageFile imageFile = null; try { //if the user enabled the option "save images to same folder" if (Settings.SaveFilesForSaveAsInSameFolder && !AppStartType.AppIsShareTarget.Equals(_appStartType)) { targetStorageFolder = Path.GetDirectoryName(currentImage.Path); } if (String.IsNullOrEmpty(targetStorageFolder)) { if (SingleFile) { //get default path imageFile = await _imageFileService.PickSaveFileAsync(currentImage.Path, suggestedFileName); } else { imageFile = await _imageFileService.PickSaveFolderAsync(currentImage.Path, suggestedFileName); } if (imageFile == null) { //if user canceled dialog try again imageFile = await _imageFileService.PickSaveFileAsync(currentImage.Path, suggestedFileName); } //File can be null when user aborted picksavefile dialog if (imageFile != null) { targetStorageFolder = Path.GetDirectoryName(imageFile.Path); await _imageFileService.WriteBytesAsync(imageFile, resizedImageFileStream.ToArray()); LastFile = imageFile; } } else { await _imageFileService.WriteBytesAsync(targetStorageFolder, suggestedFileName, resizedImageFileStream.ToArray()); } } catch (FileNotFoundException) { //happens if the targetStorageFolder from the settings was removed (or renamed) meanwhile _loggerService?.LogEvent(nameof(ResizeImages), new Dictionary <String, String>() { { nameof(FileNotFoundException), true.ToString() }, }); //happens if the targetStorageFolder was removed meanwhile Settings.DefaultSaveAsTargetFolder = String.Empty; //tell the user to save the file in an other location imageFile = await _imageFileService.PickSaveFileAsync(currentImage.Path, suggestedFileName); //try to apply the new storagefolder (if the user selected a new location) if (imageFile != null && Path.GetDirectoryName(targetStorageFolder) != Path.GetDirectoryName(imageFile.Path)) { targetStorageFolder = Path.GetDirectoryName(imageFile.Path); await _imageFileService.WriteBytesAsync(imageFile, resizedImageFileStream.ToArray()); } } catch (Contract.Exceptions.UnauthorizedAccessException) { //log the UnauthorizedAccessException _loggerService?.LogEvent(nameof(ResizeImages), new Dictionary <String, String>() { { nameof(Contract.Exceptions.UnauthorizedAccessException), true.ToString() }, }); //tell the user to save the file in an other location imageFile = await _imageFileService.PickSaveFileAsync(currentImage.Path, suggestedFileName); //try to apply the new storagefolder (if the user selected a new location) if (imageFile != null && Path.GetDirectoryName(targetStorageFolder) != Path.GetDirectoryName(imageFile.Path)) { targetStorageFolder = Path.GetDirectoryName(imageFile.Path); await _imageFileService.WriteBytesAsync(imageFile, resizedImageFileStream.ToArray()); } await ShowPermissionDeniedDialog(progressBarDialog); } if (null != imageFile) { LastFile = imageFile; } _loggerService?.LogEvent(nameof(ResizeImages), new Dictionary <String, String>() { { nameof(currentImage.Path), $"{currentImage.Path}" } }); } else if (action.Equals(ImageAction.Process)) { String TempFolder = _applicationService.GetTemporaryFolderPath(); ImageFile temp = await _imageFileService.WriteBytesAsync(TempFolder, suggestedFileName, resizedImageFileStream.ToArray()); processedImageAction?.Invoke(temp, $"{suggestedFileName}"); } //open resized image depending whether only one image is resized and the user enabled this option if (SingleFile && LastFile != null && action != ImageAction.Process && _localSettings.Settings.EnabledOpenSingleFileAfterResize) { OpenFileCommand.Execute(LastFile); } } currentImage?.Stream?.Dispose(); } catch (NotSupportedException e) { _loggerService?.LogException($"{nameof(ResizeImages)}{nameof(ImageFiles)}", e, new Dictionary <string, string>() { { nameof(suggestedFileName), suggestedFileName } }); await _pageDialogService?.ShowAsync(_resourceLoader.GetString("ImageTypNotSupported")); } catch (InvalidOperationException e) { //for example when enterted width and height is zero. 'Target width 0 and height 0 must be greater than zero.' lastException = e; } catch (FileLoadException e) { //for example The process cannot access the file because it is being used by another process. lastException = e; } catch (UnknownImageFormatException e) { lastException = e; } catch (Exception e) { lastException = e; _loggerService?.LogException($"{nameof(ResizeImages)}{nameof(ImageFiles)}", e); } if (progressBarDialog != null) { progressBarDialog.ProcessedItems++; if (progressBarDialog.AbortedClicked()) { _loggerService?.LogEvent(nameof(IResizeService.ResizeAsync), new Dictionary <String, String>() { { nameof(progressBarDialog.AbortedClicked), Boolean.TrueString } }); return(false); } } } if (_localSettings != null && _localSettings.Settings.ShowSuccessMessage && LastFile != null) { await _pageDialogService?.ShowAsync(GenerateSuccess(LastFile)); } if (IsShareTarget) { _shareService.EndShareTargetOperation(); } Resizing = false; if (lastException != null) { if (lastException is InvalidOperationException || lastException is FileLoadException) { await _pageDialogService.ShowAsync(lastException.Message); } if (lastException is UnknownImageFormatException) { String message = _resourceLoader.GetString("UnknownImageFormatException"); if (!String.IsNullOrEmpty(message)) { message = String.Format(message, string.Join(", ", _imageFileService.FileTypeFilter)); await _pageDialogService.ShowAsync(message); } } } } } catch (Exception e) { _loggerService?.LogException(nameof(ResizeImages), e); _loggerService?.LogEvent(nameof(IResizeService.ResizeAsync), new Dictionary <String, String>() { { "ResizeFinished", "false" } }); return(false); } finally { progressBarDialog?.Stop(); Resizing = false; } //log image size _loggerService?.LogEvent(nameof(IResizeService.ResizeAsync), new Dictionary <String, String>() { { "ResizeFinished", "true" } }); return(true); }