Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

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

Merged
merged 11 commits into from
Sep 27, 2019
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
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");
}

[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");
}

[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
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
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
{
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
ItemsSource = _items,
AutomationId = PickerId
};

Content = picker;
}

#if UITEST
[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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,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" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Bugzilla22229.xaml">
Expand Down
7 changes: 6 additions & 1 deletion Xamarin.Forms.Platform.Android/AppCompat/PickerRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
6 changes: 3 additions & 3 deletions Xamarin.Forms.Platform.Android/PickerManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
9 changes: 7 additions & 2 deletions Xamarin.Forms.Platform.Android/Renderers/PickerRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -119,7 +124,7 @@ void IPickerRenderer.OnClick()

if (_dialog != null)
return;

var picker = new NumberPicker(Context);
if (model.Items != null && model.Items.Any())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,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();
Expand Down