Merge "Use text input for various internationalized listeners" into oc-dev

This commit is contained in:
TreeHugger Robot
2017-04-05 23:32:50 +00:00
committed by Android (Google) Code Review
6 changed files with 188 additions and 43 deletions

View File

@@ -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")

View File

@@ -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")

View File

@@ -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;
}

View File

@@ -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")

View File

@@ -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;

View File

@@ -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) {