Merge "Use text input for various internationalized listeners" into oc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
94b6b532c0
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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();
|
||||
@@ -5567,26 +5603,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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user