Skip to content

Commit e1b1b89

Browse files
fix(controls): harden Android native registrations
1 parent 87c06d8 commit e1b1b89

9 files changed

Lines changed: 327 additions & 80 deletions

File tree

src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRenderer.cs

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -300,18 +300,38 @@ protected override void OnShellSectionChanged()
300300
base.OnShellSectionChanged();
301301

302302
var index = ((IShellItemController)ShellItem).GetItems().IndexOf(ShellSection);
303-
using (var menu = _bottomView.Menu)
303+
SetBottomMenuItemChecked(index);
304+
}
305+
306+
void SetBottomMenuItemChecked(int index)
307+
{
308+
using var menu = _bottomView.Menu;
309+
index = Math.Min(index, menu.Size() - 1);
310+
if (index < 0)
311+
return;
312+
313+
if (index < _registeredMenuItems.Count &&
314+
_registeredMenuItems[index].IsAlive())
304315
{
305-
index = Math.Min(index, menu.Size() - 1);
306-
if (index < 0)
307-
return;
308-
if (index < _registeredMenuItems.Count)
309-
_registeredMenuItems[index].SetChecked(true);
310-
else
311-
{
312-
using var menuItem = menu.GetItem(index);
316+
_registeredMenuItems[index].SetChecked(true);
317+
return;
318+
}
319+
320+
var menuItem = menu.GetItem(index);
321+
if (menuItem is null)
322+
return;
323+
324+
var disposeMenuItem = !_registeredMenuItems.Any(
325+
registeredMenuItem => ReferenceEquals(registeredMenuItem, menuItem));
326+
try
327+
{
328+
if (menuItem.IsAlive())
313329
menuItem.SetChecked(true);
314-
}
330+
}
331+
finally
332+
{
333+
if (disposeMenuItem)
334+
menuItem.Dispose();
315335
}
316336
}
317337

@@ -389,10 +409,10 @@ protected virtual void OnMoreItemSelected(ShellSection shellSection, BottomSheet
389409
dialog.Dispose();
390410
}
391411

392-
List<(string title, ImageSource icon, bool tabEnabled)> CreateTabList(ShellItem shellItem)
412+
static List<(string title, ImageSource icon, bool tabEnabled)> CreateTabList(
413+
IReadOnlyList<ShellSection> shellItems)
393414
{
394415
var items = new List<(string title, ImageSource icon, bool tabEnabled)>();
395-
var shellItems = ((IShellItemController)shellItem).GetItems();
396416

397417
for (int i = 0; i < shellItems.Count; i++)
398418
{
@@ -557,8 +577,9 @@ _updateMenuItemSource is not null &&
557577
if (ShellItemController.ShowTabs)
558578
{
559579
_menuSetup = true;
560-
var currentIndex = ((IShellItemController)ShellItem).GetItems().IndexOf(ShellSection);
561-
var items = CreateTabList(shellItem);
580+
var shellSections = ShellItemController.GetItems().ToList();
581+
var currentIndex = shellSections.IndexOf(ShellSection);
582+
var items = CreateTabList(shellSections);
562583

563584
BottomNavigationViewUtils.SetupMenu(
564585
menu,
@@ -567,15 +588,16 @@ _updateMenuItemSource is not null &&
567588
currentIndex,
568589
_bottomView,
569590
MauiContext,
570-
RegisterBottomMenuItems);
591+
menuItems => RegisterBottomMenuItems(menuItems, shellSections));
571592
}
572593

573594
UpdateTabBarVisibility();
574595
}
575596

576-
void RegisterBottomMenuItems(IReadOnlyList<IMenuItem> menuItems)
597+
void RegisterBottomMenuItems(
598+
IReadOnlyList<IMenuItem> menuItems,
599+
IReadOnlyList<ShellSection> shellSections)
577600
{
578-
var shellSections = ShellItemController.GetItems();
579601
var registrationItems = new List<(IMenuItem MenuItem, object Owner, bool IsMoreItem)>();
580602
foreach (var previousMenuItem in _registeredMenuItems)
581603
{
@@ -589,6 +611,9 @@ void RegisterBottomMenuItems(IReadOnlyList<IMenuItem> menuItems)
589611
foreach (var menuItem in menuItems)
590612
{
591613
var isMoreItem = menuItem.ItemId == MoreTabId;
614+
if (!isMoreItem && (menuItem.ItemId < 0 || menuItem.ItemId >= shellSections.Count))
615+
continue;
616+
592617
object owner = isMoreItem
593618
? ShellItem
594619
: shellSections[menuItem.ItemId];
@@ -635,7 +660,19 @@ _bottomView is null ||
635660

636661
protected virtual void UpdateShellSectionIcon(ShellSection shellSection, IMenuItem menuItem)
637662
{
638-
BottomNavigationViewUtils.SetMenuItemIcon(menuItem, shellSection.Icon, MauiContext)
663+
var source = shellSection.Icon;
664+
var index = ShellItemController.GetItems().IndexOf(shellSection);
665+
var iconUpdateIsCurrent = BottomNavigationViewUtils.BeginMenuIconUpdate(_bottomView, index);
666+
BottomNavigationViewUtils.SetMenuItemIcon(
667+
menuItem,
668+
source,
669+
MauiContext,
670+
() =>
671+
iconUpdateIsCurrent() &&
672+
ReferenceEquals(shellSection.Icon, source) &&
673+
index >= 0 &&
674+
index < _registeredMenuItems.Count &&
675+
ReferenceEquals(_registeredMenuItems[index], menuItem))
639676
.FireAndForget(e => MauiContext?.CreateLogger<ShellItemRenderer>()?
640677
.LogWarning(e, "Failed to Update Shell Section Icon"));
641678
}

src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellSearchViewAdapter.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,13 +152,18 @@ void OnListPropxyChanged(object sender, ListProxyChangedEventArgs e)
152152

153153
class CustomFilter : Filter
154154
{
155-
private readonly BaseAdapter _adapter;
155+
private readonly BaseAdapter _adapter = null;
156156

157157
public CustomFilter(BaseAdapter adapter)
158158
{
159159
_adapter = adapter;
160160
}
161161

162+
protected CustomFilter(IntPtr javaReference, global::Android.Runtime.JniHandleOwnership transfer)
163+
: base(javaReference, transfer)
164+
{
165+
}
166+
162167
protected override FilterResults PerformFiltering(ICharSequence constraint)
163168
{
164169
var results = new FilterResults();
@@ -169,7 +174,8 @@ protected override FilterResults PerformFiltering(ICharSequence constraint)
169174

170175
protected override void PublishResults(ICharSequence constraint, FilterResults results)
171176
{
172-
_adapter.NotifyDataSetChanged();
177+
if (_adapter is not null)
178+
_adapter.NotifyDataSetChanged();
173179
}
174180
}
175181

src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellToolbarTracker.cs

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,9 @@ void RegisterNavigationButton(
588588
{
589589
if (_disposed ||
590590
registrationGeneration != _navigationRegistrationGeneration ||
591-
!ReferenceEquals(Page, page))
591+
!ReferenceEquals(Page, page) ||
592+
!ReferenceEquals(_platformToolbar, toolbar) ||
593+
!toolbar.IsAlive())
592594
{
593595
return;
594596
}
@@ -602,6 +604,7 @@ void RegisterNavigationButton(
602604
for (int index = 0; index < toolbar.ChildCount; index++)
603605
{
604606
if (toolbar.GetChildAt(index) is not AppCompatImageButton button ||
607+
!button.IsAlive() ||
605608
button.Drawable is null)
606609
{
607610
continue;
@@ -879,39 +882,53 @@ static void ClearSearchViewState(AView view)
879882

880883
void OnSearchViewAttachedToWindow(object sender, AView.ViewAttachedToWindowEventArgs e)
881884
{
882-
// We only need to do this tint hack when using collapsed search handlers
883-
if (SearchHandler.SearchBoxVisibility != SearchBoxVisibility.Collapsible)
884-
return;
885-
886-
for (int i = 0; i < _platformToolbar.ChildCount; i++)
885+
// We only need to do this tint hack when using collapsed search handlers.
886+
if (SearchHandler?.SearchBoxVisibility == SearchBoxVisibility.Collapsible)
887887
{
888-
var child = _platformToolbar.GetChildAt(i);
889-
if (child is AppCompatImageButton button)
888+
for (int i = 0; i < _platformToolbar.ChildCount; i++)
890889
{
891-
// we want the newly added button which will need layout
892-
if (child.IsLayoutRequested)
890+
var child = _platformToolbar.GetChildAt(i);
891+
if (child is AppCompatImageButton button && button.IsAlive())
893892
{
894-
button.SetColorFilter(GetSearchHandlerTintColor(Page).ToPlatform(Colors.White), PorterDuff.Mode.SrcAtop);
895-
}
893+
// we want the newly added button which will need layout
894+
if (child.IsLayoutRequested)
895+
{
896+
button.SetColorFilter(GetSearchHandlerTintColor(Page).ToPlatform(Colors.White), PorterDuff.Mode.SrcAtop);
897+
}
896898

897-
button.Dispose();
899+
button.Dispose();
900+
}
898901
}
899902
}
900903

901-
_platformToolbar.Post(RefreshNativeToolbarRegistrations);
904+
if (_platformToolbar?.IsAlive() == true)
905+
_platformToolbar.Post(RefreshNativeToolbarRegistrations);
902906
}
903907

904908
void OnSearchViewDetachedFromWindow(object sender, AView.ViewDetachedFromWindowEventArgs e)
905909
{
906-
if (SearchHandler?.SearchBoxVisibility == SearchBoxVisibility.Collapsible)
910+
if (_platformToolbar?.IsAlive() == true)
907911
_platformToolbar.Post(RefreshNativeToolbarRegistrations);
908912
}
909913

910914
void RefreshNativeToolbarRegistrations()
911915
{
912-
if (_disposed)
916+
if (_disposed ||
917+
_platformToolbar is null ||
918+
!_platformToolbar.IsAlive())
913919
return;
914920

921+
var searchExpanded =
922+
SearchHandler?.SearchBoxVisibility == SearchBoxVisibility.Expanded &&
923+
ReferenceEquals(_searchView?.View.Parent, _platformToolbar);
924+
(_toolbar as Toolbar)?.RefreshNativeElementRegistrationsForSearch(searchExpanded);
925+
if (searchExpanded)
926+
{
927+
_navigationRegistrationGeneration++;
928+
_nativeNavigationRegistrations.Clear();
929+
return;
930+
}
931+
915932
var page = Page;
916933
if (page is null)
917934
{

0 commit comments

Comments
 (0)