From 5e2c75fb08dee97dea2aa6c043095509ee9f3e3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Fri, 12 Mar 2021 01:43:21 +0100 Subject: [PATCH 01/30] Base code for handlers. --- src/Core/src/Core/ITextInput.cs | 5 +++++ src/Core/src/Handlers/Entry/EntryHandler.Android.cs | 5 +++++ src/Core/src/Handlers/Entry/EntryHandler.Standard.cs | 1 + src/Core/src/Handlers/Entry/EntryHandler.cs | 3 ++- src/Core/src/Handlers/Entry/EntryHandler.iOS.cs | 5 +++++ 5 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Core/src/Core/ITextInput.cs b/src/Core/src/Core/ITextInput.cs index c8a269041e9c..33c0c2575243 100644 --- a/src/Core/src/Core/ITextInput.cs +++ b/src/Core/src/Core/ITextInput.cs @@ -19,5 +19,10 @@ public interface ITextInput : IText /// Gets a value indicating whether or not the view is read-only. /// bool IsReadOnly { get; } + + /// + /// Gets the keyboard type for the given input control. + /// + Keyboard Keyboard{ get; } } } \ No newline at end of file diff --git a/src/Core/src/Handlers/Entry/EntryHandler.Android.cs b/src/Core/src/Handlers/Entry/EntryHandler.Android.cs index e75eb9d414ba..c1dc8368993f 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.Android.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.Android.cs @@ -63,6 +63,11 @@ public static void MapIsReadOnly(EntryHandler handler, IEntry entry) handler.TypedNativeView?.UpdateIsReadOnly(entry); } + public static void MapKeyboard(EntryHandler handler, IEntry entry) + { + handler.TypedNativeView?.UpdateKeyboard(entry); + } + void OnTextChanged(string? text) { if (VirtualView == null || TypedNativeView == null) diff --git a/src/Core/src/Handlers/Entry/EntryHandler.Standard.cs b/src/Core/src/Handlers/Entry/EntryHandler.Standard.cs index 8a57d80761ca..ef3927ba62f4 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.Standard.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.Standard.cs @@ -12,5 +12,6 @@ public static void MapIsPassword(IViewHandler handler, IEntry entry) { } public static void MapIsTextPredictionEnabled(IViewHandler handler, IEntry entry) { } public static void MapPlaceholder(IViewHandler handler, IEntry entry) { } public static void MapIsReadOnly(IViewHandler handler, IEntry entry) { } + public static void MapKeyboard(IViewHandler handler, IEntry entry) { } } } \ No newline at end of file diff --git a/src/Core/src/Handlers/Entry/EntryHandler.cs b/src/Core/src/Handlers/Entry/EntryHandler.cs index 95a7515bdca8..0091cc64f813 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.cs @@ -9,7 +9,8 @@ public partial class EntryHandler [nameof(IEntry.IsPassword)] = MapIsPassword, [nameof(IEntry.IsTextPredictionEnabled)] = MapIsTextPredictionEnabled, [nameof(IEntry.Placeholder)] = MapPlaceholder, - [nameof(IEntry.IsReadOnly)] = MapIsReadOnly + [nameof(IEntry.IsReadOnly)] = MapIsReadOnly, + [nameof(IEntry.IsReadOnly)] = MapKeyboard }; public EntryHandler() : base(EntryMapper) diff --git a/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs b/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs index 8043df913f2e..0770fd51032c 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.iOS.cs @@ -71,6 +71,11 @@ public static void MapIsReadOnly(EntryHandler handler, IEntry entry) handler.TypedNativeView?.UpdateIsReadOnly(entry); } + public static void MapKeyboard(EntryHandler handler, IEntry entry) + { + handler.TypedNativeView?.UpdateKeyboard(entry); + } + void OnEditingChanged(object? sender, EventArgs e) => OnTextChanged(); void OnEditingEnded(object? sender, EventArgs e) => OnTextChanged(); From c53ac07385f6edd3ac7bca262935cf88382a9fcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Fri, 12 Mar 2021 01:43:53 +0100 Subject: [PATCH 02/30] Platform specific extension parts in the handlers and partially implementing Android handler. --- .../src/Platform/Android/EntryExtensions.cs | 60 +++++++++--- .../Platform/Android/KeyboardExtensions.cs | 94 +++++++++++++++++++ src/Core/src/Platform/iOS/EntryExtensions.cs | 5 + 3 files changed, 148 insertions(+), 11 deletions(-) create mode 100644 src/Core/src/Platform/Android/KeyboardExtensions.cs diff --git a/src/Core/src/Platform/Android/EntryExtensions.cs b/src/Core/src/Platform/Android/EntryExtensions.cs index ce2fbeb938bc..5d2a0f2beb5e 100644 --- a/src/Core/src/Platform/Android/EntryExtensions.cs +++ b/src/Core/src/Platform/Android/EntryExtensions.cs @@ -1,6 +1,8 @@ using Android.Content.Res; using Android.Text; +using Android.Text.Method; using Android.Widget; +using Microsoft.Maui.Platform.Android; namespace Microsoft.Maui { @@ -46,20 +48,43 @@ public static void UpdateIsPassword(this EditText editText, IEntry entry) internal static void SetInputType(this EditText editText, IEntry entry) { - editText.InputType = InputTypes.ClassText; - editText.InputType |= InputTypes.TextFlagMultiLine; + if (entry.IsReadOnly) + editText.InputType = InputTypes.Null; + else + { + var keyboard = entry.Keyboard; + var nativeInputTypeToUpdate = keyboard.ToInputType(); - if (entry.IsPassword && ((editText.InputType & InputTypes.ClassText) == InputTypes.ClassText)) - editText.InputType |= InputTypes.TextVariationPassword; + if (!(keyboard is CustomKeyboard)) + { + // TODO: IsSpellCheckEnabled handling must be here. - if (entry.IsPassword && ((editText.InputType & InputTypes.ClassNumber) == InputTypes.ClassNumber)) - editText.InputType |= InputTypes.NumberVariationPassword; + if ((nativeInputTypeToUpdate & InputTypes.TextFlagNoSuggestions) != InputTypes.TextFlagNoSuggestions) + { + if (!entry.IsTextPredictionEnabled) + nativeInputTypeToUpdate = nativeInputTypeToUpdate | InputTypes.TextFlagNoSuggestions; + } + } - if (!entry.IsTextPredictionEnabled && ((editText.InputType & InputTypes.TextFlagNoSuggestions) != InputTypes.TextFlagNoSuggestions)) - editText.InputType |= InputTypes.TextFlagNoSuggestions; - - if (entry.IsReadOnly) - editText.InputType = InputTypes.Null; + if (keyboard == Keyboard.Numeric) + { + // editText.KeyListener = GetDigitsKeyListener(editText.InputType); + } + + if (entry.IsPassword) + { + if (((nativeInputTypeToUpdate & InputTypes.ClassText) == InputTypes.ClassText)) + nativeInputTypeToUpdate = nativeInputTypeToUpdate | InputTypes.TextVariationPassword; + + if (((nativeInputTypeToUpdate & InputTypes.ClassNumber) == InputTypes.ClassNumber)) + nativeInputTypeToUpdate = nativeInputTypeToUpdate | InputTypes.NumberVariationPassword; + } + + if (!entry.IsTextPredictionEnabled && ((nativeInputTypeToUpdate & InputTypes.TextFlagNoSuggestions) != InputTypes.TextFlagNoSuggestions)) + nativeInputTypeToUpdate |= InputTypes.TextFlagNoSuggestions; + + editText.InputType = nativeInputTypeToUpdate; + } } public static void UpdateIsTextPredictionEnabled(this EditText editText, IEntry entry) @@ -67,6 +92,11 @@ public static void UpdateIsTextPredictionEnabled(this EditText editText, IEntry editText.SetInputType(entry); } + public static void UpdateKeyboard(this EditText editText, IEntry entry) + { + editText.SetInputType(entry); + } + public static void UpdatePlaceholder(this EditText editText, IEntry entry) { if (editText.Hint == entry.Placeholder) @@ -84,5 +114,13 @@ public static void UpdateIsReadOnly(this EditText editText, IEntry entry) editText.FocusableInTouchMode = isEditable; editText.Focusable = isEditable; } + + //protected virtual NumberKeyListener GetDigitsKeyListener(InputTypes inputTypes) + //{ + // // Override this in a custom renderer to use a different NumberKeyListener + // // or to filter out input types you don't want to allow + // // (e.g., inputTypes &= ~InputTypes.NumberFlagSigned to disallow the sign) + // return LocalizedDigitsKeyListener.Create(inputTypes); + //} } } diff --git a/src/Core/src/Platform/Android/KeyboardExtensions.cs b/src/Core/src/Platform/Android/KeyboardExtensions.cs new file mode 100644 index 000000000000..639c8b48fcb1 --- /dev/null +++ b/src/Core/src/Platform/Android/KeyboardExtensions.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Android.Text; + +namespace Microsoft.Maui.Platform.Android +{ + public static class KeyboardExtensions + { + public static InputTypes ToInputType(this Keyboard self) + { + var result = new InputTypes(); + + // ClassText: !autocaps, spellcheck, suggestions + // TextFlagNoSuggestions: !autocaps, !spellcheck, !suggestions + // InputTypes.ClassText | InputTypes.TextFlagCapSentences autocaps, spellcheck, suggestions + // InputTypes.ClassText | InputTypes.TextFlagCapSentences | InputTypes.TextFlagNoSuggestions; autocaps, !spellcheck, !suggestions + + if (self == Keyboard.Default) + result = InputTypes.ClassText | InputTypes.TextVariationNormal; + else if (self == Keyboard.Chat) + result = InputTypes.ClassText | InputTypes.TextFlagCapSentences | InputTypes.TextFlagNoSuggestions; + else if (self == Keyboard.Email) + result = InputTypes.ClassText | InputTypes.TextVariationEmailAddress; + else if (self == Keyboard.Numeric) + result = InputTypes.ClassNumber | InputTypes.NumberFlagDecimal | InputTypes.NumberFlagSigned; + else if (self == Keyboard.Telephone) + result = InputTypes.ClassPhone; + else if (self == Keyboard.Text) + result = InputTypes.ClassText | InputTypes.TextFlagCapSentences; + else if (self == Keyboard.Url) + result = InputTypes.ClassText | InputTypes.TextVariationUri; + else if (self is CustomKeyboard) + { + var custom = (CustomKeyboard)self; + var capitalizedSentenceEnabled = (custom.Flags & KeyboardFlags.CapitalizeSentence) == KeyboardFlags.CapitalizeSentence; + var capitalizedWordsEnabled = (custom.Flags & KeyboardFlags.CapitalizeWord) == KeyboardFlags.CapitalizeWord; + var capitalizedCharacterEnabled = (custom.Flags & KeyboardFlags.CapitalizeCharacter) == KeyboardFlags.CapitalizeCharacter; + + var spellcheckEnabled = (custom.Flags & KeyboardFlags.Spellcheck) == KeyboardFlags.Spellcheck; + var suggestionsEnabled = (custom.Flags & KeyboardFlags.Suggestions) == KeyboardFlags.Suggestions; + + if (!capitalizedSentenceEnabled && !spellcheckEnabled && !suggestionsEnabled) + result = InputTypes.ClassText | InputTypes.TextFlagNoSuggestions; + + if (!capitalizedSentenceEnabled && !spellcheckEnabled && suggestionsEnabled) + { + // Due to the nature of android, TextFlagAutoCorrect includes Spellcheck + // Log.Warning(null, "On Android, KeyboardFlags.Suggestions enables KeyboardFlags.Spellcheck as well due to a platform limitation."); + result = InputTypes.ClassText | InputTypes.TextFlagAutoCorrect; + } + + if (!capitalizedSentenceEnabled && spellcheckEnabled && !suggestionsEnabled) + result = InputTypes.ClassText | InputTypes.TextFlagAutoComplete; + + if (!capitalizedSentenceEnabled && spellcheckEnabled && suggestionsEnabled) + result = InputTypes.ClassText | InputTypes.TextFlagAutoCorrect; + + if (capitalizedSentenceEnabled && !spellcheckEnabled && !suggestionsEnabled) + result = InputTypes.ClassText | InputTypes.TextFlagCapSentences | InputTypes.TextFlagNoSuggestions; + + if (capitalizedSentenceEnabled && !spellcheckEnabled && suggestionsEnabled) + { + // Due to the nature of android, TextFlagAutoCorrect includes Spellcheck + // Log.Warning(null, "On Android, KeyboardFlags.Suggestions enables KeyboardFlags.Spellcheck as well due to a platform limitation."); + result = InputTypes.ClassText | InputTypes.TextFlagCapSentences | InputTypes.TextFlagAutoCorrect; + } + + if (capitalizedSentenceEnabled && spellcheckEnabled && !suggestionsEnabled) + result = InputTypes.ClassText | InputTypes.TextFlagCapSentences | InputTypes.TextFlagAutoComplete; + + if (capitalizedSentenceEnabled && spellcheckEnabled && suggestionsEnabled) + result = InputTypes.ClassText | InputTypes.TextFlagCapSentences | InputTypes.TextFlagAutoCorrect; + + // All existed before these settings. This ensures these changes are backwards compatible + // without this check TextFlagCapCharacters would win + if (custom.Flags != KeyboardFlags.All) + { + if (capitalizedWordsEnabled) + result = result | InputTypes.TextFlagCapWords; + + if (capitalizedCharacterEnabled) + result = result | InputTypes.TextFlagCapCharacters; + } + } + else + { + // Should never happens + result = InputTypes.TextVariationNormal; + } + return result; + } + } +} diff --git a/src/Core/src/Platform/iOS/EntryExtensions.cs b/src/Core/src/Platform/iOS/EntryExtensions.cs index e10c92c18b4f..fecf1fc39802 100644 --- a/src/Core/src/Platform/iOS/EntryExtensions.cs +++ b/src/Core/src/Platform/iOS/EntryExtensions.cs @@ -55,5 +55,10 @@ public static void UpdateIsReadOnly(this UITextField textField, IEntry entry) { textField.UserInteractionEnabled = !entry.IsReadOnly; } + + public static void UpdateKeyboard(this UITextField textField, IEntry entry) + { + // TODO + } } } \ No newline at end of file From 84d43aa2af7a9b2c671d776005d02c73de465101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Fri, 12 Mar 2021 01:44:40 +0100 Subject: [PATCH 03/30] Test code in sample for numeric keyboard input type. --- src/Controls/samples/Controls.Sample/Pages/MainPage.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Controls/samples/Controls.Sample/Pages/MainPage.cs b/src/Controls/samples/Controls.Sample/Pages/MainPage.cs index 2bfec77fbf7b..2f85ebb050f7 100644 --- a/src/Controls/samples/Controls.Sample/Pages/MainPage.cs +++ b/src/Controls/samples/Controls.Sample/Pages/MainPage.cs @@ -35,6 +35,12 @@ void SetupMauiLayout() verticalStack.Add(new Label { Text = "This should be a CUSTOM font!", FontFamily = "Dokdo" }); verticalStack.Add(new Label { Text = "This should have padding", Padding = new Thickness(40), BackgroundColor = Color.LightBlue }); + var passwordEntry = new Entry() { IsPassword = true }; + var numericEntry = new Entry() { Keyboard = Keyboard.Numeric }; + + verticalStack.Add(passwordEntry); + verticalStack.Add(numericEntry); + var button = new Button() { Text = _viewModel.Text, WidthRequest = 200 }; var button2 = new Button() { From ab7c3f75098ab3df33a623b4f386fadd7502185b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Sat, 13 Mar 2021 03:33:50 +0100 Subject: [PATCH 04/30] iOS Handler port. --- src/Core/src/Platform/iOS/EntryExtensions.cs | 8 +- .../src/Platform/iOS/KeyboardExtensions.cs | 79 +++++++++++++++++++ 2 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 src/Core/src/Platform/iOS/KeyboardExtensions.cs diff --git a/src/Core/src/Platform/iOS/EntryExtensions.cs b/src/Core/src/Platform/iOS/EntryExtensions.cs index fecf1fc39802..8ba63b6eae03 100644 --- a/src/Core/src/Platform/iOS/EntryExtensions.cs +++ b/src/Core/src/Platform/iOS/EntryExtensions.cs @@ -1,4 +1,5 @@ -using UIKit; +using Microsoft.Maui.Platform.iOS; +using UIKit; namespace Microsoft.Maui { @@ -58,7 +59,10 @@ public static void UpdateIsReadOnly(this UITextField textField, IEntry entry) public static void UpdateKeyboard(this UITextField textField, IEntry entry) { - // TODO + var keyboard = entry.Keyboard; + + textField.ApplyKeyboard(keyboard); + textField.ReloadInputViews(); } } } \ No newline at end of file diff --git a/src/Core/src/Platform/iOS/KeyboardExtensions.cs b/src/Core/src/Platform/iOS/KeyboardExtensions.cs new file mode 100644 index 000000000000..53fa83450ceb --- /dev/null +++ b/src/Core/src/Platform/iOS/KeyboardExtensions.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Text; +using UIKit; + +namespace Microsoft.Maui.Platform.iOS +{ + public static class KeyboardExtensions + { + public static void ApplyKeyboard(this IUITextInput textInput, Keyboard keyboard) + { + if (textInput is IUITextInputTraits traits) + ApplyKeyboard(traits, keyboard); + } + + public static void ApplyKeyboard(this IUITextInputTraits textInput, Keyboard keyboard) + { + textInput.AutocapitalizationType = UITextAutocapitalizationType.None; + textInput.AutocorrectionType = UITextAutocorrectionType.No; + textInput.SpellCheckingType = UITextSpellCheckingType.No; + textInput.KeyboardType = UIKeyboardType.Default; + + if (keyboard == Keyboard.Default) + { + textInput.AutocapitalizationType = UITextAutocapitalizationType.Sentences; + textInput.AutocorrectionType = UITextAutocorrectionType.Default; + textInput.SpellCheckingType = UITextSpellCheckingType.Default; + } + else if (keyboard == Keyboard.Chat) + { + textInput.AutocapitalizationType = UITextAutocapitalizationType.Sentences; + textInput.AutocorrectionType = UITextAutocorrectionType.Yes; + } + else if (keyboard == Keyboard.Email) + textInput.KeyboardType = UIKeyboardType.EmailAddress; + else if (keyboard == Keyboard.Numeric) + textInput.KeyboardType = UIKeyboardType.DecimalPad; + else if (keyboard == Keyboard.Telephone) + textInput.KeyboardType = UIKeyboardType.PhonePad; + else if (keyboard == Keyboard.Text) + { + textInput.AutocapitalizationType = UITextAutocapitalizationType.Sentences; + textInput.AutocorrectionType = UITextAutocorrectionType.Yes; + textInput.SpellCheckingType = UITextSpellCheckingType.Yes; + } + else if (keyboard == Keyboard.Url) + textInput.KeyboardType = UIKeyboardType.Url; + else if (keyboard is CustomKeyboard) + { + var custom = (CustomKeyboard)keyboard; + + var capitalizedSentenceEnabled = (custom.Flags & KeyboardFlags.CapitalizeSentence) == KeyboardFlags.CapitalizeSentence; + var capitalizedWordsEnabled = (custom.Flags & KeyboardFlags.CapitalizeWord) == KeyboardFlags.CapitalizeWord; + var capitalizedCharacterEnabled = (custom.Flags & KeyboardFlags.CapitalizeCharacter) == KeyboardFlags.CapitalizeCharacter; + var capitalizedNone = (custom.Flags & KeyboardFlags.None) == KeyboardFlags.None; + + var spellcheckEnabled = (custom.Flags & KeyboardFlags.Spellcheck) == KeyboardFlags.Spellcheck; + var suggestionsEnabled = (custom.Flags & KeyboardFlags.Suggestions) == KeyboardFlags.Suggestions; + + + UITextAutocapitalizationType capSettings = UITextAutocapitalizationType.None; + + // Sentence being first ensures that the behavior of ALL is backwards compatible + if (capitalizedSentenceEnabled) + capSettings = UITextAutocapitalizationType.Sentences; + else if (capitalizedWordsEnabled) + capSettings = UITextAutocapitalizationType.Words; + else if (capitalizedCharacterEnabled) + capSettings = UITextAutocapitalizationType.AllCharacters; + else if (capitalizedNone) + capSettings = UITextAutocapitalizationType.None; + + textInput.AutocapitalizationType = capSettings; + textInput.AutocorrectionType = suggestionsEnabled ? UITextAutocorrectionType.Yes : UITextAutocorrectionType.No; + textInput.SpellCheckingType = spellcheckEnabled ? UITextSpellCheckingType.Yes : UITextSpellCheckingType.No; + } + } + } +} From 505020de2a67ec6e1968619741c3d1950f0f1d43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Sat, 13 Mar 2021 03:37:06 +0100 Subject: [PATCH 05/30] KeyListener implementation for Android handler. --- .../src/Platform/Android/EntryExtensions.cs | 20 +- .../Android/LocalizedDigitsKeyListener.cs | 224 ++++++++++++++++++ 2 files changed, 229 insertions(+), 15 deletions(-) create mode 100644 src/Core/src/Platform/Android/LocalizedDigitsKeyListener.cs diff --git a/src/Core/src/Platform/Android/EntryExtensions.cs b/src/Core/src/Platform/Android/EntryExtensions.cs index 5d2a0f2beb5e..1d3ff52bbdbd 100644 --- a/src/Core/src/Platform/Android/EntryExtensions.cs +++ b/src/Core/src/Platform/Android/EntryExtensions.cs @@ -1,4 +1,5 @@ using Android.Content.Res; +using Android.OS; using Android.Text; using Android.Text.Method; using Android.Widget; @@ -62,27 +63,24 @@ internal static void SetInputType(this EditText editText, IEntry entry) if ((nativeInputTypeToUpdate & InputTypes.TextFlagNoSuggestions) != InputTypes.TextFlagNoSuggestions) { if (!entry.IsTextPredictionEnabled) - nativeInputTypeToUpdate = nativeInputTypeToUpdate | InputTypes.TextFlagNoSuggestions; + nativeInputTypeToUpdate |= InputTypes.TextFlagNoSuggestions; } } if (keyboard == Keyboard.Numeric) { - // editText.KeyListener = GetDigitsKeyListener(editText.InputType); + editText.KeyListener = LocalizedDigitsKeyListener.Create(editText.InputType); } if (entry.IsPassword) { if (((nativeInputTypeToUpdate & InputTypes.ClassText) == InputTypes.ClassText)) - nativeInputTypeToUpdate = nativeInputTypeToUpdate | InputTypes.TextVariationPassword; + nativeInputTypeToUpdate |= InputTypes.TextVariationPassword; if (((nativeInputTypeToUpdate & InputTypes.ClassNumber) == InputTypes.ClassNumber)) - nativeInputTypeToUpdate = nativeInputTypeToUpdate | InputTypes.NumberVariationPassword; + nativeInputTypeToUpdate |= InputTypes.NumberVariationPassword; } - if (!entry.IsTextPredictionEnabled && ((nativeInputTypeToUpdate & InputTypes.TextFlagNoSuggestions) != InputTypes.TextFlagNoSuggestions)) - nativeInputTypeToUpdate |= InputTypes.TextFlagNoSuggestions; - editText.InputType = nativeInputTypeToUpdate; } } @@ -114,13 +112,5 @@ public static void UpdateIsReadOnly(this EditText editText, IEntry entry) editText.FocusableInTouchMode = isEditable; editText.Focusable = isEditable; } - - //protected virtual NumberKeyListener GetDigitsKeyListener(InputTypes inputTypes) - //{ - // // Override this in a custom renderer to use a different NumberKeyListener - // // or to filter out input types you don't want to allow - // // (e.g., inputTypes &= ~InputTypes.NumberFlagSigned to disallow the sign) - // return LocalizedDigitsKeyListener.Create(inputTypes); - //} } } diff --git a/src/Core/src/Platform/Android/LocalizedDigitsKeyListener.cs b/src/Core/src/Platform/Android/LocalizedDigitsKeyListener.cs new file mode 100644 index 000000000000..c537fc3ac6b9 --- /dev/null +++ b/src/Core/src/Platform/Android/LocalizedDigitsKeyListener.cs @@ -0,0 +1,224 @@ +using System.Collections.Generic; +using Android.Text; +using Android.Text.Method; +using Java.Lang; +using Java.Text; + +namespace Microsoft.Maui.Platform.Android +{ + internal class LocalizedDigitsKeyListener : NumberKeyListener + { + readonly char _decimalSeparator; + + // I'm not aware of a situation/locale where this would need to be something different, + // but we'll make it easy to localize the sign in the future just in case + const char SignCharacter = '-'; + + static Dictionary? s_unsignedCache; + static Dictionary? s_signedCache; + + static char GetDecimalSeparator() + { + if (!(NumberFormat.Instance is DecimalFormat format)) + return '.'; + + + DecimalFormatSymbols? sym = format.DecimalFormatSymbols; + + return sym == null ? '.' : sym.DecimalSeparator; + } + + public static NumberKeyListener Create(InputTypes inputTypes) + { + if ((inputTypes & InputTypes.NumberFlagDecimal) == 0) + { + // If decimal isn't allowed, we can just use the Android version +#pragma warning disable 0618 + return DigitsKeyListener.GetInstance(inputTypes.HasFlag(InputTypes.NumberFlagSigned), false); +#pragma warning restore 0618 + } + + // Figure out what the decimal separator is for the current locale + char decimalSeparator = GetDecimalSeparator(); + + if (decimalSeparator == '.') + { + // If it's '.', then we can just use the default Android version +#pragma warning disable 0618 + return DigitsKeyListener.GetInstance(inputTypes.HasFlag(InputTypes.NumberFlagSigned), true); +#pragma warning restore 0618 + } + + // If decimals are enabled and the locale's decimal separator is not '.' + // (which is hard-coded in the Android DigitKeyListener), then use + // our custom one with a configurable decimal separator + return GetInstance(inputTypes, decimalSeparator); + } + + public static LocalizedDigitsKeyListener GetInstance(InputTypes inputTypes, char decimalSeparator) + { + if (s_signedCache == null) + s_signedCache = new Dictionary(); + + if ((inputTypes & InputTypes.NumberFlagSigned) != 0) + { + return GetInstance(inputTypes, decimalSeparator, ref s_signedCache); + } + + if (s_unsignedCache == null) + s_unsignedCache = new Dictionary(); + + return GetInstance(inputTypes, decimalSeparator, ref s_unsignedCache); + } + + static LocalizedDigitsKeyListener GetInstance(InputTypes inputTypes, char decimalSeparator, ref Dictionary cache) + { + if (cache == null) + { + cache = new Dictionary(1); + } + + if (!cache.ContainsKey(decimalSeparator)) + { + cache.Add(decimalSeparator, new LocalizedDigitsKeyListener(inputTypes, decimalSeparator)); + } + + return cache[decimalSeparator]; + } + + protected LocalizedDigitsKeyListener(InputTypes inputTypes, char decimalSeparator) + + { + _decimalSeparator = decimalSeparator; + InputType = inputTypes; + } + + public override InputTypes InputType { get; } + + char[]? _acceptedChars; + + protected override char[] GetAcceptedChars() + { + if ((InputType & InputTypes.NumberFlagSigned) == 0) + { + return _acceptedChars ?? + (_acceptedChars = new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', _decimalSeparator }); + } + + return _acceptedChars ?? + (_acceptedChars = new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', SignCharacter, _decimalSeparator }); + } + + static bool IsSignChar(char c) + { + return c == SignCharacter; + } + + bool IsDecimalPointChar(char c) + { + return c == _decimalSeparator; + } + +#pragma warning disable CS8602 // Dereference of a possibly null reference. + public override ICharSequence? FilterFormatted(ICharSequence? source, int start, int end, ISpanned? dest, int dstart, int dend) + { + // Borrowed heavily from the Android source + ICharSequence? filterFormatted = base.FilterFormatted(source, start, end, dest, dstart, dend); + + if (filterFormatted != null) + { + source = filterFormatted; + start = 0; + end = filterFormatted.Length(); + } + + int sign = -1; + int dec = -1; + + int dlen = dest.Length(); + + + // Find out if the existing text has a sign or decimal point characters. + for (var i = 0; i < dstart; i++) + { + char c = dest.CharAt(i); + if (IsSignChar(c)) + { + sign = i; + } + else if (IsDecimalPointChar(c)) + { + dec = i; + } + } + + for (int i = dend; i < dlen; i++) + { + char c = dest.CharAt(i); + if (IsSignChar(c)) + { + return new String(""); // Nothing can be inserted in front of a sign character. + } + + if (IsDecimalPointChar(c)) + { + dec = i; + } + } + + // If it does, we must strip them out from the source. + // In addition, a sign character must be the very first character, + // and nothing can be inserted before an existing sign character. + // Go in reverse order so the offsets are stable. + SpannableStringBuilder? stripped = null; + for (int i = end - 1; i >= start; i--) + { + char c = source.CharAt(i); + var strip = false; + + if (IsSignChar(c)) + { + if (i != start || dstart != 0) + { + strip = true; + } + else if (sign >= 0) + { + strip = true; + } + else + { + sign = i; + } + } + else if (IsDecimalPointChar(c)) + { + if (dec >= 0) + { + strip = true; + } + else + { + dec = i; + } + } + + if (strip) + { + if (end == start + 1) + { + return new String(""); // Only one character, and it was stripped. + } + if (stripped == null) + { + stripped = new SpannableStringBuilder(source, start, end); + } + stripped.Delete(i - start, i + 1 - start); + } + } + + return stripped == null ? (filterFormatted != null ? filterFormatted : new String("")) : stripped; + } +#pragma warning restore CS8602 // Dereference of a possibly null reference. + } +} From 633956929e8a7482b29107746ccbed45f7ebd0c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Sat, 13 Mar 2021 03:37:20 +0100 Subject: [PATCH 06/30] Keyboard added for EntryStub. --- src/Core/tests/DeviceTests/Stubs/EntryStub.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Core/tests/DeviceTests/Stubs/EntryStub.cs b/src/Core/tests/DeviceTests/Stubs/EntryStub.cs index 32d8347a986d..136edf20e10a 100644 --- a/src/Core/tests/DeviceTests/Stubs/EntryStub.cs +++ b/src/Core/tests/DeviceTests/Stubs/EntryStub.cs @@ -30,5 +30,7 @@ void OnTextChanged(string oldValue, string newValue) => TextChanged?.Invoke(this, new StubPropertyChangedEventArgs(oldValue, newValue)); public Font Font { get; set; } + + public Keyboard Keyboard { get; set; } } } \ No newline at end of file From 3795b0a9be368093bbcede6c4e4a4243ebff2c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Sun, 14 Mar 2021 17:54:35 +0100 Subject: [PATCH 07/30] fix for wrong property name mapping --- src/Core/src/Handlers/Entry/EntryHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/src/Handlers/Entry/EntryHandler.cs b/src/Core/src/Handlers/Entry/EntryHandler.cs index 0091cc64f813..d1ebe9d1ca67 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.cs @@ -10,7 +10,7 @@ public partial class EntryHandler [nameof(IEntry.IsTextPredictionEnabled)] = MapIsTextPredictionEnabled, [nameof(IEntry.Placeholder)] = MapPlaceholder, [nameof(IEntry.IsReadOnly)] = MapIsReadOnly, - [nameof(IEntry.IsReadOnly)] = MapKeyboard + [nameof(IEntry.Keyboard)] = MapKeyboard }; public EntryHandler() : base(EntryMapper) From 407d314488e8849a25ae92fc4a44aaf7e7cfb022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Sun, 14 Mar 2021 17:55:10 +0100 Subject: [PATCH 08/30] better property summary --- src/Core/src/Core/ITextInput.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/src/Core/ITextInput.cs b/src/Core/src/Core/ITextInput.cs index 33c0c2575243..999ce0d4359a 100644 --- a/src/Core/src/Core/ITextInput.cs +++ b/src/Core/src/Core/ITextInput.cs @@ -21,8 +21,8 @@ public interface ITextInput : IText bool IsReadOnly { get; } /// - /// Gets the keyboard type for the given input control. + /// Gets the keyboard input type. /// - Keyboard Keyboard{ get; } + Keyboard Keyboard { get; } } } \ No newline at end of file From 3e7b632851e7a7cb3c5a2e07aa79e014c5545c3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Sun, 14 Mar 2021 18:00:25 +0100 Subject: [PATCH 09/30] bindable proeprty added to Entry --- src/Controls/src/Core/Entry.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Controls/src/Core/Entry.cs b/src/Controls/src/Core/Entry.cs index 42d4d83f6f1d..e16dd95e0f5f 100644 --- a/src/Controls/src/Core/Entry.cs +++ b/src/Controls/src/Core/Entry.cs @@ -22,6 +22,7 @@ public partial class Entry : InputView, IFontElement, ITextAlignmentElement, IEn public new static readonly BindableProperty TextProperty = InputView.TextProperty; public new static readonly BindableProperty TextColorProperty = InputView.TextColorProperty; + public new static readonly BindableProperty KeyboardProperty = InputView.KeyboardProperty; public new static readonly BindableProperty CharacterSpacingProperty = InputView.CharacterSpacingProperty; From e80c40738a733048d6dec37288a8d1cd58922ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Sun, 14 Mar 2021 23:49:50 +0100 Subject: [PATCH 10/30] Device tests for all non custom keyboard types are implemented for both iOS and Android. --- .../DeviceTests/Data/KeyboardClassData.cs | 126 ++++++++++++++++++ .../Entry/EntryHandlerTests.Android.cs | 51 +++++++ .../Handlers/Entry/EntryHandlerTests.cs | 56 ++++++++ .../Handlers/Entry/EntryHandlerTests.iOS.cs | 29 ++++ src/Core/tests/DeviceTests/Stubs/EntryStub.cs | 2 +- 5 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 src/Core/tests/DeviceTests/Data/KeyboardClassData.cs diff --git a/src/Core/tests/DeviceTests/Data/KeyboardClassData.cs b/src/Core/tests/DeviceTests/Data/KeyboardClassData.cs new file mode 100644 index 000000000000..756b35a5a199 --- /dev/null +++ b/src/Core/tests/DeviceTests/Data/KeyboardClassData.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.Maui.Core.DeviceTests.Data +{ + public class NumericKeyboardClassData : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return new object[] { Keyboard.Numeric, true }; + yield return new object[] { Keyboard.Default, false }; + yield return new object[] { Keyboard.Chat, false }; + yield return new object[] { Keyboard.Email, false }; + yield return new object[] { Keyboard.Plain, false }; + yield return new object[] { Keyboard.Telephone, false }; + yield return new object[] { Keyboard.Text, false }; + yield return new object[] { Keyboard.Url, false }; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public class ChatKeyboardClassData : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return new object[] { Keyboard.Chat, true }; + yield return new object[] { Keyboard.Default, false }; + yield return new object[] { Keyboard.Numeric, false }; + yield return new object[] { Keyboard.Email, false }; + yield return new object[] { Keyboard.Plain, false }; + yield return new object[] { Keyboard.Telephone, false }; + yield return new object[] { Keyboard.Text, false }; + yield return new object[] { Keyboard.Url, false }; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public class EmailKeyboardClassData : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return new object[] { Keyboard.Email, true }; + yield return new object[] { Keyboard.Default, false }; + yield return new object[] { Keyboard.Numeric, false }; + yield return new object[] { Keyboard.Chat, false }; + yield return new object[] { Keyboard.Plain, false }; + yield return new object[] { Keyboard.Telephone, false }; + yield return new object[] { Keyboard.Text, false }; + yield return new object[] { Keyboard.Url, false }; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public class PlainKeyboardClassData : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return new object[] { Keyboard.Plain, true }; + yield return new object[] { Keyboard.Default, false }; + yield return new object[] { Keyboard.Numeric, false }; + yield return new object[] { Keyboard.Chat, false }; + yield return new object[] { Keyboard.Email, false }; + yield return new object[] { Keyboard.Telephone, false }; + yield return new object[] { Keyboard.Text, false }; + yield return new object[] { Keyboard.Url, false }; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public class TelephoneKeyboardClassData : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return new object[] { Keyboard.Telephone, true }; + yield return new object[] { Keyboard.Default, false }; + yield return new object[] { Keyboard.Numeric, false }; + yield return new object[] { Keyboard.Chat, false }; + yield return new object[] { Keyboard.Email, false }; + yield return new object[] { Keyboard.Plain, false }; + yield return new object[] { Keyboard.Text, false }; + yield return new object[] { Keyboard.Url, false }; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public class TextKeyboardClassData : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return new object[] { Keyboard.Text, true }; + yield return new object[] { Keyboard.Default, false }; + yield return new object[] { Keyboard.Numeric, false }; + yield return new object[] { Keyboard.Chat, false }; + yield return new object[] { Keyboard.Email, false }; + yield return new object[] { Keyboard.Plain, false }; + yield return new object[] { Keyboard.Telephone, false }; + yield return new object[] { Keyboard.Url, false }; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + public class UrlKeyboardClassData : IEnumerable + { + public IEnumerator GetEnumerator() + { + yield return new object[] { Keyboard.Url, true }; + yield return new object[] { Keyboard.Default, false }; + yield return new object[] { Keyboard.Numeric, false }; + yield return new object[] { Keyboard.Chat, false }; + yield return new object[] { Keyboard.Email, false }; + yield return new object[] { Keyboard.Plain, false }; + yield return new object[] { Keyboard.Telephone, false }; + yield return new object[] { Keyboard.Text, false }; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} diff --git a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs index 2142fbe02e15..eea2b315868e 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs @@ -1,4 +1,5 @@ using Android.Text; +using Android.Text.Method; using Android.Widget; using Microsoft.Maui.Handlers; using AColor = global::Android.Graphics.Color; @@ -42,5 +43,55 @@ bool GetNativeIsReadOnly(EntryHandler entryHandler) return !editText.Focusable && !editText.FocusableInTouchMode; } + + bool GetNativeIsNumericKeyboard(EntryHandler entryHandler) + { + var editText = GetNativeEntry(entryHandler); + var inputTypes = editText.InputType; + + return editText.KeyListener is NumberKeyListener + && (inputTypes.HasFlag(InputTypes.NumberFlagDecimal) && inputTypes.HasFlag(InputTypes.ClassNumber) && inputTypes.HasFlag(InputTypes.NumberFlagSigned)); + } + + bool GetNativeIsChatKeyboard(EntryHandler entryHandler) + { + var editText = GetNativeEntry(entryHandler); + var inputTypes = editText.InputType; + + return (inputTypes.HasFlag(InputTypes.ClassText) && inputTypes.HasFlag(InputTypes.TextFlagCapSentences) && inputTypes.HasFlag(InputTypes.TextFlagNoSuggestions)); + } + + + bool GetNativeIsEmailKeyboard(EntryHandler entryHandler) + { + var editText = GetNativeEntry(entryHandler); + var inputTypes = editText.InputType; + + return (inputTypes.HasFlag(InputTypes.ClassText) && inputTypes.HasFlag(InputTypes.TextVariationEmailAddress)); + } + + bool GetNativeIsTelephoneKeyboard(EntryHandler entryHandler) + { + var editText = GetNativeEntry(entryHandler); + var inputTypes = editText.InputType; + + return inputTypes.HasFlag(InputTypes.ClassPhone); + } + + bool GetNativeIsUrlKeyboard(EntryHandler entryHandler) + { + var editText = GetNativeEntry(entryHandler); + var inputTypes = editText.InputType; + + return inputTypes.HasFlag(InputTypes.ClassText) && inputTypes.HasFlag(InputTypes.TextVariationUri); + } + + bool GetNativeIsTextKeyboard(EntryHandler entryHandler) + { + var editText = GetNativeEntry(entryHandler); + var inputTypes = editText.InputType; + + return inputTypes.HasFlag(InputTypes.ClassText) && inputTypes.HasFlag(InputTypes.TextFlagCapSentences); + } } } \ No newline at end of file diff --git a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs index d3b96d554690..17d5c1b26185 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs @@ -1,7 +1,9 @@ using System.Threading.Tasks; using Microsoft.Maui.DeviceTests.Stubs; using Microsoft.Maui.Handlers; +using Microsoft.Maui.Core.DeviceTests.Data; using Xunit; +using Xunit.Extensions; namespace Microsoft.Maui.DeviceTests { @@ -207,5 +209,59 @@ public async Task TextChangeEventsFireCorrectly(string initialText, string newTe else Assert.Equal(0, eventFiredCount); } + + [Theory(DisplayName = "Validates Numeric Keyboard")] + [ClassData(typeof(NumericKeyboardClassData))] + public async Task ValidateNumericKeyboard(Keyboard keyboard, bool expected) + { + var entryStub = new EntryStub() { Keyboard = keyboard }; + + await ValidatePropertyInitValue(entryStub, () => expected, GetNativeIsNumericKeyboard, expected); + } + + [Theory(DisplayName = "Validates Email Keyboard")] + [ClassData(typeof(EmailKeyboardClassData))] + public async Task ValidateEmailKeyboard(Keyboard keyboard, bool expected) + { + var entryStub = new EntryStub() { Keyboard = keyboard }; + + await ValidatePropertyInitValue(entryStub, () => expected, GetNativeIsEmailKeyboard, expected); + } + + [Theory(DisplayName = "Validates Telephone Keyboard")] + [ClassData(typeof(TelephoneKeyboardClassData))] + public async Task ValidateTelephoneKeyboard(Keyboard keyboard, bool expected) + { + var entryStub = new EntryStub() { Keyboard = keyboard }; + + await ValidatePropertyInitValue(entryStub, () => expected, GetNativeIsTelephoneKeyboard, expected); + } + + [Theory(DisplayName = "Validates Url Keyboard")] + [ClassData(typeof(UrlKeyboardClassData))] + public async Task ValidateUrlKeyboard(Keyboard keyboard, bool expected) + { + var entryStub = new EntryStub() { Keyboard = keyboard }; + + await ValidatePropertyInitValue(entryStub, () => expected, GetNativeIsUrlKeyboard, expected); + } + + [Theory(DisplayName = "Validates Text Keyboard")] + [ClassData(typeof(TextKeyboardClassData))] + public async Task ValidateTextKeyboard(Keyboard keyboard, bool expected) + { + var entryStub = new EntryStub() { Keyboard = keyboard }; + + await ValidatePropertyInitValue(entryStub, () => expected, GetNativeIsTextKeyboard, expected); + } + + [Theory(DisplayName = "Validates Chat Keyboard")] + [ClassData(typeof(ChatKeyboardClassData))] + public async Task ValidateChatKeyboard(Keyboard keyboard, bool expected) + { + var entryStub = new EntryStub() { Keyboard = keyboard }; + + await ValidatePropertyInitValue(entryStub, () => expected, GetNativeIsChatKeyboard, expected); + } } } diff --git a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs index a5a888890b85..32c59a817085 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs @@ -28,5 +28,34 @@ bool GetNativeIsTextPredictionEnabled(EntryHandler entryHandler) => bool GetNativeIsReadOnly(EntryHandler entryHandler) => !GetNativeEntry(entryHandler).UserInteractionEnabled; + + bool GetNativeIsNumericKeyboard(EntryHandler entryHandler) => + GetNativeEntry(entryHandler).KeyboardType == UIKeyboardType.DecimalPad; + + bool GetNativeIsEmailKeyboard(EntryHandler entryHandler) => + GetNativeEntry(entryHandler).KeyboardType == UIKeyboardType.EmailAddress; + + bool GetNativeIsTelephoneKeyboard(EntryHandler entryHandler) => + GetNativeEntry(entryHandler).KeyboardType == UIKeyboardType.PhonePad; + + bool GetNativeIsUrlKeyboard(EntryHandler entryHandler) => + GetNativeEntry(entryHandler).KeyboardType == UIKeyboardType.Url; + + bool GetNativeIsTextKeyboard(EntryHandler entryHandler) + { + var nativeEntry = GetNativeEntry(entryHandler); + + return nativeEntry.AutocapitalizationType == UITextAutocapitalizationType.Sentences && + nativeEntry.AutocorrectionType == UITextAutocorrectionType.Yes && + nativeEntry.SpellCheckingType == UITextSpellCheckingType.Yes; + } + + bool GetNativeIsChatKeyboard(EntryHandler entryHandler) + { + var nativeEntry = GetNativeEntry(entryHandler); + + return nativeEntry.AutocapitalizationType == UITextAutocapitalizationType.Sentences && + nativeEntry.AutocorrectionType == UITextAutocorrectionType.Yes; + } } } \ No newline at end of file diff --git a/src/Core/tests/DeviceTests/Stubs/EntryStub.cs b/src/Core/tests/DeviceTests/Stubs/EntryStub.cs index 136edf20e10a..4263985bd5fd 100644 --- a/src/Core/tests/DeviceTests/Stubs/EntryStub.cs +++ b/src/Core/tests/DeviceTests/Stubs/EntryStub.cs @@ -31,6 +31,6 @@ void OnTextChanged(string oldValue, string newValue) => public Font Font { get; set; } - public Keyboard Keyboard { get; set; } + public Keyboard Keyboard { get; set; } = Keyboard.Default; } } \ No newline at end of file From 27595c55403baed6b7faaf5c60f38211d1911a10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Mon, 15 Mar 2021 00:45:05 +0100 Subject: [PATCH 11/30] MapKeyboard order changed in order to prevent unwanted changes to other properties like IsTextPredictionEnabled --- src/Core/src/Handlers/Entry/EntryHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/src/Handlers/Entry/EntryHandler.cs b/src/Core/src/Handlers/Entry/EntryHandler.cs index d1ebe9d1ca67..32ac20976808 100644 --- a/src/Core/src/Handlers/Entry/EntryHandler.cs +++ b/src/Core/src/Handlers/Entry/EntryHandler.cs @@ -4,13 +4,13 @@ public partial class EntryHandler { public static PropertyMapper EntryMapper = new PropertyMapper(ViewHandler.ViewMapper) { + [nameof(IEntry.Keyboard)] = MapKeyboard, [nameof(IEntry.Text)] = MapText, [nameof(IEntry.TextColor)] = MapTextColor, [nameof(IEntry.IsPassword)] = MapIsPassword, [nameof(IEntry.IsTextPredictionEnabled)] = MapIsTextPredictionEnabled, [nameof(IEntry.Placeholder)] = MapPlaceholder, - [nameof(IEntry.IsReadOnly)] = MapIsReadOnly, - [nameof(IEntry.Keyboard)] = MapKeyboard + [nameof(IEntry.IsReadOnly)] = MapIsReadOnly }; public EntryHandler() : base(EntryMapper) From bc5daa5506b8b25a2a8aedc575f3b903aca5049a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Mon, 15 Mar 2021 00:48:17 +0100 Subject: [PATCH 12/30] Numeric entry to samples with placeholder. --- src/Controls/samples/Controls.Sample/Pages/MainPage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controls/samples/Controls.Sample/Pages/MainPage.cs b/src/Controls/samples/Controls.Sample/Pages/MainPage.cs index 2f85ebb050f7..6c28cd2eaa72 100644 --- a/src/Controls/samples/Controls.Sample/Pages/MainPage.cs +++ b/src/Controls/samples/Controls.Sample/Pages/MainPage.cs @@ -36,7 +36,7 @@ void SetupMauiLayout() verticalStack.Add(new Label { Text = "This should have padding", Padding = new Thickness(40), BackgroundColor = Color.LightBlue }); var passwordEntry = new Entry() { IsPassword = true }; - var numericEntry = new Entry() { Keyboard = Keyboard.Numeric }; + var numericEntry = new Entry() { Keyboard = Keyboard.Numeric, Placeholder = "Numeric Entry" }; verticalStack.Add(passwordEntry); verticalStack.Add(numericEntry); From 0fa4eaaa8a1ccce240512eb462af8f66106c691f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Mon, 15 Mar 2021 01:10:21 +0100 Subject: [PATCH 13/30] Fixed merging error with namespaces. --- src/Core/src/Platform/Android/EntryExtensions.cs | 7 +------ .../Handlers/Entry/EntryHandlerTests.Android.cs | 4 +--- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Core/src/Platform/Android/EntryExtensions.cs b/src/Core/src/Platform/Android/EntryExtensions.cs index 433754ccb293..4df98b5858ca 100644 --- a/src/Core/src/Platform/Android/EntryExtensions.cs +++ b/src/Core/src/Platform/Android/EntryExtensions.cs @@ -1,13 +1,8 @@ using Android.Content.Res; -using Android.OS; using Android.Text; -using Android.Text.Method; -using Android.Widget; -using Microsoft.Maui.Platform.Android; using Android.Util; -using Android.Content.Res; -using Android.Text; using AndroidX.AppCompat.Widget; +using Microsoft.Maui.Platform.Android; namespace Microsoft.Maui { diff --git a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs index 76602da1685c..8c4943d2423d 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs @@ -1,8 +1,6 @@ -using Android.Text; -using Android.Text.Method; -using Android.Widget; using System.Threading.Tasks; using Android.Text; +using Android.Text.Method; using AndroidX.AppCompat.Widget; using Microsoft.Extensions.DependencyInjection; using Microsoft.Maui.DeviceTests.Stubs; From 220d27deacb3e5f2f301d90d686b9af7e0bbdd90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Mon, 15 Mar 2021 02:27:07 +0100 Subject: [PATCH 14/30] Better PortHandler for UpdateInputType in Android renderer. --- src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs b/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs index c92869d31ee9..491d0c5ba8a7 100644 --- a/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs +++ b/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs @@ -318,7 +318,8 @@ protected virtual void UpdateFont() EditText.SetTextSize(ComplexUnitType.Sp, (float)Element.FontSize); } - [PortHandler("Partially ported")] + [PortHandler("IsSpellCheckEnabled is missing.")] + [PortHandler("No override for GetDigitsKeyListener")] void UpdateInputType() { Entry model = Element; From 37eba9a711effe792280a7e81aea4eaa2028f8d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Mon, 15 Mar 2021 09:26:39 +0100 Subject: [PATCH 15/30] pattern matching usage in keyboard extensions for android. --- src/Core/src/Platform/Android/KeyboardExtensions.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Core/src/Platform/Android/KeyboardExtensions.cs b/src/Core/src/Platform/Android/KeyboardExtensions.cs index 639c8b48fcb1..0acf3d56ede6 100644 --- a/src/Core/src/Platform/Android/KeyboardExtensions.cs +++ b/src/Core/src/Platform/Android/KeyboardExtensions.cs @@ -30,9 +30,8 @@ public static InputTypes ToInputType(this Keyboard self) result = InputTypes.ClassText | InputTypes.TextFlagCapSentences; else if (self == Keyboard.Url) result = InputTypes.ClassText | InputTypes.TextVariationUri; - else if (self is CustomKeyboard) + else if (self is CustomKeyboard custom) { - var custom = (CustomKeyboard)self; var capitalizedSentenceEnabled = (custom.Flags & KeyboardFlags.CapitalizeSentence) == KeyboardFlags.CapitalizeSentence; var capitalizedWordsEnabled = (custom.Flags & KeyboardFlags.CapitalizeWord) == KeyboardFlags.CapitalizeWord; var capitalizedCharacterEnabled = (custom.Flags & KeyboardFlags.CapitalizeCharacter) == KeyboardFlags.CapitalizeCharacter; @@ -49,7 +48,6 @@ public static InputTypes ToInputType(this Keyboard self) // Log.Warning(null, "On Android, KeyboardFlags.Suggestions enables KeyboardFlags.Spellcheck as well due to a platform limitation."); result = InputTypes.ClassText | InputTypes.TextFlagAutoCorrect; } - if (!capitalizedSentenceEnabled && spellcheckEnabled && !suggestionsEnabled) result = InputTypes.ClassText | InputTypes.TextFlagAutoComplete; From a38ab4c2839de1d0e6798d2f01da8f6b1b76d249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Mon, 15 Mar 2021 11:11:24 +0100 Subject: [PATCH 16/30] Moved LocalizedDigitsKeyListener to Core.Android project --- .../src/Android/Cells/EntryCellRenderer.cs | 1 + .../src/Android/Compatibility.Android.csproj | 1 + .../Core/src/Android/PopupManager.cs | 1 + .../src/Android/Renderers/EditorRenderer.cs | 1 + .../src/Android/Renderers/EntryRenderer.cs | 1 + .../Renderers/LocalizedDigitsKeyListener.cs | 215 ------------------ .../Android/Renderers/SearchBarRenderer.cs | 1 + .../SearchHandlerAppearanceTracker.cs | 1 + .../Android/LocalizedDigitsKeyListener.cs | 2 +- 9 files changed, 8 insertions(+), 216 deletions(-) delete mode 100644 src/Compatibility/Core/src/Android/Renderers/LocalizedDigitsKeyListener.cs diff --git a/src/Compatibility/Core/src/Android/Cells/EntryCellRenderer.cs b/src/Compatibility/Core/src/Android/Cells/EntryCellRenderer.cs index ba2ce1fb8376..36d4d5bea45a 100644 --- a/src/Compatibility/Core/src/Android/Cells/EntryCellRenderer.cs +++ b/src/Compatibility/Core/src/Android/Cells/EntryCellRenderer.cs @@ -3,6 +3,7 @@ using Android.Text; using Android.Text.Method; using Android.Views; +using Microsoft.Maui.Platform.Android; namespace Microsoft.Maui.Controls.Compatibility.Platform.Android { diff --git a/src/Compatibility/Core/src/Android/Compatibility.Android.csproj b/src/Compatibility/Core/src/Android/Compatibility.Android.csproj index 6b1f36910b4e..c4afcd4fc132 100644 --- a/src/Compatibility/Core/src/Android/Compatibility.Android.csproj +++ b/src/Compatibility/Core/src/Android/Compatibility.Android.csproj @@ -13,6 +13,7 @@ + diff --git a/src/Compatibility/Core/src/Android/PopupManager.cs b/src/Compatibility/Core/src/Android/PopupManager.cs index 7798f3d275cf..d3885ea07172 100644 --- a/src/Compatibility/Core/src/Android/PopupManager.cs +++ b/src/Compatibility/Core/src/Android/PopupManager.cs @@ -7,6 +7,7 @@ using Android.Views; using Android.Widget; using Microsoft.Maui.Controls.Internals; +using Microsoft.Maui.Platform.Android; using AppCompatActivity = AndroidX.AppCompat.App.AppCompatActivity; using AppCompatAlertDialog = AndroidX.AppCompat.App.AlertDialog; diff --git a/src/Compatibility/Core/src/Android/Renderers/EditorRenderer.cs b/src/Compatibility/Core/src/Android/Renderers/EditorRenderer.cs index 0a9c3b842c24..41ab4ed003ee 100644 --- a/src/Compatibility/Core/src/Android/Renderers/EditorRenderer.cs +++ b/src/Compatibility/Core/src/Android/Renderers/EditorRenderer.cs @@ -9,6 +9,7 @@ using Android.Views.InputMethods; using Android.Widget; using Java.Lang; +using Microsoft.Maui.Platform.Android; namespace Microsoft.Maui.Controls.Compatibility.Platform.Android { diff --git a/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs b/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs index 491d0c5ba8a7..89b8835544c7 100644 --- a/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs +++ b/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs @@ -12,6 +12,7 @@ using AndroidX.Core.Content; using Java.Lang; using Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific; +using Microsoft.Maui.Platform.Android; namespace Microsoft.Maui.Controls.Compatibility.Platform.Android { diff --git a/src/Compatibility/Core/src/Android/Renderers/LocalizedDigitsKeyListener.cs b/src/Compatibility/Core/src/Android/Renderers/LocalizedDigitsKeyListener.cs deleted file mode 100644 index d35990ce5628..000000000000 --- a/src/Compatibility/Core/src/Android/Renderers/LocalizedDigitsKeyListener.cs +++ /dev/null @@ -1,215 +0,0 @@ -using System.Collections.Generic; -using Android.Text; -using Android.Text.Method; -using Java.Lang; -using Java.Text; - -namespace Microsoft.Maui.Controls.Compatibility.Platform.Android -{ - internal class LocalizedDigitsKeyListener : NumberKeyListener - { - readonly char _decimalSeparator; - - // I'm not aware of a situation/locale where this would need to be something different, - // but we'll make it easy to localize the sign in the future just in case - const char SignCharacter = '-'; - - static Dictionary s_unsignedCache; - static Dictionary s_signedCache; - - static char GetDecimalSeparator() - { - var format = NumberFormat.Instance as DecimalFormat; - if (format == null) - { - return '.'; - } - - DecimalFormatSymbols sym = format.DecimalFormatSymbols; - return sym.DecimalSeparator; - } - - public static NumberKeyListener Create(InputTypes inputTypes) - { - if ((inputTypes & InputTypes.NumberFlagDecimal) == 0) - { - // If decimal isn't allowed, we can just use the Android version -#pragma warning disable 0618 - return DigitsKeyListener.GetInstance(inputTypes.HasFlag(InputTypes.NumberFlagSigned), false); -#pragma warning restore 0618 - } - - // Figure out what the decimal separator is for the current locale - char decimalSeparator = GetDecimalSeparator(); - - if (decimalSeparator == '.') - { - // If it's '.', then we can just use the default Android version -#pragma warning disable 0618 - return DigitsKeyListener.GetInstance(inputTypes.HasFlag(InputTypes.NumberFlagSigned), true); -#pragma warning restore 0618 - } - - // If decimals are enabled and the locale's decimal separator is not '.' - // (which is hard-coded in the Android DigitKeyListener), then use - // our custom one with a configurable decimal separator - return GetInstance(inputTypes, decimalSeparator); - } - - public static LocalizedDigitsKeyListener GetInstance(InputTypes inputTypes, char decimalSeparator) - { - if ((inputTypes & InputTypes.NumberFlagSigned) != 0) - { - return GetInstance(inputTypes, decimalSeparator, ref s_signedCache); - } - - return GetInstance(inputTypes, decimalSeparator, ref s_unsignedCache); - } - - static LocalizedDigitsKeyListener GetInstance(InputTypes inputTypes, char decimalSeparator, ref Dictionary cache) - { - if (cache == null) - { - cache = new Dictionary(1); - } - - if (!cache.ContainsKey(decimalSeparator)) - { - cache.Add(decimalSeparator, new LocalizedDigitsKeyListener(inputTypes, decimalSeparator)); - } - - return cache[decimalSeparator]; - } - - protected LocalizedDigitsKeyListener(InputTypes inputTypes, char decimalSeparator) - { - _decimalSeparator = decimalSeparator; - InputType = inputTypes; - } - - public override InputTypes InputType { get; } - - char[] _acceptedChars; - - protected override char[] GetAcceptedChars() - { - if ((InputType & InputTypes.NumberFlagSigned) == 0) - { - return _acceptedChars ?? - (_acceptedChars = new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', _decimalSeparator }); - } - - return _acceptedChars ?? - (_acceptedChars = new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', SignCharacter, _decimalSeparator }); - } - - static bool IsSignChar(char c) - { - return c == SignCharacter; - } - - bool IsDecimalPointChar(char c) - { - return c == _decimalSeparator; - } - - public override ICharSequence FilterFormatted(ICharSequence source, int start, int end, ISpanned dest, int dstart, - int dend) - { - // Borrowed heavily from the Android source - ICharSequence filterFormatted = base.FilterFormatted(source, start, end, dest, dstart, dend); - - if (filterFormatted != null) - { - source = filterFormatted; - start = 0; - end = filterFormatted.Length(); - } - - int sign = -1; - int dec = -1; - int dlen = dest.Length(); - - // Find out if the existing text has a sign or decimal point characters. - for (var i = 0; i < dstart; i++) - { - char c = dest.CharAt(i); - if (IsSignChar(c)) - { - sign = i; - } - else if (IsDecimalPointChar(c)) - { - dec = i; - } - } - - for (int i = dend; i < dlen; i++) - { - char c = dest.CharAt(i); - if (IsSignChar(c)) - { - return new String(""); // Nothing can be inserted in front of a sign character. - } - - if (IsDecimalPointChar(c)) - { - dec = i; - } - } - - // If it does, we must strip them out from the source. - // In addition, a sign character must be the very first character, - // and nothing can be inserted before an existing sign character. - // Go in reverse order so the offsets are stable. - SpannableStringBuilder stripped = null; - for (int i = end - 1; i >= start; i--) - { - char c = source.CharAt(i); - var strip = false; - - if (IsSignChar(c)) - { - if (i != start || dstart != 0) - { - strip = true; - } - else if (sign >= 0) - { - strip = true; - } - else - { - sign = i; - } - } - else if (IsDecimalPointChar(c)) - { - if (dec >= 0) - { - strip = true; - } - else - { - dec = i; - } - } - - if (strip) - { - if (end == start + 1) - { - return new String(""); // Only one character, and it was stripped. - } - if (stripped == null) - { - stripped = new SpannableStringBuilder(source, start, end); - } - stripped.Delete(i - start, i + 1 - start); - } - } - - return stripped ?? filterFormatted; - } - } -} \ No newline at end of file diff --git a/src/Compatibility/Core/src/Android/Renderers/SearchBarRenderer.cs b/src/Compatibility/Core/src/Android/Renderers/SearchBarRenderer.cs index 32700076e4f4..76d0603e4f58 100644 --- a/src/Compatibility/Core/src/Android/Renderers/SearchBarRenderer.cs +++ b/src/Compatibility/Core/src/Android/Renderers/SearchBarRenderer.cs @@ -8,6 +8,7 @@ using Android.Util; using Android.Views; using Android.Widget; +using Microsoft.Maui.Platform.Android; namespace Microsoft.Maui.Controls.Compatibility.Platform.Android { diff --git a/src/Compatibility/Core/src/Android/Renderers/SearchHandlerAppearanceTracker.cs b/src/Compatibility/Core/src/Android/Renderers/SearchHandlerAppearanceTracker.cs index e756ef38b30b..adc963654211 100644 --- a/src/Compatibility/Core/src/Android/Renderers/SearchHandlerAppearanceTracker.cs +++ b/src/Compatibility/Core/src/Android/Renderers/SearchHandlerAppearanceTracker.cs @@ -12,6 +12,7 @@ using Android.Views; using Android.Widget; using Microsoft.Maui.Controls.Internals; +using Microsoft.Maui.Platform.Android; using AImageButton = Android.Widget.ImageButton; using AView = Android.Views.View; diff --git a/src/Core/src/Platform/Android/LocalizedDigitsKeyListener.cs b/src/Core/src/Platform/Android/LocalizedDigitsKeyListener.cs index c537fc3ac6b9..2df4be445614 100644 --- a/src/Core/src/Platform/Android/LocalizedDigitsKeyListener.cs +++ b/src/Core/src/Platform/Android/LocalizedDigitsKeyListener.cs @@ -6,7 +6,7 @@ namespace Microsoft.Maui.Platform.Android { - internal class LocalizedDigitsKeyListener : NumberKeyListener + public class LocalizedDigitsKeyListener : NumberKeyListener { readonly char _decimalSeparator; From a473775c616ee0b85046b142f379c7ece53fec2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Mon, 15 Mar 2021 11:44:02 +0100 Subject: [PATCH 17/30] Delete KeyboardExtensions in Android Compatibility. --- .../Android/Renderers/KeyboardExtensions.cs | 92 ------------------- 1 file changed, 92 deletions(-) delete mode 100644 src/Compatibility/Core/src/Android/Renderers/KeyboardExtensions.cs diff --git a/src/Compatibility/Core/src/Android/Renderers/KeyboardExtensions.cs b/src/Compatibility/Core/src/Android/Renderers/KeyboardExtensions.cs deleted file mode 100644 index cc5529f178fd..000000000000 --- a/src/Compatibility/Core/src/Android/Renderers/KeyboardExtensions.cs +++ /dev/null @@ -1,92 +0,0 @@ -using Android.Text; -using Microsoft.Maui.Controls.Internals; - -namespace Microsoft.Maui.Controls.Compatibility.Platform.Android -{ - public static class KeyboardExtensions - { - public static InputTypes ToInputType(this Keyboard self) - { - var result = new InputTypes(); - - // ClassText: !autocaps, spellcheck, suggestions - // TextFlagNoSuggestions: !autocaps, !spellcheck, !suggestions - // InputTypes.ClassText | InputTypes.TextFlagCapSentences autocaps, spellcheck, suggestions - // InputTypes.ClassText | InputTypes.TextFlagCapSentences | InputTypes.TextFlagNoSuggestions; autocaps, !spellcheck, !suggestions - - if (self == Keyboard.Default) - result = InputTypes.ClassText | InputTypes.TextVariationNormal; - else if (self == Keyboard.Chat) - result = InputTypes.ClassText | InputTypes.TextFlagCapSentences | InputTypes.TextFlagNoSuggestions; - else if (self == Keyboard.Email) - result = InputTypes.ClassText | InputTypes.TextVariationEmailAddress; - else if (self == Keyboard.Numeric) - result = InputTypes.ClassNumber | InputTypes.NumberFlagDecimal | InputTypes.NumberFlagSigned; - else if (self == Keyboard.Telephone) - result = InputTypes.ClassPhone; - else if (self == Keyboard.Text) - result = InputTypes.ClassText | InputTypes.TextFlagCapSentences; - else if (self == Keyboard.Url) - result = InputTypes.ClassText | InputTypes.TextVariationUri; - else if (self is CustomKeyboard) - { - var custom = (CustomKeyboard)self; - var capitalizedSentenceEnabled = (custom.Flags & KeyboardFlags.CapitalizeSentence) == KeyboardFlags.CapitalizeSentence; - var capitalizedWordsEnabled = (custom.Flags & KeyboardFlags.CapitalizeWord) == KeyboardFlags.CapitalizeWord; - var capitalizedCharacterEnabled = (custom.Flags & KeyboardFlags.CapitalizeCharacter) == KeyboardFlags.CapitalizeCharacter; - - var spellcheckEnabled = (custom.Flags & KeyboardFlags.Spellcheck) == KeyboardFlags.Spellcheck; - var suggestionsEnabled = (custom.Flags & KeyboardFlags.Suggestions) == KeyboardFlags.Suggestions; - - if (!capitalizedSentenceEnabled && !spellcheckEnabled && !suggestionsEnabled) - result = InputTypes.ClassText | InputTypes.TextFlagNoSuggestions; - - if (!capitalizedSentenceEnabled && !spellcheckEnabled && suggestionsEnabled) - { - // Due to the nature of android, TextFlagAutoCorrect includes Spellcheck - Log.Warning(null, "On Android, KeyboardFlags.Suggestions enables KeyboardFlags.Spellcheck as well due to a platform limitation."); - result = InputTypes.ClassText | InputTypes.TextFlagAutoCorrect; - } - - if (!capitalizedSentenceEnabled && spellcheckEnabled && !suggestionsEnabled) - result = InputTypes.ClassText | InputTypes.TextFlagAutoComplete; - - if (!capitalizedSentenceEnabled && spellcheckEnabled && suggestionsEnabled) - result = InputTypes.ClassText | InputTypes.TextFlagAutoCorrect; - - if (capitalizedSentenceEnabled && !spellcheckEnabled && !suggestionsEnabled) - result = InputTypes.ClassText | InputTypes.TextFlagCapSentences | InputTypes.TextFlagNoSuggestions; - - if (capitalizedSentenceEnabled && !spellcheckEnabled && suggestionsEnabled) - { - // Due to the nature of android, TextFlagAutoCorrect includes Spellcheck - Log.Warning(null, "On Android, KeyboardFlags.Suggestions enables KeyboardFlags.Spellcheck as well due to a platform limitation."); - result = InputTypes.ClassText | InputTypes.TextFlagCapSentences | InputTypes.TextFlagAutoCorrect; - } - - if (capitalizedSentenceEnabled && spellcheckEnabled && !suggestionsEnabled) - result = InputTypes.ClassText | InputTypes.TextFlagCapSentences | InputTypes.TextFlagAutoComplete; - - if (capitalizedSentenceEnabled && spellcheckEnabled && suggestionsEnabled) - result = InputTypes.ClassText | InputTypes.TextFlagCapSentences | InputTypes.TextFlagAutoCorrect; - - // All existed before these settings. This ensures these changes are backwards compatible - // without this check TextFlagCapCharacters would win - if (custom.Flags != KeyboardFlags.All) - { - if (capitalizedWordsEnabled) - result = result | InputTypes.TextFlagCapWords; - - if (capitalizedCharacterEnabled) - result = result | InputTypes.TextFlagCapCharacters; - } - } - else - { - // Should never happens - result = InputTypes.TextVariationNormal; - } - return result; - } - } -} \ No newline at end of file From a8e098335f375c585a6a536328e0ead663c723eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Mon, 15 Mar 2021 11:56:21 +0100 Subject: [PATCH 18/30] Possible iOS Compatibility for Extensions. --- .../Core/src/iOS/Extensions/Extensions.cs | 150 +----------------- .../src/iOS/Renderers/ButtonLayoutManager.cs | 1 + .../Core/src/iOS/Renderers/EntryRenderer.cs | 1 + .../SearchHandlerAppearanceTracker.cs | 1 + .../{KeyboardExtensions.cs => Extensions.cs} | 81 +++++++++- 5 files changed, 84 insertions(+), 150 deletions(-) rename src/Core/src/Platform/iOS/{KeyboardExtensions.cs => Extensions.cs} (56%) diff --git a/src/Compatibility/Core/src/iOS/Extensions/Extensions.cs b/src/Compatibility/Core/src/iOS/Extensions/Extensions.cs index e2d7ce489a45..7988b688e5b4 100644 --- a/src/Compatibility/Core/src/iOS/Extensions/Extensions.cs +++ b/src/Compatibility/Core/src/iOS/Extensions/Extensions.cs @@ -6,77 +6,9 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.iOS { + [PortHandler("Following methods still need to be ported.")] public static class Extensions { - public static void ApplyKeyboard(this IUITextInput textInput, Keyboard keyboard) - { - if(textInput is IUITextInputTraits traits) - ApplyKeyboard(traits, keyboard); - } - - public static void ApplyKeyboard(this IUITextInputTraits textInput, Keyboard keyboard) - { - textInput.AutocapitalizationType = UITextAutocapitalizationType.None; - textInput.AutocorrectionType = UITextAutocorrectionType.No; - textInput.SpellCheckingType = UITextSpellCheckingType.No; - textInput.KeyboardType = UIKeyboardType.Default; - - if (keyboard == Keyboard.Default) - { - textInput.AutocapitalizationType = UITextAutocapitalizationType.Sentences; - textInput.AutocorrectionType = UITextAutocorrectionType.Default; - textInput.SpellCheckingType = UITextSpellCheckingType.Default; - } - else if (keyboard == Keyboard.Chat) - { - textInput.AutocapitalizationType = UITextAutocapitalizationType.Sentences; - textInput.AutocorrectionType = UITextAutocorrectionType.Yes; - } - else if (keyboard == Keyboard.Email) - textInput.KeyboardType = UIKeyboardType.EmailAddress; - else if (keyboard == Keyboard.Numeric) - textInput.KeyboardType = UIKeyboardType.DecimalPad; - else if (keyboard == Keyboard.Telephone) - textInput.KeyboardType = UIKeyboardType.PhonePad; - else if (keyboard == Keyboard.Text) - { - textInput.AutocapitalizationType = UITextAutocapitalizationType.Sentences; - textInput.AutocorrectionType = UITextAutocorrectionType.Yes; - textInput.SpellCheckingType = UITextSpellCheckingType.Yes; - } - else if (keyboard == Keyboard.Url) - textInput.KeyboardType = UIKeyboardType.Url; - else if (keyboard is CustomKeyboard) - { - var custom = (CustomKeyboard)keyboard; - - var capitalizedSentenceEnabled = (custom.Flags & KeyboardFlags.CapitalizeSentence) == KeyboardFlags.CapitalizeSentence; - var capitalizedWordsEnabled = (custom.Flags & KeyboardFlags.CapitalizeWord) == KeyboardFlags.CapitalizeWord; - var capitalizedCharacterEnabled = (custom.Flags & KeyboardFlags.CapitalizeCharacter) == KeyboardFlags.CapitalizeCharacter; - var capitalizedNone = (custom.Flags & KeyboardFlags.None) == KeyboardFlags.None; - - var spellcheckEnabled = (custom.Flags & KeyboardFlags.Spellcheck) == KeyboardFlags.Spellcheck; - var suggestionsEnabled = (custom.Flags & KeyboardFlags.Suggestions) == KeyboardFlags.Suggestions; - - - UITextAutocapitalizationType capSettings = UITextAutocapitalizationType.None; - - // Sentence being first ensures that the behavior of ALL is backwards compatible - if (capitalizedSentenceEnabled) - capSettings = UITextAutocapitalizationType.Sentences; - else if (capitalizedWordsEnabled) - capSettings = UITextAutocapitalizationType.Words; - else if (capitalizedCharacterEnabled) - capSettings = UITextAutocapitalizationType.AllCharacters; - else if (capitalizedNone) - capSettings = UITextAutocapitalizationType.None; - - textInput.AutocapitalizationType = capSettings; - textInput.AutocorrectionType = suggestionsEnabled ? UITextAutocorrectionType.Yes : UITextAutocorrectionType.No; - textInput.SpellCheckingType = spellcheckEnabled ? UITextSpellCheckingType.Yes : UITextSpellCheckingType.No; - } - } - public static UIModalPresentationStyle ToNativeModalPresentationStyle(this PlatformConfiguration.iOSSpecific.UIModalPresentationStyle style) { switch (style) @@ -110,28 +42,6 @@ internal static UISearchBarStyle ToNativeSearchBarStyle(this PlatformConfigurati throw new ArgumentOutOfRangeException(nameof(style)); } } - - internal static UIReturnKeyType ToUIReturnKeyType(this ReturnType returnType) - { - switch (returnType) - { - case ReturnType.Go: - return UIReturnKeyType.Go; - case ReturnType.Next: - return UIReturnKeyType.Next; - case ReturnType.Send: - return UIReturnKeyType.Send; - case ReturnType.Search: - return UIReturnKeyType.Search; - case ReturnType.Done: - return UIReturnKeyType.Done; - case ReturnType.Default: - return UIReturnKeyType.Default; - default: - throw new System.NotImplementedException($"ReturnType {returnType} not supported"); - } - } - internal static DeviceOrientation ToDeviceOrientation(this UIDeviceOrientation orientation) { switch (orientation) @@ -149,64 +59,6 @@ internal static DeviceOrientation ToDeviceOrientation(this UIDeviceOrientation o } } - [PortHandler] - internal static NSMutableAttributedString AddCharacterSpacing(this NSAttributedString attributedString, string text, double characterSpacing) - { - if (attributedString == null && characterSpacing == 0) - return null; - - NSMutableAttributedString mutableAttributedString = attributedString as NSMutableAttributedString; - if (attributedString == null || attributedString.Length == 0) - { - mutableAttributedString = text == null ? new NSMutableAttributedString() : new NSMutableAttributedString(text); - } - else - { - mutableAttributedString = new NSMutableAttributedString(attributedString); - - if (!mutableAttributedString.MutableString.ToString().Equals(text)) - { - mutableAttributedString.MutableString.SetString(new NSString(text)); - } - } - - AddKerningAdjustment(mutableAttributedString, text, characterSpacing); - - return mutableAttributedString; - } - - [PortHandler] - internal static bool HasCharacterAdjustment(this NSMutableAttributedString mutableAttributedString) - { - if (mutableAttributedString == null) - return false; - - NSRange removalRange; - var attributes = mutableAttributedString.GetAttributes(0, out removalRange); - - for (uint i = 0; i < attributes.Count; i++) - if (attributes.Keys[i] is NSString nSString && nSString == UIStringAttributeKey.KerningAdjustment) - return true; - - return false; - } - - [PortHandler] - internal static void AddKerningAdjustment(NSMutableAttributedString mutableAttributedString, string text, double characterSpacing) - { - if (!string.IsNullOrEmpty(text)) - { - if (characterSpacing == 0 && !mutableAttributedString.HasCharacterAdjustment()) - return; - - mutableAttributedString.AddAttribute - ( - UIStringAttributeKey.KerningAdjustment, - NSObject.FromObject(characterSpacing), new NSRange(0, text.Length - 1) - ); - } - } - internal static bool IsHorizontal(this Button.ButtonContentLayout layout) => layout.Position == Button.ButtonContentLayout.ImagePosition.Left || layout.Position == Button.ButtonContentLayout.ImagePosition.Right; diff --git a/src/Compatibility/Core/src/iOS/Renderers/ButtonLayoutManager.cs b/src/Compatibility/Core/src/iOS/Renderers/ButtonLayoutManager.cs index c8eb4f6eb917..1544f21c1fd5 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/ButtonLayoutManager.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/ButtonLayoutManager.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using CoreGraphics; using Foundation; +using Microsoft.Maui.Platform.iOS; using UIKit; namespace Microsoft.Maui.Controls.Compatibility.Platform.iOS diff --git a/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs index 12a87eb3bd7c..2774d7f272a3 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs @@ -7,6 +7,7 @@ using UIKit; using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific; using Specifics = Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific.Entry; +using Microsoft.Maui.Platform.iOS; namespace Microsoft.Maui.Controls.Compatibility.Platform.iOS { diff --git a/src/Compatibility/Core/src/iOS/Renderers/SearchHandlerAppearanceTracker.cs b/src/Compatibility/Core/src/iOS/Renderers/SearchHandlerAppearanceTracker.cs index bdd4bd5945d4..a4e50b9223db 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/SearchHandlerAppearanceTracker.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/SearchHandlerAppearanceTracker.cs @@ -1,6 +1,7 @@ using System; using CoreGraphics; using Foundation; +using Microsoft.Maui.Platform.iOS; using UIKit; namespace Microsoft.Maui.Controls.Compatibility.Platform.iOS diff --git a/src/Core/src/Platform/iOS/KeyboardExtensions.cs b/src/Core/src/Platform/iOS/Extensions.cs similarity index 56% rename from src/Core/src/Platform/iOS/KeyboardExtensions.cs rename to src/Core/src/Platform/iOS/Extensions.cs index 53fa83450ceb..9de40f52b42c 100644 --- a/src/Core/src/Platform/iOS/KeyboardExtensions.cs +++ b/src/Core/src/Platform/iOS/Extensions.cs @@ -1,11 +1,12 @@ using System; using System.Collections.Generic; using System.Text; +using Foundation; using UIKit; namespace Microsoft.Maui.Platform.iOS { - public static class KeyboardExtensions + public static class Extensions { public static void ApplyKeyboard(this IUITextInput textInput, Keyboard keyboard) { @@ -75,5 +76,83 @@ public static void ApplyKeyboard(this IUITextInputTraits textInput, Keyboard key textInput.SpellCheckingType = spellcheckEnabled ? UITextSpellCheckingType.Yes : UITextSpellCheckingType.No; } } + + public static NSMutableAttributedString? AddCharacterSpacing(this NSAttributedString attributedString, string text, double characterSpacing) + { + if (attributedString == null && characterSpacing == 0) + return null; + + NSMutableAttributedString? mutableAttributedString = attributedString as NSMutableAttributedString; + if (attributedString == null || attributedString.Length == 0) + { + mutableAttributedString = text == null ? new NSMutableAttributedString() : new NSMutableAttributedString(text); + } + else + { + mutableAttributedString = new NSMutableAttributedString(attributedString); + + if (!mutableAttributedString.MutableString.ToString().Equals(text)) + { + mutableAttributedString.MutableString.SetString(new NSString(text)); + } + } + + AddKerningAdjustment(mutableAttributedString, text, characterSpacing); + + return mutableAttributedString; + } + + internal static bool HasCharacterAdjustment(this NSMutableAttributedString mutableAttributedString) + { + if (mutableAttributedString == null) + return false; + + NSRange removalRange; + var attributes = mutableAttributedString.GetAttributes(0, out removalRange); + + for (uint i = 0; i < attributes.Count; i++) + if (attributes.Keys[i] is NSString nSString && nSString == UIStringAttributeKey.KerningAdjustment) + return true; + + return false; + } + + internal static void AddKerningAdjustment(NSMutableAttributedString mutableAttributedString, string? text, double characterSpacing) + { + if (!string.IsNullOrEmpty(text)) + { + if (characterSpacing == 0 && !mutableAttributedString.HasCharacterAdjustment()) + return; + + mutableAttributedString.AddAttribute + ( + UIStringAttributeKey.KerningAdjustment, +#pragma warning disable CS8602 // Dereference of a possibly null reference. + NSObject.FromObject(characterSpacing), new NSRange(0, text.Length - 1) +#pragma warning restore CS8602 // Dereference of a possibly null reference. + ); + } + } + + public static UIReturnKeyType ToUIReturnKeyType(this ReturnType returnType) + { + switch (returnType) + { + case ReturnType.Go: + return UIReturnKeyType.Go; + case ReturnType.Next: + return UIReturnKeyType.Next; + case ReturnType.Send: + return UIReturnKeyType.Send; + case ReturnType.Search: + return UIReturnKeyType.Search; + case ReturnType.Done: + return UIReturnKeyType.Done; + case ReturnType.Default: + return UIReturnKeyType.Default; + default: + throw new System.NotImplementedException($"ReturnType {returnType} not supported"); + } + } } } From 42f0326f9ca8eff8e9480c40b83018e38c995704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Mon, 15 Mar 2021 12:17:08 +0100 Subject: [PATCH 19/30] Missing SearchBarRenderer usings. --- src/Compatibility/Core/src/iOS/Renderers/SearchBarRenderer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Compatibility/Core/src/iOS/Renderers/SearchBarRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/SearchBarRenderer.cs index e96ae87188c5..8a88b3289f89 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/SearchBarRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/SearchBarRenderer.cs @@ -5,6 +5,7 @@ using Foundation; using UIKit; using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific; +using Microsoft.Maui.Platform.iOS; namespace Microsoft.Maui.Controls.Compatibility.Platform.iOS { From 05dad635ce669331a0443cb4d4f094649267472a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Mon, 15 Mar 2021 12:51:21 +0100 Subject: [PATCH 20/30] Added missing usings for iOS renderers. --- src/Compatibility/Core/src/iOS/Cells/EntryCellRenderer.cs | 1 + src/Compatibility/Core/src/iOS/Renderers/DatePickerRenderer.cs | 1 + src/Compatibility/Core/src/iOS/Renderers/EditorRenderer.cs | 1 + src/Compatibility/Core/src/iOS/Renderers/PickerRenderer.cs | 1 + 4 files changed, 4 insertions(+) diff --git a/src/Compatibility/Core/src/iOS/Cells/EntryCellRenderer.cs b/src/Compatibility/Core/src/iOS/Cells/EntryCellRenderer.cs index 2cae3320e78f..6d979e445ff8 100644 --- a/src/Compatibility/Core/src/iOS/Cells/EntryCellRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Cells/EntryCellRenderer.cs @@ -1,6 +1,7 @@ using System; using System.ComponentModel; using Foundation; +using Microsoft.Maui.Platform.iOS; using UIKit; using RectangleF = CoreGraphics.CGRect; diff --git a/src/Compatibility/Core/src/iOS/Renderers/DatePickerRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/DatePickerRenderer.cs index 2bd7123922e2..50d6d4cff99e 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/DatePickerRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/DatePickerRenderer.cs @@ -4,6 +4,7 @@ using System.Linq; using Foundation; using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific; +using Microsoft.Maui.Platform.iOS; using UIKit; using RectangleF = CoreGraphics.CGRect; diff --git a/src/Compatibility/Core/src/iOS/Renderers/EditorRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/EditorRenderer.cs index f7bc0ad41f0e..8744c1756ab8 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/EditorRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/EditorRenderer.cs @@ -2,6 +2,7 @@ using System.ComponentModel; using CoreGraphics; using Foundation; +using Microsoft.Maui.Platform.iOS; using UIKit; using RectangleF = CoreGraphics.CGRect; diff --git a/src/Compatibility/Core/src/iOS/Renderers/PickerRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/PickerRenderer.cs index cf16f9f2a0c7..44edb1196993 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/PickerRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/PickerRenderer.cs @@ -8,6 +8,7 @@ using UIKit; using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific; using RectangleF = CoreGraphics.CGRect; +using Microsoft.Maui.Platform.iOS; namespace Microsoft.Maui.Controls.Compatibility.Platform.iOS { From 83183cad808fc00b8a1e9759e64d86d59ddde9f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Mon, 15 Mar 2021 13:04:38 +0100 Subject: [PATCH 21/30] more missing references in iOS renderers... --- src/Compatibility/Core/src/iOS/Platform.cs | 1 + src/Compatibility/Core/src/iOS/Renderers/LabelRenderer.cs | 1 + src/Compatibility/Core/src/iOS/Renderers/TimePickerRenderer.cs | 1 + 3 files changed, 3 insertions(+) diff --git a/src/Compatibility/Core/src/iOS/Platform.cs b/src/Compatibility/Core/src/iOS/Platform.cs index ed59a0a6e8c2..1962b88ec42e 100644 --- a/src/Compatibility/Core/src/iOS/Platform.cs +++ b/src/Compatibility/Core/src/iOS/Platform.cs @@ -9,6 +9,7 @@ using RectangleF = CoreGraphics.CGRect; using Microsoft.Maui.Controls.Internals; using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific; +using Microsoft.Maui.Platform.iOS; namespace Microsoft.Maui.Controls.Compatibility.Platform.iOS { diff --git a/src/Compatibility/Core/src/iOS/Renderers/LabelRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/LabelRenderer.cs index 9f636ec1e570..a0218f0c0de8 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/LabelRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/LabelRenderer.cs @@ -5,6 +5,7 @@ using Foundation; using System.Collections.Generic; using System.Diagnostics; +using Microsoft.Maui.Platform.iOS; #if __MOBILE__ using UIKit; diff --git a/src/Compatibility/Core/src/iOS/Renderers/TimePickerRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/TimePickerRenderer.cs index 18608fab27e7..f14d08e018b2 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/TimePickerRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/TimePickerRenderer.cs @@ -6,6 +6,7 @@ using UIKit; using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific; using RectangleF = CoreGraphics.CGRect; +using Microsoft.Maui.Platform.iOS; namespace Microsoft.Maui.Controls.Compatibility.Platform.iOS { From c4de5731136b16e1e5d4aaede6d25ca412570d81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Mon, 15 Mar 2021 14:09:50 +0100 Subject: [PATCH 22/30] Removed already ported extension methods in iOS and changed the usages in renderer. --- .../src/iOS/Renderers/ButtonLayoutManager.cs | 6 +- .../src/iOS/Renderers/DatePickerRenderer.cs | 2 +- .../Core/src/iOS/Renderers/EditorRenderer.cs | 6 +- .../Core/src/iOS/Renderers/EntryRenderer.cs | 6 +- .../Core/src/iOS/Renderers/LabelRenderer.cs | 2 +- .../Core/src/iOS/Renderers/PickerRenderer.cs | 6 +- .../src/iOS/Renderers/SearchBarRenderer.cs | 8 +-- .../src/iOS/Renderers/TimePickerRenderer.cs | 2 +- src/Core/src/Platform/iOS/Extensions.cs | 57 ------------------- 9 files changed, 19 insertions(+), 76 deletions(-) diff --git a/src/Compatibility/Core/src/iOS/Renderers/ButtonLayoutManager.cs b/src/Compatibility/Core/src/iOS/Renderers/ButtonLayoutManager.cs index 1544f21c1fd5..4eb18ccb359a 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/ButtonLayoutManager.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/ButtonLayoutManager.cs @@ -210,17 +210,17 @@ internal void UpdateText() var normal = control .GetAttributedTitle(UIControlState.Normal) - .AddCharacterSpacing(text, _element.CharacterSpacing); + .WithCharacterSpacing(_element.CharacterSpacing); var highlighted = control .GetAttributedTitle(UIControlState.Highlighted) - .AddCharacterSpacing(text, _element.CharacterSpacing); + .WithCharacterSpacing(_element.CharacterSpacing); var disabled = control .GetAttributedTitle(UIControlState.Disabled) - .AddCharacterSpacing(text, _element.CharacterSpacing); + .WithCharacterSpacing(_element.CharacterSpacing); normal.AddAttribute( UIStringAttributeKey.ForegroundColor, diff --git a/src/Compatibility/Core/src/iOS/Renderers/DatePickerRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/DatePickerRenderer.cs index 50d6d4cff99e..f93b8ed04947 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/DatePickerRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/DatePickerRenderer.cs @@ -215,7 +215,7 @@ protected internal virtual void UpdateFont() void UpdateCharacterSpacing() { - var textAttr = Control.AttributedText.AddCharacterSpacing(Control.Text, Element.CharacterSpacing); + var textAttr = Control.AttributedText.WithCharacterSpacing(Element.CharacterSpacing); if (textAttr != null) Control.AttributedText = textAttr; diff --git a/src/Compatibility/Core/src/iOS/Renderers/EditorRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/EditorRenderer.cs index 8744c1756ab8..bb8583f12e27 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/EditorRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/EditorRenderer.cs @@ -73,12 +73,12 @@ protected internal override void UpdatePlaceholderText() protected internal override void UpdateCharacterSpacing() { - var textAttr = TextView.AttributedText.AddCharacterSpacing(Element.Text, Element.CharacterSpacing); + var textAttr = TextView.AttributedText.WithCharacterSpacing(Element.CharacterSpacing); if(textAttr != null) TextView.AttributedText = textAttr; - var placeHolder = _placeholderLabel.AttributedText.AddCharacterSpacing(Element.Placeholder, Element.CharacterSpacing); + var placeHolder = _placeholderLabel.AttributedText.WithCharacterSpacing(Element.CharacterSpacing); if(placeHolder != null) _placeholderLabel.AttributedText = placeHolder; @@ -119,7 +119,7 @@ void CreatePlaceholderLabel() ); _placeholderLabel.TranslatesAutoresizingMaskIntoConstraints = false; - _placeholderLabel.AttributedText = _placeholderLabel.AttributedText.AddCharacterSpacing(Element.Placeholder, Element.CharacterSpacing); + _placeholderLabel.AttributedText = _placeholderLabel.AttributedText.WithCharacterSpacing(Element.CharacterSpacing); Control.AddConstraints(hConstraints); Control.AddConstraints(vConstraints); diff --git a/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs index 2774d7f272a3..8d4180f07c18 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/EntryRenderer.cs @@ -360,7 +360,7 @@ protected virtual void UpdatePlaceholder() UpdateAttributedPlaceholder(formatted.ToAttributed(Element, color)); } - UpdateAttributedPlaceholder(Control.AttributedPlaceholder.AddCharacterSpacing(Element.Placeholder, Element.CharacterSpacing)); + UpdateAttributedPlaceholder(Control.AttributedPlaceholder.WithCharacterSpacing(Element.CharacterSpacing)); } protected virtual void UpdateAttributedPlaceholder(NSAttributedString nsAttributedString) => @@ -377,12 +377,12 @@ void UpdateText() void UpdateCharacterSpacing() { - var textAttr = Control.AttributedText.AddCharacterSpacing(Element.Text, Element.CharacterSpacing); + var textAttr = Control.AttributedText.WithCharacterSpacing(Element.CharacterSpacing); if (textAttr != null) Control.AttributedText = textAttr; - var placeHolder = Control.AttributedPlaceholder.AddCharacterSpacing(Element.Placeholder, Element.CharacterSpacing); + var placeHolder = Control.AttributedPlaceholder.WithCharacterSpacing(Element.CharacterSpacing); if (placeHolder != null) UpdateAttributedPlaceholder(placeHolder); diff --git a/src/Compatibility/Core/src/iOS/Renderers/LabelRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/LabelRenderer.cs index a0218f0c0de8..aa0f24e511c3 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/LabelRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/LabelRenderer.cs @@ -418,7 +418,7 @@ void UpdateCharacterSpacing() if (string.IsNullOrEmpty(Element.Text)) return; #if __MOBILE__ - var textAttr = Control.AttributedText.AddCharacterSpacing(Element.Text, Element.CharacterSpacing); + var textAttr = Control.AttributedText.WithCharacterSpacing(Element.CharacterSpacing); if (textAttr != null) Control.AttributedText = textAttr; diff --git a/src/Compatibility/Core/src/iOS/Renderers/PickerRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/PickerRenderer.cs index 44edb1196993..9af263e092e5 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/PickerRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/PickerRenderer.cs @@ -188,12 +188,12 @@ protected void UpdateCharacterSpacing() if (Control == null) return; - var textAttr = Control.AttributedText.AddCharacterSpacing(Control.Text, Element.CharacterSpacing); + var textAttr = Control.AttributedText.WithCharacterSpacing(Element.CharacterSpacing); if (textAttr != null) Control.AttributedText = textAttr; - var placeHolder = Control.AttributedPlaceholder.AddCharacterSpacing(Element.Title, Element.CharacterSpacing); + var placeHolder = Control.AttributedPlaceholder.WithCharacterSpacing(Element.CharacterSpacing); if (placeHolder != null) UpdateAttributedPlaceholder(placeHolder); @@ -226,7 +226,7 @@ protected internal virtual void UpdatePlaceholder() UpdateAttributedPlaceholder(formatted.ToAttributed(Element, color)); } - UpdateAttributedPlaceholder(Control.AttributedPlaceholder.AddCharacterSpacing(Element.Title, Element.CharacterSpacing)); + UpdateAttributedPlaceholder(Control.AttributedPlaceholder.WithCharacterSpacing(Element.CharacterSpacing)); } protected virtual void UpdateAttributedPlaceholder(NSAttributedString nsAttributedString) => diff --git a/src/Compatibility/Core/src/iOS/Renderers/SearchBarRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/SearchBarRenderer.cs index 8a88b3289f89..678d29770bce 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/SearchBarRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/SearchBarRenderer.cs @@ -233,8 +233,8 @@ void UpdateCharacterSpacing() if (_textField == null) return; - _textField.AttributedText = _textField.AttributedText.AddCharacterSpacing(Element.Text, Element.CharacterSpacing); - _textField.AttributedPlaceholder = _textField.AttributedPlaceholder.AddCharacterSpacing(Element.Placeholder, Element.CharacterSpacing); + _textField.AttributedText = _textField.AttributedText.WithCharacterSpacing(Element.CharacterSpacing); + _textField.AttributedPlaceholder = _textField.AttributedPlaceholder.WithCharacterSpacing(Element.CharacterSpacing); } void UpdateHorizontalTextAlignment() @@ -323,14 +323,14 @@ void UpdatePlaceholder() ? targetColor : ColorExtensions.PlaceholderColor.ToColor(); _textField.AttributedPlaceholder = formatted.ToAttributed(Element, color); - _textField.AttributedPlaceholder.AddCharacterSpacing(Element.Placeholder, Element.CharacterSpacing); + _textField.AttributedPlaceholder.WithCharacterSpacing(Element.CharacterSpacing); } else { _textField.AttributedPlaceholder = formatted.ToAttributed(Element, targetColor.IsDefault ? ColorExtensions.PlaceholderColor.ToColor() : targetColor); - _textField.AttributedPlaceholder.AddCharacterSpacing(Element.Placeholder, Element.CharacterSpacing); + _textField.AttributedPlaceholder.WithCharacterSpacing(Element.CharacterSpacing); } } diff --git a/src/Compatibility/Core/src/iOS/Renderers/TimePickerRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/TimePickerRenderer.cs index f14d08e018b2..ac62039484ab 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/TimePickerRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/TimePickerRenderer.cs @@ -196,7 +196,7 @@ protected internal virtual void UpdateTextColor() void UpdateCharacterSpacing() { - var textAttr = Control.AttributedText.AddCharacterSpacing(Control.Text, Element.CharacterSpacing); + var textAttr = Control.AttributedText.WithCharacterSpacing(Element.CharacterSpacing); if (textAttr != null) Control.AttributedText = textAttr; diff --git a/src/Core/src/Platform/iOS/Extensions.cs b/src/Core/src/Platform/iOS/Extensions.cs index 9de40f52b42c..29cc2875d174 100644 --- a/src/Core/src/Platform/iOS/Extensions.cs +++ b/src/Core/src/Platform/iOS/Extensions.cs @@ -77,63 +77,6 @@ public static void ApplyKeyboard(this IUITextInputTraits textInput, Keyboard key } } - public static NSMutableAttributedString? AddCharacterSpacing(this NSAttributedString attributedString, string text, double characterSpacing) - { - if (attributedString == null && characterSpacing == 0) - return null; - - NSMutableAttributedString? mutableAttributedString = attributedString as NSMutableAttributedString; - if (attributedString == null || attributedString.Length == 0) - { - mutableAttributedString = text == null ? new NSMutableAttributedString() : new NSMutableAttributedString(text); - } - else - { - mutableAttributedString = new NSMutableAttributedString(attributedString); - - if (!mutableAttributedString.MutableString.ToString().Equals(text)) - { - mutableAttributedString.MutableString.SetString(new NSString(text)); - } - } - - AddKerningAdjustment(mutableAttributedString, text, characterSpacing); - - return mutableAttributedString; - } - - internal static bool HasCharacterAdjustment(this NSMutableAttributedString mutableAttributedString) - { - if (mutableAttributedString == null) - return false; - - NSRange removalRange; - var attributes = mutableAttributedString.GetAttributes(0, out removalRange); - - for (uint i = 0; i < attributes.Count; i++) - if (attributes.Keys[i] is NSString nSString && nSString == UIStringAttributeKey.KerningAdjustment) - return true; - - return false; - } - - internal static void AddKerningAdjustment(NSMutableAttributedString mutableAttributedString, string? text, double characterSpacing) - { - if (!string.IsNullOrEmpty(text)) - { - if (characterSpacing == 0 && !mutableAttributedString.HasCharacterAdjustment()) - return; - - mutableAttributedString.AddAttribute - ( - UIStringAttributeKey.KerningAdjustment, -#pragma warning disable CS8602 // Dereference of a possibly null reference. - NSObject.FromObject(characterSpacing), new NSRange(0, text.Length - 1) -#pragma warning restore CS8602 // Dereference of a possibly null reference. - ); - } - } - public static UIReturnKeyType ToUIReturnKeyType(this ReturnType returnType) { switch (returnType) From 7b57f0ae2cb28d761cde1e0994d495393ee12c66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Mon, 15 Mar 2021 14:13:30 +0100 Subject: [PATCH 23/30] Inverted LogaliczedDigitKeyListener return logic to better one. --- src/Core/src/Platform/Android/LocalizedDigitsKeyListener.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/src/Platform/Android/LocalizedDigitsKeyListener.cs b/src/Core/src/Platform/Android/LocalizedDigitsKeyListener.cs index 2df4be445614..b31c5ec78c5a 100644 --- a/src/Core/src/Platform/Android/LocalizedDigitsKeyListener.cs +++ b/src/Core/src/Platform/Android/LocalizedDigitsKeyListener.cs @@ -217,7 +217,7 @@ bool IsDecimalPointChar(char c) } } - return stripped == null ? (filterFormatted != null ? filterFormatted : new String("")) : stripped; + return stripped ?? filterFormatted ?? new String(""); } #pragma warning restore CS8602 // Dereference of a possibly null reference. } From 8b52d740a5d7db03091d51c4928962408ffcaed6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Kaan=20K=C3=B6se?= Date: Fri, 19 Mar 2021 02:04:54 +0100 Subject: [PATCH 24/30] Merged main. --- .../Platform/Android/EditTextExtensions.cs | 46 +++++++++++++++---- .../src/Platform/iOS/TextFieldExtensions.cs | 11 ++++- src/Core/tests/DeviceTests/Stubs/EntryStub.cs | 2 - 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/Core/src/Platform/Android/EditTextExtensions.cs b/src/Core/src/Platform/Android/EditTextExtensions.cs index 923dc80950e4..72f47c35fd18 100644 --- a/src/Core/src/Platform/Android/EditTextExtensions.cs +++ b/src/Core/src/Platform/Android/EditTextExtensions.cs @@ -2,6 +2,7 @@ using Android.Text; using Android.Util; using AndroidX.AppCompat.Widget; +using Microsoft.Maui.Platform.Android; namespace Microsoft.Maui { @@ -99,6 +100,11 @@ public static void UpdateIsReadOnly(this AppCompatEditText editText, IEntry entr editText.Focusable = isEditable; } + public static void UpdateKeyboard(this AppCompatEditText editText, IEntry entry) + { + editText.SetInputType(entry); + } + public static void UpdateFont(this AppCompatEditText editText, IEntry entry, IFontManager fontManager) { var font = entry.Font; @@ -122,20 +128,40 @@ public static void UpdateCharacterSpacing(this AppCompatEditText editText, IEdit internal static void SetInputType(this AppCompatEditText editText, IEntry entry) { - editText.InputType = InputTypes.ClassText; - editText.InputType |= InputTypes.TextFlagMultiLine; + if (entry.IsReadOnly) + editText.InputType = InputTypes.Null; + else + { + var keyboard = entry.Keyboard; + var nativeInputTypeToUpdate = keyboard.ToInputType(); - if (entry.IsPassword && ((editText.InputType & InputTypes.ClassText) == InputTypes.ClassText)) - editText.InputType |= InputTypes.TextVariationPassword; + if (!(keyboard is CustomKeyboard)) + { + // TODO: IsSpellCheckEnabled handling must be here. - if (entry.IsPassword && ((editText.InputType & InputTypes.ClassNumber) == InputTypes.ClassNumber)) - editText.InputType |= InputTypes.NumberVariationPassword; + if ((nativeInputTypeToUpdate & InputTypes.TextFlagNoSuggestions) != InputTypes.TextFlagNoSuggestions) + { + if (!entry.IsTextPredictionEnabled) + nativeInputTypeToUpdate |= InputTypes.TextFlagNoSuggestions; + } + } - if (!entry.IsTextPredictionEnabled && ((editText.InputType & InputTypes.TextFlagNoSuggestions) != InputTypes.TextFlagNoSuggestions)) - editText.InputType |= InputTypes.TextFlagNoSuggestions; + if (keyboard == Keyboard.Numeric) + { + editText.KeyListener = LocalizedDigitsKeyListener.Create(editText.InputType); + } - if (entry.IsReadOnly) - editText.InputType = InputTypes.Null; + if (entry.IsPassword) + { + if (((nativeInputTypeToUpdate & InputTypes.ClassText) == InputTypes.ClassText)) + nativeInputTypeToUpdate |= InputTypes.TextVariationPassword; + + if (((nativeInputTypeToUpdate & InputTypes.ClassNumber) == InputTypes.ClassNumber)) + nativeInputTypeToUpdate |= InputTypes.NumberVariationPassword; + } + + editText.InputType = nativeInputTypeToUpdate; + } } } } diff --git a/src/Core/src/Platform/iOS/TextFieldExtensions.cs b/src/Core/src/Platform/iOS/TextFieldExtensions.cs index 3bf91dda39f8..97aedc6a4988 100644 --- a/src/Core/src/Platform/iOS/TextFieldExtensions.cs +++ b/src/Core/src/Platform/iOS/TextFieldExtensions.cs @@ -1,4 +1,5 @@ -using UIKit; +using Microsoft.Maui.Platform.iOS; +using UIKit; namespace Microsoft.Maui { @@ -83,6 +84,14 @@ public static void UpdateCharacterSpacing(this UITextField textField, IText text textField.AttributedText = textAttr; } + public static void UpdateKeyboard(this UITextField textField, IEntry entry) + { + var keyboard = entry.Keyboard; + + textField.ApplyKeyboard(keyboard); + textField.ReloadInputViews(); + } + public static void UpdateFont(this UITextField textField, IText textView, IFontManager fontManager) { var uiFont = fontManager.GetFont(textView.Font); diff --git a/src/Core/tests/DeviceTests/Stubs/EntryStub.cs b/src/Core/tests/DeviceTests/Stubs/EntryStub.cs index 3c71eb0d7bc1..fe720e1315df 100644 --- a/src/Core/tests/DeviceTests/Stubs/EntryStub.cs +++ b/src/Core/tests/DeviceTests/Stubs/EntryStub.cs @@ -35,8 +35,6 @@ public string Text void OnTextChanged(string oldValue, string newValue) => TextChanged?.Invoke(this, new StubPropertyChangedEventArgs(oldValue, newValue)); - public Font Font { get; set; } - public Keyboard Keyboard { get; set; } = Keyboard.Default; } } \ No newline at end of file From 5555ab75dbfd924fef0fe89ba2c6abd634be5423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Sua=CC=81rez=20Ruiz?= Date: Tue, 6 Apr 2021 10:30:48 +0200 Subject: [PATCH 25/30] Fix build errors --- .../Platform/Android/EditTextExtensions.cs | 4 ---- .../Platform/Android/KeyboardExtensions.cs | 12 ++++------ .../Android/LocalizedDigitsKeyListener.cs | 24 +++++++++---------- .../{Extensions.cs => KeyboardExtensions.cs} | 12 ++++------ .../src/Platform/iOS/TextFieldExtensions.cs | 3 +-- .../Entry/EntryHandlerTests.Android.cs | 2 -- .../tests/DeviceTests/Stubs/EditorStub.cs | 3 ++- 7 files changed, 23 insertions(+), 37 deletions(-) rename src/Core/src/Platform/iOS/{Extensions.cs => KeyboardExtensions.cs} (95%) diff --git a/src/Core/src/Platform/Android/EditTextExtensions.cs b/src/Core/src/Platform/Android/EditTextExtensions.cs index fd32f0c65aae..68e9d41a6599 100644 --- a/src/Core/src/Platform/Android/EditTextExtensions.cs +++ b/src/Core/src/Platform/Android/EditTextExtensions.cs @@ -4,7 +4,6 @@ using Android.Text; using Android.Util; using AndroidX.AppCompat.Widget; -using Microsoft.Maui.Platform.Android; namespace Microsoft.Maui { @@ -140,9 +139,6 @@ public static void UpdateKeyboard(this AppCompatEditText editText, IEntry entry) editText.SetInputType(entry); } - public static void UpdateFont(this AppCompatEditText editText, IEntry entry, IFontManager fontManager) => - editText.UpdateFont(entry.Font, fontManager); - public static void UpdateIsReadOnly(this AppCompatEditText editText, IEditor editor) { bool isReadOnly = !editor.IsReadOnly; diff --git a/src/Core/src/Platform/Android/KeyboardExtensions.cs b/src/Core/src/Platform/Android/KeyboardExtensions.cs index 0acf3d56ede6..b1bda5deb64f 100644 --- a/src/Core/src/Platform/Android/KeyboardExtensions.cs +++ b/src/Core/src/Platform/Android/KeyboardExtensions.cs @@ -1,9 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Android.Text; +using Android.Text; -namespace Microsoft.Maui.Platform.Android +namespace Microsoft.Maui { public static class KeyboardExtensions { @@ -75,10 +72,10 @@ public static InputTypes ToInputType(this Keyboard self) if (custom.Flags != KeyboardFlags.All) { if (capitalizedWordsEnabled) - result = result | InputTypes.TextFlagCapWords; + result |= InputTypes.TextFlagCapWords; if (capitalizedCharacterEnabled) - result = result | InputTypes.TextFlagCapCharacters; + result |= InputTypes.TextFlagCapCharacters; } } else @@ -86,6 +83,7 @@ public static InputTypes ToInputType(this Keyboard self) // Should never happens result = InputTypes.TextVariationNormal; } + return result; } } diff --git a/src/Core/src/Platform/Android/LocalizedDigitsKeyListener.cs b/src/Core/src/Platform/Android/LocalizedDigitsKeyListener.cs index b31c5ec78c5a..72f77a54d0aa 100644 --- a/src/Core/src/Platform/Android/LocalizedDigitsKeyListener.cs +++ b/src/Core/src/Platform/Android/LocalizedDigitsKeyListener.cs @@ -4,7 +4,7 @@ using Java.Lang; using Java.Text; -namespace Microsoft.Maui.Platform.Android +namespace Microsoft.Maui { public class LocalizedDigitsKeyListener : NumberKeyListener { @@ -14,8 +14,8 @@ public class LocalizedDigitsKeyListener : NumberKeyListener // but we'll make it easy to localize the sign in the future just in case const char SignCharacter = '-'; - static Dictionary? s_unsignedCache; - static Dictionary? s_signedCache; + static Dictionary? UnsignedCache; + static Dictionary? SignedCache; static char GetDecimalSeparator() { @@ -57,18 +57,18 @@ public static NumberKeyListener Create(InputTypes inputTypes) public static LocalizedDigitsKeyListener GetInstance(InputTypes inputTypes, char decimalSeparator) { - if (s_signedCache == null) - s_signedCache = new Dictionary(); + if (SignedCache == null) + SignedCache = new Dictionary(); if ((inputTypes & InputTypes.NumberFlagSigned) != 0) { - return GetInstance(inputTypes, decimalSeparator, ref s_signedCache); + return GetInstance(inputTypes, decimalSeparator, ref SignedCache); } - if (s_unsignedCache == null) - s_unsignedCache = new Dictionary(); + if (UnsignedCache == null) + UnsignedCache = new Dictionary(); - return GetInstance(inputTypes, decimalSeparator, ref s_unsignedCache); + return GetInstance(inputTypes, decimalSeparator, ref UnsignedCache); } static LocalizedDigitsKeyListener GetInstance(InputTypes inputTypes, char decimalSeparator, ref Dictionary cache) @@ -101,12 +101,10 @@ protected override char[] GetAcceptedChars() { if ((InputType & InputTypes.NumberFlagSigned) == 0) { - return _acceptedChars ?? - (_acceptedChars = new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', _decimalSeparator }); + return _acceptedChars ??= new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', _decimalSeparator }; } - return _acceptedChars ?? - (_acceptedChars = new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', SignCharacter, _decimalSeparator }); + return _acceptedChars ??= new[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', SignCharacter, _decimalSeparator }; } static bool IsSignChar(char c) diff --git a/src/Core/src/Platform/iOS/Extensions.cs b/src/Core/src/Platform/iOS/KeyboardExtensions.cs similarity index 95% rename from src/Core/src/Platform/iOS/Extensions.cs rename to src/Core/src/Platform/iOS/KeyboardExtensions.cs index 29cc2875d174..96ddb1866d28 100644 --- a/src/Core/src/Platform/iOS/Extensions.cs +++ b/src/Core/src/Platform/iOS/KeyboardExtensions.cs @@ -1,12 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Foundation; -using UIKit; +using UIKit; -namespace Microsoft.Maui.Platform.iOS +namespace Microsoft.Maui { - public static class Extensions + public static class KeyboardExtensions { public static void ApplyKeyboard(this IUITextInput textInput, Keyboard keyboard) { @@ -98,4 +94,4 @@ public static UIReturnKeyType ToUIReturnKeyType(this ReturnType returnType) } } } -} +} \ No newline at end of file diff --git a/src/Core/src/Platform/iOS/TextFieldExtensions.cs b/src/Core/src/Platform/iOS/TextFieldExtensions.cs index 896ea98f6498..b06285989510 100644 --- a/src/Core/src/Platform/iOS/TextFieldExtensions.cs +++ b/src/Core/src/Platform/iOS/TextFieldExtensions.cs @@ -1,5 +1,4 @@ -using Microsoft.Maui.Platform.iOS; -using UIKit; +using UIKit; namespace Microsoft.Maui { diff --git a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs index 765ed3ed4ac4..2afc3dcf52a7 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs @@ -1,7 +1,5 @@ using System.Threading.Tasks; using System.Linq; -using System.Threading.Tasks; -using Android.Graphics.Drawables; using Android.Text; using Android.Text.Method; using Android.Views.InputMethods; diff --git a/src/Core/tests/DeviceTests/Stubs/EditorStub.cs b/src/Core/tests/DeviceTests/Stubs/EditorStub.cs index f9177e48e8be..49a1b5968f03 100644 --- a/src/Core/tests/DeviceTests/Stubs/EditorStub.cs +++ b/src/Core/tests/DeviceTests/Stubs/EditorStub.cs @@ -28,10 +28,11 @@ public string Text public bool IsTextPredictionEnabled { get; set; } + public Keyboard Keyboard { get; set; } + public event EventHandler> TextChanged; void OnTextChanged(string oldValue, string newValue) => TextChanged?.Invoke(this, new StubPropertyChangedEventArgs(oldValue, newValue)); - } } \ No newline at end of file From 85d6ee1941136a0c9083ca22802738b5e4190417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Sua=CC=81rez=20Ruiz?= Date: Tue, 6 Apr 2021 10:32:55 +0200 Subject: [PATCH 26/30] Added TODO --- src/Core/src/Platform/Android/KeyboardExtensions.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Core/src/Platform/Android/KeyboardExtensions.cs b/src/Core/src/Platform/Android/KeyboardExtensions.cs index b1bda5deb64f..adbcce74e10b 100644 --- a/src/Core/src/Platform/Android/KeyboardExtensions.cs +++ b/src/Core/src/Platform/Android/KeyboardExtensions.cs @@ -42,6 +42,8 @@ public static InputTypes ToInputType(this Keyboard self) if (!capitalizedSentenceEnabled && !spellcheckEnabled && suggestionsEnabled) { // Due to the nature of android, TextFlagAutoCorrect includes Spellcheck + + // TODO: Port Logger // Log.Warning(null, "On Android, KeyboardFlags.Suggestions enables KeyboardFlags.Spellcheck as well due to a platform limitation."); result = InputTypes.ClassText | InputTypes.TextFlagAutoCorrect; } From fef77f611351b01dc3e5733c472fb312554d6943 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Tue, 6 Apr 2021 17:31:38 -0500 Subject: [PATCH 27/30] - fix compile errors --- src/Compatibility/Core/src/Android/Cells/EntryCellRenderer.cs | 1 - src/Compatibility/Core/src/Android/PopupManager.cs | 1 - src/Compatibility/Core/src/Android/Renderers/EditorRenderer.cs | 1 - src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs | 1 - .../Core/src/Android/Renderers/SearchBarRenderer.cs | 1 - .../Core/src/Android/Renderers/SearchHandlerAppearanceTracker.cs | 1 - 6 files changed, 6 deletions(-) diff --git a/src/Compatibility/Core/src/Android/Cells/EntryCellRenderer.cs b/src/Compatibility/Core/src/Android/Cells/EntryCellRenderer.cs index 36d4d5bea45a..ba2ce1fb8376 100644 --- a/src/Compatibility/Core/src/Android/Cells/EntryCellRenderer.cs +++ b/src/Compatibility/Core/src/Android/Cells/EntryCellRenderer.cs @@ -3,7 +3,6 @@ using Android.Text; using Android.Text.Method; using Android.Views; -using Microsoft.Maui.Platform.Android; namespace Microsoft.Maui.Controls.Compatibility.Platform.Android { diff --git a/src/Compatibility/Core/src/Android/PopupManager.cs b/src/Compatibility/Core/src/Android/PopupManager.cs index d3885ea07172..7798f3d275cf 100644 --- a/src/Compatibility/Core/src/Android/PopupManager.cs +++ b/src/Compatibility/Core/src/Android/PopupManager.cs @@ -7,7 +7,6 @@ using Android.Views; using Android.Widget; using Microsoft.Maui.Controls.Internals; -using Microsoft.Maui.Platform.Android; using AppCompatActivity = AndroidX.AppCompat.App.AppCompatActivity; using AppCompatAlertDialog = AndroidX.AppCompat.App.AlertDialog; diff --git a/src/Compatibility/Core/src/Android/Renderers/EditorRenderer.cs b/src/Compatibility/Core/src/Android/Renderers/EditorRenderer.cs index 890ca740b802..b056b8895c86 100644 --- a/src/Compatibility/Core/src/Android/Renderers/EditorRenderer.cs +++ b/src/Compatibility/Core/src/Android/Renderers/EditorRenderer.cs @@ -9,7 +9,6 @@ using Android.Views.InputMethods; using Android.Widget; using Java.Lang; -using Microsoft.Maui.Platform.Android; namespace Microsoft.Maui.Controls.Compatibility.Platform.Android { diff --git a/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs b/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs index 68ef89718225..9231a6fee250 100644 --- a/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs +++ b/src/Compatibility/Core/src/Android/Renderers/EntryRenderer.cs @@ -12,7 +12,6 @@ using AndroidX.Core.Content; using Java.Lang; using Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific; -using Microsoft.Maui.Platform.Android; namespace Microsoft.Maui.Controls.Compatibility.Platform.Android { diff --git a/src/Compatibility/Core/src/Android/Renderers/SearchBarRenderer.cs b/src/Compatibility/Core/src/Android/Renderers/SearchBarRenderer.cs index 6c34385e19cd..bdf2336ffe7a 100644 --- a/src/Compatibility/Core/src/Android/Renderers/SearchBarRenderer.cs +++ b/src/Compatibility/Core/src/Android/Renderers/SearchBarRenderer.cs @@ -8,7 +8,6 @@ using Android.Util; using Android.Views; using Android.Widget; -using Microsoft.Maui.Platform.Android; namespace Microsoft.Maui.Controls.Compatibility.Platform.Android { diff --git a/src/Compatibility/Core/src/Android/Renderers/SearchHandlerAppearanceTracker.cs b/src/Compatibility/Core/src/Android/Renderers/SearchHandlerAppearanceTracker.cs index adc963654211..e756ef38b30b 100644 --- a/src/Compatibility/Core/src/Android/Renderers/SearchHandlerAppearanceTracker.cs +++ b/src/Compatibility/Core/src/Android/Renderers/SearchHandlerAppearanceTracker.cs @@ -12,7 +12,6 @@ using Android.Views; using Android.Widget; using Microsoft.Maui.Controls.Internals; -using Microsoft.Maui.Platform.Android; using AImageButton = Android.Widget.ImageButton; using AView = Android.Views.View; From c3385ba6e8247ca57441987345102481c77585a7 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Wed, 7 Apr 2021 19:09:48 +0200 Subject: [PATCH 28/30] Fix tests for Android --- .../DeviceTests/Data/KeyboardClassData.cs | 126 ------------------ .../Handlers/Entry/EntryHandlerTests.cs | 88 ++++++++++-- .../Handlers/Entry/EntryHandlerTests.iOS.cs | 3 +- 3 files changed, 76 insertions(+), 141 deletions(-) delete mode 100644 src/Core/tests/DeviceTests/Data/KeyboardClassData.cs diff --git a/src/Core/tests/DeviceTests/Data/KeyboardClassData.cs b/src/Core/tests/DeviceTests/Data/KeyboardClassData.cs deleted file mode 100644 index 756b35a5a199..000000000000 --- a/src/Core/tests/DeviceTests/Data/KeyboardClassData.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Text; - -namespace Microsoft.Maui.Core.DeviceTests.Data -{ - public class NumericKeyboardClassData : IEnumerable - { - public IEnumerator GetEnumerator() - { - yield return new object[] { Keyboard.Numeric, true }; - yield return new object[] { Keyboard.Default, false }; - yield return new object[] { Keyboard.Chat, false }; - yield return new object[] { Keyboard.Email, false }; - yield return new object[] { Keyboard.Plain, false }; - yield return new object[] { Keyboard.Telephone, false }; - yield return new object[] { Keyboard.Text, false }; - yield return new object[] { Keyboard.Url, false }; - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } - - public class ChatKeyboardClassData : IEnumerable - { - public IEnumerator GetEnumerator() - { - yield return new object[] { Keyboard.Chat, true }; - yield return new object[] { Keyboard.Default, false }; - yield return new object[] { Keyboard.Numeric, false }; - yield return new object[] { Keyboard.Email, false }; - yield return new object[] { Keyboard.Plain, false }; - yield return new object[] { Keyboard.Telephone, false }; - yield return new object[] { Keyboard.Text, false }; - yield return new object[] { Keyboard.Url, false }; - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } - - public class EmailKeyboardClassData : IEnumerable - { - public IEnumerator GetEnumerator() - { - yield return new object[] { Keyboard.Email, true }; - yield return new object[] { Keyboard.Default, false }; - yield return new object[] { Keyboard.Numeric, false }; - yield return new object[] { Keyboard.Chat, false }; - yield return new object[] { Keyboard.Plain, false }; - yield return new object[] { Keyboard.Telephone, false }; - yield return new object[] { Keyboard.Text, false }; - yield return new object[] { Keyboard.Url, false }; - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } - - public class PlainKeyboardClassData : IEnumerable - { - public IEnumerator GetEnumerator() - { - yield return new object[] { Keyboard.Plain, true }; - yield return new object[] { Keyboard.Default, false }; - yield return new object[] { Keyboard.Numeric, false }; - yield return new object[] { Keyboard.Chat, false }; - yield return new object[] { Keyboard.Email, false }; - yield return new object[] { Keyboard.Telephone, false }; - yield return new object[] { Keyboard.Text, false }; - yield return new object[] { Keyboard.Url, false }; - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } - - public class TelephoneKeyboardClassData : IEnumerable - { - public IEnumerator GetEnumerator() - { - yield return new object[] { Keyboard.Telephone, true }; - yield return new object[] { Keyboard.Default, false }; - yield return new object[] { Keyboard.Numeric, false }; - yield return new object[] { Keyboard.Chat, false }; - yield return new object[] { Keyboard.Email, false }; - yield return new object[] { Keyboard.Plain, false }; - yield return new object[] { Keyboard.Text, false }; - yield return new object[] { Keyboard.Url, false }; - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } - - public class TextKeyboardClassData : IEnumerable - { - public IEnumerator GetEnumerator() - { - yield return new object[] { Keyboard.Text, true }; - yield return new object[] { Keyboard.Default, false }; - yield return new object[] { Keyboard.Numeric, false }; - yield return new object[] { Keyboard.Chat, false }; - yield return new object[] { Keyboard.Email, false }; - yield return new object[] { Keyboard.Plain, false }; - yield return new object[] { Keyboard.Telephone, false }; - yield return new object[] { Keyboard.Url, false }; - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } - - public class UrlKeyboardClassData : IEnumerable - { - public IEnumerator GetEnumerator() - { - yield return new object[] { Keyboard.Url, true }; - yield return new object[] { Keyboard.Default, false }; - yield return new object[] { Keyboard.Numeric, false }; - yield return new object[] { Keyboard.Chat, false }; - yield return new object[] { Keyboard.Email, false }; - yield return new object[] { Keyboard.Plain, false }; - yield return new object[] { Keyboard.Telephone, false }; - yield return new object[] { Keyboard.Text, false }; - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } -} diff --git a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs index 32625c4f9966..9df65607cba5 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs @@ -1,9 +1,7 @@ using System.Threading.Tasks; using Microsoft.Maui.DeviceTests.Stubs; using Microsoft.Maui.Handlers; -using Microsoft.Maui.Core.DeviceTests.Data; using Xunit; -using Xunit.Extensions; namespace Microsoft.Maui.DeviceTests { @@ -255,54 +253,116 @@ public async Task TextChangedEventsFireCorrectly(string initialText, string newT } [Theory(DisplayName = "Validates Numeric Keyboard")] - [ClassData(typeof(NumericKeyboardClassData))] - public async Task ValidateNumericKeyboard(Keyboard keyboard, bool expected) + [InlineData(nameof(Keyboard.Chat), false)] + [InlineData(nameof(Keyboard.Default), false)] + [InlineData(nameof(Keyboard.Email), false)] + [InlineData(nameof(Keyboard.Numeric), true)] + [InlineData(nameof(Keyboard.Plain), false)] + [InlineData(nameof(Keyboard.Telephone), false)] + [InlineData(nameof(Keyboard.Text), false)] + [InlineData(nameof(Keyboard.Url), false)] + public async Task ValidateNumericKeyboard(string keyboardName, bool expected) { + var keyboard = (Keyboard)typeof(Keyboard).GetProperty(keyboardName).GetValue(null); + var entryStub = new EntryStub() { Keyboard = keyboard }; await ValidatePropertyInitValue(entryStub, () => expected, GetNativeIsNumericKeyboard, expected); } [Theory(DisplayName = "Validates Email Keyboard")] - [ClassData(typeof(EmailKeyboardClassData))] - public async Task ValidateEmailKeyboard(Keyboard keyboard, bool expected) + [InlineData(nameof(Keyboard.Chat), false)] + [InlineData(nameof(Keyboard.Default), false)] + [InlineData(nameof(Keyboard.Email), true)] + [InlineData(nameof(Keyboard.Numeric), false)] + [InlineData(nameof(Keyboard.Plain), false)] + [InlineData(nameof(Keyboard.Telephone), false)] + [InlineData(nameof(Keyboard.Text), false)] + [InlineData(nameof(Keyboard.Url), false)] + public async Task ValidateEmailKeyboard(string keyboardName, bool expected) { + var keyboard = (Keyboard)typeof(Keyboard).GetProperty(keyboardName).GetValue(null); + var entryStub = new EntryStub() { Keyboard = keyboard }; await ValidatePropertyInitValue(entryStub, () => expected, GetNativeIsEmailKeyboard, expected); } [Theory(DisplayName = "Validates Telephone Keyboard")] - [ClassData(typeof(TelephoneKeyboardClassData))] - public async Task ValidateTelephoneKeyboard(Keyboard keyboard, bool expected) + [InlineData(nameof(Keyboard.Chat), false)] + [InlineData(nameof(Keyboard.Default), false)] + [InlineData(nameof(Keyboard.Email), false)] + [InlineData(nameof(Keyboard.Numeric), false)] + [InlineData(nameof(Keyboard.Plain), false)] + [InlineData(nameof(Keyboard.Telephone), true)] + [InlineData(nameof(Keyboard.Text), false)] + [InlineData(nameof(Keyboard.Url), false)] + public async Task ValidateTelephoneKeyboard(string keyboardName, bool expected) { + var keyboard = (Keyboard)typeof(Keyboard).GetProperty(keyboardName).GetValue(null); + var entryStub = new EntryStub() { Keyboard = keyboard }; await ValidatePropertyInitValue(entryStub, () => expected, GetNativeIsTelephoneKeyboard, expected); } [Theory(DisplayName = "Validates Url Keyboard")] - [ClassData(typeof(UrlKeyboardClassData))] - public async Task ValidateUrlKeyboard(Keyboard keyboard, bool expected) + [InlineData(nameof(Keyboard.Chat), false)] + [InlineData(nameof(Keyboard.Default), false)] + [InlineData(nameof(Keyboard.Email), false)] + [InlineData(nameof(Keyboard.Numeric), false)] + [InlineData(nameof(Keyboard.Plain), false)] + [InlineData(nameof(Keyboard.Telephone), false)] + [InlineData(nameof(Keyboard.Text), false)] + [InlineData(nameof(Keyboard.Url), true)] + public async Task ValidateUrlKeyboard(string keyboardName, bool expected) { + var keyboard = (Keyboard)typeof(Keyboard).GetProperty(keyboardName).GetValue(null); + var entryStub = new EntryStub() { Keyboard = keyboard }; await ValidatePropertyInitValue(entryStub, () => expected, GetNativeIsUrlKeyboard, expected); } [Theory(DisplayName = "Validates Text Keyboard")] - [ClassData(typeof(TextKeyboardClassData))] - public async Task ValidateTextKeyboard(Keyboard keyboard, bool expected) +#if __ANDROID__ + [InlineData(nameof(Keyboard.Chat), true)] +#else + [InlineData(nameof(Keyboard.Chat), false)] +#endif + [InlineData(nameof(Keyboard.Default), false)] + [InlineData(nameof(Keyboard.Email), false)] + [InlineData(nameof(Keyboard.Numeric), false)] + [InlineData(nameof(Keyboard.Plain), false)] + [InlineData(nameof(Keyboard.Telephone), false)] + [InlineData(nameof(Keyboard.Text), true)] + [InlineData(nameof(Keyboard.Url), false)] + public async Task ValidateTextKeyboard(string keyboardName, bool expected) { + var keyboard = (Keyboard)typeof(Keyboard).GetProperty(keyboardName).GetValue(null); + var entryStub = new EntryStub() { Keyboard = keyboard }; await ValidatePropertyInitValue(entryStub, () => expected, GetNativeIsTextKeyboard, expected); } [Theory(DisplayName = "Validates Chat Keyboard")] - [ClassData(typeof(ChatKeyboardClassData))] - public async Task ValidateChatKeyboard(Keyboard keyboard, bool expected) + [InlineData(nameof(Keyboard.Chat), true)] + [InlineData(nameof(Keyboard.Default), false)] + [InlineData(nameof(Keyboard.Email), false)] + [InlineData(nameof(Keyboard.Numeric), false)] + [InlineData(nameof(Keyboard.Plain), false)] + [InlineData(nameof(Keyboard.Telephone), false)] +#if __ANDROID__ + [InlineData(nameof(Keyboard.Text), true)] +#else + [InlineData(nameof(Keyboard.Text), false)] +#endif + [InlineData(nameof(Keyboard.Url), false)] + public async Task ValidateChatKeyboard(string keyboardName, bool expected) { + var keyboard = (Keyboard)typeof(Keyboard).GetProperty(keyboardName).GetValue(null); + var entryStub = new EntryStub() { Keyboard = keyboard }; await ValidatePropertyInitValue(entryStub, () => expected, GetNativeIsChatKeyboard, expected); diff --git a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs index 2324ae2739e3..20948f75b774 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.iOS.cs @@ -166,7 +166,8 @@ bool GetNativeIsChatKeyboard(EntryHandler entryHandler) var nativeEntry = GetNativeEntry(entryHandler); return nativeEntry.AutocapitalizationType == UITextAutocapitalizationType.Sentences && - nativeEntry.AutocorrectionType == UITextAutocorrectionType.Yes; + nativeEntry.AutocorrectionType == UITextAutocorrectionType.Yes && + nativeEntry.SpellCheckingType == UITextSpellCheckingType.No; } double GetNativeUnscaledFontSize(EntryHandler entryHandler) => From 4232070a4233209b65cd8c35858826816c29a41c Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Wed, 7 Apr 2021 19:33:40 +0200 Subject: [PATCH 29/30] Fix iOS tests --- src/Core/src/Platform/iOS/TextFieldExtensions.cs | 4 ++++ src/Core/tests/DeviceTests/Stubs/EditorStub.cs | 2 +- src/Core/tests/DeviceTests/Stubs/EntryStub.cs | 2 +- src/Core/tests/DeviceTests/Stubs/SearchBarStub.cs | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Core/src/Platform/iOS/TextFieldExtensions.cs b/src/Core/src/Platform/iOS/TextFieldExtensions.cs index 52299a69c5e9..ee936d3d9ba5 100644 --- a/src/Core/src/Platform/iOS/TextFieldExtensions.cs +++ b/src/Core/src/Platform/iOS/TextFieldExtensions.cs @@ -86,6 +86,10 @@ public static void UpdateKeyboard(this UITextField textField, IEntry entry) var keyboard = entry.Keyboard; textField.ApplyKeyboard(keyboard); + + if (keyboard is not CustomKeyboard) + textField.UpdateIsTextPredictionEnabled(entry); + textField.ReloadInputViews(); } diff --git a/src/Core/tests/DeviceTests/Stubs/EditorStub.cs b/src/Core/tests/DeviceTests/Stubs/EditorStub.cs index 49a1b5968f03..5ee389f1271b 100644 --- a/src/Core/tests/DeviceTests/Stubs/EditorStub.cs +++ b/src/Core/tests/DeviceTests/Stubs/EditorStub.cs @@ -26,7 +26,7 @@ public string Text public int MaxLength { get; set; } = int.MaxValue; - public bool IsTextPredictionEnabled { get; set; } + public bool IsTextPredictionEnabled { get; set; } = true; public Keyboard Keyboard { get; set; } diff --git a/src/Core/tests/DeviceTests/Stubs/EntryStub.cs b/src/Core/tests/DeviceTests/Stubs/EntryStub.cs index baf4e08c632c..79d8c42b8291 100644 --- a/src/Core/tests/DeviceTests/Stubs/EntryStub.cs +++ b/src/Core/tests/DeviceTests/Stubs/EntryStub.cs @@ -18,7 +18,7 @@ public string Text public bool IsPassword { get; set; } - public bool IsTextPredictionEnabled { get; set; } + public bool IsTextPredictionEnabled { get; set; } = true; public string Placeholder { get; set; } diff --git a/src/Core/tests/DeviceTests/Stubs/SearchBarStub.cs b/src/Core/tests/DeviceTests/Stubs/SearchBarStub.cs index 29a4e6ebdcad..bc96b252aa9f 100644 --- a/src/Core/tests/DeviceTests/Stubs/SearchBarStub.cs +++ b/src/Core/tests/DeviceTests/Stubs/SearchBarStub.cs @@ -16,7 +16,7 @@ public partial class SearchBarStub : StubBase, ISearchBar public TextAlignment HorizontalTextAlignment { get; set; } - public bool IsTextPredictionEnabled { get; set; } + public bool IsTextPredictionEnabled { get; set; } = true; public bool IsReadOnly { get; set; } From e46da290afb48a2449b7baad9be2c70ef324ca96 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Wed, 7 Apr 2021 19:58:24 +0200 Subject: [PATCH 30/30] Fix tests properly --- src/Core/src/Platform/Android/EditTextExtensions.cs | 2 +- .../Handlers/Entry/EntryHandlerTests.Android.cs | 5 ++--- .../tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs | 8 -------- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/src/Core/src/Platform/Android/EditTextExtensions.cs b/src/Core/src/Platform/Android/EditTextExtensions.cs index 473fad13be6e..f07827611884 100644 --- a/src/Core/src/Platform/Android/EditTextExtensions.cs +++ b/src/Core/src/Platform/Android/EditTextExtensions.cs @@ -210,7 +210,7 @@ internal static void SetInputType(this AppCompatEditText editText, IEntry entry) var keyboard = entry.Keyboard; var nativeInputTypeToUpdate = keyboard.ToInputType(); - if (!(keyboard is CustomKeyboard)) + if (keyboard is not CustomKeyboard) { // TODO: IsSpellCheckEnabled handling must be here. diff --git a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs index cb3f327a15ac..46940c3c1f41 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Android.cs @@ -141,10 +141,9 @@ bool GetNativeIsChatKeyboard(EntryHandler entryHandler) var editText = GetNativeEntry(entryHandler); var inputTypes = editText.InputType; - return (inputTypes.HasFlag(InputTypes.ClassText) && inputTypes.HasFlag(InputTypes.TextFlagCapSentences) && inputTypes.HasFlag(InputTypes.TextFlagNoSuggestions)); + return inputTypes.HasFlag(InputTypes.ClassText) && inputTypes.HasFlag(InputTypes.TextFlagCapSentences) && inputTypes.HasFlag(InputTypes.TextFlagNoSuggestions); } - bool GetNativeIsEmailKeyboard(EntryHandler entryHandler) { var editText = GetNativeEntry(entryHandler); @@ -174,7 +173,7 @@ bool GetNativeIsTextKeyboard(EntryHandler entryHandler) var editText = GetNativeEntry(entryHandler); var inputTypes = editText.InputType; - return inputTypes.HasFlag(InputTypes.ClassText) && inputTypes.HasFlag(InputTypes.TextFlagCapSentences); + return inputTypes.HasFlag(InputTypes.ClassText) && inputTypes.HasFlag(InputTypes.TextFlagCapSentences) && !inputTypes.HasFlag(InputTypes.TextFlagNoSuggestions); } double GetNativeUnscaledFontSize(EntryHandler entryHandler) diff --git a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs index 9df65607cba5..88bc56bf6b79 100644 --- a/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs +++ b/src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.cs @@ -325,11 +325,7 @@ public async Task ValidateUrlKeyboard(string keyboardName, bool expected) } [Theory(DisplayName = "Validates Text Keyboard")] -#if __ANDROID__ - [InlineData(nameof(Keyboard.Chat), true)] -#else [InlineData(nameof(Keyboard.Chat), false)] -#endif [InlineData(nameof(Keyboard.Default), false)] [InlineData(nameof(Keyboard.Email), false)] [InlineData(nameof(Keyboard.Numeric), false)] @@ -353,11 +349,7 @@ public async Task ValidateTextKeyboard(string keyboardName, bool expected) [InlineData(nameof(Keyboard.Numeric), false)] [InlineData(nameof(Keyboard.Plain), false)] [InlineData(nameof(Keyboard.Telephone), false)] -#if __ANDROID__ - [InlineData(nameof(Keyboard.Text), true)] -#else [InlineData(nameof(Keyboard.Text), false)] -#endif [InlineData(nameof(Keyboard.Url), false)] public async Task ValidateChatKeyboard(string keyboardName, bool expected) {