diff options
| author | 2017-02-15 18:19:09 -0800 | |
|---|---|---|
| committer | 2017-04-05 14:07:22 -0700 | |
| commit | 889c6503a1764d6dbc1f7f4e23e3b392daab330b (patch) | |
| tree | b040d3635aa0a899a7e665e1d6c23b9d0f875a18 | |
| parent | eabb5621dd67a159500c7a35eaa2f7999004c004 (diff) | |
Use text input for various internationalized listeners
Since the characters that need to be entered for an internationalized
input type may not be available on the old non-internationalized
layouts, switch to the full text layout if there are any such
characters.
TextView is also modified to call the locale-aware listeners if
the target SDK is set to O or later.
Test: Manual
Bug: https://code.google.com/p/android/issues/detail?id=2626
Bug: https://code.google.com/p/android/issues/detail?id=82993
Bug: 8319249
Bug: 33276673
Bug: 34394455
Change-Id: I544bf0cc893a475ab5bf88cbad01cb981c6fef91
| -rw-r--r-- | core/java/android/text/method/DateKeyListener.java | 17 | ||||
| -rw-r--r-- | core/java/android/text/method/DateTimeKeyListener.java | 19 | ||||
| -rw-r--r-- | core/java/android/text/method/DigitsKeyListener.java | 68 | ||||
| -rw-r--r-- | core/java/android/text/method/TimeKeyListener.java | 17 | ||||
| -rw-r--r-- | core/java/android/widget/TextView.java | 87 | ||||
| -rw-r--r-- | core/java/com/android/internal/util/ArrayUtils.java | 23 |
6 files changed, 188 insertions, 43 deletions
diff --git a/core/java/android/text/method/DateKeyListener.java b/core/java/android/text/method/DateKeyListener.java index e14cd2cd725d..0accbf6c74e5 100644 --- a/core/java/android/text/method/DateKeyListener.java +++ b/core/java/android/text/method/DateKeyListener.java @@ -22,6 +22,7 @@ import android.text.InputType; import android.view.KeyEvent; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.ArrayUtils; import java.util.HashMap; import java.util.LinkedHashSet; @@ -37,8 +38,11 @@ import java.util.Locale; public class DateKeyListener extends NumberKeyListener { public int getInputType() { - return InputType.TYPE_CLASS_DATETIME - | InputType.TYPE_DATETIME_VARIATION_DATE; + if (mNeedsAdvancedInput) { + return InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL; + } else { + return InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_DATE; + } } @Override @@ -65,7 +69,13 @@ public class DateKeyListener extends NumberKeyListener final boolean success = NumberKeyListener.addDigits(chars, locale) && NumberKeyListener.addFormatCharsFromSkeletons( chars, locale, SKELETONS, SYMBOLS_TO_IGNORE); - mCharacters = success ? NumberKeyListener.collectionToArray(chars) : CHARACTERS; + if (success) { + mCharacters = NumberKeyListener.collectionToArray(chars); + mNeedsAdvancedInput = !ArrayUtils.containsAll(CHARACTERS, mCharacters); + } else { + mCharacters = CHARACTERS; + mNeedsAdvancedInput = false; + } } /** @@ -110,6 +120,7 @@ public class DateKeyListener extends NumberKeyListener }; private final char[] mCharacters; + private final boolean mNeedsAdvancedInput; private static final Object sLock = new Object(); @GuardedBy("sLock") diff --git a/core/java/android/text/method/DateTimeKeyListener.java b/core/java/android/text/method/DateTimeKeyListener.java index 62e3adea9b7a..551db5560128 100644 --- a/core/java/android/text/method/DateTimeKeyListener.java +++ b/core/java/android/text/method/DateTimeKeyListener.java @@ -22,6 +22,7 @@ import android.text.InputType; import android.view.KeyEvent; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.ArrayUtils; import java.util.HashMap; import java.util.LinkedHashSet; @@ -37,10 +38,13 @@ import java.util.Locale; public class DateTimeKeyListener extends NumberKeyListener { public int getInputType() { - return InputType.TYPE_CLASS_DATETIME - | InputType.TYPE_DATETIME_VARIATION_NORMAL; + if (mNeedsAdvancedInput) { + return InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL; + } else { + return InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_NORMAL; + } } - + @Override @NonNull protected char[] getAcceptedChars() @@ -70,7 +74,13 @@ public class DateTimeKeyListener extends NumberKeyListener chars, locale, SKELETON_12HOUR, SYMBOLS_TO_IGNORE) && NumberKeyListener.addFormatCharsFromSkeleton( chars, locale, SKELETON_24HOUR, SYMBOLS_TO_IGNORE); - mCharacters = success ? NumberKeyListener.collectionToArray(chars) : CHARACTERS; + if (success) { + mCharacters = NumberKeyListener.collectionToArray(chars); + mNeedsAdvancedInput = !ArrayUtils.containsAll(CHARACTERS, mCharacters); + } else { + mCharacters = CHARACTERS; + mNeedsAdvancedInput = false; + } } /** @@ -114,6 +124,7 @@ public class DateTimeKeyListener extends NumberKeyListener }; private final char[] mCharacters; + private final boolean mNeedsAdvancedInput; private static final Object sLock = new Object(); @GuardedBy("sLock") diff --git a/core/java/android/text/method/DigitsKeyListener.java b/core/java/android/text/method/DigitsKeyListener.java index 26c69ab01da0..d9f2dcf2e896 100644 --- a/core/java/android/text/method/DigitsKeyListener.java +++ b/core/java/android/text/method/DigitsKeyListener.java @@ -27,6 +27,7 @@ import android.text.Spanned; import android.view.KeyEvent; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.ArrayUtils; import java.util.HashMap; import java.util.LinkedHashSet; @@ -42,8 +43,12 @@ import java.util.Locale; public class DigitsKeyListener extends NumberKeyListener { private char[] mAccepted; + private boolean mNeedsAdvancedInput; private final boolean mSign; private final boolean mDecimal; + private final boolean mStringMode; + @Nullable + private final Locale mLocale; private static final String DEFAULT_DECIMAL_POINT_CHARS = "."; private static final String DEFAULT_SIGN_CHARS = "-+"; @@ -112,11 +117,17 @@ public class DigitsKeyListener extends NumberKeyListener this(locale, false, false); } - private void setToCompat(boolean sign, boolean decimal) { + private void setToCompat() { mDecimalPointChars = DEFAULT_DECIMAL_POINT_CHARS; mSignChars = DEFAULT_SIGN_CHARS; - final int kind = (sign ? SIGN : 0) | (decimal ? DECIMAL : 0); + final int kind = (mSign ? SIGN : 0) | (mDecimal ? DECIMAL : 0); mAccepted = COMPATIBILITY_CHARACTERS[kind]; + mNeedsAdvancedInput = false; + } + + private void calculateNeedForAdvancedInput() { + final int kind = (mSign ? SIGN : 0) | (mDecimal ? DECIMAL : 0); + mNeedsAdvancedInput = !ArrayUtils.containsAll(COMPATIBILITY_CHARACTERS[kind], mAccepted); } // Takes a sign string and strips off its bidi controls, if any. @@ -144,14 +155,16 @@ public class DigitsKeyListener extends NumberKeyListener public DigitsKeyListener(@Nullable Locale locale, boolean sign, boolean decimal) { mSign = sign; mDecimal = decimal; + mStringMode = false; + mLocale = locale; if (locale == null) { - setToCompat(sign, decimal); + setToCompat(); return; } LinkedHashSet<Character> chars = new LinkedHashSet<>(); final boolean success = NumberKeyListener.addDigits(chars, locale); if (!success) { - setToCompat(sign, decimal); + setToCompat(); return; } if (sign || decimal) { @@ -161,7 +174,7 @@ public class DigitsKeyListener extends NumberKeyListener final String plusString = stripBidiControls(symbols.getPlusSignString()); if (minusString.length() > 1 || plusString.length() > 1) { // non-BMP and multi-character signs are not supported. - setToCompat(sign, decimal); + setToCompat(); return; } final char minus = minusString.charAt(0); @@ -181,7 +194,7 @@ public class DigitsKeyListener extends NumberKeyListener final String separatorString = symbols.getDecimalSeparatorString(); if (separatorString.length() > 1) { // non-BMP and multi-character decimal separators are not supported. - setToCompat(sign, decimal); + setToCompat(); return; } final Character separatorChar = Character.valueOf(separatorString.charAt(0)); @@ -190,13 +203,19 @@ public class DigitsKeyListener extends NumberKeyListener } } mAccepted = NumberKeyListener.collectionToArray(chars); + calculateNeedForAdvancedInput(); } private DigitsKeyListener(@NonNull final String accepted) { mSign = false; mDecimal = false; + mStringMode = true; + mLocale = null; mAccepted = new char[accepted.length()]; accepted.getChars(0, accepted.length(), mAccepted, 0); + // Theoretically we may need advanced input, but for backward compatibility, we don't change + // the input type. + mNeedsAdvancedInput = false; } /** @@ -280,13 +299,38 @@ public class DigitsKeyListener extends NumberKeyListener return result; } - public int getInputType() { - int contentType = InputType.TYPE_CLASS_NUMBER; - if (mSign) { - contentType |= InputType.TYPE_NUMBER_FLAG_SIGNED; + /** + * Returns a DigitsKeyListener based on an the settings of a existing DigitsKeyListener, with + * the locale modified. + * + * @hide + */ + @NonNull + public static DigitsKeyListener getInstance( + @Nullable Locale locale, + @NonNull DigitsKeyListener listener) { + if (listener.mStringMode) { + return listener; // string-mode DigitsKeyListeners have no locale. + } else { + return getInstance(locale, listener.mSign, listener.mDecimal); } - if (mDecimal) { - contentType |= InputType.TYPE_NUMBER_FLAG_DECIMAL; + } + + /** + * Returns the input type for the listener. + */ + public int getInputType() { + int contentType; + if (mNeedsAdvancedInput) { + contentType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL; + } else { + contentType = InputType.TYPE_CLASS_NUMBER; + if (mSign) { + contentType |= InputType.TYPE_NUMBER_FLAG_SIGNED; + } + if (mDecimal) { + contentType |= InputType.TYPE_NUMBER_FLAG_DECIMAL; + } } return contentType; } diff --git a/core/java/android/text/method/TimeKeyListener.java b/core/java/android/text/method/TimeKeyListener.java index c9f9f9fc5dde..5b1db11777dd 100644 --- a/core/java/android/text/method/TimeKeyListener.java +++ b/core/java/android/text/method/TimeKeyListener.java @@ -22,6 +22,7 @@ import android.text.InputType; import android.view.KeyEvent; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.ArrayUtils; import java.util.HashMap; import java.util.LinkedHashSet; @@ -37,8 +38,11 @@ import java.util.Locale; public class TimeKeyListener extends NumberKeyListener { public int getInputType() { - return InputType.TYPE_CLASS_DATETIME - | InputType.TYPE_DATETIME_VARIATION_TIME; + if (mNeedsAdvancedInput) { + return InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL; + } else { + return InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_TIME; + } } @Override @@ -70,7 +74,13 @@ public class TimeKeyListener extends NumberKeyListener chars, locale, SKELETON_12HOUR, SYMBOLS_TO_IGNORE) && NumberKeyListener.addFormatCharsFromSkeleton( chars, locale, SKELETON_24HOUR, SYMBOLS_TO_IGNORE); - mCharacters = success ? NumberKeyListener.collectionToArray(chars) : CHARACTERS; + if (success) { + mCharacters = NumberKeyListener.collectionToArray(chars); + mNeedsAdvancedInput = !ArrayUtils.containsAll(CHARACTERS, mCharacters); + } else { + mCharacters = CHARACTERS; + mNeedsAdvancedInput = false; + } } /** @@ -114,6 +124,7 @@ public class TimeKeyListener extends NumberKeyListener }; private final char[] mCharacters; + private final boolean mNeedsAdvancedInput; private static final Object sLock = new Object(); @GuardedBy("sLock") diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index b38366393a4d..2721afdc171a 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -598,6 +598,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private Layout mLayout; private boolean mLocalesChanged = false; + // True if setKeyListener() has been explicitly called + private boolean mListenerChanged = false; + // True if internationalized input should be used for numbers and date and time. + private final boolean mUseInternationalizedInput; + @ViewDebug.ExportedProperty(category = "text") private int mGravity = Gravity.TOP | Gravity.START; private boolean mHorizontallyScrolling; @@ -1356,6 +1361,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final boolean numberPasswordInputType = variation == (EditorInfo.TYPE_CLASS_NUMBER | EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD); + mUseInternationalizedInput = + context.getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.O; + if (inputMethod != null) { Class<?> c; @@ -1398,15 +1406,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mEditor.mInputType = inputType = EditorInfo.TYPE_CLASS_PHONE; } else if (numeric != 0) { createEditorIfNeeded(); - mEditor.mKeyListener = DigitsKeyListener.getInstance((numeric & SIGNED) != 0, - (numeric & DECIMAL) != 0); - inputType = EditorInfo.TYPE_CLASS_NUMBER; - if ((numeric & SIGNED) != 0) { - inputType |= EditorInfo.TYPE_NUMBER_FLAG_SIGNED; - } - if ((numeric & DECIMAL) != 0) { - inputType |= EditorInfo.TYPE_NUMBER_FLAG_DECIMAL; - } + mEditor.mKeyListener = DigitsKeyListener.getInstance( + mUseInternationalizedInput ? getTextLocale() : null, + (numeric & SIGNED) != 0, + (numeric & DECIMAL) != 0); + inputType = mEditor.mKeyListener.getInputType(); mEditor.mInputType = inputType; } else if (autotext || autocap != -1) { TextKeyListener.Capitalize cap; @@ -2308,19 +2312,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_autoText */ public void setKeyListener(KeyListener input) { + mListenerChanged = true; setKeyListenerOnly(input); fixFocusableAndClickableSettings(); if (input != null) { createEditorIfNeeded(); - try { - mEditor.mInputType = mEditor.mKeyListener.getInputType(); - } catch (IncompatibleClassChangeError e) { - mEditor.mInputType = EditorInfo.TYPE_CLASS_TEXT; - } - // Change inputType, without affecting transformation. - // No need to applySingleLine since mSingleLine is unchanged. - setInputTypeSingleLine(mSingleLine); + setInputTypeFromEditor(); } else { if (mEditor != null) mEditor.mInputType = EditorInfo.TYPE_NULL; } @@ -2329,6 +2327,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (imm != null) imm.restartInput(this); } + private void setInputTypeFromEditor() { + try { + mEditor.mInputType = mEditor.mKeyListener.getInputType(); + } catch (IncompatibleClassChangeError e) { + mEditor.mInputType = EditorInfo.TYPE_CLASS_TEXT; + } + // Change inputType, without affecting transformation. + // No need to applySingleLine since mSingleLine is unchanged. + setInputTypeSingleLine(mSingleLine); + } + private void setKeyListenerOnly(KeyListener input) { if (mEditor == null && input == null) return; // null is the default value @@ -3390,6 +3399,29 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return mTextPaint.getTextLocales(); } + private void changeListenerLocaleTo(@NonNull Locale locale) { + if (mListenerChanged) { + // If a listener has been explicitly set, don't change it. We may break something. + return; + } + if (mEditor != null) { + KeyListener listener = mEditor.mKeyListener; + if (listener instanceof DigitsKeyListener) { + listener = DigitsKeyListener.getInstance(locale, (DigitsKeyListener) listener); + } else if (listener instanceof DateKeyListener) { + listener = DateKeyListener.getInstance(locale); + } else if (listener instanceof TimeKeyListener) { + listener = TimeKeyListener.getInstance(locale); + } else if (listener instanceof DateTimeKeyListener) { + listener = DateTimeKeyListener.getInstance(locale); + } else { + return; + } + setKeyListenerOnly(listener); + setInputTypeFromEditor(); + } + } + /** * Set the default {@link LocaleList} of the text in this TextView to a one-member list * containing just the given value. @@ -3401,6 +3433,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public void setTextLocale(@NonNull Locale locale) { mLocalesChanged = true; mTextPaint.setTextLocale(locale); + changeListenerLocaleTo(locale); if (mLayout != null) { nullLayouts(); requestLayout(); @@ -3422,6 +3455,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public void setTextLocales(@NonNull @Size(min = 1) LocaleList locales) { mLocalesChanged = true; mTextPaint.setTextLocales(locales); + changeListenerLocaleTo(locales.get(0)); if (mLayout != null) { nullLayouts(); requestLayout(); @@ -3433,7 +3467,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (!mLocalesChanged) { - mTextPaint.setTextLocales(LocaleList.getDefault()); + final LocaleList locales = LocaleList.getDefault(); + mTextPaint.setTextLocales(locales); + changeListenerLocaleTo(locales.get(0)); if (mLayout != null) { nullLayouts(); requestLayout(); @@ -5566,26 +5602,35 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener input = TextKeyListener.getInstance(autotext, cap); } else if (cls == EditorInfo.TYPE_CLASS_NUMBER) { input = DigitsKeyListener.getInstance( + mUseInternationalizedInput ? getTextLocale() : null, (type & EditorInfo.TYPE_NUMBER_FLAG_SIGNED) != 0, (type & EditorInfo.TYPE_NUMBER_FLAG_DECIMAL) != 0); + if (mUseInternationalizedInput) { + type = input.getInputType(); // Override type, if necessary for i18n. + } } else if (cls == EditorInfo.TYPE_CLASS_DATETIME) { + final Locale locale = mUseInternationalizedInput ? getTextLocale() : null; switch (type & EditorInfo.TYPE_MASK_VARIATION) { case EditorInfo.TYPE_DATETIME_VARIATION_DATE: - input = DateKeyListener.getInstance(); + input = DateKeyListener.getInstance(locale); break; case EditorInfo.TYPE_DATETIME_VARIATION_TIME: - input = TimeKeyListener.getInstance(); + input = TimeKeyListener.getInstance(locale); break; default: - input = DateTimeKeyListener.getInstance(); + input = DateTimeKeyListener.getInstance(locale); break; } + if (mUseInternationalizedInput) { + type = input.getInputType(); // Override type, if necessary for i18n. + } } else if (cls == EditorInfo.TYPE_CLASS_PHONE) { input = DialerKeyListener.getInstance(); } else { input = TextKeyListener.getInstance(); } setRawInputType(type); + mListenerChanged = false; if (direct) { createEditorIfNeeded(); mEditor.mKeyListener = input; diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java index f4dd5a62112c..2c8e4e0414b6 100644 --- a/core/java/com/android/internal/util/ArrayUtils.java +++ b/core/java/com/android/internal/util/ArrayUtils.java @@ -236,6 +236,29 @@ public class ArrayUtils { return false; } + public static boolean contains(@Nullable char[] array, char value) { + if (array == null) return false; + for (char element : array) { + if (element == value) { + return true; + } + } + return false; + } + + /** + * Test if all {@code check} items are contained in {@code array}. + */ + public static <T> boolean containsAll(@Nullable char[] array, char[] check) { + if (check == null) return true; + for (char checkItem : check) { + if (!contains(array, checkItem)) { + return false; + } + } + return true; + } + public static long total(@Nullable long[] array) { long total = 0; if (array != null) { |