Optional parameters for TextClassifier APIs.

TextClassifier methods now take a @Nullable Options parameter which
is a container for optional parameters to its methods.
This way, if we need to pass an optional parameter to one of
TextClassifier's methods, we won't have to overload the method.
Instead, we'll introduce a new Options field.

This CL also adds a hidden optional field for suggestSelection.
This is used to make sure that "dark launched" models do not return
new selection indices to clients. Clients e.g. TextView that are
dark-launch aware may use the field to explicitly request for
dark-launch results.

Test: bit FrameworksCoreTests:android.widget.TextViewActivityTest
Test: bit CtsWidgetTestCases:android.widget.cts.TextViewTest
Test: Also see I843eadff8a2b674388055364c1f25c8d4ccea771
Change-Id: I58e44451ac8a8e3a5bc3959198447f5549870902
This commit is contained in:
Abodunrinwa Toki
2017-10-28 02:28:45 +01:00
parent e74399de6f
commit 2b6020fc80
8 changed files with 244 additions and 60 deletions

View File

@@ -48947,14 +48947,22 @@ package android.view.textclassifier {
method public android.view.textclassifier.TextClassification.Builder setText(java.lang.String);
}
public static final class TextClassification.Options {
ctor public TextClassification.Options();
method public android.os.LocaleList getDefaultLocales();
method public android.view.textclassifier.TextClassification.Options setDefaultLocales(android.os.LocaleList);
}
public final class TextClassificationManager {
method public android.view.textclassifier.TextClassifier getTextClassifier();
method public void setTextClassifier(android.view.textclassifier.TextClassifier);
}
public abstract interface TextClassifier {
method public abstract android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int, android.os.LocaleList);
method public abstract android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList);
method public default android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int, android.view.textclassifier.TextClassification.Options);
method public default android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int, android.os.LocaleList);
method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.view.textclassifier.TextSelection.Options);
method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList);
field public static final android.view.textclassifier.TextClassifier NO_OP;
field public static final java.lang.String TYPE_ADDRESS = "address";
field public static final java.lang.String TYPE_EMAIL = "email";
@@ -48977,6 +48985,12 @@ package android.view.textclassifier {
method public android.view.textclassifier.TextSelection.Builder setEntityType(java.lang.String, float);
}
public static final class TextSelection.Options {
ctor public TextSelection.Options();
method public android.os.LocaleList getDefaultLocales();
method public android.view.textclassifier.TextSelection.Options setDefaultLocales(android.os.LocaleList);
}
}
package android.view.textservice {

View File

@@ -52684,14 +52684,22 @@ package android.view.textclassifier {
method public android.view.textclassifier.TextClassification.Builder setText(java.lang.String);
}
public static final class TextClassification.Options {
ctor public TextClassification.Options();
method public android.os.LocaleList getDefaultLocales();
method public android.view.textclassifier.TextClassification.Options setDefaultLocales(android.os.LocaleList);
}
public final class TextClassificationManager {
method public android.view.textclassifier.TextClassifier getTextClassifier();
method public void setTextClassifier(android.view.textclassifier.TextClassifier);
}
public abstract interface TextClassifier {
method public abstract android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int, android.os.LocaleList);
method public abstract android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList);
method public default android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int, android.view.textclassifier.TextClassification.Options);
method public default android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int, android.os.LocaleList);
method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.view.textclassifier.TextSelection.Options);
method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList);
field public static final android.view.textclassifier.TextClassifier NO_OP;
field public static final java.lang.String TYPE_ADDRESS = "address";
field public static final java.lang.String TYPE_EMAIL = "email";
@@ -52714,6 +52722,12 @@ package android.view.textclassifier {
method public android.view.textclassifier.TextSelection.Builder setEntityType(java.lang.String, float);
}
public static final class TextSelection.Options {
ctor public TextSelection.Options();
method public android.os.LocaleList getDefaultLocales();
method public android.view.textclassifier.TextSelection.Options setDefaultLocales(android.os.LocaleList);
}
}
package android.view.textservice {

View File

@@ -49565,14 +49565,22 @@ package android.view.textclassifier {
method public android.view.textclassifier.TextClassification.Builder setText(java.lang.String);
}
public static final class TextClassification.Options {
ctor public TextClassification.Options();
method public android.os.LocaleList getDefaultLocales();
method public android.view.textclassifier.TextClassification.Options setDefaultLocales(android.os.LocaleList);
}
public final class TextClassificationManager {
method public android.view.textclassifier.TextClassifier getTextClassifier();
method public void setTextClassifier(android.view.textclassifier.TextClassifier);
}
public abstract interface TextClassifier {
method public abstract android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int, android.os.LocaleList);
method public abstract android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList);
method public default android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int, android.view.textclassifier.TextClassification.Options);
method public default android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int, android.os.LocaleList);
method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.view.textclassifier.TextSelection.Options);
method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList);
field public static final android.view.textclassifier.TextClassifier NO_OP;
field public static final java.lang.String TYPE_ADDRESS = "address";
field public static final java.lang.String TYPE_EMAIL = "email";
@@ -49595,6 +49603,12 @@ package android.view.textclassifier {
method public android.view.textclassifier.TextSelection.Builder setEntityType(java.lang.String, float);
}
public static final class TextSelection.Options {
ctor public TextSelection.Options();
method public android.os.LocaleList getDefaultLocales();
method public android.view.textclassifier.TextSelection.Options setDefaultLocales(android.os.LocaleList);
}
}
package android.view.textservice {

View File

@@ -23,6 +23,7 @@ import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.LocaleList;
import android.view.View.OnClickListener;
import android.view.textclassifier.TextClassifier.EntityType;
@@ -438,4 +439,31 @@ public final class TextClassification {
mLogType, mVersionInfo);
}
}
/**
* TextClassification optional input parameters.
*/
public static final class Options {
private LocaleList mDefaultLocales;
/**
* @param defaultLocales ordered list of locale preferences that may be used to disambiguate
* the provided text. If no locale preferences exist, set this to null or an empty
* locale list.
*/
public Options setDefaultLocales(@Nullable LocaleList defaultLocales) {
mDefaultLocales = defaultLocales;
return this;
}
/**
* @return ordered list of locale preferences that can be used to disambiguate
* the provided text.
*/
@Nullable
public LocaleList getDefaultLocales() {
return mDefaultLocales;
}
}
}

View File

@@ -56,23 +56,7 @@ public interface TextClassifier {
* No-op TextClassifier.
* This may be used to turn off TextClassifier features.
*/
TextClassifier NO_OP = new TextClassifier() {
@Override
public TextSelection suggestSelection(
CharSequence text,
int selectionStartIndex,
int selectionEndIndex,
LocaleList defaultLocales) {
return new TextSelection.Builder(selectionStartIndex, selectionEndIndex).build();
}
@Override
public TextClassification classifyText(
CharSequence text, int startIndex, int endIndex, LocaleList defaultLocales) {
return TextClassification.EMPTY;
}
};
TextClassifier NO_OP = new TextClassifier() {};
/**
* Returns suggested text selection start and end indices, recognized entity types, and their
@@ -82,21 +66,34 @@ public interface TextClassifier {
* by the sub sequence starting at selectionStartIndex and ending at selectionEndIndex)
* @param selectionStartIndex start index of the selected part of text
* @param selectionEndIndex end index of the selected part of text
* @param defaultLocales ordered list of locale preferences that can be used to disambiguate
* the provided text. If no locale preferences exist, set this to null or an empty locale
* list in which case the classifier will decide whether to use no locale information, use
* a default locale, or use the system default.
* @param options optional input parameters
*
* @throws IllegalArgumentException if text is null; selectionStartIndex is negative;
* selectionEndIndex is greater than text.length() or not greater than selectionStartIndex
*/
@WorkerThread
@NonNull
TextSelection suggestSelection(
default TextSelection suggestSelection(
@NonNull CharSequence text,
@IntRange(from = 0) int selectionStartIndex,
@IntRange(from = 0) int selectionEndIndex,
@Nullable LocaleList defaultLocales);
@Nullable TextSelection.Options options) {
return new TextSelection.Builder(selectionStartIndex, selectionEndIndex).build();
}
/**
* @see #suggestSelection(CharSequence, int, int, TextSelection.Options)
*/
// TODO: Consider deprecating (b/68846316)
@WorkerThread
@NonNull
default TextSelection suggestSelection(
@NonNull CharSequence text,
@IntRange(from = 0) int selectionStartIndex,
@IntRange(from = 0) int selectionEndIndex,
@Nullable LocaleList defaultLocales) {
return new TextSelection.Builder(selectionStartIndex, selectionEndIndex).build();
}
/**
* Classifies the specified text and returns a {@link TextClassification} object that can be
@@ -106,21 +103,34 @@ public interface TextClassifier {
* by the sub sequence starting at startIndex and ending at endIndex)
* @param startIndex start index of the text to classify
* @param endIndex end index of the text to classify
* @param defaultLocales ordered list of locale preferences that can be used to disambiguate
* the provided text. If no locale preferences exist, set this to null or an empty locale
* list in which case the classifier will decide whether to use no locale information, use
* a default locale, or use the system default.
* @param options optional input parameters
*
* @throws IllegalArgumentException if text is null; startIndex is negative;
* endIndex is greater than text.length() or not greater than startIndex
*/
@WorkerThread
@NonNull
TextClassification classifyText(
default TextClassification classifyText(
@NonNull CharSequence text,
@IntRange(from = 0) int startIndex,
@IntRange(from = 0) int endIndex,
@Nullable LocaleList defaultLocales);
@Nullable TextClassification.Options options) {
return TextClassification.EMPTY;
}
/**
* @see #classifyText(CharSequence, int, int, TextClassification.Options)
*/
// TODO: Consider deprecating (b/68846316)
@WorkerThread
@NonNull
default TextClassification classifyText(
@NonNull CharSequence text,
@IntRange(from = 0) int startIndex,
@IntRange(from = 0) int endIndex,
@Nullable LocaleList defaultLocales) {
return TextClassification.EMPTY;
}
/**
* Returns a {@link LinksInfo} that may be applied to the text to annotate it with links

View File

@@ -100,16 +100,24 @@ final class TextClassifierImpl implements TextClassifier {
@Override
public TextSelection suggestSelection(
@NonNull CharSequence text, int selectionStartIndex, int selectionEndIndex,
@Nullable LocaleList defaultLocales) {
@Nullable TextSelection.Options options) {
validateInput(text, selectionStartIndex, selectionEndIndex);
try {
if (text.length() > 0) {
final SmartSelection smartSelection = getSmartSelection(defaultLocales);
final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
final SmartSelection smartSelection = getSmartSelection(locales);
final String string = text.toString();
final int[] startEnd = smartSelection.suggest(
string, selectionStartIndex, selectionEndIndex);
final int start = startEnd[0];
final int end = startEnd[1];
final int start;
final int end;
if (getSettings().isDarkLaunch() && !options.isDarkLaunchAllowed()) {
start = selectionStartIndex;
end = selectionEndIndex;
} else {
final int[] startEnd = smartSelection.suggest(
string, selectionStartIndex, selectionEndIndex);
start = startEnd[0];
end = startEnd[1];
}
if (start <= end
&& start >= 0 && end <= string.length()
&& start <= selectionStartIndex && end >= selectionEndIndex) {
@@ -139,18 +147,27 @@ final class TextClassifierImpl implements TextClassifier {
}
// Getting here means something went wrong, return a NO_OP result.
return TextClassifier.NO_OP.suggestSelection(
text, selectionStartIndex, selectionEndIndex, defaultLocales);
text, selectionStartIndex, selectionEndIndex, options);
}
@Override
public TextSelection suggestSelection(
@NonNull CharSequence text, int selectionStartIndex, int selectionEndIndex,
@Nullable LocaleList defaultLocales) {
return suggestSelection(text, selectionStartIndex, selectionEndIndex,
new TextSelection.Options().setDefaultLocales(defaultLocales));
}
@Override
public TextClassification classifyText(
@NonNull CharSequence text, int startIndex, int endIndex,
@Nullable LocaleList defaultLocales) {
@Nullable TextClassification.Options options) {
validateInput(text, startIndex, endIndex);
try {
if (text.length() > 0) {
final String string = text.toString();
SmartSelection.ClassificationResult[] results = getSmartSelection(defaultLocales)
final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
final SmartSelection.ClassificationResult[] results = getSmartSelection(locales)
.classifyText(string, startIndex, endIndex,
getHintFlags(string, startIndex, endIndex));
if (results.length > 0) {
@@ -165,8 +182,15 @@ final class TextClassifierImpl implements TextClassifier {
Log.e(LOG_TAG, "Error getting text classification info.", t);
}
// Getting here means something went wrong, return a NO_OP result.
return TextClassifier.NO_OP.classifyText(
text, startIndex, endIndex, defaultLocales);
return TextClassifier.NO_OP.classifyText(text, startIndex, endIndex, options);
}
@Override
public TextClassification classifyText(
@NonNull CharSequence text, int startIndex, int endIndex,
@Nullable LocaleList defaultLocales) {
return classifyText(text, startIndex, endIndex,
new TextClassification.Options().setDefaultLocales(defaultLocales));
}
@Override

View File

@@ -19,6 +19,8 @@ package android.view.textclassifier;
import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.LocaleList;
import android.view.textclassifier.TextClassifier.EntityType;
import com.android.internal.util.Preconditions;
@@ -181,4 +183,55 @@ public final class TextSelection {
mStartIndex, mEndIndex, mEntityConfidence, mLogSource, mVersionInfo);
}
}
/**
* TextSelection optional input parameters.
*/
public static final class Options {
private LocaleList mDefaultLocales;
private boolean mDarkLaunchAllowed;
/**
* @param defaultLocales ordered list of locale preferences that may be used to disambiguate
* the provided text. If no locale preferences exist, set this to null or an empty
* locale list.
*/
public Options setDefaultLocales(@Nullable LocaleList defaultLocales) {
mDefaultLocales = defaultLocales;
return this;
}
/**
* @return ordered list of locale preferences that can be used to disambiguate
* the provided text.
*/
@Nullable
public LocaleList getDefaultLocales() {
return mDefaultLocales;
}
/**
* @param allowed whether or not the TextClassifier should return selection suggestions
* when "dark launched". When a TextClassifier is dark launched, it can suggest
* selection changes that should not be used to actually change the user's selection.
* Instead, the suggested selection is logged, compared with the user's selection
* interaction, and used to generate quality metrics for the TextClassifier.
*
* @hide
*/
public void setDarkLaunchAllowed(boolean allowed) {
mDarkLaunchAllowed = allowed;
}
/**
* Returns true if the TextClassifier should return selection suggestions when
* "dark launched". Otherwise, returns false.
*
* @hide
*/
public boolean isDarkLaunchAllowed() {
return mDarkLaunchAllowed;
}
}
}

View File

@@ -20,10 +20,12 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UiThread;
import android.annotation.WorkerThread;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.AsyncTask;
import android.os.Build;
import android.os.LocaleList;
import android.text.Layout;
import android.text.Selection;
@@ -81,6 +83,7 @@ public final class SelectionActionModeHelper {
mEditor = Preconditions.checkNotNull(editor);
mTextView = mEditor.getTextView();
mTextClassificationHelper = new TextClassificationHelper(
mTextView.getContext(),
mTextView.getTextClassifier(),
getText(mTextView),
0, 1, mTextView.getTextLocales());
@@ -385,6 +388,7 @@ public final class SelectionActionModeHelper {
private void resetTextClassificationHelper() {
mTextClassificationHelper.init(
mTextView.getContext(),
mTextView.getTextClassifier(),
getText(mTextView),
mTextView.getSelectionStart(), mTextView.getSelectionEnd(),
@@ -787,6 +791,7 @@ public final class SelectionActionModeHelper {
private static final int TRIM_DELTA = 120; // characters
private Context mContext;
private TextClassifier mTextClassifier;
/** The original TextView text. **/
@@ -795,7 +800,10 @@ public final class SelectionActionModeHelper {
private int mSelectionStart;
/** End index relative to mText. */
private int mSelectionEnd;
private LocaleList mLocales;
private final TextSelection.Options mSelectionOptions = new TextSelection.Options();
private final TextClassification.Options mClassificationOptions =
new TextClassification.Options();
/** Trimmed text starting from mTrimStart in mText. */
private CharSequence mTrimmedText;
@@ -816,21 +824,24 @@ public final class SelectionActionModeHelper {
/** Whether the TextClassifier has been initialized. */
private boolean mHot;
TextClassificationHelper(TextClassifier textClassifier,
TextClassificationHelper(Context context, TextClassifier textClassifier,
CharSequence text, int selectionStart, int selectionEnd, LocaleList locales) {
init(textClassifier, text, selectionStart, selectionEnd, locales);
init(context, textClassifier, text, selectionStart, selectionEnd, locales);
}
@UiThread
public void init(TextClassifier textClassifier,
public void init(Context context, TextClassifier textClassifier,
CharSequence text, int selectionStart, int selectionEnd, LocaleList locales) {
mContext = Preconditions.checkNotNull(context);
mTextClassifier = Preconditions.checkNotNull(textClassifier);
mText = Preconditions.checkNotNull(text).toString();
mLastClassificationText = null; // invalidate.
Preconditions.checkArgument(selectionEnd > selectionStart);
mSelectionStart = selectionStart;
mSelectionEnd = selectionEnd;
mLocales = locales;
mClassificationOptions.setDefaultLocales(locales);
mSelectionOptions.setDefaultLocales(locales)
.setDarkLaunchAllowed(true);
}
@WorkerThread
@@ -843,8 +854,16 @@ public final class SelectionActionModeHelper {
public SelectionResult suggestSelection() {
mHot = true;
trimText();
final TextSelection selection = mTextClassifier.suggestSelection(
mTrimmedText, mRelativeStart, mRelativeEnd, mLocales);
final TextSelection selection;
if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.O_MR1) {
selection = mTextClassifier.suggestSelection(
mTrimmedText, mRelativeStart, mRelativeEnd, mSelectionOptions);
} else {
// Use old APIs.
selection = mTextClassifier.suggestSelection(
mTrimmedText, mRelativeStart, mRelativeEnd,
mSelectionOptions.getDefaultLocales());
}
// Do not classify new selection boundaries if TextClassifier should be dark launched.
if (!mTextClassifier.getSettings().isDarkLaunch()) {
mSelectionStart = Math.max(0, selection.getSelectionStartIndex() + mTrimStart);
@@ -874,20 +893,28 @@ public final class SelectionActionModeHelper {
if (!Objects.equals(mText, mLastClassificationText)
|| mSelectionStart != mLastClassificationSelectionStart
|| mSelectionEnd != mLastClassificationSelectionEnd
|| !Objects.equals(mLocales, mLastClassificationLocales)) {
|| !Objects.equals(
mClassificationOptions.getDefaultLocales(),
mLastClassificationLocales)) {
mLastClassificationText = mText;
mLastClassificationSelectionStart = mSelectionStart;
mLastClassificationSelectionEnd = mSelectionEnd;
mLastClassificationLocales = mLocales;
mLastClassificationLocales = mClassificationOptions.getDefaultLocales();
trimText();
final TextClassification classification;
if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.O_MR1) {
classification = mTextClassifier.classifyText(
mTrimmedText, mRelativeStart, mRelativeEnd, mClassificationOptions);
} else {
// Use old APIs.
classification = mTextClassifier.classifyText(
mTrimmedText, mRelativeStart, mRelativeEnd,
mClassificationOptions.getDefaultLocales());
}
mLastClassificationResult = new SelectionResult(
mSelectionStart,
mSelectionEnd,
mTextClassifier.classifyText(
mTrimmedText, mRelativeStart, mRelativeEnd, mLocales),
selection);
mSelectionStart, mSelectionEnd, classification, selection);
}
return mLastClassificationResult;