[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:
Родитель
4b60149912
Коммит
7d25f18233
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
Загрузка…
Ссылка в новой задаче