diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue5159.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue5159.cs new file mode 100644 index 00000000000..d7415c34109 --- /dev/null +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue5159.cs @@ -0,0 +1,164 @@ +using System; +using Xamarin.Forms.Internals; +using Xamarin.Forms.CustomAttributes; +#if UITEST +using Xamarin.Forms.Core.UITests; +using Xamarin.UITest; +using NUnit.Framework; +#endif + +namespace Xamarin.Forms.Controls.Issues +{ +#if UITEST + [Category(UITestCategories.Picker)] + [Category(UITestCategories.DatePicker)] + [Category(UITestCategories.TimePicker)] +#endif + [Preserve(AllMembers = true)] + [Issue(IssueTracker.Github, 5159, "[Android] Calling Focus on all Pickers running an API 28 devices no longer opens Picker", PlatformAffected.Android)] + public class Issue5159 : TestContentPage + { + const string DatePickerButton = "DatePickerButton"; + const string TimePickerButton = "TimePickerButton"; + const string PickerButton = "PickerButton"; + readonly string[] _pickerValues = { "Foo", "Bar", "42", "1337" }; + + protected override void Init() + { + var stackLayout = new StackLayout + { + VerticalOptions = LayoutOptions.Center, + HorizontalOptions = LayoutOptions.Center + }; + + // DatePicker + var datePickerButton = new Button + { + Text = "Show DatePicker", + AutomationId = DatePickerButton + }; + + var datePicker = new DatePicker + { + IsVisible = false + }; + + datePickerButton.Clicked += (s, a) => + { + Device.BeginInvokeOnMainThread(() => + { + if (datePicker.IsFocused) + datePicker.Unfocus(); + + datePicker.Focus(); + }); + }; + + // TimePicker + var timePickerButton = new Button + { + Text = "Show TimePicker", + AutomationId = TimePickerButton + }; + + var timePicker = new TimePicker + { + IsVisible = false + }; + + timePickerButton.Clicked += (s, a) => + { + Device.BeginInvokeOnMainThread(() => + { + if (timePicker.IsFocused) + timePicker.Unfocus(); + + timePicker.Focus(); + }); + }; + + // Picker + var pickerButton = new Button + { + Text = "Show Picker", + AutomationId = PickerButton + }; + + var picker = new Picker + { + IsVisible = false, + ItemsSource = _pickerValues + }; + + pickerButton.Clicked += (s, a) => + { + Device.BeginInvokeOnMainThread(() => + { + if (picker.IsFocused) + picker.Unfocus(); + + picker.Focus(); + }); + }; + + stackLayout.Children.Add(datePickerButton); + stackLayout.Children.Add(datePicker); + + stackLayout.Children.Add(timePickerButton); + stackLayout.Children.Add(timePicker); + + stackLayout.Children.Add(pickerButton); + stackLayout.Children.Add(picker); + + Content = stackLayout; + } + +#if UITEST && __ANDROID__ + [Test] + [UiTest(typeof(DatePicker))] + public void InvisibleDatepickerShowsDialogOnFocus() + { + RunningApp.WaitForElement(DatePickerButton); + RunningApp.Screenshot("Issue 5159 page is showing in all it's glory"); + RunningApp.Tap(DatePickerButton); + + RunningApp.WaitForElement(x => x.Class("DatePicker")); + + RunningApp.Screenshot("DatePicker is shown"); + RunningApp.TapCoordinates(5, 100); + } + + [Test] + [UiTest(typeof(TimePicker))] + public void InvisibleTimepickerShowsDialogOnFocus() + { + RunningApp.WaitForElement(TimePickerButton); + RunningApp.Screenshot("Issue 5159 page is showing in all it's glory"); + RunningApp.Tap(TimePickerButton); + + RunningApp.WaitForElement(x => x.Class("timePicker")); + + RunningApp.Screenshot("TimePicker is shown"); + RunningApp.TapCoordinates(5, 100); + } + + [Test] + [UiTest(typeof(Picker))] + public void InvisiblePickerShowsDialogOnFocus() + { + RunningApp.WaitForElement(PickerButton); + RunningApp.Screenshot("Issue 5159 page is showing in all it's glory"); + RunningApp.Tap(PickerButton); + + RunningApp.WaitForElement("Foo"); + + RunningApp.Screenshot("Picker is shown"); + + RunningApp.Tap("Foo"); + + RunningApp.WaitForNoElement("Foo"); + + } +#endif + } +} diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue7311.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue7311.cs new file mode 100644 index 00000000000..6f3ce3e42bc --- /dev/null +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue7311.cs @@ -0,0 +1,63 @@ +using System; +using Xamarin.Forms.CustomAttributes; +using Xamarin.Forms.Internals; + +#if UITEST +using Xamarin.UITest; +using NUnit.Framework; +using Xamarin.UITest.iOS; +#endif + +namespace Xamarin.Forms.Controls.Issues +{ + [Preserve(AllMembers = true)] + [Issue(IssueTracker.Github, 7311, "[Bug] [Android] Error back hardware button with Picker", PlatformAffected.Android)] + public class Issue7311 : TestContentPage + { + const string FirstPickerItem = "Uno"; + const string PickerId = "CaptainPickard"; + readonly string[] _items = { FirstPickerItem, "Dos", "Tres" }; + + protected override void Init() + { + var picker = new Picker + { + ItemsSource = _items, + AutomationId = PickerId + }; + + Content = new StackLayout() + { + Children = + { + new Label() + { + Text = "Open Picker. Click hardware back button to close picker. Click hardware button a second time and it should navigate back to gallery" + }, + picker + } + }; + } + +#if UITEST && __ANDROID__ + [Test] + public void OpeningPickerPressingBackButtonTwiceShouldNotOpenPickerAgain() + { + RunningApp.WaitForElement(PickerId); + RunningApp.Tap(PickerId); + + RunningApp.WaitForElement(FirstPickerItem); + + RunningApp.Back(); + + RunningApp.WaitForNoElement(FirstPickerItem); + + RunningApp.Back(); + + RunningApp.WaitForNoElement(FirstPickerItem, "Picker is again visible after second back button press", TimeSpan.FromSeconds(10)); + + RunningApp.Screenshot("Back at the previous page, not showing the picker again"); + } +#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 3fa552e83b7..81ec9517289 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 @@ -1016,6 +1016,8 @@ + + diff --git a/Xamarin.Forms.Platform.Android/AppCompat/PickerRenderer.cs b/Xamarin.Forms.Platform.Android/AppCompat/PickerRenderer.cs index 0ae620026f1..e46ab893dc0 100644 --- a/Xamarin.Forms.Platform.Android/AppCompat/PickerRenderer.cs +++ b/Xamarin.Forms.Platform.Android/AppCompat/PickerRenderer.cs @@ -90,7 +90,12 @@ protected override void OnFocusChangeRequested(object sender, VisualElement.Focu base.OnFocusChangeRequested(sender, e); if (e.Focus) - CallOnClick(); + { + if (Clickable) + CallOnClick(); + else + ((IPickerRenderer)this)?.OnClick(); + } else if (_dialog != null) { _dialog.Hide(); diff --git a/Xamarin.Forms.Platform.Android/PickerManager.cs b/Xamarin.Forms.Platform.Android/PickerManager.cs index 471494f45e9..aefbd7954a3 100644 --- a/Xamarin.Forms.Platform.Android/PickerManager.cs +++ b/Xamarin.Forms.Platform.Android/PickerManager.cs @@ -10,8 +10,8 @@ namespace Xamarin.Forms.Platform.Android { internal static class PickerManager { - readonly static HashSet availableKeys = new HashSet(new[] { - Keycode.Tab, Keycode.Forward, Keycode.Back, Keycode.DpadDown, Keycode.DpadLeft, Keycode.DpadRight, Keycode.DpadUp + readonly static HashSet AvailableKeys = new HashSet(new[] { + Keycode.Tab, Keycode.Forward, Keycode.DpadDown, Keycode.DpadLeft, Keycode.DpadRight, Keycode.DpadUp }); public static void Init(EditText editText) @@ -42,7 +42,7 @@ public static void OnFocusChanged(bool gainFocus, EditText sender, IPopupTrigger static void OnKeyPress(object sender, AView.KeyEventArgs e) { - if (!availableKeys.Contains(e.KeyCode)) + if (!AvailableKeys.Contains(e.KeyCode)) { e.Handled = false; return; diff --git a/Xamarin.Forms.Platform.Android/Renderers/DatePickerRenderer.cs b/Xamarin.Forms.Platform.Android/Renderers/DatePickerRenderer.cs index cce0a5bcf42..be04ecb0068 100644 --- a/Xamarin.Forms.Platform.Android/Renderers/DatePickerRenderer.cs +++ b/Xamarin.Forms.Platform.Android/Renderers/DatePickerRenderer.cs @@ -94,7 +94,12 @@ protected override void OnFocusChangeRequested(object sender, VisualElement.Focu base.OnFocusChangeRequested(sender, e); if (e.Focus) - CallOnClick(); + { + if (Clickable) + CallOnClick(); + else + ((IPickerRenderer)this)?.OnClick(); + } else if (_dialog != null) { _dialog.Hide(); diff --git a/Xamarin.Forms.Platform.Android/Renderers/PickerRenderer.cs b/Xamarin.Forms.Platform.Android/Renderers/PickerRenderer.cs index 26d25d6154c..4c0e2aab3f1 100644 --- a/Xamarin.Forms.Platform.Android/Renderers/PickerRenderer.cs +++ b/Xamarin.Forms.Platform.Android/Renderers/PickerRenderer.cs @@ -104,7 +104,12 @@ protected override void OnFocusChangeRequested(object sender, VisualElement.Focu base.OnFocusChangeRequested(sender, e); if (e.Focus) - CallOnClick(); + { + if (Clickable) + CallOnClick(); + else + ((IPickerRenderer)this)?.OnClick(); + } else if (_dialog != null) { _dialog.Hide(); @@ -119,7 +124,7 @@ void IPickerRenderer.OnClick() if (_dialog != null) return; - + var picker = new NumberPicker(Context); if (model.Items != null && model.Items.Any()) { diff --git a/Xamarin.Forms.Platform.Android/Renderers/TimePickerRenderer.cs b/Xamarin.Forms.Platform.Android/Renderers/TimePickerRenderer.cs index d0ed9fc7d2b..1ce033e38e9 100644 --- a/Xamarin.Forms.Platform.Android/Renderers/TimePickerRenderer.cs +++ b/Xamarin.Forms.Platform.Android/Renderers/TimePickerRenderer.cs @@ -16,6 +16,7 @@ public abstract class TimePickerRendererBase : ViewRenderer