Repository navigation
[net11] [Android] Implemented material3 support for CarouselView - #35640
Dhivya-SF4094 wants to merge 12 commits into
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 35640Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35640" |
|
/review -b feature/refactor-copilot-yml |
1 similar comment
|
/review -b feature/refactor-copilot-yml |
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 6 findings
See inline comments for details.
This comment has been minimized.
This comment has been minimized.
kubaflo
left a comment
There was a problem hiding this comment.
Could you check the ai's suggestions?
4b948d7 to
90d2165
Compare
|
/review -b feature/enhanced-reviewer -p android |
This comment has been minimized.
This comment has been minimized.
922ec1a to
101cb69
Compare
|
/review rerun |
|
/review -b feature/enhanced-reviewer -p android |
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 6 findings
See inline comments for details.
|
/review -b feature/enhanced-reviewer -p android |
@kubaflo We can hold this for now. We need to discuss the Material Carousel behavior further, as the new Material implementation will not support some existing MAUI APIs that users may expect to work. Please hold off on merging this PR until we confirm the behavior. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
kubaflo
left a comment
There was a problem hiding this comment.
Could you please resolve the merge conflicts?
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
could you please resolve conflicts |
… when Loop = True
… CarouselView Handler2 classes
|
Resolved conflicts in this PR, Please review once. |
This comment has been minimized.
This comment has been minimized.
|
Note 🔍
|
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 1 findings
See inline comments for details.
| // Subclasses backed by a layout manager that has no virtual-range concept | ||
| // (e.g. Material's CarouselLayoutManager) can override this to force the | ||
| // non-loop code paths regardless of the public Carousel.Loop value. | ||
| protected virtual bool IsLoopEnabled => Carousel?.Loop == true; |
There was a problem hiding this comment.
🔍 AI-Generated Review (multi-model)
[major] Logic and Correctness — The new IsLoopEnabled abstraction is only partially applied: later collection-change paths in this same type still branch on Carousel.Loop directly (for example CollectionItemsSourceChanged/HandleReplaceAction). In the Material3 subclass (MauiCarouselRecyclerView2) where IsLoopEnabled is forced false, a Loop=true CarouselView can still enter loop-only recentering on collection mutations and jump to virtual-range indexes against a non-loop adapter, producing incorrect post-update position/current-item behavior.
MauiBot
left a comment
There was a problem hiding this comment.
AI Review Summary
@Dhivya-SF4094 — new AI review results are available based on commit
e7e479a.
🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix
Gate Result: ⚠️ INCONCLUSIVE
Platform: ANDROID · Base: net11.0 · Merge base: d73fb56e
🩺 Pre-existing build failure (not the fix) — both the without-fix baseline AND the with-fix build fail with a build error, so the PR's fix is NOT the cause. This is a broken main/merge-base or a toolchain/environment failure (e.g. an ILLink IL1012 trimmer crash). The gate cannot verify anything; investigate the build environment rather than the PR.
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/37.2.0-rc.2.56/tools/Xamarin.Android.Tooling.targets(55,5): error XARSD7028: System.IO.FileNotFoundException: Could not load file or assem...
| Test | Without Fix (expect FAIL) | With Fix (expect PASS) |
|---|---|---|
🖥️ Material3CarouselViewFeatureTests Material3CarouselViewFeatureTests |
🛠️ BUILD ERROR | 🛠️ BUILD ERROR |
🔴 Without fix — 🖥️ Material3CarouselViewFeatureTests: 🛠️ BUILD ERROR · 166s
Error-relevant lines (filtered from the build log):
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/37.2.0-rc.2.56/tools/Xamarin.Android.Tooling.targets(55,5): error XARSD7028: System.IO.FileNotFoundException: Could not load file or assembly 'System.Diagnostics.TraceSource, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified. [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net11.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/37.2.0-rc.2.56/tools/Xamarin.Android.Tooling.targets(55,5): error XARSD7028: [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net11.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/37.2.0-rc.2.56/tools/Xamarin.Android.Tooling.targets(55,5): error XARSD7028: File name: 'System.Diagnostics.TraceSource, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net11.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/37.2.0-rc.2.56/tools/Xamarin.Android.Tooling.targets(55,5): error XARSD7028: Requested by: netstandard, Version=2.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51 [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net11.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/37.2.0-rc.2.56/tools/Xamarin.Android.Tooling.targets(55,5): error XARSD7028: --> Microsoft.Deployment.DotNet.Releases, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net11.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/37.2.0-rc.2.56/tools/Xamarin.Android.Tooling.targets(55,5): error XARSD7028: --> System.Private.CoreLib, Version=11.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net11.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/37.2.0-rc.2.56/tools/Xamarin.Android.Tooling.targets(55,5): error XARSD7028: at Xamarin.Android.Tasks.ResolveSdks.RunTask() [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net11.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/37.2.0-rc.2.56/tools/Xamarin.Android.Tooling.targets(55,5): error XARSD7028: at Microsoft.Android.Build.Tasks.AndroidTask.Execute() [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net11.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/37.2.0-rc.2.56/tools/Xamarin.Android.Tooling.targets(55,5): error XARSD7028: [cached failure] Failed to open file '/home/vsts/work/1/s/.dotnet/shared/Microsoft.NETCore.App/11.0.0-rc.2.26467.104/System.Diagnostics.TraceSource.dll'. The system cannot find the file specified. [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net11.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/37.2.0-rc.2.56/tools/Xamarin.Android.Tooling.targets(55,5): error XARSD7028: (0x80070002) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net11.0-android]
Build FAILED.
🟢 With fix — 🖥️ Material3CarouselViewFeatureTests: 🛠️ BUILD ERROR · 145s
Error-relevant lines (filtered from the build log):
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/37.2.0-rc.2.56/tools/Xamarin.Android.Tooling.targets(55,5): error XARSD7028: System.IO.FileNotFoundException: Could not load file or assembly 'System.Diagnostics.TraceSource, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified. [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net11.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/37.2.0-rc.2.56/tools/Xamarin.Android.Tooling.targets(55,5): error XARSD7028: [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net11.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/37.2.0-rc.2.56/tools/Xamarin.Android.Tooling.targets(55,5): error XARSD7028: File name: 'System.Diagnostics.TraceSource, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net11.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/37.2.0-rc.2.56/tools/Xamarin.Android.Tooling.targets(55,5): error XARSD7028: Requested by: netstandard, Version=2.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51 [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net11.0-android]
This Gate section was shortened to keep every required review section visible. Full details remain available in the pipeline build artifacts.
📋 Pre-Flight — Context & Validation
Issue: #35637 - [Android] Implement material3 support for CarouselView
PR: #35640 - [net11] [Android] Implemented material3 support for CarouselView
Platforms Affected: Android (net11.0, Material 3 path)
Files Changed: 10 implementation/API files, 1 UI test file, 1 repository-instruction file (1,383 additions, 74 deletions)
Key Findings
- The PR conditionally registers
Items2.CarouselViewHandler2for Android whenRuntimeFeature.IsMaterial3Enabledis true. The new handler uses Material'sCarouselLayoutManagerwithFullScreenCarouselStrategy. - Direct recycler children are wrapped in
MaskableFrameLayout; custom templated/text holders, an adapter, and a Material-specific scroll listener provide binding, sizing, snapping, position, and empty-view behavior. - The PR avoids feeding the legacy approximately 16,384-item virtual loop range to
CarouselLayoutManagerby exposing the real item count and forcing non-loop code paths. It explicitly leavesLoop,PeekAreaInsets,LinearItemsLayout.ItemSpacing, and per-item visual states unsupported on this path. - The linked issue asks broadly for Material 3 presentation and interaction while retaining consistency with MAUI. A maintainer asked that merge be held while unsupported existing
CarouselViewAPIs were discussed. - A prior August review tried bounded edge-jump wrapping with the real item count and reported the focused CarouselView regression scope passing. New candidates must not repeat either that edge-jump mechanism or the PR's force-non-loop mechanism.
- The current gate is INCONCLUSIVE, not failing: both baseline and PR builds stop in the Android SDK before tests because
System.Diagnostics.TraceSource.dllis missing. The PR remains unverified by this run. - The mechanical regression cross-reference is clean. Detected UI categories are
CollectionViewandMaterial3. - The PR includes six new production files.
try-fixmust honorEstablishBrokenBaseline.ps1; if its baseline state reports anyNewFiles, the attempt must be recorded asBlockedwithout editing rather than bypassing restoration safeguards.
Code Review Summary
Verdict: SKIPPED
Confidence: N/A
Errors: 0 | Warnings: 0 | Suggestions: 0
Key code review findings:
- Dedicated expert review is intentionally reserved for STEP 5b; STEP 5a gathered context and inspected the diff only.
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #35640 | Register a Material-specific Items2 handler, wrap all item roots in MaskableFrameLayout, use CarouselLayoutManager, and disable unsupported looping by exposing the real item count. |
10 implementation/API files | Build environment failed before the focused Android UI tests could run. |
Primary test command: pwsh .github/scripts/BuildAndRunHostApp.ps1 -Platform android -TestFilter "Material3CarouselViewFeatureTests"
Additional mandatory regression tests: None were supplied for STEP 5a; do not run category-wide or full suites.
🔬 Code Review — Deep Analysis
Expert Evaluation — Raw PR
Verdict: NEEDS_CHANGES
Confidence: medium
Independent Assessment
The submitted Android Material 3 path registers Items2.CarouselViewHandler2, wraps every direct recycler child in a Material MaskableFrameLayout, uses CarouselLayoutManager with FullScreenCarouselStrategy, and exposes the real source count instead of MAUI's approximately 16,384-position virtual loop range. The approach addresses the reported Material layout/GC failure, but intentionally does not implement Loop, PeekAreaInsets, LinearItemsLayout.ItemSpacing, or per-item visual states.
Blocking Finding
- [major] Logic and Correctness — loop abstraction is applied inconsistently.
MauiCarouselRecyclerView2.IsLoopEnabledreturnsfalse, but collection-change branches inherited fromMauiCarouselRecyclerViewstill inspectCarousel.Loopdirectly. BecauseCarouselView.Loopdefaults totrue, an add/remove/replace can enter virtual-loop recentering or loop-specific rebind behavior whileCarouselViewAdapter2exposes only the real item count. The resulting virtual target/index assumptions are incompatible with the Material adapter and can leavePosition/CurrentItemor the displayed item incorrect after a mutation. The expert finding is anchored atsrc/Controls/src/Core/Handlers/Items/Android/MauiCarouselRecyclerView.cs:127.
Test and Review Evidence
- The trusted Gate is INCONCLUSIVE, not failed: both baseline and PR builds stopped before test execution because the Android SDK could not load
System.Diagnostics.TraceSource.dll. - The regression cross-reference is CLEAN with no overlap or revert findings.
- The change is platform-specific handler/UI plumbing in the frequently regressed CarouselView family, so confidence is capped at medium by blast radius and further reduced by the unexecuted focused Android tests.
- Live required-check and prior-review queries were unavailable through unauthenticated
gh; this does not change the code-level finding.
Required Refinement
Apply the IsLoopEnabled abstraction consistently to every inherited path whose behavior assumes the virtual loop adapter, then run the single required focused command in the authorized pr-plus-reviewer sandbox. No broader redesign or full-suite validation is warranted in this pass.
🛠️ Try-Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | gpt-5.3-codex try-fix |
Use the Material handler only for non-looping carousels; retain the legacy Android handler for Loop=true. |
0 files | Harness-owned dirty worktree prevented baseline establishment; no edit or test was permitted. | |
| 2 | gpt-5.6-sol try-fix |
Keep Material active and implement looping through a rotating three-slot logical adapter window. | 0 files | The same baseline-safety block prevented implementation and testing. | |
| PR | PR #35640 | Register a Material-specific Items2 handler, use CarouselLayoutManager with maskable item roots, expose the real item count, and force non-loop behavior. |
10 implementation/API files | Both baseline and PR builds failed in the Android SDK because System.Diagnostics.TraceSource.dll was missing. |
Candidate 1 Narrative
Candidate 1 avoids both the PR's force-non-loop behavior and the previously tried edge-jump mechanism. It keeps the approximately 16,384-item legacy virtual range away from Material's layout manager by selecting the existing legacy handler for looped carousels and reserving Material's handler for explicitly non-looping instances. The approach could not be implemented because EstablishBrokenBaseline.ps1 rejected the pre-existing harness changes before creating a baseline. It therefore remains unverified rather than failed.
Candidate 2 Narrative
Candidate 2 also avoids every earlier mechanism. It retains the Material handler for both loop modes but gives CarouselLayoutManager only three physical slots. A logical anchor maps those slots to previous/current/next source items and rotates after each settled page, keeping the landed item centered without a real-list edge jump. This is more behaviorally complete than disabling loop but materially more complex: source counts below three, mutations, directionality, gesture settling, and programmatic position synchronization all need focused validation. Baseline establishment failed before any edit or test, so this candidate is also unverified rather than failed.
Comparison
| Candidate | Main Advantage | Main Risk |
|---|---|---|
| 1 | Reuses mature legacy loop behavior and keeps Material's layout manager isolated from virtual looping. | Default Loop=true would not use the Material carousel, and per-instance selection must occur inside a hybrid handler because DI handler registration is global. |
| 2 | Keeps the Material carousel active while bounding its physical adapter range and preserving conceptual loop semantics. | Introduces non-trivial logical/physical position mapping and collection-update edge cases. |
Cross-Pollination
Skipped by the STEP 5a hard execution contract.
Exhausted: Yes — the hard two-candidate budget was consumed; no follow-up or audit agents were launched.
Selected Fix: None — selection is limited to passing candidates, and both alternatives were blocked before implementation/testing while the PR gate was inconclusive. Candidate 1 is simpler; candidate 2 preserves more Material behavior, but neither has empirical evidence from this run.
📝 PR Finalize — Recommended Title & Description
Assessment: ✏️ Recommend updating — the current description incorrectly says all direct Carousel.Loop checks were replaced, while the submitted HEAD retains virtual-loop branches in collection and loop-update paths.
Recommended title
[net11] [Android] CarouselView: Add Material 3 CarouselLayoutManager support
Recommended description
### Description of Change
This pull request introduces a Material Design Carousel implementation in the Android handler code, focused on compatibility with Material's `CarouselLayoutManager` and its unique requirements. It adds adapter and view-holder classes that wrap carousel items in a `MaskableFrameLayout`, a Material-specific scroll listener, and extensibility points in the existing carousel base classes. It also includes targeted changes for loop-sensitive paths and EmptyView layout.
**Material Carousel Support and Adapter Enhancements:**
* Added `CarouselViewAdapter2`, a new adapter for `MauiCarouselRecyclerView2`, which wraps each item in a `MaskableFrameLayout` to satisfy Material's `CarouselLayoutManager` requirements. This adapter disables virtual looping and exposes the real item count to avoid the performance problems and infinite layout loops caused by giving Material's carousel the legacy virtual range. (`src/Controls/src/Core/Handlers/Items2/Android/Adapters/CarouselViewAdapter2.cs`)
* Introduced `MaskableCarouselItemViewHolder`, which manages MAUI content inside a `MaskableFrameLayout` and handles item templates and data binding for the Material carousel. (`src/Controls/src/Core/Handlers/Items2/Android/MaskableCarouselItemViewHolder.cs`)
* Added `CarouselViewOnScrollListener2`, which works directly with `CarouselLayoutManager`, does not depend on the legacy loop manager, and tracks visible items and snap positions. (`src/Controls/src/Core/Handlers/Items2/Android/CarouselViewOnScrollListener2.cs`)
* Raw string items (a `CarouselView` bound to plain strings with no `ItemTemplate`) are wrapped in a `MaskableFrameLayout` through `MaskableTextItemViewHolder`, because `CarouselLayoutManager` rejects direct RecyclerView children that are not `MaskableFrameLayout` instances.
* The Material handler renders `EmptyView` with a plain `LinearLayoutManager` instead of Material's `CarouselLayoutManager`. `MauiCarouselRecyclerView2.SelectLayoutManager` returns a `LinearLayoutManager` while `EmptyViewAdapter` is active, because Material's full-viewport keyline mask would otherwise collapse a normally sized EmptyView.
This pr-finalize section was shortened to keep every required review section visible. Full details remain available in the pipeline build artifacts.
🏁 Report — Final Recommendation
⚠️ Final Recommendation: REQUEST CHANGES
Winner: pr-plus-reviewer
The submitted PR has a concrete loop-state inconsistency: MauiCarouselRecyclerView2 overrides IsLoopEnabled to false, yet inherited collection and loop-update paths still branch on the public Carousel.Loop value, which defaults to true. pr-plus-reviewer is the only candidate that both removes that mismatch with a focused implementation and passes the required regression scope.
Comparative ranking
| Rank | Candidate | Implementation | Regression evidence | Assessment |
|---|---|---|---|---|
| 1 | pr-plus-reviewer |
Three Carousel.Loop checks that assume virtual adapter positions now use IsLoopEnabled. |
PASS — 17/17 focused Android tests. | Best balance of correctness, minimal blast radius, and empirical evidence; preserves the submitted Material handler design while fixing the expert finding. |
| 2 | pr |
Submitted Material CarouselLayoutManager handler with real item count and forced non-loop behavior. |
INCONCLUSIVE Gate; tests did not run because both baseline and PR builds hit the same missing System.Diagnostics.TraceSource.dll. |
Concrete implementation, but the expert verified that default Loop=true can still select virtual-loop collection-update behavior against the real-count adapter. |
| 3 | try-fix-1 |
Proposed legacy-handler fallback for Loop=true; no patch produced. |
BLOCKED before validation by baseline-safety checks. | Conceptually simple and preserves mature looping, but global handler registration makes per-instance routing non-trivial and there is no implementation evidence. |
| 4 | try-fix-2 |
Proposed rotating three-slot logical loop window; no patch produced. | BLOCKED before validation by baseline-safety checks. | Would preserve Material presentation and conceptual looping, but adds substantial mapping/mutation/RTL/settling complexity with no implementation or test evidence. |
No candidate recorded a regression-test failure. The sole passing candidate is ranked above every inconclusive or blocked candidate, as required.
Why the winner is not the raw PR
At CarouselView.Loop=true, the raw PR's Material adapter exposes only real positions, but post-mutation recentering and equal-count replacement can still follow code written for the approximately 16,384-position virtual adapter. This can target an out-of-range virtual position or apply the wrong rebind/update sequence, leaving the displayed item, Position, or CurrentItem inconsistent. Because this is a reachable correctness defect in the submitted code, the inconclusive Gate is not the reason for requesting changes.
Winning change and validation
pr-plus-reviewer changes only MauiCarouselRecyclerView.cs, consistently using the effective IsLoopEnabled capability in the three remaining virtual-loop behavior branches. The authorized sandbox identity matched /home/vsts/work/_temp/pr-35640-pr-plus-reviewer, the immutable baseline was 652532e489b328661aad2efc49f64361b880fb8e, and git diff --check passed before validation.
The exact required command ran once from that sandbox and passed all 17 selected tests. Its output and project paths are rooted in the candidate worktree, not the raw PR worktree. No full suite or repeated repair/retest cycle was run.
Remaining uncertainty
The selected suite exercises collection additions but does not isolate equal-count replacement or runtime loop toggling. Those paths remain reasoned rather than independently demonstrated; this does not outweigh the winner's direct correction and passing required scope, but it should inform human review.
📱 UI Tests — CollectionView,Material3
Detected UI test categories: CollectionView,Material3
❌ Deep UI tests — 595 passed, 87 failed, 3 skipped across 2 categories on platform-pool agent (replaces in-process counts above). 381 new snapshot tests need a baseline PNG (added separately by a maintainer — not a regression).
🧪 UI Test Execution Results (deep, platform pool)
| Category | Tests | Snapshot diffs |
|---|---|---|
CollectionView |
560/639 (79 ❌) | 1 diff PNG |
Material3 |
35/427 (8 ❌, 381 ⚠ new baseline, 3 skipped) | — |
🔍 AI analysis of failures — PR-related vs unrelated
🔍 AI-generated triage (GitHub Copilot CLI) — a heuristic judgement of whether each deep UI test failure is connected to this PR's changes. Verify before relying on it.
Mixed / uncertain: see the grouped assessment below.
- ℹ Uncertain — Material3 CarouselView coverage (unknown subset of 389 tests): Android Material3 CarouselView is the one Material3 area directly changed, including its handler and test file, but the supplied failures omit those rows and cannot distinguish a PR regression from the run-wide missing-baseline pattern.
- ● Unrelated — Non-Carousel Material3 snapshot failures (hundreds of tests): Button, Label, ImageButton, Editor, and DatePicker rows all report that Android baseline snapshots do not exist, which is a baseline-availability issue in controls the PR does not change.
- ● Unrelated — Material3 element-wait timeouts (at least 1 test): the DatePicker dialog timeout is a recognized nondeterministic UI-automation pattern and neither DatePicker nor its Android handler is touched by this PR.
- ● Unrelated — CollectionView failures (79 tests): the Appium click null references and scroll-index mismatches exercise CollectionView header/footer and scrolling paths, while the substantive Android changes are confined to CarouselView and the only
MauiRecyclerViewedit is whitespace.
Strongest signal: the failures shown span many untouched Material3 controls and overwhelmingly report absent Android baselines; only the omitted Material3 CarouselView rows warrant targeted follow-up.
❌ CollectionView — 79 failed tests
VerifyFooterTemplateWithItemsSourceObservableCollections25
System.NullReferenceException : Object reference not set to an instance of an object.
at UITest.Appium.HelperExtensions.Click(IUIElement element) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 449
at UITest.Appium.HelperExtensions.<>c__DisplayClass2_0.<Tap>b__0() in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 36
at UITest.Appium.HelperExtensions.<>c__DisplayClass189_0.<RunWithTimeout>b__0() in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 3136
at System.Threading.Tasks.Task`1.InnerInvoke()
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Tas
...
VerifyHeaderStringWithItemsSourceObservableCollection5
System.NullReferenceException : Object reference not set to an instance of an object.
at UITest.Appium.HelperExtensions.Click(IUIElement element) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 449
at UITest.Appium.HelperExtensions.<>c__DisplayClass2_0.<Tap>b__0() in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 36
at UITest.Appium.HelperExtensions.<>c__DisplayClass189_0.<RunWithTimeout>b__0() in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 3136
at System.Threading.Tasks.Task`1.InnerInvoke()
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Tas
...
VerifyFooterStringWithItemsLayoutVerticalGrid
System.NullReferenceException : Object reference not set to an instance of an object.
at UITest.Appium.HelperExtensions.Click(IUIElement element) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 449
at UITest.Appium.HelperExtensions.<>c__DisplayClass2_0.<Tap>b__0() in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 36
at UITest.Appium.HelperExtensions.<>c__DisplayClass189_0.<RunWithTimeout>b__0() in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 3136
at System.Threading.Tasks.Task`1.InnerInvoke()
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Tas
...
VerifyGroupItemScrollToByItemWithStartPositionAndVerticalList_Carrot
Assert.That(App.WaitForElement("FirstIndexLabel").GetText(), Is.EqualTo("27"))
String lengths are both 2. Strings differ at index 0.
Expected: "27"
But was: "41"
-----------^
at Microsoft.Maui.TestCases.Tests.CollectionView_ScrollingFeatureTests.VerifyGroupItemScrollToByItemWithStartPositionAndVerticalList_Carrot() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_ScrollingFeatureTests.cs:line 1994
1) at Microsoft.Maui.TestCases.Tests.CollectionView_ScrollingFeatureTests.VerifyGroupItemScrollToByItemWithStartPositionAndVerticalList_Carrot() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_ScrollingFeatureTests.cs:line 1994
VerifyFooterStringWhenHeaderString
System.NullReferenceException : Object reference not set to an instance of an object.
at UITest.Appium.HelperExtensions.Click(IUIElement element) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 449
at UITest.Appium.HelperExtensions.<>c__DisplayClass2_0.<Tap>b__0() in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 36
at UITest.Appium.HelperExtensions.<>c__DisplayClass189_0.<RunWithTimeout>b__0() in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 3136
at System.Threading.Tasks.Task`1.InnerInvoke()
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Tas
...
VerifyHeaderViewWhenBasicDataTemplateView
System.NullReferenceException : Object reference not set to an instance of an object.
at UITest.Appium.HelperExtensions.Click(IUIElement element) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 449
at UITest.Appium.HelperExtensions.<>c__DisplayClass2_0.<Tap>b__0() in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 36
at UITest.Appium.HelperExtensions.<>c__DisplayClass189_0.<RunWithTimeout>b__0() in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 3136
at System.Threading.Tasks.Task`1.InnerInvoke()
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Tas
...
VerifyHeaderStringWithItemsLayoutVerticalGrid
System.NullReferenceException : Object reference not set to an instance of an object.
at UITest.Appium.HelperExtensions.Click(IUIElement element) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 449
at UITest.Appium.HelperExtensions.<>c__DisplayClass2_0.<Tap>b__0() in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 36
at UITest.Appium.HelperExtensions.<>c__DisplayClass189_0.<RunWithTimeout>b__0() in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 3136
at System.Threading.Tasks.Task`1.InnerInvoke()
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Tas
...
VerifyScrollToByItemWithStartPositionAndHorizontalList_Kiwi
Assert.That(App.WaitForElement("FirstIndexLabel").GetText(), Is.EqualTo("12"))
String lengths are both 2. Strings differ at index 0.
Expected: "12"
But was: "24"
-----------^
at Microsoft.Maui.TestCases.Tests.CollectionView_ScrollingFeatureTests.VerifyScrollToByItemWithStartPositionAndHorizontalList_Kiwi() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_ScrollingFeatureTests.cs:line 2558
1) at Microsoft.Maui.TestCases.Tests.CollectionView_ScrollingFeatureTests.VerifyScrollToByItemWithStartPositionAndHorizontalList_Kiwi() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/CollectionView_ScrollingFeatureTests.cs:line 2558
VerifyFooterTemplateWhenFooterView
System.NullReferenceException : Object reference not set to an instance of an object.
at UITest.Appium.HelperExtensions.Click(IUIElement element) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 449
at UITest.Appium.HelperExtensions.<>c__DisplayClass2_0.<Tap>b__0() in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 36
at UITest.Appium.HelperExtensions.<>c__DisplayClass189_0.<RunWithTimeout>b__0() in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 3136
at System.Threading.Tasks.Task`1.InnerInvoke()
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
This uitests section was shortened to keep every required review section visible. Full details remain available in the pipeline build artifacts.
🧭 Next Steps — reviewer changes required
The reviewer-enhanced candidate identified changes that are not yet in the submitted PR.
Why: pr-plus-reviewer consistently applies the PR's IsLoopEnabled abstraction to the remaining virtual-loop behavior branches, preventing Material's real-count adapter from receiving virtual-loop update logic. It is the only candidate with a concrete refinement and passing evidence: all 17 required focused Android tests passed.
Address the actionable findings in this review before merging.
This comment has been minimized.
This comment has been minimized.
|
Azure Pipelines will not run the associated pipelines, because the pull request was updated after the run command was issued. Review the pull request again and issue a new run command. |
|
@Dhivya-SF4094, is this draft PR ready for review? If it still needs work, please leave it as a draft; otherwise, please mark it ready for review. |
PR Test Gate
🧪 Gate analysis — click to expand⚠ Inconclusive. The PR could not be merged with its target branch; no tests ran. No interpretable test-group summary was produced. No additional test execution is inferred. Setup did not capture a verified PR head; no commit is claimed as tested. No immutable without-fix baseline was prepared. |
Description of Change
This pull request introduces a new implementation for Material Design Carousel support in the Android handler code, focusing on compatibility with Material's
CarouselLayoutManagerand its unique requirements. The most significant changes include the addition of new adapter and view holder classes that wrap carousel items in aMaskableFrameLayout, a new scroll listener tailored for Material's carousel, and several extensibility points in the base classes to support these requirements. There are also targeted refactors to enable more flexible handling of looping and empty views.Material Carousel Support and Adapter Enhancements:
CarouselViewAdapter2, a new adapter forMauiCarouselRecyclerView2, which wraps each item in aMaskableFrameLayoutto satisfy Material'sCarouselLayoutManagerrequirements. This adapter also disables virtual looping, exposing the real item count to avoid performance issues and infinite layout loops with the Material carousel. (src/Controls/src/Core/Handlers/Items2/Android/Adapters/CarouselViewAdapter2.cs)MaskableCarouselItemViewHolder, a view holder that manages MAUI content inside aMaskableFrameLayout, ensuring the correct handling of item templates and data binding for the Material carousel. (src/Controls/src/Core/Handlers/Items2/Android/MaskableCarouselItemViewHolder.cs)CarouselViewOnScrollListener2, a scroll listener tailored forMauiCarouselRecyclerView2that works directly withCarouselLayoutManagerand does not depend on the legacy loop manager. It accurately tracks visible items and snap positions.(src/Controls/src/Core/Handlers/Items2/Android/CarouselViewOnScrollListener2.cs)Loop Logic Improvements:
MauiCarouselRecyclerViewby introducing theIsLoopEnabledproperty, allowing subclasses to override loop behavior (for example, to disable looping when using a layout manager that does not support virtual looping), and replaced direct checks ofCarousel.LoopwithIsLoopEnabledthroughout the code.Important
The Material3 path is locked to
FullScreenCarouselStrategy(one item masked to the full viewport, snapping one page at a time). The followingCarouselViewbehaviors are intentionally not supported under this strategy and are documented in code:Fixed
Fixes #35637
Screenshots
35637_BeforeChanges.mov
35637_AfterChanges.mov
UI Test results