Skip to content

Commit 1e89758

Browse files
[Android] Fix: Modal Animation Repeats When Returning from Background 2 (#29557)
* [Android] clean up modalnavigationmanager * fix failing tests * Handle animation while poping * subscribe to event before showing dailog * change style's names * apply new styles * set result to tcs once dailog dismiss
1 parent 4c1051e commit 1e89758

File tree

2 files changed

+53
-120
lines changed

2 files changed

+53
-120
lines changed

src/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.Android.cs

Lines changed: 46 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,14 @@
11
using System;
22
using System.Collections.Generic;
33
using System.ComponentModel;
4-
using System.Linq;
5-
using System.Runtime.CompilerServices;
64
using System.Threading.Tasks;
75
using Android.Content;
86
using Android.Graphics.Drawables;
97
using Android.OS;
10-
using Android.Runtime;
118
using Android.Views;
12-
using Android.Views.Animations;
139
using AndroidX.Activity;
14-
using AndroidX.AppCompat.App;
1510
using AndroidX.Fragment.App;
1611
using Microsoft.Maui.LifecycleEvents;
17-
using AAnimation = Android.Views.Animations.Animation;
1812
using AColor = Android.Graphics.Color;
1913
using AView = Android.Views.View;
2014

@@ -23,9 +17,6 @@ namespace Microsoft.Maui.Controls.Platform
2317
internal partial class ModalNavigationManager
2418
{
2519
ViewGroup? _modalParentView;
26-
bool _navAnimationInProgress;
27-
internal const string CloseContextActionsSignalName = "Xamarin.CloseContextActions";
28-
AAnimation? _dismissAnimation;
2920
bool _platformActivated;
3021

3122
readonly Stack<string> _modals = [];
@@ -44,8 +35,6 @@ void OnWindowPropertyChanging(object sender, PropertyChangingEventArgs e)
4435
return;
4536
}
4637

47-
var handler = _currentPage?.Handler;
48-
var windowP = _window.Page;
4938
if (CurrentPage is not null &&
5039
_window.Page != CurrentPage)
5140
{
@@ -89,76 +78,45 @@ internal void SetModalParentView(ViewGroup viewGroup)
8978
_modalParentView = viewGroup;
9079
}
9180

92-
ViewGroup GetModalParentView()
93-
{
94-
return _modalParentView ??
95-
_window?.PlatformActivity?.Window?.DecorView as ViewGroup ??
96-
throw new InvalidOperationException("Root View Needs to be set");
97-
}
98-
99-
// AFAICT this is specific to ListView and Context Items
100-
internal bool NavAnimationInProgress
101-
{
102-
get { return _navAnimationInProgress; }
103-
set
104-
{
105-
if (_navAnimationInProgress == value)
106-
return;
107-
_navAnimationInProgress = value;
108-
109-
#pragma warning disable CS0618 // TODO: Remove when we internalize/replace MessagingCenter
110-
if (value)
111-
MessagingCenter.Send(this, CloseContextActionsSignalName);
112-
#pragma warning restore CS0618 // Type or member is obsolete
113-
}
114-
}
115-
11681
Task<Page> PopModalPlatformAsync(bool animated)
11782
{
11883
Page modal = CurrentPlatformModalPage;
11984
_platformModalPages.Remove(modal);
12085

86+
TaskCompletionSource<Page> tcs = new();
87+
12188
var fragmentManager = WindowMauiContext.GetFragmentManager();
12289

12390
var dialogFragmentId = _modals.Pop();
12491
var dialogFragment = (ModalFragment?)fragmentManager.FindFragmentByTag(dialogFragmentId);
125-
126-
// If for the dialog is null what we want to do?
92+
12793
if (dialogFragment is null)
12894
{
129-
return Task.FromResult(modal);
95+
tcs.TrySetResult(modal);
96+
return tcs.Task;
13097
}
13198

132-
var source = new TaskCompletionSource<Page>();
99+
EventHandler? OnDialogDismiss = null;
133100

134-
if (animated && dialogFragment.View is not null)
101+
OnDialogDismiss = (_, _) =>
135102
{
136-
_dismissAnimation ??= AnimationUtils.LoadAnimation(WindowMauiContext.Context, Resource.Animation.nav_modal_default_exit_anim)!;
103+
dialogFragment.DialogDismissEvent -= OnDialogDismiss;
104+
tcs.TrySetResult(modal);
105+
};
137106

138-
_dismissAnimation.AnimationEnd += OnAnimationEnded;
107+
dialogFragment.DialogDismissEvent += OnDialogDismiss;
139108

140-
dialogFragment.View.StartAnimation(_dismissAnimation);
109+
if (animated)
110+
{
111+
dialogFragment.Dialog?.Window?.SetWindowAnimations(Resource.Style.modal_exit_animation);
112+
modal.Dispatcher.Dispatch(() => dialogFragment.Dismiss());
141113
}
142114
else
143115
{
144116
dialogFragment.Dismiss();
145-
source.TrySetResult(modal);
146117
}
147118

148-
return source.Task;
149-
150-
void OnAnimationEnded(object? sender, AAnimation.AnimationEndEventArgs e)
151-
{
152-
if (sender is not AAnimation animation)
153-
{
154-
return;
155-
}
156-
157-
animation.AnimationEnd -= OnAnimationEnded;
158-
dialogFragment.Dismiss();
159-
source.TrySetResult(modal);
160-
_dismissAnimation = null;
161-
}
119+
return tcs.Task;
162120
}
163121

164122
// The CurrentPage doesn't represent the root of the platform hierarchy.
@@ -192,8 +150,6 @@ async Task PresentModal(Page modal, bool animated)
192150
{
193151
TaskCompletionSource<bool> animationCompletionSource = new();
194152

195-
var parentView = GetModalParentView();
196-
197153
var dialogFragment = new ModalFragment(WindowMauiContext, modal)
198154
{
199155
Cancelable = false,
@@ -203,39 +159,30 @@ async Task PresentModal(Page modal, bool animated)
203159
var fragmentManager = WindowMauiContext.GetFragmentManager();
204160
var dialogFragmentId = AView.GenerateViewId().ToString();
205161
_modals.Push(dialogFragmentId);
206-
dialogFragment.Show(fragmentManager, dialogFragmentId);
207-
208-
if (animated)
209-
{
210-
NavAnimationInProgress = true;
211-
dialogFragment!.AnimationEnded += OnAnimationEnded;
212162

213-
await animationCompletionSource.Task;
214-
}
215-
else
216-
{
217-
NavAnimationInProgress = false;
218-
animationCompletionSource.TrySetResult(true);
219-
}
163+
EventHandler? OnDialogShown = null;
220164

221-
void OnAnimationEnded(object? sender, EventArgs e)
165+
OnDialogShown = (_, _) =>
222166
{
223-
dialogFragment!.AnimationEnded -= OnAnimationEnded;
224-
NavAnimationInProgress = false;
167+
dialogFragment!.DialogShowEvent -= OnDialogShown;
225168
animationCompletionSource.SetResult(true);
226-
}
169+
};
170+
171+
dialogFragment!.DialogShowEvent += OnDialogShown;
172+
173+
dialogFragment.Show(fragmentManager, dialogFragmentId);
174+
175+
await animationCompletionSource.Task;
227176
}
228177

229178
internal class ModalFragment : DialogFragment
230179
{
231180
Page _modal;
232181
IMauiContext _mauiWindowContext;
233182
NavigationRootManager? _navigationRootManager;
183+
public event EventHandler? DialogShowEvent;
184+
public event EventHandler? DialogDismissEvent;
234185
static readonly ColorDrawable TransparentColorDrawable = new(AColor.Transparent);
235-
bool _pendingAnimation = true;
236-
237-
public event EventHandler? AnimationEnded;
238-
239186

240187
public bool IsAnimated { get; internal set; }
241188

@@ -263,6 +210,20 @@ public ModalFragment(IMauiContext mauiContext, Page modal)
263210
{
264211
dialog.Window.SetSoftInputMode(attributes.SoftInputMode);
265212
}
213+
EventHandler? OnDialogShown = null;
214+
215+
OnDialogShown = (_, _) =>
216+
{
217+
dialog.ShowEvent -= OnDialogShown;
218+
DialogShowEvent?.Invoke(this, EventArgs.Empty);
219+
};
220+
221+
dialog.ShowEvent += OnDialogShown;
222+
223+
if (IsAnimated)
224+
{
225+
dialog.Window?.SetWindowAnimations(Resource.Style.modal_enter_animation);
226+
}
266227

267228
if (mainActivityWindow is not null)
268229
{
@@ -356,29 +317,13 @@ public override void OnStart()
356317
int width = ViewGroup.LayoutParams.MatchParent;
357318
int height = ViewGroup.LayoutParams.MatchParent;
358319
dialog.Window.SetLayout(width, height);
359-
360-
if (IsAnimated)
361-
{
362-
var animation = AnimationUtils.LoadAnimation(_mauiWindowContext.Context, Resource.Animation.nav_modal_default_enter_anim)!;
363-
View.StartAnimation(animation);
364-
365-
animation.AnimationEnd += OnAnimationEnded;
366-
}
367-
368-
void OnAnimationEnded(object? sender, AAnimation.AnimationEndEventArgs e)
369-
{
370-
if (sender is not AAnimation animation)
371-
{
372-
return;
373-
}
374-
375-
animation.AnimationEnd -= OnAnimationEnded;
376-
FireAnimationEnded();
377-
}
378320
}
379321

380322
public override void OnDismiss(IDialogInterface dialog)
381323
{
324+
base.OnDismiss(dialog);
325+
DialogDismissEvent?.Invoke(this, EventArgs.Empty);
326+
DialogDismissEvent = null;
382327
_modal.PropertyChanged -= OnModalPagePropertyChanged;
383328
_modal.HandlerChanged -= OnPageHandlerChanged;
384329

@@ -391,28 +336,9 @@ public override void OnDismiss(IDialogInterface dialog)
391336
_modal = null!;
392337
_mauiWindowContext = null!;
393338
_navigationRootManager?.Disconnect();
394-
_navigationRootManager = null;
395-
base.OnDismiss(dialog);
396-
}
397-
398-
public override void OnDestroy()
399-
{
400-
base.OnDestroy();
401-
FireAnimationEnded();
339+
_navigationRootManager = null;
402340
}
403341

404-
void FireAnimationEnded()
405-
{
406-
if (!_pendingAnimation)
407-
{
408-
return;
409-
}
410-
411-
_pendingAnimation = false;
412-
AnimationEnded?.Invoke(this, EventArgs.Empty);
413-
}
414-
415-
416342
sealed class CustomComponentDialog : ComponentDialog
417343
{
418344
public CustomComponentDialog(Context context, int themeResId) : base(context, themeResId)

src/Core/src/Platform/Android/Resources/values/styles.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,11 @@
101101
<style name="scrollViewTheme">
102102
<item name="scrollViewStyle">@style/scrollViewScrollBars</item>
103103
</style>
104+
105+
<style name="modal_enter_animation" >
106+
<item name="android:windowEnterAnimation">@anim/nav_modal_default_enter_anim</item>
107+
</style>
108+
<style name="modal_exit_animation" >
109+
<item name="android:windowExitAnimation">@anim/nav_modal_default_exit_anim</item>
110+
</style>
104111
</resources>

0 commit comments

Comments
 (0)