summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorE.Z. Hart <hartez@users.noreply.github.com>2017-07-19 21:26:46 (GMT)
committerJason Smith <jason.smith@xamarin.com>2017-07-19 21:26:46 (GMT)
commit6de1c5c8d75449834419d5760d78e72fce4b251e (patch)
tree3775fe3055f7246c031f49fd5d00ff510a9a1df3
parent7b69ea969a3bd2b5346def3a0aa8eb7f3b93f2b1 (diff)
downloadxamarin-forms-6de1c5c8d75449834419d5760d78e72fce4b251e.zip
xamarin-forms-6de1c5c8d75449834419d5760d78e72fce4b251e.tar.gz
xamarin-forms-6de1c5c8d75449834419d5760d78e72fce4b251e.tar.bz2
[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
-rw-r--r--Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/PopAfterRemove.cs86
-rw-r--r--Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems1
-rw-r--r--Xamarin.Forms.Core.UITests.Shared/UITestCategories.cs1
-rw-r--r--Xamarin.Forms.Platform.Android/AppCompat/NavigationPageRenderer.cs53
4 files changed, 122 insertions, 19 deletions
diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/PopAfterRemove.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/PopAfterRemove.cs
new file mode 100644
index 0000000..c7508fb
--- /dev/null
+++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/PopAfterRemove.cs
@@ -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
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
index b4155f6..0495474 100644
--- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
+++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
@@ -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" />
diff --git a/Xamarin.Forms.Core.UITests.Shared/UITestCategories.cs b/Xamarin.Forms.Core.UITests.Shared/UITestCategories.cs
index cca59b5..2b2ff80 100644
--- a/Xamarin.Forms.Core.UITests.Shared/UITestCategories.cs
+++ b/Xamarin.Forms.Core.UITests.Shared/UITestCategories.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";
}
diff --git a/Xamarin.Forms.Platform.Android/AppCompat/NavigationPageRenderer.cs b/Xamarin.Forms.Platform.Android/AppCompat/NavigationPageRenderer.cs
index eb2da7a..bd96bf3 100644
--- a/Xamarin.Forms.Platform.Android/AppCompat/NavigationPageRenderer.cs
+++ b/Xamarin.Forms.Platform.Android/AppCompat/NavigationPageRenderer.cs
@@ -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();