Add themed splash screen support - #35710
Conversation
Adds light/dark splash screen metadata support for Android and Apple targets, including Android night-qualified resources and iOS/Mac Catalyst asset catalog launch screens. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 35710Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35710" |
|
/review -b feature/enhanced-reviewer |
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 1 findings
See inline comments for details.
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
kubaflo
left a comment
There was a problem hiding this comment.
Could you check the ai's suggestions?
|
/review -b feature/enhanced-reviewer -p android |
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 4 findings
See inline comments for details.
kubaflo
left a comment
There was a problem hiding this comment.
Could you please check the ai's suggestions?
There was a problem hiding this comment.
Copilot's findings
Suppressed comments (1)
src/SingleProject/Resizetizer/src/nuget/buildTransitive/Microsoft.Maui.Resizetizer.After.targets:475
_MauiHasSplashScreensis derived from@(MauiSplashScreen)count, but downstream tasks now consume@(_MauiSplashScreenWithHashes). IfResizetizeCollectItemsis skipped (e.g.,DisableResizetizer=true),@(_MauiSplashScreenWithHashes)will be empty while@(MauiSplashScreen)is not, causing splash generation to no-op and potentially leaving stale intermediate splash outputs to be included later under the'$(_MauiHasSplashScreens)' == 'true'ItemGroup. Consider basing_MauiHasSplashScreenson@(_MauiSplashScreenWithHashes)instead so the target’s gating matches the items actually passed to the tasks.
<PropertyGroup>
<_MauiHasSplashScreens>false</_MauiHasSplashScreens>
<_MauiHasSplashScreens Condition="'@(MauiSplashScreen->Count())' != '0'">true</_MauiHasSplashScreens>
<_MauiSplashScreenIdentities>@(MauiSplashScreen->'%(Identity)', '|')</_MauiSplashScreenIdentities>
- Files reviewed: 13/13 changed files
- Comments generated: 0 new
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 83d5a2cc-c4eb-4be5-ad04-e89a051c2989
There was a problem hiding this comment.
Copilot's findings
Suppressed comments (1)
src/SingleProject/Resizetizer/test/UnitTests/MauiResizetizerTargetsTests.cs:100
- Typo in test name:
Resizetize...should beResizetizer...for consistency with the target name (ResizetizeCollectItems) vs project/component naming (Resizetizer). Renaming improves searchability and avoids confusion when scanning test output.
[Fact]
public void ResizetizeCollectItemsHashesOnlyFirstSplashDarkFile()
{
- Files reviewed: 14/14 changed files
- Comments generated: 0 new
This comment has been minimized.
This comment has been minimized.
…the other leg A missing native shared library (libSkiaSharp/libHarfBuzzSharp .so) on the gate agent crashes the test host before the fixed code runs, so the fix is unverifiable via that test. The existing guard only excluded native-lib failures present in BOTH the without-fix AND with-fix runs ($bothNativeLib). It missed the case where the without-fix leg failed for a DIFFERENT reason — most commonly a compile-coupled build error (new API + test in the same project, so the reverted baseline never reached the native-lib load) — leaving the with-fix libSkiaSharp DllNotFound counted as a genuine FAILED. Reclassify any with-fix NativeLibLoadFailure as env/INCONCLUSIVE in the aggregation loop (mark $w.EnvError), independent of the without-fix leg. A genuine assertion regression never presents as a DllNotFoundException, so this cannot mask a real failure. (build 14850956, PR #35710: GenerateSplash* libSkiaSharp DllNotFound on the Linux android gate, without-fix compile-coupled Passed=False/Failed=0 → wrongly FAILED.) +1 Pester test (38 pass). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 15d2af20-e4ab-4e88-9011-cfbd83513bc0
This comment has been minimized.
This comment has been minimized.
MauiBot
left a comment
There was a problem hiding this comment.
AI Review Summary
@jfversluis — new AI review results are available based on this last commit:
2e19455.
🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix
Gate Result: ⚠️ INCONCLUSIVE
Platform: ANDROID · Base: net11.0 · Merge base: af127e85
🩺 Could not verify — environment/infrastructure error. The gate ran the tests but hit an environment error (an emulator/simulator/Appium/XHarness flake, a device that would not boot, or an empty/invalid result file), so it could not record a real pass/fail. The /review to retry on a fresh agent.
With-fix run failed to load a native shared library (e.g. libSkiaSharp/libHarfBuzzSharp) on the gate agent — the test host crashed before exercising the fix, so it is unverifiable here (environment, not a fix failure). Common for Resizetizer/Graphics image tests on a Linux (android) gate agent that lacks the SkiaSharp native runtime.
| Test | Without Fix (expect FAIL) | With Fix (expect PASS) |
|---|---|---|
🧪 GenerateSplashAndroidResourcesTests GenerateSplashAndroidResourcesTests |
🛠️ BUILD ERROR | |
🧪 GenerateSplashAssetCatalogTests GenerateSplashAssetCatalogTests |
🛠️ BUILD ERROR | |
🧪 GenerateSplashStoryboardTests GenerateSplashStoryboardTests |
🛠️ BUILD ERROR | |
🧪 MauiResizetizerTargetsTests MauiResizetizerTargetsTests |
🛠️ BUILD ERROR | ✅ PASS — 14s |
🧪 ResizeImageInfoTests ResizeImageInfoTests |
🛠️ BUILD ERROR | ✅ PASS — 7s |
🔴 Without fix — 🧪 GenerateSplashAndroidResourcesTests: 🛠️ BUILD ERROR · 7s
Error-relevant lines (filtered from the build log):
/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAssetCatalogTests.cs(15,72): error CS0246: The type or namespace name 'GenerateSplashAssetCatalog' could not be found (are you missing a using directive or an assembly reference?) [/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/Resizetizer.UnitTests.csproj]
/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAssetCatalogTests.cs(22,13): error CS0246: The type or namespace name 'GenerateSplashAssetCatalog' could not be found (are you missing a using directive or an assembly reference?) [/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/Resizetizer.UnitTests.csproj]
🟢 With fix — 🧪 GenerateSplashAndroidResourcesTests: ⚠️ ENV ERROR · 28s
Error-relevant lines (filtered from the build log):
at SkiaSharp.SKObject..cctor()
at System.Runtime.CompilerServices.InitHelpers.CallClassConstructor(Void* cctor, Void* instantiatingArg, Exception* pException)
at SkiaSharp.SKObject.DeregisterHandle(IntPtr handle, SKObject instance)
at SkiaSharp.SKObject.set_Handle(IntPtr value)
at SkiaSharp.SKNativeObject.Dispose(Boolean disposing)
at SkiaSharp.SKObject.Dispose(Boolean disposing)
at SkiaSharp.SKPaint.Dispose(Boolean disposing)
at SkiaSharp.SKNativeObject.Finalize()
at System.GC.RunFinalizers()
[xUnit.net 00:00:01.32] ERROR : Error occurred in processing Android-specific image resources. System.DllNotFoundException: Unable to load shared library 'libSkiaSharp' or one of its dependencies. In order to help diagnose loading problems, consider using a tool like strace. If you're using glibc, consider setting the LD_DEBUG environment variable:
[xUnit.net 00:00:01.33] ERROR : Error occurred in processing Android-specific image resources. System.DllNotFoundException: Unable to load shared library 'libSkiaSharp' or one of its dependencies. In order to help diagnose loading problems, consider using a tool like strace. If you're using glibc, consider setting the LD_DEBUG environment variable:
at Microsoft.Maui.Resizetizer.Tests.GenerateSplashAndroidResourcesTests.XmlIsValidForNonSquare(String image, String width, String height) in /_/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAndroidResourcesTests.cs:line 288
at InvokeStub_GenerateSplashAndroidResourcesTests.XmlIsValidForNonSquare(Object, Span`1)
at Microsoft.Maui.Resizetizer.Tests.GenerateSplashAndroidResourcesTests.ChangingDarkImageFromResizedToOriginalCleansStaleNightDensityImages() in /_/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAndroidResourcesTests.cs:line 160
at Microsoft.Maui.Resizetizer.Tests.GenerateSplashAndroidResourcesTests.TwoImagesWithOnlyPathOnlyGeneratesFirstImage() in /_/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAndroidResourcesTests.cs:line 439
at Microsoft.Maui.Resizetizer.Tests.GenerateSplashAndroidResourcesTests.RemovingDarkColorAndLightColorRemovesStaleNightValuesFile() in /_/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAndroidResourcesTests.cs:line 244
at Microsoft.Maui.Resizetizer.Tests.GenerateSplashAndroidResourcesTests.XmlIsValid(String inputColor, String outputColor) in /_/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAndroidResourcesTests.cs:line 57
at InvokeStub_GenerateSplashAndroidResourcesTests.XmlIsValid(Object, Span`1)
at Microsoft.Maui.Resizetizer.Tests.GenerateSplashAndroidResourcesTests.ChangingDarkImageFromOriginalToResizedCleansStaleNightOriginalImage() in /_/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAndroidResourcesTests.cs:line 189
[xUnit.net 00:00:01.34] ERROR : Error occurred in processing Android-specific image resources. System.DllNotFoundException: Unable to load shared library 'libSkiaSharp' or one of its dependencies. In order to help diagnose loading problems, consider using a tool like strace. If you're using glibc, consider setting the LD_DEBUG environment variable:
[xUnit.net 00:00:01.36] ERROR : Error occurred in processing Android-specific image resources. System.DllNotFoundException: Unable to load shared library 'libSkiaSharp' or one of its dependencies. In order to help diagnose loading problems, consider using a tool like strace. If you're using glibc, consider setting the LD_DEBUG environment variable:
[xUnit.net 00:00:01.37] ERROR : Error occurred in processing Android-specific image resources. System.DllNotFoundException: Unable to load shared library 'libSkiaSharp' or one of its dependencies. In order to help diagnose loading problems, consider using a tool like strace. If you're using glibc, consider setting the LD_DEBUG environment variable:
🔴 Without fix — 🧪 GenerateSplashAssetCatalogTests: 🛠️ BUILD ERROR · 6s
Error-relevant lines (filtered from the build log):
/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAssetCatalogTests.cs(15,72): error CS0246: The type or namespace name 'GenerateSplashAssetCatalog' could not be found (are you missing a using directive or an assembly reference?) [/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/Resizetizer.UnitTests.csproj]
/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAssetCatalogTests.cs(22,13): error CS0246: The type or namespace name 'GenerateSplashAssetCatalog' could not be found (are you missing a using directive or an assembly reference?) [/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/Resizetizer.UnitTests.csproj]
🟢 With fix — 🧪 GenerateSplashAssetCatalogTests: ⚠️ ENV ERROR · 20s
Error-relevant lines (filtered from the build log):
at Microsoft.Maui.Resizetizer.Resizer.get_Tools() in /_/src/SingleProject/Resizetizer/src/Resizer.cs:line 28
at Microsoft.Maui.Resizetizer.Resizer.Rasterize(DpiPath dpi, String destination) in /_/src/SingleProject/Resizetizer/src/Resizer.cs:line 114
at Microsoft.Maui.Resizetizer.Resizer.Resize(DpiPath dpi, String inputsFile) in /_/src/SingleProject/Resizetizer/src/Resizer.cs:line 105
at Microsoft.Maui.Resizetizer.GenerateSplashAssetCatalog.WriteImages(Resizer resizer) in /_/src/SingleProject/Resizetizer/src/GenerateSplashAssetCatalog.cs:line 92
at Microsoft.Maui.Resizetizer.GenerateSplashAssetCatalog.Execute() in /_/src/SingleProject/Resizetizer/src/GenerateSplashAssetCatalog.cs:line 44
at Microsoft.Maui.Resizetizer.Tests.GenerateSplashAssetCatalogTests.RemovingColorMetadataDeletesStaleColorAsset() in /_/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAssetCatalogTests.cs:line 172
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
ERROR : Error occurred in processing Apple-specific storyboard resources. System.DllNotFoundException: Unable to load shared library 'libSkiaSharp' or one of its dependencies. In order to help diagnose loading problems, consider using a tool like strace. If you're using glibc, consider setting the LD_DEBUG environment variable:
at Microsoft.Maui.Resizetizer.Resizer.GetOriginalSize() in /_/src/SingleProject/Resizetizer/src/Resizer.cs:line 111
at Microsoft.Maui.Resizetizer.GenerateSplashAssetCatalog.Execute() in /_/src/SingleProject/Resizetizer/src/GenerateSplashAssetCatalog.cs:line 42
at Microsoft.Maui.Resizetizer.Tests.GenerateSplashAssetCatalogTests.RasterWithoutResizePreservesOriginalImageDimensions() in /_/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAssetCatalogTests.cs:line 94
at Microsoft.Maui.Resizetizer.Tests.GenerateSplashAssetCatalogTests.RasterWithoutBaseSizeGeneratesAllReferencedImageFiles() in /_/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAssetCatalogTests.cs:line 78
at Microsoft.Maui.Resizetizer.Tests.GenerateSplashAssetCatalogTests.DarkMetadataGeneratesImageAndColorAssetCatalogs() in /_/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAssetCatalogTests.cs:line 44
at Microsoft.Maui.Resizetizer.Tests.GenerateSplashAssetCatalogTests.DarkFileWithoutColorDoesNotGenerateColorAsset() in /_/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAssetCatalogTests.cs:line 153
at Microsoft.Maui.Resizetizer.Tests.GenerateSplashAssetCatalogTests.DarkColorOnlyGeneratesImageSetAndColorAssetWithWarning() in /_/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAssetCatalogTests.cs:line 220
at Microsoft.Maui.Resizetizer.SkiaSharpSvgTools..ctor(String filename, Nullable`1 baseSize, Nullable`1 backgroundColor, Nullable`1 tintColor, ILogger logger) in /_/src/SingleProject/Resizetizer/src/SkiaSharpSvgTools.cs:line 18
at Microsoft.Maui.Resizetizer.Tests.GenerateSplashAssetCatalogTests.DarkTintColorOnlyGeneratesTintedDarkImage() in /_/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAssetCatalogTests.cs:line 199
[xUnit.net 00:00:00.85] ERROR : Error occurred in processing Apple-specific storyboard resources. System.DllNotFoundException: Unable to load shared library 'libSkiaSharp' or one of its dependencies. In order to help diagnose loading problems, consider using a tool like strace. If you're using glibc, consider setting the LD_DEBUG environment variable:
at Microsoft.Maui.Resizetizer.Tests.GenerateSplashAssetCatalogTests.NonPngRasterWithoutResizeUsesMatchingAssetFilenames() in /_/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAssetCatalogTests.cs:line 122
🔴 Without fix — 🧪 GenerateSplashStoryboardTests: 🛠️ BUILD ERROR · 5s
Error-relevant lines (filtered from the build log):
/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAssetCatalogTests.cs(15,72): error CS0246: The type or namespace name 'GenerateSplashAssetCatalog' could not be found (are you missing a using directive or an assembly reference?) [/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/Resizetizer.UnitTests.csproj]
/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAssetCatalogTests.cs(22,13): error CS0246: The type or namespace name 'GenerateSplashAssetCatalog' could not be found (are you missing a using directive or an assembly reference?) [/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/Resizetizer.UnitTests.csproj]
🟢 With fix — 🧪 GenerateSplashStoryboardTests: ⚠️ ENV ERROR · 20s
Error-relevant lines (filtered from the build log):
at Microsoft.Maui.Resizetizer.Resizer.Resize(DpiPath dpi, String inputsFile) in /_/src/SingleProject/Resizetizer/src/Resizer.cs:line 105
at Microsoft.Maui.Resizetizer.GenerateSplashStoryboard.WriteImages(Resizer resizer) in /_/src/SingleProject/Resizetizer/src/GenerateSplashStoryboard.cs:line 66
at Microsoft.Maui.Resizetizer.GenerateSplashStoryboard.Execute() in /_/src/SingleProject/Resizetizer/src/GenerateSplashStoryboard.cs:line 46
at Microsoft.Maui.Resizetizer.Tests.GenerateSplashStoryboardTests.SingleImageWithBaseSizeSucceeds(String alias, String outputName) in /_/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashStoryboardTests.cs:line 249
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
ERROR : Error occurred in processing Apple-specific storyboard resources. System.DllNotFoundException: Unable to load shared library 'libSkiaSharp' or one of its dependencies. In order to help diagnose loading problems, consider using a tool like strace. If you're using glibc, consider setting the LD_DEBUG environment variable:
at InvokeStub_GenerateSplashStoryboardTests.SingleImageWithBaseSizeSucceeds(Object, Span`1)
at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at Microsoft.Maui.Resizetizer.SkiaSharpSvgTools..ctor(String filename, Nullable`1 baseSize, Nullable`1 backgroundColor, Nullable`1 tintColor, ILogger logger) in /_/src/SingleProject/Resizetizer/src/SkiaSharpSvgTools.cs:line 18
at Microsoft.Maui.Resizetizer.Tests.GenerateSplashStoryboardTests.SplashScreenResectsAlias(String alias, String outputImage) in /_/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashStoryboardTests.cs:line 117
[xUnit.net 00:00:00.98] ERROR : Error occurred in processing Apple-specific storyboard resources. System.DllNotFoundException: Unable to load shared library 'libSkiaSharp' or one of its dependencies. In order to help diagnose loading problems, consider using a tool like strace. If you're using glibc, consider setting the LD_DEBUG environment variable:
[xUnit.net 00:00:00.99] ERROR : Error occurred in processing Apple-specific storyboard resources. System.DllNotFoundException: Unable to load shared library 'libSkiaSharp' or one of its dependencies. In order to help diagnose loading problems, consider using a tool like strace. If you're using glibc, consider setting the LD_DEBUG environment variable:
at InvokeStub_GenerateSplashStoryboardTests.SplashScreenResectsAlias(Object, Span`1)
at Microsoft.Maui.Resizetizer.Tests.GenerateSplashStoryboardTests.TwoImagesWithOnlyPathOnlyGeneratesFirstImage() in /_/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashStoryboardTests.cs:line 218
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
at Microsoft.Maui.Resizetizer.Tests.GenerateSplashStoryboardTests.XmlIsValid(String inputColor, String r, String g, String b, String a) in /_/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashStoryboardTests.cs:line 70
at System.Reflection.MethodBaseInvoker.InvokeWithManyArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at InvokeStub_GenerateSplashStoryboardTests.XmlIsValid(Object, Span`1)
at Microsoft.Maui.Resizetizer.Tests.GenerateSplashStoryboardTests.SingleImageWithOnlyPathSucceeds() in /_/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashStoryboardTests.cs:line 201
at Microsoft.Maui.Resizetizer.Tests.GenerateSplashStoryboardTests.RemovesThemedAssetCatalogWhenGeneratingStoryboard() in /_/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashStoryboardTests.cs:line 99
🔴 Without fix — 🧪 MauiResizetizerTargetsTests: 🛠️ BUILD ERROR · 5s
Error-relevant lines (filtered from the build log):
/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAssetCatalogTests.cs(15,72): error CS0246: The type or namespace name 'GenerateSplashAssetCatalog' could not be found (are you missing a using directive or an assembly reference?) [/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/Resizetizer.UnitTests.csproj]
/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAssetCatalogTests.cs(22,13): error CS0246: The type or namespace name 'GenerateSplashAssetCatalog' could not be found (are you missing a using directive or an assembly reference?) [/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/Resizetizer.UnitTests.csproj]
🟢 With fix — 🧪 MauiResizetizerTargetsTests: PASS ✅ · 14s
(no coded error found; showing last 1200 chars)
cts/bin/Resizetizer.UnitTests/Debug/net11.0/Microsoft.Maui.Resizetizer.UnitTests.dll (.NETCoreApp,Version=v11.0)
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:00.01] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 11.0.0-rc.1.26379.102)
[xUnit.net 00:00:00.16] Discovering: Microsoft.Maui.Resizetizer.UnitTests
[xUnit.net 00:00:00.57] Discovered: Microsoft.Maui.Resizetizer.UnitTests
[xUnit.net 00:00:00.58] Starting: Microsoft.Maui.Resizetizer.UnitTests
Passed Microsoft.Maui.Resizetizer.Tests.MauiResizetizerTargetsTests.ResizetizeCollectItemsHashesOnlyFirstSplashDarkFile [2 s]
Passed Microsoft.Maui.Resizetizer.Tests.MauiResizetizerTargetsTests.ProcessMauiSplashScreensWarnsWhenThemedAppleSplashIsUnsupported [2 s]
Passed Microsoft.Maui.Resizetizer.Tests.MauiResizetizerTargetsTests.ProcessMauiSplashScreensPassesNormalizedSplashItemsToAndroid [9 ms]
[xUnit.net 00:00:07.90] Finished: Microsoft.Maui.Resizetizer.UnitTests
Passed Microsoft.Maui.Resizetizer.Tests.MauiResizetizerTargetsTests.ProcessMauiSplashScreensSelectsFirstSplashForThemedAppleMetadata [2 s]
Test Run Successful.
Total tests: 4
Passed: 4
Total time: 8.9588 Seconds
🔴 Without fix — 🧪 ResizeImageInfoTests: 🛠️ BUILD ERROR · 5s
Error-relevant lines (filtered from the build log):
/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAssetCatalogTests.cs(15,72): error CS0246: The type or namespace name 'GenerateSplashAssetCatalog' could not be found (are you missing a using directive or an assembly reference?) [/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/Resizetizer.UnitTests.csproj]
/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAssetCatalogTests.cs(22,13): error CS0246: The type or namespace name 'GenerateSplashAssetCatalog' could not be found (are you missing a using directive or an assembly reference?) [/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/Resizetizer.UnitTests.csproj]
🟢 With fix — 🧪 ResizeImageInfoTests: PASS ✅ · 7s
(no coded error found; showing last 1200 chars)
.JPEG", isVector: False) [< 1 ms]
Passed Microsoft.Maui.Resizetizer.Tests.ResizeImageInfoTests+IsVector.ReturnsCorrectFolder(filename: "image.PNG", isVector: False) [< 1 ms]
Passed Microsoft.Maui.Resizetizer.Tests.ResizeImageInfoTests+IsVector.SupportsNoExtension(filename: "image") [< 1 ms]
Passed Microsoft.Maui.Resizetizer.Tests.ResizeImageInfoTests+IsVector.SupportsNoExtension(filename: "IMAGE") [< 1 ms]
Passed Microsoft.Maui.Resizetizer.Tests.ResizeImageInfoTests+IsVector.DoesNotCrashOnNullOrEmpty(filename: null) [2 ms]
Passed Microsoft.Maui.Resizetizer.Tests.ResizeImageInfoTests+IsVector.DoesNotCrashOnNullOrEmpty(filename: "") [< 1 ms]
Passed Microsoft.Maui.Resizetizer.Tests.ResizeImageInfoTests+Parse.DarkTintColorDoesNotFallbackToTintColorWhenDarkFileIsSpecified [40 ms]
[xUnit.net 00:00:00.89] Finished: Microsoft.Maui.Resizetizer.UnitTests
Passed Microsoft.Maui.Resizetizer.Tests.ResizeImageInfoTests+Parse.DarkTintColorFallsBackToTintColorOnlyWhenDarkFileIsNotSpecified [12 ms]
Passed Microsoft.Maui.Resizetizer.Tests.ResizeImageInfoTests+Parse.SupportsDarkSplashMetadata [< 1 ms]
Test Run Successful.
Total tests: 19
Passed: 19
Total time: 1.9610 Seconds
⚠️ Failure Details (8 tests)
- 🛠️ GenerateSplashAndroidResourcesTests without fix: build failed before tests could run
/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAssetCatalogTests.cs(15,72): error CS0246: The type or namespace name 'GenerateSplashAssetCatalog' could not be found (ar...
- 🛠️ GenerateSplashAssetCatalogTests without fix: build failed before tests could run
/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAssetCatalogTests.cs(15,72): error CS0246: The type or namespace name 'GenerateSplashAssetCatalog' could not be found (ar...
- 🛠️ GenerateSplashStoryboardTests without fix: build failed before tests could run
/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAssetCatalogTests.cs(15,72): error CS0246: The type or namespace name 'GenerateSplashAssetCatalog' could not be found (ar...
- 🛠️ MauiResizetizerTargetsTests without fix: build failed before tests could run
/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAssetCatalogTests.cs(15,72): error CS0246: The type or namespace name 'GenerateSplashAssetCatalog' could not be found (ar...
- 🛠️ ResizeImageInfoTests without fix: build failed before tests could run
/home/vsts/work/1/s/src/SingleProject/Resizetizer/test/UnitTests/GenerateSplashAssetCatalogTests.cs(15,72): error CS0246: The type or namespace name 'GenerateSplashAssetCatalog' could not be found (ar...
⚠️ GenerateSplashAndroidResourcesTests with fix:With-fix run failed to load a native shared library (e.g. libSkiaSharp/libHarfBuzzSharp) on the gate agent — the test host crashed before exercising the fix, so it is unverifiable here (environment, not a fix failure). Common for Resizetizer/Graphics image tests on a Linux (android) gate agent that lacks the SkiaSharp native runtime.⚠️ GenerateSplashAssetCatalogTests with fix:With-fix run failed to load a native shared library (e.g. libSkiaSharp/libHarfBuzzSharp) on the gate agent — the test host crashed before exercising the fix, so it is unverifiable here (environment, not a fix failure). Common for Resizetizer/Graphics image tests on a Linux (android) gate agent that lacks the SkiaSharp native runtime.⚠️ GenerateSplashStoryboardTests with fix:With-fix run failed to load a native shared library (e.g. libSkiaSharp/libHarfBuzzSharp) on the gate agent — the test host crashed before exercising the fix, so it is unverifiable here (environment, not a fix failure). Common for Resizetizer/Graphics image tests on a Linux (android) gate agent that lacks the SkiaSharp native runtime.
📁 Fix files reverted (6 files)
src/SingleProject/Resizetizer/src/CreatePartialInfoPlistTask.cssrc/SingleProject/Resizetizer/src/DpiPath.cssrc/SingleProject/Resizetizer/src/GenerateSplashAndroidResources.cssrc/SingleProject/Resizetizer/src/GenerateSplashStoryboard.cssrc/SingleProject/Resizetizer/src/ResizeImageInfo.cssrc/SingleProject/Resizetizer/src/nuget/buildTransitive/Microsoft.Maui.Resizetizer.After.targets
New files (not reverted):
src/SingleProject/Resizetizer/src/GenerateSplashAssetCatalog.cs
📋 Pre-Flight — Context & Validation
Issue: N/A - No linked issue found in PR body
PR: #35710 - Add themed splash screen support
Platforms Affected: Android, iOS, iPadOS, Mac Catalyst
Files Changed: 7 implementation, 6 test
Key Findings
- PR adds additive
MauiSplashScreendark metadata support: Android night-qualified resources and AppleUILaunchScreenasset-catalog launch resources for supported OS versions. - Existing projects without dark metadata are intended to keep legacy behavior; Apple targets below 14 warn and keep storyboard fallback.
- Changed implementation files are in Resizetizer build tasks, not runtime UI handlers; Android is the requested test platform.
- Gate result provided by caller is inconclusive due build/environment errors; gate verification was not rerun.
Code Review Summary
Verdict: NEEDS_DISCUSSION
Confidence: low
Errors: 0 | Warnings: 0 | Suggestions: 0
Key code review findings:
- ℹ No new code findings; prior ❌ findings identified in earlier reviews appear fixed in current head.
- ℹ CI/check status remained undetermined in this environment, so the review confidence is capped low.
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #35710 | Thread dark splash metadata through Resizetizer tasks; Android writes night-qualified resources and Apple writes asset-catalog launch resources. | CreatePartialInfoPlistTask.cs, DpiPath.cs, GenerateSplashAndroidResources.cs, GenerateSplashAssetCatalog.cs, GenerateSplashStoryboard.cs, ResizeImageInfo.cs, Microsoft.Maui.Resizetizer.After.targets + tests |
Original PR; gate was already handled separately and not rerun. |
🔬 Code Review — Deep Analysis
Code Review — PR #35710
Independent Assessment
What this changes: Adds themed/light-dark MauiSplashScreen support in Resizetizer. Android emits night-qualified splash resources; Apple targets can emit launch-screen asset catalogs and named color/image assets when supported.
Inferred motivation: Enable theme-aware splash images/colors without changing existing splash behavior.
Reconciliation with PR Narrative
Author claims: Additive themed splash metadata for Android and Apple; preserve existing behavior; warn/fallback below Apple 14.
Agreement/disagreement: Matches the current code and tests.
Prior Review Reconciliation
| Prior ❌ Error Finding | Source | Status | Evidence |
|---|---|---|---|
| Android dark alias used wrong extension | MauiBot inline | ✅ Fixed | GetDarkOutputAlias() chooses dark/vector/raster extension. |
| Stale Android night resources | MauiBot inline | ✅ Fixed | CleanStaleNightImageResources() removes opposite output mode. |
| Apple metadata used later splash items | MauiBot/kubaflo | ✅ Fixed | targets compute _MauiFirstSplashScreen*. |
Apple Resize=false/non-PNG copy issues |
MauiBot inline | ✅ Fixed | asset task preserves copied raster extension and copy dimensions. |
| Target tests depended on repo root | MauiBot inline | ✅ Fixed | targets file copied to test output. |
Blast Radius Assessment
- Runs for all instances: No; gated to MAUI splash processing, with themed Apple path only when dark metadata and platform support are present.
- Startup impact: Yes; affects generated launch-screen resources.
- Static/shared state: No.
External Output Contract
| Consumer token/pattern | Producer location | Producer emission condition | Consumer assumption | Ordinary negative case | Downstream effect |
|---|---|---|---|---|---|
| N/A | N/A | No changed code classifies external tool output | N/A | N/A | N/A |
CI Status
- Required-check result: pending/undetermined.
gh pr checks --requiredunavailable due missing auth; public check-runs for head2e19455still show pending/in-progress checks. - Classification: undetermined.
- Action taken: loaded
azdo-build-investigator;ci-analysisunavailable in this environment; confidence capped low and no LGTM.
Findings
No new code findings.
Failure-Mode Probing
- Existing project without dark metadata: Android cleans stale night resources; Apple uses legacy storyboard path.
DarkFilerelative path: normalized through_MauiSplashScreenWithHashesbefore task use.- Multiple splash items: first item drives metadata, matching generators’
FirstOrDefault(). Resize=falseraster/JPEG: copied asset filenames preserve extension; tests cover this.- Apple target below 14: warning emitted; themed path disabled.
- Stale storyboard/asset transitions: themed task deletes storyboard/image set; storyboard task deletes themed assets.
Verdict: NEEDS_DISCUSSION
Confidence: low
Summary: Code review found no unresolved correctness issues in the current head, and prior major findings appear fixed. However CI is still pending/undetermined from available evidence, so the skill rules prohibit LGTM until required checks complete or failures are classified.
🛠️ Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | try-fix-1 | Centralized Android resource path decisions in a VariantPaths object and consolidated night cleanup. |
5 files | 21/33 tests passed; remaining image-resize tests hit missing Linux libSkiaSharp.so. Not selected. |
|
| 2 | try-fix-2 | Treat dark Android splash output as theme-tagged ResizeImageInfo variants (ThemeQualifier) and run one variant loop instead of threading bool dark through helpers. |
✅ PASS (84/84) | 5 files | Expert reviewer selected this candidate as simpler/safer than the PR's current Android generator structure, while retaining the rest of PR wiring/tests. |
| PR | PR #35710 | Current PR implementation threads dark metadata through Resizetizer tasks and helper methods. | 7 implementation, 6 test | Original PR; gate phase was already completed separately and not rerun. |
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| claude-opus-4.6 | 1 | Yes | Candidate 1: centralize resource path/cleanup decisions in VariantPaths; blocked by missing Linux SkiaSharp native dependency. |
| claude-opus-4.7 | 1 | Yes | Candidate 2: move theme identity into ResizeImageInfo variants; passed all targeted Resizetizer tests. |
| maui-expert-reviewer | selection | Yes | Reviewed Candidate 2 diff and recommended SELECT_CANDIDATE_2; noted it should be integrated on top of the PR's existing MSBuild target wiring and tests, not used as a standalone full patch. |
Exhausted: No — stopped because Candidate 2 passed all targeted tests and expert review found it demonstrably simpler/safer than the PR's current Android generator structure.
Selected Fix: Candidate #2 — variant-based Android resource generation keeps light and dark output on one data-driven path, reduces helper-branch divergence, consolidates stale night-resource cleanup, and passed 84/84 targeted tests.
📝 Recommended PR Title & Description
Assessment: ✏️ Recommend updating — the description is accurate and detailed, but the title should name the Resizetizer component and affected platform scope for a better merge headline.
Recommended title
[Android/Apple] Resizetizer: Add themed splash screen support
Recommended description
## Description
Adds additive light/dark splash screen metadata to `MauiSplashScreen` without changing existing splash behavior:
```xml
<MauiSplashScreen Include="Resources\Splash\splash.svg"
Color="#FFFFFF"
DarkFile="Resources\Splash\splash-dark.svg"
DarkColor="#000000"
DarkTintColor="#FFFFFF"
BaseSize="128,128" />
```
- Android generates standard `night`-qualified splash image/color resources.
- iOS, iPadOS, and Mac Catalyst generate `UILaunchScreen` asset catalog launch resources when `SupportedOSPlatformVersion >= 14.0`.
- Apple targets below 14 warn and preserve existing fallback behavior.
- Existing projects without dark splash metadata continue to use the current splash pipeline.
## Testing
- `dotnet test src\SingleProject\Resizetizer\test\UnitTests\Resizetizer.UnitTests.csproj --no-restore --filter "GenerateSplashAssetCatalogTests|GenerateSplashAndroidResourcesTests|GenerateSplashStoryboardTests|ResizeImageInfoTests" -p:UseSharedCompilation=false`
- `dotnet build src\SingleProject\Resizetizer\src\Resizetizer.csproj --no-restore -v:minimal -p:UseSharedCompilation=false`
- Android visual verification with DevFlow theme switching and screenshots.
🏁 Report — Final Recommendation
Comparative Analysis — PR #35710
Candidates
| Rank | Candidate | Regression/test result | Assessment |
|---|---|---|---|
| 1 | try-fix-2 |
✅ PASS (84/84 targeted Resizetizer tests) | Best candidate. It preserves the PR behavior while making Android light/dark generation data-driven through theme-tagged variants, reducing duplicated bool dark branching and consolidating stale night-resource cleanup. Expert STEP 5a review selected it as simpler/safer, with zero inline findings. |
| 2 | pr |
Correct on code review merits and has no current expert inline findings, but remains less maintainable than try-fix-2 in Android generation because light/dark behavior is threaded through helper booleans and separate cleanup paths. |
|
| 2 | pr-plus-reviewer |
Equivalent to pr; the expert reviewer produced no actionable feedback, so no sandbox changes were applied. |
|
| 4 | try-fix-1 |
Centralized path selection was a reasonable alternative, but only 21/33 tests completed because the environment initially lacked Linux SkiaSharp native assets. It cannot outrank a passing candidate. |
Comparison
pr and pr-plus-reviewer are viable on code review merits: the current PR threads dark splash metadata through Resizetizer, generates Android night resources, generates Apple launch-screen asset catalogs for supported versions, warns/falls back below Apple 14, and preserves the no-dark metadata path. The expert reviewer found no actionable inline comments, so inline-findings.json is [] and pr-plus-reviewer does not differ from the submitted PR fix.
try-fix-2 is the strongest result because it keeps the same intended behavior while improving the Android implementation shape. Instead of passing bool dark into each helper, it models light/dark as ordered ResizeImageInfo variants with a ThemeQualifier, then runs one resource-generation loop. That reduces divergence between light and dark paths and keeps stale cleanup tied to the same variant decision. It also passed the complete targeted test filter used in STEP 5a (GenerateSplashAssetCatalogTests | GenerateSplashAndroidResourcesTests | GenerateSplashStoryboardTests | ResizeImageInfoTests).
try-fix-1 remains below all non-blocked candidates because its regression evidence is incomplete. The block appears environmental rather than a code failure, but the explicit ranking rule requires candidates with stronger test evidence to rank higher.
Winner
try-fix-2 wins. It is the only candidate with a passing targeted regression run, and it is a clearer, lower-divergence implementation of the PR's Android resource generation while retaining the PR's Apple and MSBuild wiring concepts.
🧭 Next Steps — alternative fix proposed (try-fix-2)
Automated review — alternative fix proposed
The expert-reviewer evaluation compared the PR fix against automatically generated candidates and selected try-fix-2 as the strongest fix.
Why: try-fix-2 won because it is the only candidate with a passing targeted regression run and it simplifies Android themed splash generation by treating light/dark output as data variants instead of branching helper calls. The raw PR and pr-plus-reviewer remain code-review clean but unverified, and pr-plus-reviewer has no changes because the expert reviewer reported zero inline findings.
Please consider applying the candidate diff below (or use it as guidance). Once you push an update, this workflow will re-trigger and re-evaluate.
Candidate diff (try-fix-2)
diff --git a/src/SingleProject/Resizetizer/src/CreatePartialInfoPlistTask.cs b/src/SingleProject/Resizetizer/src/CreatePartialInfoPlistTask.cs
index 956630fea7..e50bfb48aa 100644
--- a/src/SingleProject/Resizetizer/src/CreatePartialInfoPlistTask.cs
+++ b/src/SingleProject/Resizetizer/src/CreatePartialInfoPlistTask.cs
@@ -16,6 +16,10 @@ namespace Microsoft.Maui.Resizetizer
public string Storyboard { get; set; }
+ public string LaunchScreenImage { get; set; }
+
+ public string LaunchScreenColor { get; set; }
+
const string plistHeader =
@"<?xml version=""1.0"" encoding=""UTF-8""?>
<!DOCTYPE plist PUBLIC ""-//Apple//DTD PLIST 1.0//EN"" ""http://www.apple.com/DTDs/PropertyList-1.0.dtd"">
@@ -52,7 +56,26 @@ namespace Microsoft.Maui.Resizetizer
f.WriteLine(" </array>");
}
- if (!string.IsNullOrEmpty(Storyboard))
+ if (!string.IsNullOrEmpty(LaunchScreenImage) || !string.IsNullOrEmpty(LaunchScreenColor))
+ {
+ f.WriteLine(" <key>UILaunchScreen</key>");
+ f.WriteLine(" <dict>");
+
+ if (!string.IsNullOrEmpty(LaunchScreenImage))
+ {
+ f.WriteLine(" <key>UIImageName</key>");
+ f.WriteLine($" <string>{LaunchScreenImage}</string>");
+ }
+
+ if (!string.IsNullOrEmpty(LaunchScreenColor))
+ {
+ f.WriteLine(" <key>UIColorName</key>");
+ f.WriteLine($" <string>{LaunchScreenColor}</string>");
+ }
+
+ f.WriteLine(" </dict>");
+ }
+ else if (!string.IsNullOrEmpty(Storyboard))
{
f.WriteLine(" <key>UILaunchStoryboardName</key>");
f.WriteLine($" <string>{Path.GetFileNameWithoutExtension(Storyboard)}</string>");
diff --git a/src/SingleProject/Resizetizer/src/DpiPath.cs b/src/SingleProject/Resizetizer/src/DpiPath.cs
index b38fb37cd5..d4e8f7b711 100644
--- a/src/SingleProject/Resizetizer/src/DpiPath.cs
+++ b/src/SingleProject/Resizetizer/src/DpiPath.cs
@@ -48,6 +48,9 @@ namespace Microsoft.Maui.Resizetizer
public static DpiPath Original =>
new DpiPath("drawable", 1.0m);
+ public static DpiPath OriginalNight =>
+ new DpiPath("drawable-night", 1.0m);
+
public static DpiPath[] Image
=> new[]
{
@@ -58,6 +61,16 @@ namespace Microsoft.Maui.Resizetizer
new DpiPath("drawable-xxxhdpi", 4.0m),
};
+ public static DpiPath[] ImageNight
+ => new[]
+ {
+ new DpiPath("drawable-night-mdpi", 1.0m),
+ new DpiPath("drawable-night-hdpi", 1.5m),
+ new DpiPath("drawable-night-xhdpi", 2.0m),
+ new DpiPath("drawable-night-xxhdpi", 3.0m),
+ new DpiPath("drawable-night-xxxhdpi", 4.0m),
+ };
+
public static DpiPath[] AppIcon
=> new[]
{
@@ -90,6 +103,20 @@ namespace Microsoft.Maui.Resizetizer
{
public const string AppIconPath = "Assets.xcassets/{name}.appiconset";
+ public const string SplashImageName = "MauiSplashImage";
+ public const string SplashImageDarkName = "MauiSplashImageDark";
+
+ public const string SplashImageSetPath = "Assets.xcassets/MauiSplashImage.imageset";
+ public const string SplashColorSetPath = "Assets.xcassets/MauiSplashColor.colorset";
+
+ public static DpiPath[] SplashImageAsset
+ => new[]
+ {
+ new DpiPath(SplashImageSetPath, 1.0m),
+ new DpiPath(SplashImageSetPath, 2.0m, null, "@2x"),
+ new DpiPath(SplashImageSetPath, 3.0m, null, "@3x"),
+ };
+
public static DpiPath Original =>
new DpiPath("Resources", 1.0m);
diff --git a/src/SingleProject/Resizetizer/src/GenerateSplashAndroidResources.cs b/src/SingleProject/Resizetizer/src/GenerateSplashAndroidResources.cs
index 0e0fd63fd8..92ddd6ad0d 100644
--- a/src/SingleProject/Resizetizer/src/GenerateSplashAndroidResources.cs
+++ b/src/SingleProject/Resizetizer/src/GenerateSplashAndroidResources.cs
@@ -1,4 +1,6 @@
-using System;
+#nullable enable
+using System;
+using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
@@ -13,19 +15,40 @@ namespace Microsoft.Maui.Resizetizer
/// <summary>
/// Generates files relating to the Android splash screen:
/// - Resources/values/maui_colors.xml
- /// - Resources/drawable/maui_splash_image.xml
- /// - Resources/drawable-v31/maui_splash_image.xml
- /// - Resources/[drawable]/[splash].png
+ /// - Resources/values-night/maui_colors.xml (when dark colour metadata is present)
+ /// - Resources/drawable[-night]/maui_splash_image.xml
+ /// - Resources/drawable[-night]-v31/maui_splash_image.xml
+ /// - Resources/drawable[-night]-*/[splash].png (or drawable[-night]/[splash].* when Resize=false)
+ ///
+ /// This implementation processes light/dark as data variants of ResizeImageInfo (see
+ /// ResizeImageInfo.GetAndroidSplashVariants). Each variant carries a ThemeQualifier
+ /// (empty for light, "-night" for dark) and the same helpers write both variants —
+ /// no `bool dark` parameter is threaded through the write path.
/// </summary>
public class GenerateSplashAndroidResources : Task, ILogger
{
[Required]
- public string IntermediateOutputPath { get; set; }
+ public string IntermediateOutputPath { get; set; } = null!;
[Required]
- public ITaskItem[] MauiSplashScreen { get; set; }
+ public ITaskItem[]? MauiSplashScreen { get; set; }
- public string InputsFile { get; set; }
+ public string? InputsFile { get; set; }
+
+ static readonly XmlWriterSettings Settings = new XmlWriterSettings { Indent = true };
+ const string Namespace = "http://schemas.android.com/apk/res/android";
+ const string Comment = "This file was auto-generated by .NET MAUI.";
+ const float PreferredImageSize = 108f;
+
+ // Night-mode directory suffixes used both for writing and for stale-resource cleanup.
+ static readonly string[] NightImageDensityDirs = new[]
+ {
+ "drawable-night-mdpi",
+ "drawable-night-hdpi",
+ "drawable-night-xhdpi",
+ "drawable-night-xxhdpi",
+ "drawable-night-xxxhdpi",
+ };
public override bool Execute()
{
@@ -42,12 +65,39 @@ namespace Microsoft.Maui.Resizetizer
{
var info = ResizeImageInfo.Parse(splash);
- var resizer = new Resizer(info, IntermediateOutputPath, this);
+ // Materialise ordered variants (light, then optional dark). Each carries its
+ // ThemeQualifier; downstream helpers only concatenate that onto directory names.
+ var variants = info.GetAndroidSplashVariants(
+ darkAliasFactory: () => GetDarkOutputAlias(info)).ToList();
+
+ var resizers = new List<Resizer>();
+ Resizer? lightResizer = null;
+ foreach (var variant in variants)
+ {
+ if (variant.ThemeQualifier == "-night" && variant.BaseSize is null
+ && !string.IsNullOrWhiteSpace(info.DarkFilename))
+ {
+ variant.BaseSize = lightResizer?.BaseSize ?? lightResizer?.GetOriginalSize();
+ }
+
+ var resizer = new Resizer(variant, IntermediateOutputPath, this);
+ if (lightResizer is null)
+ lightResizer = resizer;
+ resizers.Add(resizer);
+ }
- WriteImages(resizer);
- WriteColors(resizer);
- WriteDrawable(resizer);
- WriteDrawable_v31(resizer);
+ // Clean stale night resources up front — the write path re-creates what is needed.
+ CleanNightResourcesIfNeeded(hasDark: variants.Count > 1,
+ darkResize: variants.Count > 1 ? variants[1].Resize : true);
+
+ foreach (var resizer in resizers)
+ {
+ WriteImages(resizer);
+ WriteDrawable(resizer);
+ WriteDrawable_v31(resizer);
+ }
+
+ WriteColors(variants);
return !Log.HasLoggedErrors;
}
@@ -58,16 +108,14 @@ namespace Microsoft.Maui.Resizetizer
}
}
- static readonly XmlWriterSettings Settings = new XmlWriterSettings { Indent = true };
- const string Namespace = "http://schemas.android.com/apk/res/android";
- const string Comment = "This file was auto-generated by .NET MAUI.";
- const float PreferredImageSize = 108f;
-
private void WriteImages(Resizer resizer)
{
+ var qualifier = resizer.Info.ThemeQualifier ?? string.Empty;
+ var isNight = qualifier == "-night";
+
if (resizer.Info.Resize)
{
- foreach (var dpi in DpiPath.Android.Image)
+ foreach (var dpi in isNight ? DpiPath.Android.ImageNight : DpiPath.Android.Image)
{
Log.LogMessage(MessageImportance.Low, $"Splash Screen Resize: " + dpi);
resizer.Resize(dpi, InputsFile);
@@ -75,16 +123,32 @@ namespace Microsoft.Maui.Resizetizer
}
else
{
- var dpi = DpiPath.Android.Original;
+ var dpi = isNight ? DpiPath.Android.OriginalNight : DpiPath.Android.Original;
Log.LogMessage(MessageImportance.Low, $"Splash Screen Copy: " + dpi);
resizer.CopyFile(dpi, InputsFile);
}
}
- void WriteColors(Resizer resizer)
+ void WriteColors(IList<ResizeImageInfo> variants)
+ {
+ // Always write the light values/maui_colors.xml — matches legacy behaviour even when Color is null.
+ var light = variants[0];
+ WriteColors(light.Color, "values");
+
+ if (variants.Count > 1)
+ {
+ var dark = variants[1];
+ if (dark.Color is not null)
+ WriteColors(dark.Color, "values-night");
+ else
+ DeleteNightColors();
+ }
+ }
+
+ void WriteColors(SKColor? color, string directory)
{
- var dir = Path.Combine(IntermediateOutputPath, "values");
+ var dir = Path.Combine(IntermediateOutputPath, directory);
Directory.CreateDirectory(dir);
var colorsFile = Path.Combine(dir, "maui_colors.xml");
@@ -95,11 +159,11 @@ namespace Microsoft.Maui.Resizetizer
writer.WriteComment(Comment);
writer.WriteStartElement("resources");
- if (resizer.Info.Color is not null)
+ if (color is not null)
{
writer.WriteStartElement("color");
writer.WriteAttributeString("name", "maui_splash_color");
- writer.WriteString(resizer.Info.Color.ToString());
+ writer.WriteString(color.ToString());
writer.WriteEndElement();
}
@@ -108,12 +172,13 @@ namespace Microsoft.Maui.Resizetizer
void WriteDrawable(Resizer resizer)
{
- var dir = Path.Combine(IntermediateOutputPath, "drawable");
+ var qualifier = resizer.Info.ThemeQualifier ?? string.Empty;
+ var dir = Path.Combine(IntermediateOutputPath, "drawable" + qualifier);
Directory.CreateDirectory(dir);
var drawableFile = Path.Combine(dir, "maui_splash_image.xml");
- Log.LogMessage(MessageImportance.Low, $"Splash Screen Drawable: " + drawableFile);
+ Log.LogMessage(MessageImportance.Low, $"Splash Screen Drawable{(qualifier.Length > 0 ? " (" + qualifier.TrimStart('-') + ")" : "")}: " + drawableFile);
using var writer = XmlWriter.Create(drawableFile, Settings);
writer.WriteComment(Comment);
@@ -132,14 +197,15 @@ namespace Microsoft.Maui.Resizetizer
void WriteDrawable_v31(Resizer resizer)
{
+ var qualifier = resizer.Info.ThemeQualifier ?? string.Empty;
var size = CalculateScaledSize(resizer);
- var dir = Path.Combine(IntermediateOutputPath, "drawable-v31");
+ var dir = Path.Combine(IntermediateOutputPath, "drawable" + qualifier + "-v31");
Directory.CreateDirectory(dir);
var drawableFile = Path.Combine(dir, "maui_splash_image.xml");
- Log.LogMessage(MessageImportance.Low, $"Splash Screen Drawable (v31): " + drawableFile);
+ Log.LogMessage(MessageImportance.Low, $"Splash Screen Drawable ({(qualifier.Length > 0 ? qualifier.TrimStart('-') + " " : "")}v31): " + drawableFile);
using var writer = XmlWriter.Create(drawableFile, Settings);
writer.WriteComment(Comment);
@@ -164,6 +230,60 @@ namespace Microsoft.Maui.Resizetizer
Log?.LogMessage(message);
}
+ // When switching from dark-mode to no-dark-mode, or between Resize=true/false, delete
+ // stale night resource directories so they cannot leak into the AAPT input.
+ void CleanNightResourcesIfNeeded(bool hasDark, bool darkResize)
+ {
+ if (!hasDark)
+ {
+ DeleteDirs(new[]
+ {
+ "values-night",
+ "drawable-night",
+ "drawable-night-v31",
+ }.Concat(NightImageDensityDirs));
+ }
+ else
+ {
+ // Only the opposite-shape image directories are stale — keep the ones we will re-write.
+ if (darkResize)
+ DeleteDirs(new[] { "drawable-night" });
+ else
+ DeleteDirs(NightImageDensityDirs);
+ }
+ }
+
+ void DeleteDirs(IEnumerable<string> relative)
+ {
+ foreach (var d in relative)
+ {
+ var path = Path.Combine(IntermediateOutputPath, d);
+ if (Directory.Exists(path))
+ Directory.Delete(path, recursive: true);
+ }
+ }
+
+ void DeleteNightColors()
+ {
+ var colorsFile = Path.Combine(IntermediateOutputPath, "values-night", "maui_colors.xml");
+ if (File.Exists(colorsFile))
+ File.Delete(colorsFile);
+ }
+
+ static string GetDarkOutputAlias(ResizeImageInfo info)
+ {
+ // Force a raster extension whenever the effective dark source is a vector — Android drawable
+ // resources reference @drawable/<OutputName> and can't co-locate .svg / .png resources under
+ // the same name across variants.
+ var extension = info.DarkIsVector || (string.IsNullOrWhiteSpace(info.DarkFilename) && info.IsVector)
+ ? Resizer.RasterFileExtension
+ : !string.IsNullOrWhiteSpace(info.DarkFilename)
+ ? Path.GetExtension(info.DarkFilename)
+ : info.OutputExtension;
+
+ return info.OutputName + extension;
+ }
+
static SKSize CalculateScaledSize(Resizer resizer)
{
var size = resizer.BaseSize ?? resizer.GetOriginalSize();
diff --git a/src/SingleProject/Resizetizer/src/GenerateSplashStoryboard.cs b/src/SingleProject/Resizetizer/src/GenerateSplashStoryboard.cs
index 472495b831..249a8ca26c 100644
--- a/src/SingleProject/Resizetizer/src/GenerateSplashStoryboard.cs
+++ b/src/SingleProject/Resizetizer/src/GenerateSplashStoryboard.cs
@@ -33,6 +33,7 @@ namespace Microsoft.Maui.Resizetizer
{
// write an empty (white) storyboard
WriteEmptyStoryboard();
+ CleanThemedAssetCatalog();
}
else
{
@@ -43,6 +44,7 @@ namespace Microsoft.Maui.Resizetizer
WriteImages(resizer);
WriteStoryboard(resizer);
+ CleanThemedAssetCatalog();
}
return !Log.HasLoggedErrors;
@@ -105,6 +107,16 @@ namespace Microsoft.Maui.Resizetizer
SubstituteStoryboard(writer, "MauiNoSplash.storyboard", null, SKColors.White);
}
+ private void CleanThemedAssetCatalog()
+ {
+ var assetCatalog = Path.Combine(IntermediateOutputPath, "Assets.xcassets");
+ if (Directory.Exists(assetCatalog))
+ {
+ Log.LogMessage(MessageImportance.Low, $"Splash Screen: removing themed asset catalog at " + assetCatalog);
+ Directory.Delete(assetCatalog, recursive: true);
+ }
+ }
+
internal static void SubstituteStoryboard(TextWriter writer, string resourceName, string? image, SKColor color)
{
float r = color.Red / (float)byte.MaxValue;
diff --git a/src/SingleProject/Resizetizer/src/ResizeImageInfo.cs b/src/SingleProject/Resizetizer/src/ResizeImageInfo.cs
index 79913d4b6b..28d3d3d6a8 100644
--- a/src/SingleProject/Resizetizer/src/ResizeImageInfo.cs
+++ b/src/SingleProject/Resizetizer/src/ResizeImageInfo.cs
@@ -40,6 +40,24 @@ namespace Microsoft.Maui.Resizetizer
public SKColor? Color { get; set; }
+ public SKColor? DarkTintColor { get; set; }
+
+ public SKColor? DarkColor { get; set; }
+
+ public string? DarkFilename { get; set; }
+
+ public bool DarkIsVector => IsVectorFilename(DarkFilename);
+
+ public bool HasDarkMode =>
+ DarkColor is not null ||
+ DarkTintColor is not null ||
+ !string.IsNullOrWhiteSpace(DarkFilename);
+
+ // Theme qualifier appended to output resource directories/filenames for platform generators
+ // (e.g. "" for the light variant, "-night" for the Android dark variant). Empty by default;
+ // each generator decides whether to honour it.
+ public string ThemeQualifier { get; set; } = string.Empty;
+
public bool IsVector => IsVectorFilename(Filename);
public bool IsAppIcon { get; set; }
@@ -112,6 +130,16 @@ namespace Microsoft.Maui.Resizetizer
if (info.Color is null && !string.IsNullOrEmpty(color))
throw new InvalidDataException($"Unable to parse color value '{color}' for '{info.Filename}'.");
+ var darkTintColor = image.GetMetadata("DarkTintColor");
+ info.DarkTintColor = Utils.ParseColorString(darkTintColor);
+ if (info.DarkTintColor is null && !string.IsNullOrEmpty(darkTintColor))
+ throw new InvalidDataException($"Unable to parse color value '{darkTintColor}' for '{info.Filename}'.");
+
+ var darkColor = image.GetMetadata("DarkColor");
+ info.DarkColor = Utils.ParseColorString(darkColor);
+ if (info.DarkColor is null && !string.IsNullOrEmpty(darkColor))
+ throw new InvalidDataException($"Unable to parse color value '{darkColor}' for '{info.Filename}'.");
+
if (bool.TryParse(image.GetMetadata("IsAppIcon"), out var iai))
info.IsAppIcon = iai;
@@ -138,6 +166,16 @@ namespace Microsoft.Maui.Resizetizer
info.MonochromeFilename = monoFileInfo.FullName;
}
+ var darkFile = image.GetMetadata("DarkFile");
+ if (!string.IsNullOrEmpty(darkFile))
+ {
+ var darkFileInfo = new FileInfo(darkFile);
+ if (!darkFileInfo.Exists)
+ throw new FileNotFoundException("Unable to find dark file: " + darkFileInfo.FullName, darkFileInfo.FullName);
+
+ info.DarkFilename = darkFileInfo.FullName;
+ }
+
// make sure the image is a foreground if this is an icon
if (info.IsAppIcon && string.IsNullOrEmpty(info.ForegroundFilename))
{
@@ -150,5 +188,41 @@ namespace Microsoft.Maui.Resizetizer
return info;
}
+
+ public ResizeImageInfo CreateDarkVariant(string? alias = null, string themeQualifier = "")
+ {
+ var hasDarkFile = !string.IsNullOrWhiteSpace(DarkFilename);
+
+ return new ResizeImageInfo
+ {
+ ItemSpec = ItemSpec,
+ Alias = alias ?? Alias,
+ Filename = hasDarkFile ? DarkFilename : Filename,
+ BaseSize = BaseSize,
+ Resize = Resize,
+ TintColor = DarkTintColor ?? (hasDarkFile ? null : TintColor),
+ Color = DarkColor ?? Color,
+ IsAppIcon = IsAppIcon,
+ ForegroundFilename = ForegroundFilename,
+ ForegroundScale = ForegroundScale,
+ MonochromeFilename = MonochromeFilename,
+ ThemeQualifier = themeQualifier,
+ };
+ }
+
+ // Emits the ordered set of ResizeImageInfo variants an Android splash generator should process.
+ // The light variant is always emitted first (ThemeQualifier = ""), followed by an optional dark
+ // variant tagged with ThemeQualifier = "-night" when dark metadata is present. Downstream generators
+ // use ThemeQualifier as a directory suffix — they do NOT need to know about "dark" as a concept.
+ public IEnumerable<ResizeImageInfo> GetAndroidSplashVariants(Func<string>? darkAliasFactory = null)
+ {
+ yield return this;
+
+ if (HasDarkMode)
+ {
+ var alias = darkAliasFactory?.Invoke();
+ yield return CreateDarkVariant(alias, themeQualifier: "-night");
+ }
+ }
}
}
Asset catalog components written as bare 1 values are interpreted by actool as 1/255. Emit a floating-point 1.0 value for fully opaque and full-intensity components, and cover the generated alpha values. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Full empirical device-validation resultsValidation is complete after pushing fix
Scenarios exercised
Issue found and fixedInitial iOS testing found that asset-catalog components serialized as bare iOS before fix - light launch pr35710-ios-before-fix-light-launch.mp4iOS before fix - dark launch pr35710-ios-dark-launch.mp4Final screenshotsAndroid - light (left), dark (right) iOS - light (left), dark (right) Final launch recordingsAndroid - light cold launch pr35710-ios-before-fix-dark-launch.mp4Android - dark cold launch pr35710-ios-light-launch.mp4iOS - light cold launch pr35710-android-dark-launch.mp4iOS - dark cold launch pr35710-android-light-launch.mp4Automated coverageResizetizer tests: 630 passed, 2 platform skips, 0 failed. Verdict: the PR works as intended on the tested Android emulator and iOS simulator after the pushed iOS serialization fix. |
🧪 Empirical validation — Android emulator + iOS simulatorI built and ran the .NET MAUI Sandbox app (this repo's designated hands-on test harness) from the PR branch ( Sandbox test configuration (added, not part of the PR)<MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#FFFFFF"
DarkFile="Resources\Splash\splash-dark.svg" DarkColor="#000000" DarkTintColor="#FFFFFF"
BaseSize="168,208" />Plus a Environment
Android — ✅ PassRebuilt Home → Sandbox → app cold-launches showing the themed splash → transitions to
I also diffed the generated iOS — ✅ Pass (core PR behavior), with one pre-existing/unrelated caveat noted below
|


Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Description
Adds additive light/dark splash screen metadata to
MauiSplashScreenwithout changing existing splash behavior:night-qualified splash image/color resources.UILaunchScreenasset catalog launch resources whenSupportedOSPlatformVersion >= 14.0.Testing
dotnet test src\\SingleProject\\Resizetizer\\test\\UnitTests\\Resizetizer.UnitTests.csproj --no-restore --filter "GenerateSplashAssetCatalogTests|GenerateSplashAndroidResourcesTests|GenerateSplashStoryboardTests|ResizeImageInfoTests" -p:UseSharedCompilation=falsedotnet build src\\SingleProject\\Resizetizer\\src\\Resizetizer.csproj --no-restore -v:minimal -p:UseSharedCompilation=false