Skip to content

[net11.0][Android] Implement handler based Shell architecture replacing legacy renderers - #34758

Merged
PureWeen merged 64 commits into
net11.0from
Net11.0-Android-Shell-Handler
Jun 24, 2026
Merged

[net11.0][Android] Implement handler based Shell architecture replacing legacy renderers#34758
PureWeen merged 64 commits into
net11.0from
Net11.0-Android-Shell-Handler

Conversation

@Tamilarasan-Paranthaman

@Tamilarasan-Paranthaman Tamilarasan-Paranthaman commented Mar 31, 2026

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

This PR introduces handler-based Shell architecture for Android, replacing the legacy renderer-based approach (ShellRenderer, ShellFlyoutRenderer, ShellItemRenderer, ShellSectionRenderer) with new handler classes that reuse standard MAUI platform components. The new architecture follows the same patterns used by FlyoutViewHandler, NavigationViewHandler, and TabbedPageManager, achieving the long-standing architectural goal of building Shell from the same building blocks as non-Shell features.

Platform: Android only. iOS/MacCatalyst continue to use legacy ShellRenderer.

Motivation

The legacy Shell renderers on Android are monolithic, tightly coupled, and duplicate functionality that already exists in standard MAUI handlers:

  • ShellFlyoutRenderer extends DrawerLayout directly (IS-A relationship) — duplicates FlyoutViewHandler drawer logic
  • ShellItemRenderer manages its own BottomNavigationView — duplicates TabbedPageManager tab logic
  • ShellSectionRenderer manages its own toolbar, navigation stack, and content tabs — duplicates NavigationViewHandler and TabbedPageManager patterns
  • Fragment management, layout inflation, and view hierarchy are all custom per-renderer

The new handler architecture:

  • Reuses MauiDrawerLayout — same shared component as FlyoutViewHandler
  • Reuses TabbedViewManager — shared tab management for both Shell and TabbedPage
  • Uses XML layout inflation — follows established MAUI patterns (navigationlayout.axml, shellitemlayout.axml, shellsectionlayout.axml)
  • Maps cleanly to Shell's virtual view hierarchy — Shell → ShellHandler, ShellItem → ShellItemHandler, ShellSection → ShellSectionHandler

Architecture Overview

Handler Hierarchy

Shell (IFlyoutView)
└── ShellHandler : ViewHandler<Shell, MauiDrawerLayout>
    ├── MauiDrawerLayout (shared with FlyoutViewHandler)
    │   ├── navigationlayout.axml (content root)
    │   └── Flyout content (ShellFlyoutTemplatedContentRenderer — reused from legacy)
    │
    └── ShellItem
        └── ShellItemHandler : ElementHandler<ShellItem, ViewPager2>
            ├── shellitemlayout.axml (CoordinatorLayout + ViewPager2)
            ├── BottomNavigationView (via TabbedViewManager)
            ├── Toolbar (shared across all sections — persists on section switch)
            └── ShellSection
                └── ShellSectionHandler : ElementHandler<ShellSection, AView>
                    ├── shellsectionlayout.axml (LinearLayout + ViewPager2)
                    ├── TabLayout (via TabbedViewManager, hidden if single content)
                    └── ShellContent → StackNavigationManager (independent nav stack per content)

Key Design Decisions

Decision Rationale
Toolbar at ShellItem level Persists across section switches — no flicker, no recreate. Sections update toolbar content, not recreate it.
LinearLayout for ShellSection (not CoordinatorLayout) CoordinatorLayout caused a white-gap bug when TabLayout was hidden (empty AppBarLayout + ScrollingViewBehavior). LinearLayout correctly reclaims space with layout_weight.
XML layout inflation Follows NavigationViewHandler/FlyoutViewHandler patterns. Resolves ?attr/ theme tokens at inflation time. Developers can override via Android resource overlays.
OffscreenPageLimit = total items Prevents FragmentStateAdapter save/restore which causes crashes (fragments lose MAUI state). Same approach as TabbedPageRenderer.
Adapter pattern for legacy interfaces ShellItemHandlerAdapter and ShellSectionHandlerAdapter implement IShellItemRenderer/IShellSectionRenderer, maintaining compatibility with ShellToolbarTracker and appearance trackers.
ITabbedViewSource interface Lightweight alternative to ITabbedView (which extends IView). Lets Shell adapters provide tab data to TabbedViewManager without 40+ IView stub members.

Customization Surface

The handler architecture preserves the full customization surface from the legacy renderer path. All factory methods, virtual hooks, and shared classes are accessible to developers.

Protected Virtual Methods (New Handler Hooks)

Handler Method Purpose
ShellItemHandler OnTabReselected(ShellSection) Customize behavior when user taps the already-selected bottom tab (e.g., scroll-to-top, refresh)
ShellItemHandler OnSectionChanged(ShellSection, bool) Intercept section/tab switches
ShellItemHandler CreateMoreBottomSheet(Action<int, BottomSheetDialog>, List<...>) Customize the "More" overflow sheet for 5+ tabs
ShellSectionHandler OnCreateNavigationAnimation(Context, bool, bool) Customize push/pop page transition animations. Wired through ShellStackNavigationManagerStackNavigationManager.OnCreateNavigationAnimation() (base is public virtual, also benefits NavigationPage)
ShellHandler 8 IShellContext factory methods All delegate to protected virtual methods: CreateTrackerForToolbar(), CreateTabLayoutAppearanceTracker(), CreateBottomNavViewAppearanceTracker(), CreateToolbarAppearanceTracker(), CreateShellFlyoutContentRenderer(), CreateShellFlyoutRenderer(), CreateFragmentForPage(), CreateShellItemTransition()

Shared Classes (Not Internalized)

Class Status Virtual Methods
ShellToolbarTracker Shared public class — used by both renderer and handler paths 17 protected virtual methods (navigation icons, search, toolbar items, back button)
ShellFlyoutRecyclerAdapter Shared — used by both paths GenerateItemList()protected virtual
Appearance trackers Shared — 3 tracker interfaces fully preserved All via IShellContext factories

Public Fragment Classes

All wrapper fragments are top-level public classes in their own files, following the one-class-per-file convention used by the legacy Shell classes. Developers can subclass them to override OnCreateView, OnResume, OnViewCreated, etc.

Fragment File Purpose
ShellItemWrapperFragment ShellItemWrapperFragment.Android.cs Hosts ShellItemHandler's layout (CoordinatorLayout + VP2 + BNV + Toolbar)
ShellSectionWrapperFragment ShellSectionWrapperFragment.Android.cs Hosts ShellSectionHandler's layout (LinearLayout + VP2 + TabLayout)
ShellContentNavigationFragment ShellContentNavigationFragment.Android.cs Hosts a ShellContent page with its own StackNavigationManager for independent navigation

Fragment Architecture

Shell handlers use wrapper fragments to integrate with Android's FragmentManager:

FragmentManager (Activity)
└── ShellItemWrapperFragment (committed by ShellHandler)
    ├── Inflates shellitemlayout.axml
    ├── Sets up ShellItemHandler with real ViewPager2 from XML
    │
    └── ChildFragmentManager
        └── ShellSectionWrapperFragment (via ViewPager2 FragmentStateAdapter)
            ├── Inflates shellsectionlayout.axml
            ├── Sets up ShellSectionHandler with real ViewPager2 from XML
            │
            └── ChildFragmentManager
                └── ShellContentNavigationFragment (via ViewPager2 FragmentStateAdapter)
                    ├── Uses StackNavigationManager for navigation
                    └── Hosts actual page content

All fragment classes are public and in their own files. All have default constructors for Android's Fragment.instantiate() reflection requirement, with null guards in OnCreateView() for graceful restoration handling.

New Files

Core Layer (src/Core/)

File Purpose
Platform/Android/MauiDrawerLayout.cs Shared DrawerLayout wrapper for FlyoutViewHandler and ShellHandler. Supports three layout modes: Flyout, SideBySide, Padding. Implements flyout width calculation per Material Design guidelines.
Platform/Android/Resources/Layout/shellitemlayout.axml XML layout for ShellItemHandler — CoordinatorLayout + ViewPager2. Toolbar, tabs, and BottomNavigationView placed into NRM slots.
Platform/Android/Resources/Layout/shellsectionlayout.axml XML layout for ShellSectionHandler — LinearLayout + ViewPager2.
Core/ITab.cs ITab interface — Title, Icon, IsEnabled for tab items in ITabbedView/ITabbedViewSource.
Primitives/TabBarPlacement.cs TabBarPlacement enum — Top / Bottom.

Controls Layer (src/Controls/)

File Purpose
Handlers/Shell/ShellHandler.Android.cs Main Shell handler. Uses MauiDrawerLayout with navigationlayout.axml. Manages flyout content, scrim brushes (including gradient via ScrimBrushDrawable), flyout behavior modes. Implements IShellContext to create handler-based renderers. 8 protected virtual factory methods.
Handlers/Shell/ShellItemHandler.Android.cs ShellItem handler. Manages bottom navigation via TabbedViewManager, shared toolbar (persists across section switches), ViewPager2 for section fragments, appearance tracking. 3 protected virtual hooks: OnTabReselected, OnSectionChanged, CreateMoreBottomSheet.
Handlers/Shell/ShellSectionHandler.Android.cs ShellSection handler. Uses ViewPager2 for content switching (unified — works for single and multiple ShellContent). TabLayout visibility controlled by item count. Each ShellContent gets its own StackNavigationManager for independent navigation. 1 protected virtual hook: OnCreateNavigationAnimation.
Handlers/Shell/ShellItemWrapperFragment.Android.cs Public fragment — hosts ShellItemHandler's layout. Inflates shellitemlayout.axml, sets up toolbar, BNV, VP2 adapter, back button handling.
Handlers/Shell/ShellSectionWrapperFragment.Android.cs Public fragment — hosts ShellSectionHandler's layout. Sets up VP2 adapter, toolbar updates on resume.
Handlers/Shell/ShellContentNavigationFragment.Android.cs Public fragment — hosts a ShellContent page with independent StackNavigationManager. Handles navigation requests, toolbar updates, tab visibility based on navigation depth.
Handlers/Shell/ShellTabbedViewAdapters.Android.cs Adapter classes: ShellItemTabbedViewAdapter (bottom tabs), ShellSectionTabbedViewAdapter (top tabs), ShellSectionTab, ShellContentTab. Bridge Shell data model to ITabbedViewSource for TabbedViewManager.
Platform/Android/ITabbedViewSource.cs Lightweight interface for TabbedViewManager consumers. Same shape as ITabbedView but doesn't extend IView.
Platform/Android/TabbedViewManager.cs Shared tab management — handles ViewPager2, BottomNavigationView, TabLayout, fragment placement, tab appearance. Used by TabbedPageManager, ShellItemHandler, and ShellSectionHandler.

Modified Files

Handler Registration

File Change
Hosting/AppHostBuilderExtensions.cs Android Shell handlers (ShellHandler, ShellItemHandler, ShellSectionHandler) are now always registered — same as Windows and Tizen. No feature switch needed.
Handlers/Shell/ShellHandler.cs Added Shell property mapper entries — routes property changes to platform-specific handler methods.

Handler as Default (No Feature Switch)

Shell handlers are always registered on Android. No RuntimeFeature switch or MSBuild property is needed — this follows the standard MAUI handler pattern where handlers are the default and legacy renderers are opt-in via explicit registration.

Shared Component Extraction

File Change
Handlers/FlyoutView/FlyoutViewHandler.Android.cs Refactored to use MauiDrawerLayout instead of inline DrawerLayout logic. Drawer functionality extracted to shared MauiDrawerLayout.
Platform/Android/TabbedPageManager.cs Refactored to use TabbedViewManager for tab management. Reduced from ~984 lines of duplicated logic to delegating to the shared manager.
Platform/Android/Navigation/StackNavigationManager.cs Minor changes for Shell integration — supports Shell's per-content navigation stacks. Added public virtual OnCreateNavigationAnimation() for custom page transitions.

Core Interfaces

File Change
Core/ITabbedView.cs Expanded with full tab management properties: Tabs, CurrentTab, BarBackgroundColor, BarBackground, BarTextColor, UnselectedTabColor, SelectedTabColor, TabBarPlacement, OffscreenPageLimit, IsSwipePagingEnabled, IsSmoothScrollEnabled, TabsChanged event.

Compatibility Layer Fixes

File Change
ShellFlyoutTemplatedContentRenderer.cs Updated to work with both renderer and handler paths. The handler's IShellContext provides handler-based adapters while keeping the same flyout content rendering.
ShellFlyoutRecyclerAdapter.cs Minor fix for handler compatibility.
ShellSearchViewAdapter.cs Added JNI constructor on CustomFilter inner class — required for Android runtime to instantiate via reflection.
ShellToolbarTracker.cs Updated to work with handler-provided toolbar. Shared between renderer and handler paths. All 17 protected virtual methods preserved.
ShellToolbarAppearanceTracker.cs Minor update for handler compatibility.

Virtual View Changes

File Change
Element/Element.cs Changes to support handler-based Shell element hierarchy.
Shell/ShellContent.cs Added support for handler-based Shell content management.
TabbedPage/TabbedPage.cs Updated to implement expanded ITabbedView interface members.
Style.cs Minor cleanup.

PublicAPI

Updated PublicAPI.Unshipped.txt files across all TFMs for new public types and interface members:

  • MauiDrawerLayout, MauiDrawerLayout.FlyoutLayoutMode
  • ITab, ITabbedView expanded members
  • TabBarPlacement enum
  • ShellHandler, ShellItemHandler, ShellSectionHandler handler types
  • ShellItemWrapperFragment, ShellSectionWrapperFragment, ShellContentNavigationFragment fragment classes
  • Protected virtual methods: OnTabReselected, OnSectionChanged, CreateMoreBottomSheet, OnCreateNavigationAnimation

Shared Infrastructure Summary

The PR achieves the core architectural goal — Shell now uses the same building blocks as non-Shell features:

Component Shell Usage Non-Shell Usage
MauiDrawerLayout ShellHandler (flyout drawer) FlyoutViewHandler (FlyoutPage)
TabbedViewManager ShellItemHandler (bottom tabs), ShellSectionHandler (top tabs) TabbedPageManager (TabbedPage)
StackNavigationManager ShellSectionHandler (per-content nav stack) NavigationViewHandler (NavigationPage)
navigationlayout.axml ShellHandler (content root) FlyoutViewHandler, NavigationViewHandler
ITab / ITabbedView Shell adapters TabbedPage

Handler as Default

Shell handlers are now the default on Android. The legacy renderer (ShellRenderer) is still available for apps that need it — register it explicitly in MauiProgram.cs.

How to Opt Out (Use Legacy Renderer)

// MauiProgram.cs
builder.ConfigureMauiHandlers(handlers =>
{
    handlers.AddHandler<Shell, Microsoft.Maui.Controls.Handlers.Compatibility.ShellRenderer>();
});

This follows the standard MAUI pattern — handler registration via AddHandler always wins (last registration takes precedence). No MSBuild property or feature switch is needed.

What Changes By Default

Element Before (Renderer) After (Handler)
Shell ShellRenderer ShellHandler → MauiDrawerLayout
ShellItem Managed by ShellItemRenderer ShellItemHandler → ViewPager2 + TabbedViewManager
ShellSection Managed by ShellSectionRenderer ShellSectionHandler → ViewPager2 + StackNavigationManager

Note: iOS/MacCatalyst are not affected — they always use ShellRenderer regardless of this change.

Testing

  • Controls.TestCases.HostApp.csproj uses the default handler path — all UI tests run against the handler architecture.
  • Controls.DeviceTests.csproj explicitly registers ShellRenderer to continue using the legacy renderer path. Shell device tests are tightly coupled to renderer-specific internals (fragment structure, view hierarchy assertions). Migrating them to the handler path is planned as a separate follow-up PR to keep this PR focused on the handler architecture itself.

Migration Guidance For Existing Custom Shell Renderers

This section is for apps that subclass legacy Android Shell renderers. Since handlers are now the default, these apps have two options:

  1. Stay on legacy renderer: Register ShellRenderer explicitly (see "How to Opt Out" above)
  2. Migrate to handlers: Use the mapping table below to port customizations

Old -> New Mapping

Legacy customization point Handler path
ShellRenderer subclass ShellHandler subclass
ShellItemRenderer subclass ShellItemHandler subclass
ShellSectionRenderer subclass ShellSectionHandler subclass
CreateTrackerForToolbar() ShellHandler.CreateTrackerForToolbar() (protected virtual)
CreateBottomNavViewAppearanceTracker() ShellHandler.CreateBottomNavViewAppearanceTracker() (protected virtual)
CreateTabLayoutAppearanceTracker() ShellHandler.CreateTabLayoutAppearanceTracker() (protected virtual)
CreateToolbarAppearanceTracker() ShellHandler.CreateToolbarAppearanceTracker() (protected virtual)
CreateShellItemRenderer() ShellHandler.CreateShellItemRenderer() (protected virtual)
CreateShellSectionRenderer() ShellHandler.CreateShellSectionRenderer() (protected virtual)
CreateFragmentForPage() ShellHandler.CreateFragmentForPage() (protected virtual)
ShellItemRenderer.OnTabReselected() ShellItemHandler.OnTabReselected() (protected virtual)
ShellItemRenderer "More" overflow customization ShellItemHandler.CreateMoreBottomSheet() (protected virtual)

Adapter Wrapping Pattern (Important)

In the handler architecture, CreateShellItemRenderer() / CreateShellSectionRenderer() still return compatibility interfaces, but runtime behavior is bridged through handler adapters.

public class MyShellHandler : ShellHandler
{
    protected override IShellItemRenderer CreateShellItemRenderer(ShellItem shellItem)
    {
        // Register your custom ShellItemHandler and let the base factory resolve it.
        // The base implementation wraps the resolved handler with the compatibility adapter.
        return base.CreateShellItemRenderer(shellItem);
    }
}

public class MyShellItemHandler : ShellItemHandler
{
    protected override void OnTabReselected(ShellSection shellSection)
    {
        // custom behavior (scroll-to-top, refresh, pop-to-root, etc.)
        base.OnTabReselected(shellSection);
    }
}

// MauiProgram.cs
builder.ConfigureMauiHandlers(handlers =>
{
    handlers.AddHandler<ShellItem, MyShellItemHandler>();
});

Animation API Translation

Legacy Handler
SetupAnimation(ShellNavigationSource, FragmentTransaction, Page) OnCreateNavigationAnimation(Context, bool isPopping, bool enter)

What changed:

  • New API returns Android.Views.Animations.Animation? instead of mutating FragmentTransaction.
  • Direct FragmentTransaction customization is not exposed on this hook.
  • ShellNavigationSource does not map 1:1; push/pop intent maps to isPopping + enter.

Issues Fixed

Fixes #32985

Copilot AI review requested due to automatic review settings March 31, 2026 14:23
@github-actions

github-actions Bot commented Mar 31, 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 -- 34758

Or

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

@Tamilarasan-Paranthaman
Tamilarasan-Paranthaman marked this pull request as draft March 31, 2026 14:24
@dotnet-policy-service dotnet-policy-service Bot added the partner/syncfusion Issues / PR's with Syncfusion collaboration label Mar 31, 2026
@Tamilarasan-Paranthaman Tamilarasan-Paranthaman added platform/android area-controls-shell Shell Navigation, Routes, Tabs, Flyout labels Mar 31, 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.

Pull request overview

Implements a new handler-based Shell architecture for Android (opt-in via UseAndroidShellHandlers) and refactors shared Android navigation/tab/flyout infrastructure to reduce duplication with existing MAUI handlers/managers.

Changes:

  • Added RuntimeFeature.UseAndroidShellHandlers (default false) and MSBuild plumbing to enable the feature switch.
  • Introduced shared Android components/layouts (e.g., MauiDrawerLayout, TabbedViewManager, shellitemlayout.axml, shellsectionlayout.axml) and refactored FlyoutViewHandler/TabbedPageManager to use them.
  • Updated Shell compatibility components and test projects to validate the new Android Shell handlers in CI (plus a UI test stabilization tweak).

Reviewed changes

Copilot reviewed 40 out of 40 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/Core/src/RuntimeFeature.cs Adds UseAndroidShellHandlers runtime feature switch.
src/Core/src/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt Public API updates for new tab abstractions/enums.
src/Core/src/PublicAPI/netstandard/PublicAPI.Unshipped.txt Public API updates for new tab abstractions/enums.
src/Core/src/PublicAPI/net/PublicAPI.Unshipped.txt Public API updates for new tab abstractions/enums.
src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt Public API updates for new tab abstractions/enums.
src/Core/src/PublicAPI/net-tizen/PublicAPI.Unshipped.txt Public API updates for new tab abstractions/enums.
src/Core/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt Public API updates for new tab abstractions/enums.
src/Core/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt Public API updates for new tab abstractions/enums.
src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt Public API updates including MauiDrawerLayout and handler signature changes.
src/Core/src/Primitives/TabBarPlacement.cs Introduces TabBarPlacement enum.
src/Core/src/Platform/Android/Resources/values/styles.xml Adds Android style for Shell TabLayout.
src/Core/src/Platform/Android/Resources/Layout/shellsectionlayout.axml New Android layout used by ShellSection handler.
src/Core/src/Platform/Android/Resources/Layout/shellitemlayout.axml New Android layout used by ShellItem handler.
src/Core/src/Platform/Android/Navigation/StackNavigationManager.cs Adds navigation request queueing and Shell integration for per-tab containers.
src/Core/src/Platform/Android/MauiDrawerLayout.cs New shared DrawerLayout wrapper used by FlyoutView/Shell.
src/Core/src/Handlers/FlyoutView/FlyoutViewHandler.Android.cs Refactors FlyoutViewHandler to use MauiDrawerLayout.
src/Core/src/Core/ITabbedView.cs Expands ITabbedView to support shared tab management surface.
src/Core/src/Core/ITab.cs Adds ITab abstraction for tab items.
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/XFIssue/ShellSearchHandlerItemSizing.cs Stabilizes screenshot capture via retry/tolerance.
src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj Enables UseAndroidShellHandlers for UI test host app.
src/Controls/tests/DeviceTests/Controls.DeviceTests.csproj Enables UseAndroidShellHandlers for device tests.
src/Controls/src/Core/TabbedPage/TabbedPage.cs Implements expanded ITabbedView surface on TabbedPage.
src/Controls/src/Core/Shell/ShellContent.cs Propagates title updates to support handler-based tab title refresh.
src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt Adds public handler types/methods for Android Shell handlers.
src/Controls/src/Core/Platform/Android/TabbedViewManager.cs New shared manager for ViewPager2 + BottomNavigationView/TabLayout behavior.
src/Controls/src/Core/Platform/Android/TabbedPageManager.cs Refactors TabbedPageManager to delegate tab UI logic to TabbedViewManager.
src/Controls/src/Core/Platform/Android/ITabbedViewSource.cs Adds internal adapter interface to supply tab data without IView.
src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs Conditionally registers Android Shell handlers based on runtime feature.
src/Controls/src/Core/Handlers/Shell/ShellTabbedViewAdapters.Android.cs Adds Shell adapters bridging ShellItem/ShellSection to tab source model.
src/Controls/src/Core/Handlers/Shell/ShellHandler.Tizen.cs Adds stub mappers to satisfy shared mapper entries.
src/Controls/src/Core/Handlers/Shell/ShellHandler.cs Extends property mapper for Android/Tizen/Windows handler scenarios.
src/Controls/src/Core/Handlers/Shell/ShellHandler.Android.cs New Android ShellHandler implementation built on MauiDrawerLayout.
src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellToolbarTracker.cs Compatibility updates for toolbar/search behavior and back icon progress handling.
src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellToolbarAppearanceTracker.cs Adds null-guard in SetAppearance.
src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellSearchViewAdapter.cs Adds JNI ctor + null guard for filter publish.
src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFlyoutTemplatedContentRenderer.cs Avoids double-updates when running under new handler path; exposes update methods.
src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFlyoutRecyclerAdapter.cs Adds additional null/dispose safety.
src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.targets Maps MSBuild property to runtime feature switch.

Comment thread src/Controls/src/Core/Platform/Android/TabbedPageManager.cs
Comment thread src/Controls/src/Core/Handlers/Shell/ShellHandler.Android.cs
Comment thread src/Core/src/Platform/Android/Navigation/StackNavigationManager.cs Outdated
Comment thread src/Core/src/Platform/Android/Navigation/StackNavigationManager.cs
@vishnumenon2684 vishnumenon2684 added the community ✨ Community Contribution label Apr 1, 2026
@Tamilarasan-Paranthaman
Tamilarasan-Paranthaman force-pushed the Net11.0-Android-Shell-Handler branch 2 times, most recently from b6433e8 to 0c8fa75 Compare April 6, 2026 13:23
@sheiksyedm

Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests , maui-pr-devicetests

@azure-pipelines

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

@Tamilarasan-Paranthaman
Tamilarasan-Paranthaman force-pushed the Net11.0-Android-Shell-Handler branch 2 times, most recently from 99080f6 to 603ddbc Compare April 6, 2026 14:41
@sheiksyedm

Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests , maui-pr-devicetests

@azure-pipelines

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

@Tamilarasan-Paranthaman
Tamilarasan-Paranthaman force-pushed the Net11.0-Android-Shell-Handler branch from c7bc2c4 to d695579 Compare April 9, 2026 13:57
@sheiksyedm

Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests , maui-pr-devicetests

@azure-pipelines

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

@sheiksyedm

Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests

@azure-pipelines

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

@Tamilarasan-Paranthaman
Tamilarasan-Paranthaman force-pushed the Net11.0-Android-Shell-Handler branch from edc031f to 74d7ac7 Compare June 23, 2026 15:01
@sheiksyedm

Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests , maui-pr-devicetests

@azure-pipelines

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

@kubaflo

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

Tests Failure Analysis

@Tamilarasan-Paranthaman — test-failure review results are available based on commit 74d7ac7.
To request a fresh review after new comments, commits, or CI runs, comment /review tests.

Overall Needs human investigation Failures 9 Platform Android | iOS | macOS

Test Failure Review: Needs human investigation - click to expand

Overall verdict: Needs human investigation

The base branch (net11.0) has 5 consecutive failures for both maui-pr and maui-pr-uitests, indicating pre-existing instability. However, two items need human review: a visual baseline failure (Issue16918Test) for a test not updated by this PR despite its Android Shell rendering changes, and a 180-minute MacCatalyst Shell timeout on a job that exercises cross-platform Shell code modified in this PR.

Failure Verdict Evidence
maui-pr (Run Integration Tests AOT macOS) Likely unrelated base branch has 5 consecutive failures on net11.0 for maui-pr; PR modifies no macOS code; AOT macOS test failure is pre-existing
maui-pr-uitests (Android UITests CarouselView API 30) Needs human investigation Android platform matches PR focus; PR makes significant changes to Android navigation infrastructure (StackNavigationManager, MauiDrawerLayout, NavigationViewFragment) which could affect all Android UI tests; CarouselView itself is not changed but the host environment may be affected
Issue16918Test (VisualTestFailedException, 2 occurrences) Needs human investigation Visual baseline mismatch for a test not updated in this PR; the PR updates 6 Android Shell snapshots and changes Android Shell rendering, meaning other Shell-adjacent visual tests may now also differ from their baselines; needs a snapshot update if PR-caused
SettingSourceWhenDetachedDoesNotCrash (TimeoutException) Needs human investigation Test name is Shell-adjacent; PR makes substantial Shell architecture changes including cross-platform ShellHandler.cs and Shell.cs; single occurrence with no retry confirmation
MacCatalyst Shell (Cancelled, 180-min timeout) Needs human investigation PR changes cross-platform ShellHandler.cs, Shell.cs, ShellContent.cs, and AppHostBuilderExtensions.cs; a 180-minute hang on the macOS Shell job could indicate a deadlock or navigation hang introduced by the cross-platform changes; base branch was not cancelled on this job in recent runs
MacCatalyst Image, ImageButton, IndicatorView, InputTransparent, IsEnabled, IsVisible Likely unrelated Non-Shell categories on macOS; PR targets Android platform; base branch has 5 consecutive failures for maui-pr-uitests
MacCatalyst ListView Likely unrelated Non-Shell category on macOS; area and platform mismatch with PR scope; base branch consistently failing
MacCatalyst WebView / iOS WebView Likely unrelated WebView tests on macOS/iOS; no WebView changes in PR; base branch consistently failing
iOS UITests Editor, Effects, Essentials, FlyoutPage, Focus, Fonts, Frame, Gestures, GraphicsView Likely unrelated Mostly non-Shell iOS categories; while FlyoutPage uses shared Shell code, the other failing categories suggest a broader pre-existing issue on this iOS job; base branch consistently failing
Issue1939Test (TimeoutException, 2 occurrences) Likely unrelated Pure timeout repeated across retry attempts; pattern consistent with flaky test; base branch also failing
DropEventCoordinates / DragAndDropBetweenLayouts Likely unrelated Drag-and-drop tests; PR makes no changes to gesture or drag/drop code; app-crash message in DragAndDropBetweenLayouts is consistent with known flaky app-restart behavior

Recommended action

Investigate the MacCatalyst Shell 180-minute timeout by checking whether the cross-platform Shell code changes (ShellHandler.cs, Shell.cs) introduce any synchronous waits or navigation deadlocks on macOS. Also verify whether Issue16918Test is a Shell visual test that needs a new snapshot baseline — if so, add the missing snapshot to TestCases.Android.Tests/snapshots/android/. The remaining macOS and iOS failures appear to be pre-existing given the base branch's consistent failure state; a rerun after the two items above are addressed would confirm.

Evidence details

PR scope: 50 changed files; Android and cross-platform Shell handler code; areas: Shell, Handler, Navigation, Layout. Changed test files include 6 new Android snapshots (ActionModeMenuShouldNotBeVisibleAfterSwitchingTab, BackButtonBehavior_IsVisible_False_ProgrammaticNavStillWorks, ShouldUpdateSearchViewOnPageNavigation, TabBarShouldBeVisibleAfterNavigatingFromModalViaGoToAsync, VerifyShellFlyout_FlyoutIcon, VerifyShellMenuItemsAlignedInRTL) and 3 test case C# files.

Cross-platform changes: src/Controls/src/Core/Handlers/Shell/ShellHandler.cs, src/Controls/src/Core/Shell/Shell.cs, src/Controls/src/Core/Shell/ShellContent.cs, src/Controls/src/Core/ShellToolbar.cs, src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs, src/Core/src/Core/ITab.cs, src/Core/src/Primitives/TabBarPlacement.cs are platform-neutral and affect all platforms.

Build 1477569 (maui-pr): Build results. Failed timeline records: AOT macOS job, Run Integration Tests - AOT task. Issue: "Test suite had 1 failure(s)." Recent base-branch builds: 1477477 (failed), 1476359 (failed), 1476200 (failed), 1476157 (failed), 1475412 (failed) — all on refs/heads/net11.0.

Build 1477582 (maui-pr-uitests): Build results. 30 failed/cancelled timeline records. Recent base-branch builds: 1477478 (failed), 1476416 (failed), 1476158 (failed), 1475414 (canceled), 1473225 (failed) — all on refs/heads/net11.0.

MacCatalyst Shell cancellation: Job exceeded 180-minute limit on agent Azure Pipelines 156. Timeline issues: "The job running on agent Azure Pipelines 156 ran longer than the maximum time of 180 minutes." The Controls Shell job also had a preceding PowerShell exited with code '1' with RetryHelper failure before the timeout cancellation.

Limitations: No AzDO bearer token was available; authenticated _apis/test run APIs were not queried. Test job attribution (jobId) for the 5 deduplicated test failures could not be resolved from public log APIs — job assignment is based on build-log text matching only. Device-test Helix aggregate data was not present in gathered context.

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

Looks like there are some test failures

PureWeen added a commit that referenced this pull request Jun 23, 2026
Both were found by running the gatherer against a live PR (#34758):

1. Crash: the @() array-subexpression operator on a List[object] throws
   ArgumentException ("Argument types do not match") from PowerShell's
   PSToObjectArrayBinder/MaybeDebase for certain element shapes, aborting the
   run under $ErrorActionPreference=Stop so NO gate is produced at all. The
   three gate List materializations (pending/failing checks, unexplained legs)
   now use .ToArray() -- a direct CLR call that bypasses the dynamic binder --
   instead of @().

2. Cross-build contamination / latent false-green vector: the _apis/test/runs
   list endpoint SILENTLY IGNORES the buildIds filter and returns project-wide
   runs from the beginning of time (a maui build's query returned 2022-era
   Roslyn/runtime crossgen runs with build.id 602). Those phantom runs report
   zero failures, so their failedTests sum to 0 and could falsely confirm a
   clean device-test build (deviceTestFailedConfirmedZero) over the REAL build
   that failed. Scope the query by buildUri=vstfs:///Build/Build/<id> (honored
   server-side) and drop any run carrying an explicit mismatched build id
   (defense in depth).

Verified end-to-end against PR #34758: the gatherer now completes and the
test-run set contains only that build's runs (0 cross-repo contaminants,
was 20). Harness extended to 167 assertions (PROD-1/PROD-2), all green.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Tamilarasan-Paranthaman

Copy link
Copy Markdown
Member Author

Looks like there are some test failures

@kubaflo, the failures on iOS and Mac are unrelated to this PR.

@PureWeen
PureWeen merged commit e0e17e6 into net11.0 Jun 24, 2026
129 of 138 checks passed
@PureWeen
PureWeen deleted the Net11.0-Android-Shell-Handler branch June 24, 2026 13:41
jfversluis pushed a commit that referenced this pull request Jul 9, 2026
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Regression Details 
Shell.TitleView is not centered on Windows due to the mapper execution
order change in ShellHandler in PR (#34758)

### Root Cause:

ShellHandler executes mapper entries in registration order. After a
recent change, MapCurrentItem began running before MapToolbar, causing
navigation and layout to occur before the toolbar and TitleView were
initialized.

Previously, MapToolbar executed first, ensuring the CommandBar was fully
initialized before layout occurred. This allowed
TitleViewManager.UpdateTitleViewWidth() to use a valid
CommandBar.ActualWidth and correctly center the TitleView.

With the updated order, MapCurrentItem triggers navigation and layout
before MapToolbar runs. As a result, CommandBar.ActualWidth is not yet
valid during the initial width calculation, causing the TitleView width
to be computed incorrectly and appear left-aligned instead of centered.

### Description of Change:

Restore the execution order so that MapToolbar runs before
MapCurrentItem. This ensures the MauiToolbar and TitleView are fully
initialized before navigation triggers layout, allowing
TitleViewManager.UpdateTitleViewWidth() to calculate the correct width
and center the Shell.TitleView as expected.

### Issues Fixed:
Fixes #36322 

### Tested the behaviour in the following platforms
- [ ] Android
- [x] Windows
- [ ] iOS
- [ ] Mac

### Output Screenshot
Before Issue Fix | After Issue Fix |
|----------|----------|
|<image width="400" height="200" alt="Before Fix"
src="https://github.com/user-attachments/assets/f54df19f-993b-4c56-b47b-823f85cf0b9a">|<image
width="400" height="200" alt="After Fix"
src="https://github.com/user-attachments/assets/ad043a05-8bdf-4906-8335-8db545d5edb7">|
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 25, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-controls-shell Shell Navigation, Routes, Tabs, Flyout community ✨ Community Contribution p/0 Current heighest priority issues that we are targeting for a release. partner/syncfusion Issues / PR's with Syncfusion collaboration platform/android s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates s/agent-gate-passed AI verified tests catch the bug (fail without fix, pass with fix) 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.

9 participants