[inflight regression] Android SwipeItem keeps stale implicit FontImageSource tint after BackgroundColor changes
- Dominant language
- C#
- Stars
- 23.3k
- Forks
- 2k
- Avg merge
- 1d 15h
- Merged PRs (30d)
- 290
Description
### Regression
Introduced by [PR #36271](https://github.com/dotnet/maui/pull/36271), merge commit `d31bd4e615c81445ea355c70c1eae2b25f1d7149`.
The missing dependent icon-tint refresh was also identified during review:
https://github.com/dotnet/maui/pull/36271#discussion_r3573537570
Current `main` still has the original #31917 behavior where this direct `BackgroundColor` mutation is not propagated to the native item. In `inflight/current`, the background and text now update, but the existing font-icon drawable does not. That introduces an internally inconsistent state where the icon can have the same color as its new background.
### Summary
On Android, an already-revealed `SwipeItem` can keep a stale implicit `FontImageSource` tint after its `BackgroundColor` changes.
The native background and text color update correctly. The existing icon drawable keeps the contrast color derived from the old background. A light-to-dark change therefore produces a black icon on a black background; the reverse direction can produce white on white.
The action remains clickable, but its icon can become effectively invisible.
### Repro Project
Branch:
https://github.com/AdamEssenmacher/maui/tree/repro/android-swipeitem-implicit-font-icon-tint-20260714
Project:
https://github.com/AdamEssenmacher/maui/tree/repro/android-swipeitem-implicit-font-icon-tint-20260714/src/Controls/samples/Controls.Sample.Sandbox
The repro uses an implicit-color `FontImageSource`, opens the action programmatically, and directly measures the rendered native Android compound drawable. It includes an explicit Source-remap button as a control.
### Steps To Reproduce
```bash
git clone https://github.com/AdamEssenmacher/maui.git maui-swipeitem-tint-repro
cd maui-swipeitem-tint-repro
git checkout repro/android-swipeitem-implicit-font-icon-tint-20260714
./build.sh -restore
dotnet build Microsoft.Maui.BuildTasks.slnf -v:minimal
dotnet build src/Controls/samples/Controls.Sample.Sandbox/Maui.Controls.Sample.Sandbox.csproj \
-f net10.0-android \
-p:RuntimeIdentifier=android-arm64 \
-p:EmbedAssembliesIntoApk=true \
--no-restore -v:minimal
adb install -r \
artifacts/bin/Maui.Controls.Sample.Sandbox/Debug/net10.0-android/android-arm64/com.microsoft.maui.sandbox-Signed.apk
adb shell am start -W -n com.microsoft.maui.sandbox/.MainActivity
```
On a short emulator, `adb shell wm size 480x1200` can be used temporarily so every control is visible without scrolling. Restore it afterward with `adb shell wm size reset`.
1. Wait for the app to open the `SwipeItem` action automatically.
2. Confirm the initial report says `RESULT: MATCH` for the white background and black icon.
3. Without scrolling, closing, or reopening the action, tap **2. Set SwipeItem background black**.
4. Observe the action and the native color report.
5. Tap **3. Force icon Source remap** as the control case.
Keeping the action attached is important. Closing/reopening or otherwise reattaching it calls `UpdateSize()`, which can reapply the Source and mask the defect.
### Expected Behavior
When the background changes to black, a `FontImageSource` with no explicit `Color` should be retinted using the new background-derived text color.
The text and icon should both become white.
### Actual Behavior
After changing only `SwipeItem.BackgroundColor`:
```text
BLACK / BackgroundColor changed only
SwipeItem background=#000000
expected icon=WHITE
native icon=#000000 luma=0 -> BLACK
native text=#FFFFFF
RESULT: STALE / REGRESSION
```
The action background is black and its text is white, but the icon remains black and is not visibly distinguishable from the background.
Forcing only the Source mapper immediately corrects the same drawable:
```text
BLACK / after explicit Source remap
SwipeItem background=#000000
expected icon=WHITE
native icon=#FEFEFE luma=254 -> WHITE
native text=#FFFFFF
RESULT: MATCH
```
Resetting the background to white without another Source remap leaves that white icon stale on white, reproducing the same defect in the reverse direction.
### Validation
The repro does not infer icon color from a screenshot. It renders the native `TextView` compound drawable into a 96x96 transparent bitmap and computes an alpha-weighted RGB/luminance value from its visible pixels.
This separates the stale-tint problem from icon font loading, sizing, anti-aliasing, or screenshot interpretation.
### Cause
PR #36271 adds a `SwipeItem.BackgroundColorProperty` callback that calls:
```csharp
swipeItem.Handler?.UpdateValue(nameof(ISwipeItemMenuItem.Background));
```
Android `MapBackground` updates the native background and recomputes the `TextView` text color. However, the implicit `FontImageSource` tint is applied inside `SwipeItemMenuItemImageSourcePartSetter.SetImageSource()` from `item.GetTextColor()` only when the Source mapper runs.
The background callback does not invalidate Source, so the already-rendered drawable keeps the previous tint.
The Issue31917 regression test added by #36271 contains no `IconImageSource`, so it verifies background changes but cannot detect the stale dependent icon tint.
### User Impact
Confirmed trigger:
- Android `SwipeItem`
- `IconImageSource` is a `FontImageSource` with `Color == null`
- `BackgroundColor` changes while the action remains revealed/attached
- Source is not remapped at the same time
This can affect background-only bindings, dynamic resources, custom theme services, or state-driven action colors. Icon-only actions can appear blank. Actions with text remain operable but look broken and lose the icon's visual affordance.
The problem normally self-heals after the action is closed/reopened. A full `UserAppTheme` switch in this repro also remapped Source and did not reproduce, so this report is specifically about background-only changes while the native action remains attached.
There is no observed crash, command failure, data loss, or material performance impact.
### Suggested Fix Direction
When a SwipeItem background change alters the derived foreground color, refresh or retint Source when:
```csharp
item.Source is IFontImageSource { Color: null }
```
This should avoid unnecessary reloads for PNG/SVG sources and explicitly colored font icons. Add focused coverage with an actual implicit-color `FontImageSource`; the current text/background-only screenshot test does not cover this dependency.
Android is confirmed. The iOS/Mac Catalyst implicit font-icon path also derives tint during Source mapping and should be validated separately.
### Environment
- `dotnet/maui` `inflight/current`: `d31bd4e615c81445ea355c70c1eae2b25f1d7149`
- Android 13 / API 33
- arm64 `Maui_Tiny_API33` emulator
- `net10.0-android`
- MAUI build tasks and repro APK both built successfully
Contributor guide
Research direction
Start with the SwipeItem.BackgroundColorProperty callback and Android MapBackground, then trace SwipeItemMenuItemImageSourcePartSetter.SetImageSource() to see when implicit FontImageSource tint is derived. Run the linked Sandbox repro and review the Issue31917 regression test; done means an attached action retints an implicit-color font icon after a background change without affecting explicit-color or image sources.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, csharp
- Domain
- frontend, mobile
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100