diff options
-rw-r--r-- | api/current.txt | 18 | ||||
-rw-r--r-- | api/system-current.txt | 18 | ||||
-rw-r--r-- | api/test-current.txt | 18 | ||||
-rw-r--r-- | core/java/android/view/textclassifier/TextClassification.java | 28 | ||||
-rw-r--r-- | core/java/android/view/textclassifier/TextClassifier.java | 68 | ||||
-rw-r--r-- | core/java/android/view/textclassifier/TextClassifierImpl.java | 46 | ||||
-rw-r--r-- | core/java/android/view/textclassifier/TextSelection.java | 53 | ||||
-rw-r--r-- | core/java/android/widget/SelectionActionModeHelper.java | 55 |
8 files changed, 244 insertions, 60 deletions
diff --git a/api/current.txt b/api/current.txt index 9f4e05170372..0eb47154638e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -48955,14 +48955,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"; @@ -48985,6 +48993,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 { diff --git a/api/system-current.txt b/api/system-current.txt index 86b68f6fdbc3..1747f23785c4 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -52694,14 +52694,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"; @@ -52724,6 +52732,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 { diff --git a/api/test-current.txt b/api/test-current.txt index c8d676a18fc2..5664391d2c76 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -49592,14 +49592,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"; @@ -49622,6 +49630,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 { diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java index 26d2141e3486..2779aa2db93a 100644 --- a/core/java/android/view/textclassifier/TextClassification.java +++ b/core/java/android/view/textclassifier/TextClassification.java @@ -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; + } + } } diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java index 46dbd0e34a1c..07455c1d3297 100644 --- a/core/java/android/view/textclassifier/TextClassifier.java +++ b/core/java/android/view/textclassifier/TextClassifier.java @@ -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 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); + @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 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); + @Nullable LocaleList defaultLocales) { + return TextClassification.EMPTY; + } /** * Returns a {@link LinksInfo} that may be applied to the text to annotate it with links diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java index 1c07be4bcd75..2799f2bfda8a 100644 --- a/core/java/android/view/textclassifier/TextClassifierImpl.java +++ b/core/java/android/view/textclassifier/TextClassifierImpl.java @@ -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 diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java index 11ebe8359b9c..0a67954a2e33 100644 --- a/core/java/android/view/textclassifier/TextSelection.java +++ b/core/java/android/view/textclassifier/TextSelection.java @@ -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; + } + } } diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java index 5e22650a83cc..71854ae89290 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -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; |