Skip to content

Add MonochromeFile support for Android adaptive icons#34569

Merged
jfversluis merged 3 commits into
net11.0from
feature/android-monochrome-icon
Apr 9, 2026
Merged

Add MonochromeFile support for Android adaptive icons#34569
jfversluis merged 3 commits into
net11.0from
feature/android-monochrome-icon

Conversation

@jfversluis

Copy link
Copy Markdown
Member

Description

Fixes #22543

Android 13+ supports themed icons with three layers: background, foreground, and monochrome. MAUI currently hardcodes the monochrome layer to always reference the foreground drawable. This causes incorrect rendering when the foreground contains multiple colors — themed icons display the full-color foreground instead of a proper monochrome silhouette.

This PR adds a new MonochromeFile metadata property for MauiIcon, allowing developers to provide a dedicated monochrome image:

<MauiIcon Include="Resources/AppIcon/icon_bg.svg"
          ForegroundFile="Resources/AppIcon/icon_fg.svg"
          MonochromeFile="Resources/AppIcon/icon_mono.svg" />

Behavior

MonochromeFile specified? Monochrome layer Behavior
Yes @mipmap/{name}_monochrome Separate monochrome PNG generated at each density
No @mipmap/{name}_foreground Backwards compatible — existing behavior preserved

Changes

  • ResizeImageInfo.cs — Added MonochromeFilename and MonochromeIsVector properties; parse MonochromeFile metadata from ITaskItem
  • AndroidAdaptiveIconGenerator.cs — Added ProcessMonochrome() method that generates monochrome PNGs at each density; two XML templates (with/without monochrome); removed stale XML caching that prevented incremental updates
  • ResizetizeImagesTests.cs — 3 new tests:
    • AppIconWithMonochromeFileGeneratesMonochromeLayer — verifies monochrome PNGs and XML reference
    • AppIconWithoutMonochromeFileFallsBackToForeground — verifies backwards compatibility
    • AppIconMonochromeLayerHasCorrectSize — verifies density-correct sizing

Testing

  • 571 Resizetizer unit tests pass (3 new + 568 existing, 0 regressions)
  • 20 pre-existing failures in SkiaSharpAppIconToolsTests are unrelated

Copilot AI review requested due to automatic review settings March 19, 2026 13:04
@github-actions

github-actions Bot commented Mar 19, 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 -- 34569

Or

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

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

Adds opt-in support for Android adaptive icon monochrome layers in the Resizetizer pipeline, allowing MAUI apps to provide a dedicated monochrome source image for Android 13+ themed icons while preserving existing behavior when not provided.

Changes:

  • Parse new MonochromeFile metadata into ResizeImageInfo and expose vector detection for it.
  • Generate {name}_monochrome.png at all app icon part densities when MonochromeFile is provided, and emit adaptive icon XML referencing that layer.
  • Add unit tests validating monochrome generation, backward-compatible fallback behavior, and expected output sizing.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/SingleProject/Resizetizer/src/ResizeImageInfo.cs Adds MonochromeFilename/MonochromeIsVector and parses MonochromeFile item metadata.
src/SingleProject/Resizetizer/src/AndroidAdaptiveIconGenerator.cs Generates monochrome app icon part PNGs and updates adaptive icon XML to reference either monochrome or foreground.
src/SingleProject/Resizetizer/test/UnitTests/ResizetizeImagesTests.cs Adds tests for monochrome layer generation, fallback behavior, and size correctness.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread src/SingleProject/Resizetizer/src/AndroidAdaptiveIconGenerator.cs Outdated
@jfversluis jfversluis requested a review from mattleibow March 19, 2026 13:11
@jfversluis jfversluis added t/breaking 💥 area-single-project Splash Screen, Multi-Targeting, MauiFont, MauiImage, MauiAsset, Resizetizer platform/android labels Mar 19, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

🚨 API change(s) detected @davidortinau FYI

@jfversluis jfversluis left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch — replaced the unconditional File.WriteAllText with a WriteFileIfChanged helper that reads existing content first and only writes when it differs. This preserves incremental build timestamps while still picking up changes when MonochromeFile is added/removed. See c0fe91e.

@kubaflo

kubaflo commented Apr 3, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Monochrome icon support for Android — Adds MonochromeFile metadata to MauiIcon for Android 13+ themed icons. Clean implementation following existing ForegroundFile patterns. Good caching fix (WriteFileIfChanged).

Findings

Severity Finding
🔴 Critical MSBuild targets not updatedMonochromeFile is missing from two locations in Microsoft.Maui.Resizetizer.After.targets: (a) Multi-project path resolution (~line 241, where ForegroundFile gets GetFullPath) — relative paths will break in class library projects; (b) Build invalidation tracking (~line 328) — changing MonochromeFile metadata won't trigger a rebuild
🟡 Warning No test for FileNotFoundException when MonochromeFile points to nonexistent file
🟡 Warning No test for switching between monochrome/non-monochrome XML content (the exact scenario the WriteFileIfChanged caching fix addresses)
🟡 Warning ProcessMonochrome does not apply ForegroundScale — no MonochromeScale property exposed for sizing control
🔵 Info Good: Removed stale XML caching bug with WriteFileIfChanged
🔵 Info ProcessMonochrome follows existing ProcessForeground/ProcessBackground patterns cleanly

Overall Assessment

⚠️ Needs MSBuild targets update before merge — wire MonochromeFile through the GetMauiItems target (path resolution) and WriteLinesToFile (build invalidation), following the existing ForegroundFile pattern.

C# and test code are well-structured. Once the .targets file is updated, this is good to go.


Review performed by Copilot CLI

@jfversluis jfversluis added this to the .NET 11.0-preview4 milestone Apr 7, 2026
jfversluis and others added 3 commits April 7, 2026 16:14
Android 13+ supports themed icons with a separate monochrome layer.
Previously, MAUI always hardcoded the monochrome layer to reference
the foreground drawable, which causes incorrect rendering when the
foreground contains multiple colors.

Add MonochromeFile metadata to MauiIcon so developers can provide a
dedicated monochrome image for themed icons:

  <MauiIcon Include="icon_bg.svg"
            ForegroundFile="icon_fg.svg"
            MonochromeFile="icon_mono.svg" />

When MonochromeFile is provided:
- A separate _monochrome.png is generated at each density
- The adaptive-icon XML references @mipmap/{name}_monochrome

When MonochromeFile is omitted:
- Backwards compatible: foreground is used as monochrome (existing behavior)

Also removed the early-return cache in ProcessAdaptiveIcon that
prevented the XML from being regenerated when MonochromeFile changed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Avoid unnecessary timestamp updates on incremental builds by comparing
file content before writing. This preserves proper incremental build
behavior while still picking up MonochromeFile changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…uild invalidation

Add MonochromeFile to GetMauiItems target so relative paths are resolved
via GetFullPath for class library projects. Add MonochromeFile to the
WriteLinesToFile build invalidation tracking so changing the monochrome
file metadata triggers a rebuild.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jfversluis jfversluis force-pushed the feature/android-monochrome-icon branch from c0fe91e to 42310e1 Compare April 7, 2026 14:18
@kubaflo

kubaflo commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

Code Review — PR #34569

Independent Assessment

What this changes: Adds MonochromeFile metadata support to MauiIcon for Android adaptive icons. This allows specifying a dedicated monochrome SVG/PNG for the Android 13+ themed icon monochrome layer, instead of defaulting to the foreground drawable. Changes span Resizetizer (image processing), MSBuild targets (metadata plumbing), and 3 new unit tests.

Target branch: net11.0 — correct for new feature. Milestone .NET 11.0-preview4.

Reconciliation with PR Narrative

Agreement: ✅ Code matches description. When MonochromeFile is specified, a separate {name}_monochrome.png is generated at each density and the adaptive icon XML references it. Without MonochromeFile, existing behavior is preserved (foreground used as monochrome).


Findings

✅ Good — Fully backwards compatible

  • Without MonochromeFile: AdaptiveIconDrawableXml template is used → <monochrome android:drawable="@mipmap/{name}_foreground" /> — identical to current behavior
  • With MonochromeFile: AdaptiveIconDrawableWithMonochromeXml template → <monochrome android:drawable="@mipmap/{name}_monochrome" />
  • Test AppIconWithoutMonochromeFileFallsBackToForeground explicitly verifies backwards compatibility ✅

✅ Good — ProcessMonochrome follows established patterns

The new method mirrors ProcessBackground/ProcessForeground exactly:

  • Same Utils.FileExists → timestamp check → SkiaSharpTools.CreateResize flow
  • Same DpiPath.Android.AppIconParts density iteration
  • Same incremental build optimization (skip if destination is newer than source)
  • Correctly omits TintColor and ForegroundScale — monochrome icons are silhouettes where the system applies its own theming

✅ Good — Fixed stale XML caching bug

The old ProcessAdaptiveIcon had an early return:

if (File.Exists(destination) && File.Exists(roundDestination))
    return; // Never updated once generated!

This meant adding MonochromeFile to an existing project would NOT update the adaptive icon XML. The new code uses WriteFileIfChanged — always checks content, only writes when changed. This is both a correctness fix and incremental-build friendly.

✅ Good — MSBuild integration

  • MonochromeFile metadata correctly resolved via GetFullPath only when non-empty
  • Added to the incremental build inputs file (_ResizetizerInputsFile) ensuring metadata changes invalidate the build
  • MauiIcon items split into two conditions (with/without monochrome) to avoid GetFullPath on empty strings

✅ Good — Input validation

ResizeImageInfo.Parse throws FileNotFoundException if MonochromeFile is specified but doesn't exist — fails fast with a clear message.

✅ Good — Test coverage

  • AppIconWithMonochromeFileGeneratesMonochromeLayer — verifies PNGs generated + XML references monochrome drawable
  • AppIconWithoutMonochromeFileFallsBackToForeground — backwards compatibility
  • AppIconMonochromeLayerHasCorrectSize — correct density sizing (108×108 mdpi, 216×216 xhdpi)

💡 Observation — t/breaking label may be inaccurate

The PR is labeled t/breaking 💥 but the change is additive — MonochromeFile is optional with full backwards compatibility. The only behavioral change is the removal of the stale XML caching (which was a bug, not intentional behavior). Projects that don't specify MonochromeFile produce identical output. Consider whether this label is warranted.

💡 Suggestion — ProcessMonochrome unreachable else branch

if (monochromeExists)
    // ... resize from file
else
    // ... manufacture imaginary

The else branch creates a blank monochrome, but HasMonochromeFile (which gates ProcessMonochrome) requires MonochromeFilename to be non-empty, and Parse throws if the file doesn't exist. So the manufacture branch is unreachable in practice. It's harmless defensive code (file could be deleted between parse and build), but a comment noting this would prevent future confusion:

// Safety net: file validated during Parse, but could be deleted before build

ℹ️ Note — CI Status

No CI failures detected. Clean build.


Devil's Advocate

  • "Should ForegroundScale apply to monochrome too?" — No. Android's monochrome layer is a silhouette — it should fill the adaptive icon safe zone at full scale. The system applies theming and masking.
  • "Should TintColor apply to monochrome?" — No. The monochrome layer is meant to be a single-color silhouette. The system colorizes it based on the wallpaper. Applying a user tint would conflict.
  • "Could WriteFileIfChanged cause issues with byte-level encoding differences?"File.ReadAllText + string comparison is reliable for XML content. The templates are constant strings with no platform-dependent encoding. The File.WriteAllText default UTF-8 encoding matches.
  • "What about raster (PNG/JPEG) monochrome files?"MonochromeIsVector correctly delegates to IsVectorFilename, so both vector (SVG) and raster inputs are handled by SkiaSharpTools.Create.

Verdict: LGTM

Confidence: high
Summary: Clean, well-structured Resizetizer feature that follows existing patterns exactly. Fully backwards compatible. Fixes a latent XML caching bug. Good test coverage for the feature, backwards compat, and sizing. Prior review feedback (incremental build timestamps) addressed with WriteFileIfChanged. Ready for merge.

Review performed by Copilot CLI using the code-review skill

@jfversluis jfversluis merged commit 6504b2c into net11.0 Apr 9, 2026
36 of 41 checks passed
@jfversluis jfversluis deleted the feature/android-monochrome-icon branch April 9, 2026 10:20
@github-actions github-actions Bot locked and limited conversation to collaborators May 10, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-single-project Splash Screen, Multi-Targeting, MauiFont, MauiImage, MauiAsset, Resizetizer platform/android t/breaking 💥

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants