diff --git a/src/SingleProject/Resizetizer/src/SkiaSharpTools.cs b/src/SingleProject/Resizetizer/src/SkiaSharpTools.cs index e81d311351c8..41562c4994a1 100644 --- a/src/SingleProject/Resizetizer/src/SkiaSharpTools.cs +++ b/src/SingleProject/Resizetizer/src/SkiaSharpTools.cs @@ -1,12 +1,54 @@ using System; using System.Diagnostics; using System.IO; +using System.Threading; +using System.Runtime.InteropServices; using SkiaSharp; namespace Microsoft.Maui.Resizetizer { internal abstract class SkiaSharpTools { + const int ERROR_ACCESS_DENIED = -2147024891; + const int ERROR_SHARING_VIOLATION = -2147024864; + const int DEFAULT_FILE_WRITE_RETRY_ATTEMPTS = 10; + const int DEFAULT_FILE_WRITE_RETRY_DELAY_MS = 1000; + + static int fileWriteRetry = -1; + static int fileWriteRetryDelay = -1; + + /// + /// Checks for the environment variable DOTNET_ANDROID_FILE_WRITE_RETRY_ATTEMPTS to + /// see if a custom value for the number of times to retry writing a file has been + /// set. + /// + /// The value of DOTNET_ANDROID_FILE_WRITE_RETRY_ATTEMPTS or the default of DEFAULT_FILE_WRITE_RETRY_ATTEMPTS + public static int GetFileWriteRetryAttempts () + { + if (fileWriteRetry == -1) { + var retryVariable = Environment.GetEnvironmentVariable ("DOTNET_ANDROID_FILE_WRITE_RETRY_ATTEMPTS"); + if (string.IsNullOrEmpty (retryVariable) || !int.TryParse (retryVariable, out fileWriteRetry)) + fileWriteRetry = DEFAULT_FILE_WRITE_RETRY_ATTEMPTS; + } + return fileWriteRetry; + } + + /// + /// Checks for the environment variable DOTNET_ANDROID_FILE_WRITE_RETRY_DELAY_MS to + /// see if a custom value for the delay between trying to write a file has been + /// set. + /// + /// The value of DOTNET_ANDROID_FILE_WRITE_RETRY_DELAY_MS or the default of DEFAULT_FILE_WRITE_RETRY_DELAY_MS + public static int GetFileWriteRetryDelay () + { + if (fileWriteRetryDelay == -1) { + var delayVariable = Environment.GetEnvironmentVariable ("DOTNET_ANDROID_FILE_WRITE_RETRY_DELAY_MS"); + if (string.IsNullOrEmpty (delayVariable) || !int.TryParse (delayVariable, out fileWriteRetryDelay)) + fileWriteRetryDelay = DEFAULT_FILE_WRITE_RETRY_DELAY_MS; + } + return fileWriteRetryDelay; + } + static SkiaSharpTools() { // DO NOT DELETE! @@ -150,8 +192,35 @@ void Draw(SKBitmap tempBitmap, double additionalScale, SKSize originalSize, floa void Save(string destination, SKBitmap tempBitmap) { - using var stream = File.Create(destination); - tempBitmap.Encode(stream, SKEncodedImageFormat.Png, 100); + int attempt = 0; + int attempts = GetFileWriteRetryAttempts (); + int delay = GetFileWriteRetryDelay (); + while (attempt <= attempts) + { + try + { + using var stream = File.Create(destination); + tempBitmap.Encode(stream, SKEncodedImageFormat.Png, 100); + } + catch (Exception ex) + { + switch (ex) + { + case UnauthorizedAccessException: + case IOException: + var code = Marshal.GetHRForException(ex); + if ((code != ERROR_ACCESS_DENIED && code != ERROR_SHARING_VIOLATION) || attempt == attempts) + { + throw; + } + break; + default: + throw; + } + attempt++; + Thread.Sleep(delay); + } + } } public abstract SKSize GetOriginalSize(); diff --git a/src/SingleProject/Resizetizer/src/nuget/buildTransitive/Microsoft.Maui.Resizetizer.After.targets b/src/SingleProject/Resizetizer/src/nuget/buildTransitive/Microsoft.Maui.Resizetizer.After.targets index 0338df96324b..4a215b116acd 100644 --- a/src/SingleProject/Resizetizer/src/nuget/buildTransitive/Microsoft.Maui.Resizetizer.After.targets +++ b/src/SingleProject/Resizetizer/src/nuget/buildTransitive/Microsoft.Maui.Resizetizer.After.targets @@ -59,21 +59,6 @@ _CleanResizetizer; - <_ResizetizerInputsFile>$(IntermediateOutputPath)mauiimage.inputs - <_ResizetizerOutputsFile>$(IntermediateOutputPath)mauiimage.outputs - <_ResizetizerStampFile>$(IntermediateOutputPath)mauiimage.stamp - <_MauiFontInputsFile>$(IntermediateOutputPath)mauifont.inputs - <_MauiFontStampFile>$(IntermediateOutputPath)mauifont.stamp - <_MauiSplashInputsFile>$(IntermediateOutputPath)mauisplash.inputs - <_MauiSplashStampFile>$(IntermediateOutputPath)mauisplash.stamp - <_MauiManifestStampFile>$(IntermediateOutputPath)mauimanifest.stamp - - <_ResizetizerIntermediateOutputRoot>$(IntermediateOutputPath)resizetizer\ - <_MauiIntermediateImages>$(_ResizetizerIntermediateOutputRoot)r\ - <_MauiIntermediateFonts>$(_ResizetizerIntermediateOutputRoot)f\ - <_MauiIntermediateSplashScreen>$(_ResizetizerIntermediateOutputRoot)sp\ - <_MauiIntermediateManifest>$(_ResizetizerIntermediateOutputRoot)m\ - <_ResizetizerPlatformIdentifier>$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) <_ResizetizerNoTargetPlatform Condition="'$(_ResizetizerPlatformIdentifier)' == ''">True <_ResizetizerPlatformIsAndroid Condition="'$(_ResizetizerPlatformIdentifier)' == 'android'">True @@ -84,6 +69,24 @@ <_ResizetizerPlatformIsWindows Condition="$(_ResizetizerPlatformIdentifier.Contains('windows')) == 'True'">True <_ResizetizerPlatformIsTizen Condition="'$(_ResizetizerPlatformIdentifier)' == 'tizen'">True + <_ResizetizerIntermediateOutputPath Condition=" '$(_ResizetizerIntermediateOutputPath)' == '' And '$(_ResizetizerPlatformIsAndroid)' == 'True' And '$(DesignTimeBuild)' == 'True' ">$(IntermediateOutputPath)designtime + <_ResizetizerIntermediateOutputPath Condition=" '$(_ResizetizerIntermediateOutputPath)' == '' " >$(IntermediateOutputPath) + + <_ResizetizerInputsFile>$(_ResizetizerIntermediateOutputPath)mauiimage.inputs + <_ResizetizerOutputsFile>$(_ResizetizerIntermediateOutputPath)mauiimage.outputs + <_ResizetizerStampFile>$(_ResizetizerIntermediateOutputPath)mauiimage.stamp + <_MauiFontInputsFile>$(_ResizetizerIntermediateOutputPath)mauifont.inputs + <_MauiFontStampFile>$(_ResizetizerIntermediateOutputPath)mauifont.stamp + <_MauiSplashInputsFile>$(_ResizetizerIntermediateOutputPath)mauisplash.inputs + <_MauiSplashStampFile>$(_ResizetizerIntermediateOutputPath)mauisplash.stamp + <_MauiManifestStampFile>$(_ResizetizerIntermediateOutputPath)mauimanifest.stamp + + <_ResizetizerIntermediateOutputRoot>$(_ResizetizerIntermediateOutputPath)resizetizer\ + <_MauiIntermediateImages>$(_ResizetizerIntermediateOutputRoot)r\ + <_MauiIntermediateFonts>$(_ResizetizerIntermediateOutputRoot)f\ + <_MauiIntermediateSplashScreen>$(_ResizetizerIntermediateOutputRoot)sp\ + <_MauiIntermediateManifest>$(_ResizetizerIntermediateOutputRoot)m\ + False <_ResizetizerDefaultInvalidFilenamesErrorMessage>One or more invalid file names were detected. File names must be lowercase, start and end with a letter character, and contain only alphanumeric characters or underscores: @@ -504,7 +507,7 @@ - +