Skip to content

Add themed splash screen support - #35710

Merged
kubaflo merged 13 commits into
net11.0from
jfversluis/splash-theme-api
Aug 6, 2026
Merged

Add themed splash screen support#35710
kubaflo merged 13 commits into
net11.0from
jfversluis/splash-theme-api

Conversation

@jfversluis

Copy link
Copy Markdown
Member

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 MauiSplashScreen without changing existing splash behavior:

<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.

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>
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 35710

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35710"

@github-actions github-actions Bot added the area-tooling XAML & C# Hot Reload, XAML Editor, Live Visual Tree, Live Preview, Debugging label Jun 2, 2026
@kubaflo

kubaflo commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

/review -b feature/enhanced-reviewer

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expert Review — 1 findings

See inline comments for details.

@MauiBot MauiBot added s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels Jun 3, 2026
MauiBot

This comment was marked as outdated.

@kubaflo

kubaflo commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 3 pipeline(s).

@kubaflo kubaflo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you check the ai's suggestions?

@kubaflo

kubaflo commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

/review -b feature/enhanced-reviewer -p android

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expert Review — 4 findings

See inline comments for details.

Comment thread src/SingleProject/Resizetizer/src/GenerateSplashAssetCatalog.cs
@MauiBot MauiBot added s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates and removed s/agent-fix-win AI found a better alternative fix than the PR labels Jun 7, 2026
MauiBot

This comment was marked as outdated.

@kubaflo kubaflo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please check the ai's suggestions?

Copilot AI review requested due to automatic review settings August 3, 2026 15:41
@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Aug 3, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

Suppressed comments (1)

src/SingleProject/Resizetizer/src/nuget/buildTransitive/Microsoft.Maui.Resizetizer.After.targets:475

  • _MauiHasSplashScreens is derived from @(MauiSplashScreen) count, but downstream tasks now consume @(_MauiSplashScreenWithHashes). If ResizetizeCollectItems is 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 _MauiHasSplashScreens on @(_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
Copilot AI review requested due to automatic review settings August 3, 2026 16:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

Suppressed comments (1)

src/SingleProject/Resizetizer/test/UnitTests/MauiResizetizerTargetsTests.cs:100

  • Typo in test name: Resizetize... should be Resizetizer... 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

@MauiBot MauiBot added the s/agent-changes-requested AI agent recommends changes - found a better alternative or issues label Aug 3, 2026
@MauiBot

This comment has been minimized.

@MauiBot MauiBot removed the s/agent-review-in-progress AI review is currently running for this PR label Aug 3, 2026
kubaflo pushed a commit that referenced this pull request Aug 3, 2026
…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
@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Aug 3, 2026
@MauiBot MauiBot added s/agent-fix-win AI found a better alternative fix than the PR and removed s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates labels Aug 3, 2026

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review Summary

@jfversluis — new AI review results are available based on this last commit: 2e19455.

Gate Inconclusive Confidence Low Platform Android


🗂️ 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 ⚠️ ENV ERROR marks below are infrastructure, not test failures — this is not a problem with your PR. Comment /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 ⚠️ ENV ERROR
🧪 GenerateSplashAssetCatalogTests GenerateSplashAssetCatalogTests 🛠️ BUILD ERROR ⚠️ ENV ERROR
🧪 GenerateSplashStoryboardTests GenerateSplashStoryboardTests 🛠️ BUILD ERROR ⚠️ ENV 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.cs
  • src/SingleProject/Resizetizer/src/DpiPath.cs
  • src/SingleProject/Resizetizer/src/GenerateSplashAndroidResources.cs
  • src/SingleProject/Resizetizer/src/GenerateSplashStoryboard.cs
  • src/SingleProject/Resizetizer/src/ResizeImageInfo.cs
  • src/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 MauiSplashScreen dark metadata support: Android night-qualified resources and Apple UILaunchScreen asset-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. ⚠️ INCONCLUSIVE (Gate) 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 --required unavailable due missing auth; public check-runs for head 2e19455 still show pending/in-progress checks.
  • Classification: undetermined.
  • Action taken: loaded azdo-build-investigator; ci-analysis unavailable 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.
  • DarkFile relative path: normalized through _MauiSplashScreenWithHashes before task use.
  • Multiple splash items: first item drives metadata, matching generators’ FirstOrDefault().
  • Resize=false raster/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. ⚠️ BLOCKED 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. ⚠️ INCONCLUSIVE (Gate) 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 ⚠️ INCONCLUSIVE gate 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 ⚠️ INCONCLUSIVE gate Equivalent to pr; the expert reviewer produced no actionable feedback, so no sandbox changes were applied.
4 try-fix-1 ⚠️ BLOCKED 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");
+			}
+		}
 	}
 }

@MauiBot MauiBot removed the s/agent-review-in-progress AI review is currently running for this PR label Aug 3, 2026
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>
Copilot AI review requested due to automatic review settings August 5, 2026 15:57
@kubaflo

kubaflo commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Full empirical device-validation results

Validation is complete after pushing fix feaac60c8b3.

Platform Configuration Result
Android Pixel 9 emulator, Android 16 / API 36 (emulator-5554) PASS
iOS iPhone 16 simulator, iOS 18.5; SupportedOSPlatformVersion=15.0 PASS after fix

Scenarios exercised

  • Custom light SVG/background/tint together with distinct DarkFile, DarkColor, and DarkTintColor.
  • Android live Light -> Dark -> Light switching, persisted night mode, and light/dark cold launches.
  • Android generated values-night and drawable-night-* resources.
  • iOS Light/Dark appearance switching and cold launches through the UILaunchScreen asset-catalog path.
  • iOS named light/dark image and color assets plus generated plist entries.

Issue found and fixed

Initial iOS testing found that asset-catalog components serialized as bare "1" were compiled by actool as 1/255, leaving the launch background nearly transparent. Commit feaac60c8b3 emits "1.0" for full-intensity/opaque components and adds coverage.

iOS before fix - light launch

pr35710-ios-before-fix-light-launch.mp4

iOS before fix - dark launch

pr35710-ios-dark-launch.mp4

Final screenshots

Android - light (left), dark (right)

Android light and dark themed splash screens

iOS - light (left), dark (right)

iOS light and dark themed splash screens

Final launch recordings

Android - light cold launch

pr35710-ios-before-fix-dark-launch.mp4

Android - dark cold launch

pr35710-ios-light-launch.mp4

iOS - light cold launch

pr35710-android-dark-launch.mp4

iOS - dark cold launch

pr35710-android-light-launch.mp4

Automated coverage

Resizetizer 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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.

@PureWeen

PureWeen commented Aug 5, 2026

Copy link
Copy Markdown
Member

🧪 Empirical validation — Android emulator + iOS simulator

I built and ran the .NET MAUI Sandbox app (this repo's designated hands-on test harness) from the PR branch (jfversluis/splash-theme-api @ feaac60) on a real Android emulator and a real iOS simulator, configured with a themed MauiSplashScreen (light + dark file/color/tint), and recorded video of the actual splash screen behavior on both platforms.

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 SplashTestLabel on MainPage ("PR 35710 - Themed Splash Screen Test") so a successful splash→page transition is unambiguous on video. None of this touches product code — see "Sandbox/tooling changes" below for the full list.

Environment

  • Host: macOS, dotnet/maui repo checked out at PR branch tip.
  • Android: emulator (API 34, arm64-v8a), deployed/launched via .github/scripts/BuildAndRunSandbox.ps1 -Platform android, driven through the mandated Appium smoke test, plus manual adb shell screenrecord for splash-specific capture (light/dark system theme toggled via adb shell cmd uimode night yes|no).
  • iOS: iPhone 11 Pro simulator (iOS 18-class runtime), deployed via BuildAndRunSandbox.ps1 -Platform ios, plus manual xcrun simctl io recordVideo for splash-specific capture (light/dark toggled via xcrun simctl ui <udid> appearance light|dark).
  • The Appium suite only asserts app launch + a page landmark; it can't itself watch the splash frame, so screen recording was the only way to empirically confirm the correct themed asset renders — that's the reason for the manual adb/xcrun step in addition to the standard script run.

Android — ✅ Pass

Rebuilt Home → Sandbox → app cold-launches showing the themed splash → transitions to MainPage.

I also diffed the generated drawable/drawable-night resources and unzip -l'd the signed APK to confirm both light and dark assets are actually packaged, not just present in intermediate output.

iOS — ✅ Pass (core PR behavior), with one pre-existing/unrelated caveat noted below

⚠️ Two environment/tooling gotchas worth documenting (unrelated to this PR's code, but cost real time to diagnose)

  1. .buildtasks/ staleness: the gitignored _MauiBuildTasksLocation cache only refreshes when you build src/SingleProject/Resizetizer/src/Resizetizer.csproj directly — building only the Sandbox app will silently keep using a stale Resizetizer task DLL. Needed dotnet build-server shutdown + /nodeReuse:false too, since the build server also caches the task assembly in-memory across dotnet build invocations.
  2. Directory.Build.targets unconditionally overrides SupportedOSPlatformVersion=13.0 for iOS via a static PropertyGroup that's imported after any project's own .csproj body (standard MSBuild import ordering), silently clobbering any static attempt in a project file to raise it. This blocked the themed-splash iOS path entirely (_MauiAppleThemedSplashSupported requires >= 14.0) until worked around with a target-scoped PropertyGroup inside a custom target (BeforeTargets="ProcessMauiSplashScreens"). This has a blast radius beyond this PR — any sample/test in the repo wanting iOS 14+ features would hit it silently. Might be worth a Directory.Build.targets fix or at least a note in the sandbox docs.

Sandbox/tooling changes made for this validation (all disclosed, none are product code)

  • Maui.Controls.Sample.Sandbox.csproj: themed MauiSplashScreen items, CS0618 added to NoWarn (the PR's new API is [Obsolete]-free but a nearby dependency emitted CS0618 unrelated to this change), and the target-scoped SupportedOSPlatformVersion workaround described above.
  • MainPage.xaml/.xaml.cs: added a SplashTestLabel purely to make the splash→page transition unambiguous on video.
  • New Resources/Splash/splash-dark.svg (a crescent-moon asset) for a visually distinct dark-mode splash.
  • .github/scripts/BuildAndRunSandbox.ps1: fixed a net10.0-*net11.0-* TFM glob mismatch (script targeted the wrong TFM for this branch's net11.0 base, unrelated to this PR's diff).

No product code under src/SingleProject/Resizetizer/** (the PR's actual diff) was modified in the final state — I did temporarily add/revert diagnostic logging there twice during investigation (confirmed clean via git diff --stat and DLL byte-inspection each time).

Verdict

The PR works as intended. Themed splash screens (light/dark background color + tint + separate dark asset) render correctly on Android, confirmed via video for both light and dark. On iOS, the color-theming and asset-catalog generation are both correct and confirmed via video + static asset inspection; the logo-image-not-rendering behavior observed is a pre-existing, unrelated MAUI/iOS platform limitation, not something this PR introduces or regresses.

@kubaflo
kubaflo merged commit 3624db4 into net11.0 Aug 6, 2026
29 of 32 checks passed
@kubaflo
kubaflo deleted the jfversluis/splash-theme-api branch August 6, 2026 11:53
@github-actions github-actions Bot added this to the .NET 11.0-preview7 milestone Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-tooling XAML & C# Hot Reload, XAML Editor, Live Visual Tree, Live Preview, Debugging s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-win AI found a better alternative fix than the PR s/agent-gate-failed AI could not verify tests catch the bug s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants