[Android] 28+ Make non-visible pickers work again (#7289)

* Repro + fix

* Final fix

* Update Issue5159.cs

* Fixes #7311

* Test tweaks

* Update TimePickerRenderer.cs

* Revert "Update TimePickerRenderer.cs"

This reverts commit 06c1172d2501ec533cdf5b1eabafa1d402687980.

* Update TimePickerRenderer.cs

* Update TimePickerRenderer.cs

* - added instructions
This commit is contained in:
Gerald Versluis 2019-09-26 22:18:17 -07:00 коммит произвёл Shane Neuville
Родитель 4b60149912
Коммит 7d25f18233
8 изменённых файлов: 278 добавлений и 8 удалений

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

@ -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
}
}

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

@ -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
}
}

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

@ -1019,6 +1019,8 @@
<Compile Include="$(MSBuildThisFileDirectory)GitHub6926.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue5503.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ShellTitleView.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue5159.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue7311.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue7053.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue6894.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue6929.cs" />

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

@ -90,7 +90,12 @@ namespace Xamarin.Forms.Platform.Android.AppCompat
base.OnFocusChangeRequested(sender, e);
if (e.Focus)
CallOnClick();
{
if (Clickable)
CallOnClick();
else
((IPickerRenderer)this)?.OnClick();
}
else if (_dialog != null)
{
_dialog.Hide();

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

@ -10,8 +10,8 @@ namespace Xamarin.Forms.Platform.Android
{
internal static class PickerManager
{
readonly static HashSet<Keycode> availableKeys = new HashSet<Keycode>(new[] {
Keycode.Tab, Keycode.Forward, Keycode.Back, Keycode.DpadDown, Keycode.DpadLeft, Keycode.DpadRight, Keycode.DpadUp
readonly static HashSet<Keycode> AvailableKeys = new HashSet<Keycode>(new[] {
Keycode.Tab, Keycode.Forward, Keycode.DpadDown, Keycode.DpadLeft, Keycode.DpadRight, Keycode.DpadUp
});
public static void Init(EditText editText)
@ -42,7 +42,7 @@ namespace Xamarin.Forms.Platform.Android
static void OnKeyPress(object sender, AView.KeyEventArgs e)
{
if (!availableKeys.Contains(e.KeyCode))
if (!AvailableKeys.Contains(e.KeyCode))
{
e.Handled = false;
return;

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

@ -94,7 +94,12 @@ namespace Xamarin.Forms.Platform.Android
base.OnFocusChangeRequested(sender, e);
if (e.Focus)
CallOnClick();
{
if (Clickable)
CallOnClick();
else
((IPickerRenderer)this)?.OnClick();
}
else if (_dialog != null)
{
_dialog.Hide();

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

@ -104,7 +104,12 @@ namespace Xamarin.Forms.Platform.Android
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 @@ namespace Xamarin.Forms.Platform.Android
if (_dialog != null)
return;
var picker = new NumberPicker(Context);
if (model.Items != null && model.Items.Any())
{

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

@ -16,6 +16,7 @@ namespace Xamarin.Forms.Platform.Android
{
int _originalHintTextColor;
AlertDialog _dialog;
bool _isDisposed;
bool Is24HourView
{
@ -86,7 +87,12 @@ namespace Xamarin.Forms.Platform.Android
base.OnFocusChangeRequested(sender, e);
if (e.Focus)
CallOnClick();
{
if (Clickable)
CallOnClick();
else
((IPickerRenderer)this)?.OnClick();
}
else if (_dialog != null)
{
_dialog.Hide();
@ -95,6 +101,7 @@ namespace Xamarin.Forms.Platform.Android
if (Forms.IsLollipopOrNewer)
_dialog.CancelEvent -= OnCancelButtonClicked;
_dialog?.Dispose();
_dialog = null;
}
}
@ -109,6 +116,25 @@ namespace Xamarin.Forms.Platform.Android
return dialog;
}
protected override void Dispose(bool disposing)
{
if (_isDisposed)
return;
_isDisposed = true;
if (disposing)
{
if (Forms.IsLollipopOrNewer && _dialog.IsAlive())
_dialog.CancelEvent -= OnCancelButtonClicked;
_dialog?.Dispose();
_dialog = null;
}
base.Dispose(disposing);
}
void IPickerRenderer.OnClick()
{
if (_dialog != null && _dialog.IsShowing)