summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Roozbeh Pournader <roozbeh@google.com> 2017-02-15 18:19:09 -0800
committer Roozbeh Pournader <roozbeh@google.com> 2017-04-05 14:07:22 -0700
commit889c6503a1764d6dbc1f7f4e23e3b392daab330b (patch)
treeb040d3635aa0a899a7e665e1d6c23b9d0f875a18
parenteabb5621dd67a159500c7a35eaa2f7999004c004 (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.java17
-rw-r--r--core/java/android/text/method/DateTimeKeyListener.java19
-rw-r--r--core/java/android/text/method/DigitsKeyListener.java68
-rw-r--r--core/java/android/text/method/TimeKeyListener.java17
-rw-r--r--core/java/android/widget/TextView.java87
-rw-r--r--core/java/com/android/internal/util/ArrayUtils.java23
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) {