[Android] Handle fragment removal inside of the RemovePage method (#1053)

* Repro

* Handle fragment removal inside of RemovePage method

* Fix class name for test

* Split long strings
This commit is contained in:
E.Z. Hart 2017-07-19 15:26:46 -06:00 коммит произвёл Jason Smith
Родитель 7b69ea969a
Коммит 6de1c5c8d7
4 изменённых файлов: 122 добавлений и 19 удалений

Просмотреть файл

@ -0,0 +1,86 @@
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;
#if UITEST
using Xamarin.UITest;
using NUnit.Framework;
using Xamarin.Forms.Core.UITests;
#endif
namespace Xamarin.Forms.Controls.Issues
{
#if UITEST
[Category(UITestCategories.Navigation)]
#endif
[Preserve(AllMembers = true)]
[Issue(IssueTracker.None, 0101100101,
"PopAsync crashing after RemovePage when support packages are updated to 25.1.1",
PlatformAffected.Android)]
public class PopAfterRemove : TestNavigationPage
{
ContentPage _intermediate1;
ContentPage _intermediate2;
protected override async void Init()
{
_intermediate1 = Intermediate();
_intermediate2 = Intermediate();
await PushAsync(Root());
await PushAsync(_intermediate1);
await PushAsync(_intermediate2);
await PushAsync(Last());
}
const string StartTest = "Start Test";
const string RootLabel = "Root";
ContentPage Last()
{
var test = new Button { Text = StartTest };
var instructions = new Label
{
Text =
$"Tap the button labeled '{StartTest}'. The app should navigate to a page displaying the label "
+ $"'{RootLabel}'. If the application crashes, the test has failed."
};
var layout = new StackLayout();
layout.Children.Add(instructions);
layout.Children.Add(test);
test.Clicked += (sender, args) =>
{
Navigation.RemovePage(_intermediate2);
Navigation.RemovePage(_intermediate1);
Navigation.PopAsync(true);
};
return new ContentPage { Content = layout };
}
static ContentPage Root()
{
return new ContentPage { Content = new Label { Text = RootLabel } };
}
static ContentPage Intermediate()
{
return new ContentPage { Content = new Label { Text = "Page" } };
}
#if UITEST
[Test]
public void PopAsyncAfterRemovePageDoesNotCrash()
{
RunningApp.WaitForElement(StartTest);
RunningApp.Tap(StartTest);
RunningApp.WaitForElement(RootLabel);
}
#endif
}
}

Просмотреть файл

@ -271,6 +271,7 @@
<DependentUpon>PlatformSpecifics_iOSTranslucentNavBarX.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)PopAfterRemove.cs" />
<Compile Include="$(MSBuildThisFileDirectory)TestPages\ScreenshotConditionalApp.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla41842.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla42277.cs" />

Просмотреть файл

@ -33,6 +33,7 @@
public const string InputTransparent = "InputTransparent";
public const string IsEnabled = "IsEnabled";
public const string Gestures = "Gestures";
public const string Navigation = "Navigation";
public const string ManualReview = "ManualReview";
}

Просмотреть файл

@ -79,7 +79,7 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
FragmentManager FragmentManager => _fragmentManager ?? (_fragmentManager = ((FormsAppCompatActivity)Context).SupportFragmentManager);
IPageController PageController => Element as IPageController;
IPageController PageController => Element;
bool ToolbarVisible
{
@ -437,11 +437,6 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
ResetToolbar();
}
void FilterPageFragment(Page page)
{
_fragmentStack.RemoveAll(f => ((FragmentContainer)f).Page == page);
}
void HandleToolbarItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == MenuItem.IsEnabledProperty.PropertyName || e.PropertyName == MenuItem.TextProperty.PropertyName || e.PropertyName == MenuItem.IconProperty.PropertyName)
@ -552,30 +547,50 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
_drawerToggle.DrawerIndicatorEnabled = true;
}
Fragment GetPageFragment(Page page)
{
for (int n = 0; n < _fragmentStack.Count; n++)
{
if ((_fragmentStack[n] as FragmentContainer)?.Page == page)
{
return _fragmentStack[n];
}
}
return null;
}
void RemovePage(Page page)
{
Fragment fragment = GetPageFragment(page);
if (fragment == null)
{
return;
}
// Go ahead and take care of the fragment bookkeeping for the page being removed
FragmentTransaction transaction = FragmentManager.BeginTransaction();
transaction.DisallowAddToBackStack();
transaction.Remove(fragment);
transaction.CommitAllowingStateLoss();
FragmentManager.ExecutePendingTransactions();
// And remove the fragment from our own stack
_fragmentStack.Remove(fragment);
// Now handle all the XF removal/cleanup
IVisualElementRenderer rendererToRemove = Android.Platform.GetRenderer(page);
if (rendererToRemove != null)
{
var containerToRemove = (PageContainer)rendererToRemove.View.Parent;
rendererToRemove.View?.RemoveFromParent();
rendererToRemove?.Dispose();
// This causes a NullPointerException in API 25.1+ when we later call ExecutePendingTransactions();
// We may want to remove this in the future if it is resolved in the Android SDK.
if ((int)Build.VERSION.SdkInt < 25)
{
containerToRemove?.RemoveFromParent();
containerToRemove?.Dispose();
}
containerToRemove?.RemoveFromParent();
containerToRemove?.Dispose();
}
// Also remove this page from the fragmentStack
FilterPageFragment(page);
Device.StartTimer(TimeSpan.FromMilliseconds(10), () =>
{
UpdateToolbar();