Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 9 additions & 45 deletions src/Core/src/Platform/Android/MauiWindowInsetListener.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using Android.Content;
using Android.Views;
using AndroidX.Core.Graphics;
Expand All @@ -12,11 +13,6 @@

namespace Microsoft.Maui.Platform
{
/// <summary>
/// Registry entry for tracking view instances and their associated listeners.
/// Uses WeakReference to avoid memory leaks when views are disposed.
/// </summary>
internal record ViewEntry(WeakReference<object> View, MauiWindowInsetListener Listener);

/// <summary>
/// Manages window insets and safe area handling for Android views.
Expand All @@ -36,8 +32,9 @@ internal class MauiWindowInsetListener : WindowInsetsAnimationCompat.Callback, I
// Static tracking for views that have local inset listeners.
// This registry allows child views to find their appropriate listener without
// relying on a global activity-level listener.
// Uses ConditionalWeakTable for O(1) lookup and automatic cleanup.
// Thread Safety: All access must be on UI thread (enforced by Android's threading model).
static readonly List<ViewEntry> _registeredViews = new();
static readonly ConditionalWeakTable<AView, MauiWindowInsetListener> _registeredViews = new();

/// <summary>
/// Registers a view to use this local listener instead of the global one.
Expand All @@ -47,23 +44,7 @@ internal class MauiWindowInsetListener : WindowInsetsAnimationCompat.Callback, I
/// <param name="view">The view to register</param>
internal void RegisterView(AView view)
{
// Clean up dead references and check for existing registration
for (int i = _registeredViews.Count - 1; i >= 0; i--)
{
var entry = _registeredViews[i];
if (!entry.View.TryGetTarget(out var existingView))
{
_registeredViews.RemoveAt(i);
}
else if (existingView == view)
{
// Already registered, no need to add again
return;
}
}

// Add this view to the registry
_registeredViews.Add(new ViewEntry(new WeakReference<object>(view), this));
_registeredViews.AddOrUpdate(view, this);
}

/// <summary>
Expand All @@ -73,14 +54,10 @@ internal void RegisterView(AView view)
/// <param name="view">The view to unregister</param>
internal static MauiWindowInsetListener? UnregisterView(AView view)
{
for (int i = _registeredViews.Count - 1; i >= 0; i--)
if (_registeredViews.TryGetValue(view, out var listener))
{
if (_registeredViews[i].View.TryGetTarget(out var registeredView) && registeredView == view)
{
var listener = _registeredViews[i].Listener;
_registeredViews.RemoveAt(i);
return listener;
}
_registeredViews.Remove(view);
return listener;
}
return null;
}
Expand All @@ -107,22 +84,9 @@ internal void RegisterView(AView view)
return null;
}

if (parent is AView parentView)
if (parent is AView parentView && _registeredViews.TryGetValue(parentView, out var listener))
{
// Check if this parent view is registered
// Clean up dead references while searching
for (int i = _registeredViews.Count - 1; i >= 0; i--)
{
var entry = _registeredViews[i];
if (!entry.View.TryGetTarget(out var registeredView))
{
_registeredViews.RemoveAt(i);
}
else if (ReferenceEquals(registeredView, parentView))
{
return entry.Listener;
}
}
return listener;
}

parent = parent.Parent;
Expand Down
Loading