TaskCompletionSource и отклонение причин, по которым не отображаются несколько настраиваемых alertdialogs

У меня возникла проблема, связанная с объектом TaskCompletionSource и функцией Dismiss предупреждения. Эта проблема не появляется в версии IOS приложения

Когда приложение отправляет уведомление, два предупреждения с рабочей функциональностью будут отображаться, когда пользователь активирует приложение:

  • Аутентификация
  • Заполнение значения.

Однако, когда я вхожу в приложение, я получаю только проверку подлинности (потому что это предупреждение вызывается первым в приложении), и второе предупреждение никогда не появляется. Я уже пытался переопределить функцию Dismiss и установить для объекта ObjectCompletionSource значение null, но это заставляет одно и то же предупреждение появляться X раз до того, как приложение завершится с ошибкой. Есть ли способ повторить объект TaskCompletionSource, чтобы я мог видеть все предупреждения? Или какие модификации мне нужно сделать для функции Dismiss, чтобы объект TaskCompletion был завершен после отображения всех предупреждений?

Пример кода фрагмента Android:

public static readonly int AlertWidth = Device.Idiom == TargetIdiom.Phone ? 270 : 320;

    class AlertDialogFragment : DialogFragment
    {
        public string Title;
        public string Body;
        public View Content;
        public List<AlertButton> Buttons;
        public TaskCompletionSource<object> tsc;


        public Dialog AndroidCustomAlert(Activity activ)
        {
            Android.Views.LayoutInflater inflater = Android.Views.LayoutInflater.From(activ);
            Android.Views.View view = inflater.Inflate(Resource.Layout.AlertDialogLayout, null);

            AlertDialog.Builder builder = new AlertDialog.Builder(activ);
            builder.SetView(view);
            Android.Widget.TextView title = view.FindViewById<Android.Widget.TextView>(Resource.Id.Login);
            title.Text = Title;

            Android.Widget.TextView body = view.FindViewById<Android.Widget.TextView>(Resource.Id.pincodeText);
            body.Text = Body;
            body.MovementMethod = new Android.Text.Method.ScrollingMovementMethod();

            Android.Widget.EditText pincode = view.FindViewById<Android.Widget.EditText>(Resource.Id.pincodeEditText);
            Android.Widget.Button btnPositive = view.FindViewById<Android.Widget.Button>(Resource.Id.btnLoginLL);
            Android.Widget.Button btnNegative = view.FindViewById<Android.Widget.Button>(Resource.Id.btnClearLL);
            Android.Widget.Button btnNeutral = view.FindViewById<Android.Widget.Button>(Resource.Id.btnNeutral);

            if (Title.Contains("Time"))
            {
                Android.Views.View secondView = inflater.Inflate(Resource.Layout.TimePickerLayout, null);
                builder.SetView(secondView);

                btnPositive = secondView.FindViewById<Android.Widget.Button>(Resource.Id.btnLoginLL);
                btnNegative = secondView.FindViewById<Android.Widget.Button>(Resource.Id.btnClearLL);
                var tp = secondView.FindViewById<Android.Widget.TimePicker>(Resource.Id.timePicker1);
                tp.SetIs24HourView((Java.Lang.Boolean)true);
                //Positive button feedback
                btnPositive.Text = Buttons.Last().Text;
                btnPositive.Click += delegate
                {
                    var car = (Xamarin.Forms.TimePicker)Content;
                    var ts = new TimeSpan(tp.Hour, tp.Minute, 0);
                    car.Time = ts;

                    CommandsForButtons(Buttons.Last());
                };

                //Negative button feedback
                btnNegative.Text = Buttons.First().Text;
                btnNegative.Click += delegate
                {

                    CommandsForButtons(Buttons.First());
                };
            }
            else if (Title.Contains("How are you"))
            {
                btnPositive.Visibility = Android.Views.ViewStates.Gone;
                btnNegative.Visibility = Android.Views.ViewStates.Gone;
                btnNeutral.Visibility = Android.Views.ViewStates.Visible;
                pincode.Visibility = Android.Views.ViewStates.Gone;

                var happySlider = view.FindViewById<Android.Widget.SeekBar>(Resource.Id.happinessSlider);
                happySlider.SetProgress(5, false);
                happySlider.Visibility = Android.Views.ViewStates.Visible;
                btnNeutral.Text = Buttons.First().Text;
                btnNeutral.Click += delegate
                {
                    var car = (StackLayout)Content;

                    var layoutView = (Xamarin.Forms.AbsoluteLayout)car.Children[1];
                    var slider = (Slider)layoutView.Children[1];

                    var totalHappyValue = happySlider.Progress / 10;
                    slider.Value = totalHappyValue;

                    CommandsForButtons(Buttons.First());
                };
            }
            else
            {

                //Checks if there are no buttons, and if there aren't any, creates a neutral one
                if (Buttons == null || Buttons.Count == 0)
                {
                    btnPositive.Visibility = Android.Views.ViewStates.Gone;
                    btnNegative.Visibility = Android.Views.ViewStates.Gone;
                    btnNeutral.Visibility = Android.Views.ViewStates.Visible;
                    pincode.Visibility = Android.Views.ViewStates.Gone;

                    Buttons = new List<AlertButton> {
                new AlertButton {
                    Text = "Oké",
                    IsPreferred = true,
                    Action = () => false
                    }
                };
                    btnNeutral.Text = Buttons.First().Text;
                    btnNeutral.Click += delegate
                    {

                        CommandsForButtons(Buttons.First());
                    };
                }

                if (Content == null)
                {
                    pincode.Visibility = Android.Views.ViewStates.Gone;
                }

                //Positive button feedback
                btnPositive.Text = Buttons.Last().Text;
                btnPositive.Click += delegate
                {
                    var test = (StackLayout)Content;

                    if (test != null)
                    {
                        var car = (Entry)test.Children[0];
                        car.Text = pincode.Text;
                    }

                    CommandsForButtons(Buttons.Last());
                };

                //Negative button feedback
                btnNegative.Text = Buttons.First().Text;
                btnNegative.Click += delegate
                {

                    CommandsForButtons(Buttons.First());
                };
            }
            return builder.Create();
        }

        public void CommandsForButtons(AlertButton button)
        {
            Func<Task> dismiss = null;
            var command = new Command(async () =>
            {
                var ab = button;
                var cont = true;
                if (ab.Action != null)
                    cont = ab.Action();
                if (ab.ActionAsync != null)
                {
                    cont = cont && await ab.ActionAsync();
                }
                if (!cont)
                {
                    await dismiss();
                }
            });

            dismiss = async () =>
            {
                dismiss = async () => { };
                await Task.Run(() =>
                {
                    Dismiss();
                    tsc.SetResult(null);
                });

                Log.Debug("TSC", tsc.Task.Status.ToString());

            };

            command.Execute(this);
        }

        public override Dialog OnCreateDialog(Bundle savedInstanceState)
        {
            var test = AndroidCustomAlert(Activity);
            test.SetCanceledOnTouchOutside(false);
            return test;
        }

        public override void Dismiss()
        {
            base.Dismiss();

        }
    }

    public async Task Show(string title, string body, View content, List<AlertButton> buttons)
    {

        var tcs = new TaskCompletionSource<object>();

        var adf = new AlertDialogFragment
        {
            Title = title,
            Body = body,
            Content = content,
            Buttons = buttons,
            tsc = tcs
        };
        var FragmentManager = ((Activity)Forms.Context).FragmentManager;
        FragmentTransaction ft = FragmentManager.BeginTransaction();

        //Remove fragment else it will crash as it is already added to backstack
        Fragment prev = FragmentManager.FindFragmentByTag("alert");
        if (prev != null)
        {
            ft.Remove(prev);
        }

        ft.AddToBackStack(null);
        adf.Show(ft, "alert");
        await tcs.Task;
    }

Методы:

await Authentication();
await UserCheck();

И код IOS:

public static readonly int AlertWidth = Device.Idiom == TargetIdiom.Phone ? 270 : 320;

    public async Task Show(string title, string body, View content, List<AlertButton> buttons)
    {
        if (buttons == null || buttons.Count == 0)
        {
            buttons = new List<AlertButton> {
                new AlertButton {
                    Text = "Oké",
                    IsPreferred = true,
                    Action = () => false
                }
            };
        }

        Func<Task> dismiss = null;

        var captionSize = (double)StyleKit.PhoneDarkLabelStyles.Caption.Setters.First(s => s.Property == Label.FontSizeProperty).Value;
        var titleSize = (double)StyleKit.PhoneDarkLabelStyles.Title.Setters.First(s => s.Property == Label.FontSizeProperty).Value;

        var top = new StackLayout {
            Padding = new Thickness(15, 20, 15, 20),
            Spacing = 3,
            Children = {
                new Label {
                    Text = title,
                    Style = StyleKit.PhoneDarkLabelStyles.Title,
                    FontSize = Math.Max(16, titleSize),
                    HorizontalTextAlignment = TextAlignment.Center
                },
                new Label {
                    Text = body,
                    Style = StyleKit.PhoneDarkLabelStyles.Body,
                    //FontSize = ,
                    FontSize = Math.Max(14, captionSize),
                    HorizontalTextAlignment = TextAlignment.Center
                } ,
                new ContentView {
                    Padding = new Thickness(0,5,0,-10),
                    VerticalOptions = LayoutOptions.EndAndExpand,
                    Content = content
                } 
            }
        };

        var buttonViews = buttons.Select(ab => new Button {
            FontSize = Math.Max(16, titleSize),
            Text = ab.Text,
            FontAttributes = ab.IsPreferred ? FontAttributes.Bold : FontAttributes.None,
            TextColor = ab.IsDestructive ? Color.Red : Color.Default,
            Command = new Command(async () => {
                var cont = true;
                if (ab.Action != null)
                    cont = ab.Action();
                if (ab.ActionAsync != null)
                    cont = cont && await ab.ActionAsync();
                if (!cont)
                    await dismiss();
            })
        }).ToList();

        var grid = new Grid {
            RowDefinitions = {
                new RowDefinition { Height = GridLength.Auto },
                new RowDefinition { Height = GridLength.Auto }
            },
            ColumnSpacing = 0,
            RowSpacing = 0
        };
        buttons.ForEach(button => {
            grid.ColumnDefinitions.Add(
                new ColumnDefinition {
                    Width = AlertWidth / buttonViews.Count
                }
            );
        });

        for (int i = 0; i < buttonViews.Count; i++)
        {
            grid.Children.Add(new BorderView {
                BorderColor = Color.FromRgba(0,0,0,0.2),
                Thickness = new Thickness(0, 1, (i + 1 < buttonViews.Count) ? 1 : 0, 0)
            }, i, 1);
            grid.Children.Add(buttonViews[i], i, 1);
        }
        grid.Children.Add(top, 0, buttons.Count, 0, 1);

        var box = new Frame {
            WidthRequest = AlertWidth,
            BackgroundColor = Color.FromRgba(1,1,1,0.96),
            Padding = 0,
            Content = grid
        };
        var outer = new AbsoluteLayout {
            BackgroundColor = Color.FromRgba(0,0,0,0.65),
            Opacity = 0,
            Children = { box }
        };
        AbsoluteLayout.SetLayoutFlags(box, AbsoluteLayoutFlags.PositionProportional);
        AbsoluteLayout.SetLayoutBounds(box,
            new Rectangle(0.5, 0.5, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));

        var page = new ContentPage {
            Content = /* new ScrollView { Content = */ outer // }
        };

        var tcs = new TaskCompletionSource<object>();

        var topVC = UIApplication.SharedApplication.KeyWindow.RootViewController;
        while (topVC.PresentedViewController != null) {
            topVC = topVC.PresentedViewController;
        }

        var vc = page.CreateViewController();
        topVC.Add(vc.View);
        var innerView = vc.View.Subviews[0].Subviews[0];
        vc.View.RemoveFromSuperview();

        dismiss = async () => {
            dismiss = async () => {};
            await outer.FadeTo(0, 50);
            innerView.RemoveFromSuperview();
            tcs.SetResult(null);
        };

        topVC.Add(innerView);

        var kbh = new KeyboardHelper();
        kbh.KeyboardChanged += async (sender, e) => {
            await box.TranslateTo(0, e.Visible ? (-e.Height / 2f) : 0, 100, Easing.CubicInOut);
        };

        await outer.FadeTo(1, 100);

        await tcs.Task;
    }

Ответ 1

Это выглядит как и/или проблема между потоками или просто не устанавливает завершение задачи.

В версии IOS у вас есть

     dismiss = async () => {
        dismiss = async () => {};
        await outer.FadeTo(0, 50);
        innerView.RemoveFromSuperview();
        tcs.SetResult(null);
    };

В версии для Android нет соответствующего выражения tcs.SetResult(null);.

Также следует отметить асинхронный вызов, так как ожидание вызова SetResult будет блокировать основной поток без него.