summaryrefslogtreecommitdiff
path: root/android/view/textclassifier
diff options
context:
space:
mode:
authorJeff Davidson <jpd@google.com>2018-02-08 15:30:06 -0800
committerJeff Davidson <jpd@google.com>2018-02-08 15:30:06 -0800
commita192cc2a132cb0ee8588e2df755563ec7008c179 (patch)
tree380e4db22df19c819bd37df34bf06e7568916a50 /android/view/textclassifier
parent98fe7819c6d14f4f464a5cac047f9e82dee5da58 (diff)
downloadandroid-28-a192cc2a132cb0ee8588e2df755563ec7008c179.tar.gz
Update fullsdk to 4575844
/google/data/ro/projects/android/fetch_artifact \ --bid 4575844 \ --target sdk_phone_x86_64-sdk \ sdk-repo-linux-sources-4575844.zip Test: TreeHugger Change-Id: I81e0eb157b4ac3b38408d0ef86f9d6286471f87a
Diffstat (limited to 'android/view/textclassifier')
-rw-r--r--android/view/textclassifier/EntityConfidence.java76
-rw-r--r--android/view/textclassifier/TextClassification.java278
-rw-r--r--android/view/textclassifier/TextClassifier.java39
-rw-r--r--android/view/textclassifier/TextClassifierConstants.java12
-rw-r--r--android/view/textclassifier/TextClassifierImpl.java12
-rw-r--r--android/view/textclassifier/TextLinks.java115
-rw-r--r--android/view/textclassifier/TextSelection.java105
7 files changed, 533 insertions, 104 deletions
diff --git a/android/view/textclassifier/EntityConfidence.java b/android/view/textclassifier/EntityConfidence.java
index 19660d95..69a59a5b 100644
--- a/android/view/textclassifier/EntityConfidence.java
+++ b/android/view/textclassifier/EntityConfidence.java
@@ -18,6 +18,8 @@ package android.view.textclassifier;
import android.annotation.FloatRange;
import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.ArrayMap;
import com.android.internal.util.Preconditions;
@@ -30,17 +32,16 @@ import java.util.Map;
/**
* Helper object for setting and getting entity scores for classified text.
*
- * @param <T> the entity type.
* @hide
*/
-final class EntityConfidence<T> {
+final class EntityConfidence implements Parcelable {
- private final ArrayMap<T, Float> mEntityConfidence = new ArrayMap<>();
- private final ArrayList<T> mSortedEntities = new ArrayList<>();
+ private final ArrayMap<String, Float> mEntityConfidence = new ArrayMap<>();
+ private final ArrayList<String> mSortedEntities = new ArrayList<>();
EntityConfidence() {}
- EntityConfidence(@NonNull EntityConfidence<T> source) {
+ EntityConfidence(@NonNull EntityConfidence source) {
Preconditions.checkNotNull(source);
mEntityConfidence.putAll(source.mEntityConfidence);
mSortedEntities.addAll(source.mSortedEntities);
@@ -54,24 +55,16 @@ final class EntityConfidence<T> {
* @param source a map from entity to a confidence value in the range 0 (low confidence) to
* 1 (high confidence).
*/
- EntityConfidence(@NonNull Map<T, Float> source) {
+ EntityConfidence(@NonNull Map<String, Float> source) {
Preconditions.checkNotNull(source);
// Prune non-existent entities and clamp to 1.
mEntityConfidence.ensureCapacity(source.size());
- for (Map.Entry<T, Float> it : source.entrySet()) {
+ for (Map.Entry<String, Float> it : source.entrySet()) {
if (it.getValue() <= 0) continue;
mEntityConfidence.put(it.getKey(), Math.min(1, it.getValue()));
}
-
- // Create a list of entities sorted by decreasing confidence for getEntities().
- mSortedEntities.ensureCapacity(mEntityConfidence.size());
- mSortedEntities.addAll(mEntityConfidence.keySet());
- mSortedEntities.sort((e1, e2) -> {
- float score1 = mEntityConfidence.get(e1);
- float score2 = mEntityConfidence.get(e2);
- return Float.compare(score2, score1);
- });
+ resetSortedEntitiesFromMap();
}
/**
@@ -79,7 +72,7 @@ final class EntityConfidence<T> {
* high confidence to low confidence.
*/
@NonNull
- public List<T> getEntities() {
+ public List<String> getEntities() {
return Collections.unmodifiableList(mSortedEntities);
}
@@ -89,7 +82,7 @@ final class EntityConfidence<T> {
* classified text.
*/
@FloatRange(from = 0.0, to = 1.0)
- public float getConfidenceScore(T entity) {
+ public float getConfidenceScore(String entity) {
if (mEntityConfidence.containsKey(entity)) {
return mEntityConfidence.get(entity);
}
@@ -100,4 +93,51 @@ final class EntityConfidence<T> {
public String toString() {
return mEntityConfidence.toString();
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mEntityConfidence.size());
+ for (Map.Entry<String, Float> entry : mEntityConfidence.entrySet()) {
+ dest.writeString(entry.getKey());
+ dest.writeFloat(entry.getValue());
+ }
+ }
+
+ public static final Parcelable.Creator<EntityConfidence> CREATOR =
+ new Parcelable.Creator<EntityConfidence>() {
+ @Override
+ public EntityConfidence createFromParcel(Parcel in) {
+ return new EntityConfidence(in);
+ }
+
+ @Override
+ public EntityConfidence[] newArray(int size) {
+ return new EntityConfidence[size];
+ }
+ };
+
+ private EntityConfidence(Parcel in) {
+ final int numEntities = in.readInt();
+ mEntityConfidence.ensureCapacity(numEntities);
+ for (int i = 0; i < numEntities; ++i) {
+ mEntityConfidence.put(in.readString(), in.readFloat());
+ }
+ resetSortedEntitiesFromMap();
+ }
+
+ private void resetSortedEntitiesFromMap() {
+ mSortedEntities.clear();
+ mSortedEntities.ensureCapacity(mEntityConfidence.size());
+ mSortedEntities.addAll(mEntityConfidence.keySet());
+ mSortedEntities.sort((e1, e2) -> {
+ float score1 = mEntityConfidence.get(e1);
+ float score2 = mEntityConfidence.get(e2);
+ return Float.compare(score2, score1);
+ });
+ }
}
diff --git a/android/view/textclassifier/TextClassification.java b/android/view/textclassifier/TextClassification.java
index 7ffbf635..7089677d 100644
--- a/android/view/textclassifier/TextClassification.java
+++ b/android/view/textclassifier/TextClassification.java
@@ -22,8 +22,13 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.LocaleList;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.ArrayMap;
import android.view.View.OnClickListener;
import android.view.textclassifier.TextClassifier.EntityType;
@@ -52,7 +57,7 @@ import java.util.Map;
* Button button = new Button(context);
* button.setCompoundDrawablesWithIntrinsicBounds(classification.getIcon(), null, null, null);
* button.setText(classification.getLabel());
- * button.setOnClickListener(classification.getOnClickListener());
+ * button.setOnClickListener(v -> context.startActivity(classification.getIntent()));
* }</pre>
*
* <p>e.g. starting an action mode with menu items that can handle the classified text:
@@ -90,7 +95,6 @@ import java.util.Map;
* ...
* });
* }</pre>
- *
*/
public final class TextClassification {
@@ -99,6 +103,10 @@ public final class TextClassification {
*/
static final TextClassification EMPTY = new TextClassification.Builder().build();
+ // TODO(toki): investigate a way to derive this based on device properties.
+ private static final int MAX_PRIMARY_ICON_SIZE = 192;
+ private static final int MAX_SECONDARY_ICON_SIZE = 144;
+
@NonNull private final String mText;
@Nullable private final Drawable mPrimaryIcon;
@Nullable private final String mPrimaryLabel;
@@ -107,8 +115,7 @@ public final class TextClassification {
@NonNull private final List<Drawable> mSecondaryIcons;
@NonNull private final List<String> mSecondaryLabels;
@NonNull private final List<Intent> mSecondaryIntents;
- @NonNull private final List<OnClickListener> mSecondaryOnClickListeners;
- @NonNull private final EntityConfidence<String> mEntityConfidence;
+ @NonNull private final EntityConfidence mEntityConfidence;
@NonNull private final String mSignature;
private TextClassification(
@@ -120,12 +127,10 @@ public final class TextClassification {
@NonNull List<Drawable> secondaryIcons,
@NonNull List<String> secondaryLabels,
@NonNull List<Intent> secondaryIntents,
- @NonNull List<OnClickListener> secondaryOnClickListeners,
@NonNull Map<String, Float> entityConfidence,
@NonNull String signature) {
Preconditions.checkArgument(secondaryLabels.size() == secondaryIntents.size());
Preconditions.checkArgument(secondaryIcons.size() == secondaryIntents.size());
- Preconditions.checkArgument(secondaryOnClickListeners.size() == secondaryIntents.size());
mText = text;
mPrimaryIcon = primaryIcon;
mPrimaryLabel = primaryLabel;
@@ -134,8 +139,7 @@ public final class TextClassification {
mSecondaryIcons = secondaryIcons;
mSecondaryLabels = secondaryLabels;
mSecondaryIntents = secondaryIntents;
- mSecondaryOnClickListeners = secondaryOnClickListeners;
- mEntityConfidence = new EntityConfidence<>(entityConfidence);
+ mEntityConfidence = new EntityConfidence(entityConfidence);
mSignature = signature;
}
@@ -186,7 +190,6 @@ public final class TextClassification {
* @see #getSecondaryIntent(int)
* @see #getSecondaryLabel(int)
* @see #getSecondaryIcon(int)
- * @see #getSecondaryOnClickListener(int)
*/
@IntRange(from = 0)
public int getSecondaryActionsCount() {
@@ -198,13 +201,10 @@ public final class TextClassification {
* classified text.
*
* @param index Index of the action to get the icon for.
- *
* @throws IndexOutOfBoundsException if the specified index is out of range.
- *
* @see #getSecondaryActionsCount() for the number of actions available.
* @see #getSecondaryIntent(int)
* @see #getSecondaryLabel(int)
- * @see #getSecondaryOnClickListener(int)
* @see #getIcon()
*/
@Nullable
@@ -228,13 +228,10 @@ public final class TextClassification {
* the classified text.
*
* @param index Index of the action to get the label for.
- *
* @throws IndexOutOfBoundsException if the specified index is out of range.
- *
* @see #getSecondaryActionsCount()
* @see #getSecondaryIntent(int)
* @see #getSecondaryIcon(int)
- * @see #getSecondaryOnClickListener(int)
* @see #getLabel()
*/
@Nullable
@@ -257,13 +254,10 @@ public final class TextClassification {
* Returns one of the <i>secondary</i> intents that may be fired to act on the classified text.
*
* @param index Index of the action to get the intent for.
- *
* @throws IndexOutOfBoundsException if the specified index is out of range.
- *
* @see #getSecondaryActionsCount()
* @see #getSecondaryLabel(int)
* @see #getSecondaryIcon(int)
- * @see #getSecondaryOnClickListener(int)
* @see #getIntent()
*/
@Nullable
@@ -282,29 +276,10 @@ public final class TextClassification {
}
/**
- * Returns one of the <i>secondary</i> OnClickListeners that may be triggered to act on the
- * classified text.
- *
- * @param index Index of the action to get the click listener for.
- *
- * @throws IndexOutOfBoundsException if the specified index is out of range.
- *
- * @see #getSecondaryActionsCount()
- * @see #getSecondaryIntent(int)
- * @see #getSecondaryLabel(int)
- * @see #getSecondaryIcon(int)
- * @see #getOnClickListener()
- */
- @Nullable
- public OnClickListener getSecondaryOnClickListener(int index) {
- return mSecondaryOnClickListeners.get(index);
- }
-
- /**
* Returns the <i>primary</i> OnClickListener that may be triggered to act on the classified
- * text.
- *
- * @see #getSecondaryOnClickListener(int)
+ * text. This field is not parcelable and will be null for all objects read from a parcel.
+ * Instead, call Context#startActivity(Intent) with the result of #getSecondaryIntent(int).
+ * Note that this may fail if the activity doesn't have permission to send the intent.
*/
@Nullable
public OnClickListener getOnClickListener() {
@@ -334,6 +309,42 @@ public final class TextClassification {
mSignature);
}
+ /** Helper for parceling via #ParcelableWrapper. */
+ private void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mText);
+ final Bitmap primaryIconBitmap = drawableToBitmap(mPrimaryIcon, MAX_PRIMARY_ICON_SIZE);
+ dest.writeInt(primaryIconBitmap != null ? 1 : 0);
+ if (primaryIconBitmap != null) {
+ primaryIconBitmap.writeToParcel(dest, flags);
+ }
+ dest.writeString(mPrimaryLabel);
+ dest.writeInt(mPrimaryIntent != null ? 1 : 0);
+ if (mPrimaryIntent != null) {
+ mPrimaryIntent.writeToParcel(dest, flags);
+ }
+ // mPrimaryOnClickListener is not parcelable.
+ dest.writeTypedList(drawablesToBitmaps(mSecondaryIcons, MAX_SECONDARY_ICON_SIZE));
+ dest.writeStringList(mSecondaryLabels);
+ dest.writeTypedList(mSecondaryIntents);
+ mEntityConfidence.writeToParcel(dest, flags);
+ dest.writeString(mSignature);
+ }
+
+ /** Helper for unparceling via #ParcelableWrapper. */
+ private TextClassification(Parcel in) {
+ mText = in.readString();
+ mPrimaryIcon = in.readInt() == 0
+ ? null : new BitmapDrawable(null, Bitmap.CREATOR.createFromParcel(in));
+ mPrimaryLabel = in.readString();
+ mPrimaryIntent = in.readInt() == 0 ? null : Intent.CREATOR.createFromParcel(in);
+ mPrimaryOnClickListener = null; // not parcelable
+ mSecondaryIcons = bitmapsToDrawables(in.createTypedArrayList(Bitmap.CREATOR));
+ mSecondaryLabels = in.createStringArrayList();
+ mSecondaryIntents = in.createTypedArrayList(Intent.CREATOR);
+ mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
+ mSignature = in.readString();
+ }
+
/**
* Creates an OnClickListener that starts an activity with the specified intent.
*
@@ -349,6 +360,68 @@ public final class TextClassification {
}
/**
+ * Returns a Bitmap representation of the Drawable
+ *
+ * @param drawable The drawable to convert.
+ * @param maxDims The maximum edge length of the resulting bitmap (in pixels).
+ */
+ @Nullable
+ private static Bitmap drawableToBitmap(@Nullable Drawable drawable, int maxDims) {
+ if (drawable == null) {
+ return null;
+ }
+ final int actualWidth = Math.max(1, drawable.getIntrinsicWidth());
+ final int actualHeight = Math.max(1, drawable.getIntrinsicHeight());
+ final double scaleWidth = ((double) maxDims) / actualWidth;
+ final double scaleHeight = ((double) maxDims) / actualHeight;
+ final double scale = Math.min(1.0, Math.min(scaleWidth, scaleHeight));
+ final int width = (int) (actualWidth * scale);
+ final int height = (int) (actualHeight * scale);
+ if (drawable instanceof BitmapDrawable) {
+ final BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
+ if (actualWidth != width || actualHeight != height) {
+ return Bitmap.createScaledBitmap(
+ bitmapDrawable.getBitmap(), width, height, /*filter=*/false);
+ } else {
+ return bitmapDrawable.getBitmap();
+ }
+ } else {
+ final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ drawable.draw(canvas);
+ return bitmap;
+ }
+ }
+
+ /**
+ * Returns a list of drawables converted to Bitmaps
+ *
+ * @param drawables The drawables to convert.
+ * @param maxDims The maximum edge length of the resulting bitmaps (in pixels).
+ */
+ private static List<Bitmap> drawablesToBitmaps(List<Drawable> drawables, int maxDims) {
+ final List<Bitmap> bitmaps = new ArrayList<>(drawables.size());
+ for (Drawable drawable : drawables) {
+ bitmaps.add(drawableToBitmap(drawable, maxDims));
+ }
+ return bitmaps;
+ }
+
+ /** Returns a list of drawable wrappers for a list of bitmaps. */
+ private static List<Drawable> bitmapsToDrawables(List<Bitmap> bitmaps) {
+ final List<Drawable> drawables = new ArrayList<>(bitmaps.size());
+ for (Bitmap bitmap : bitmaps) {
+ if (bitmap != null) {
+ drawables.add(new BitmapDrawable(null, bitmap));
+ } else {
+ drawables.add(null);
+ }
+ }
+ return drawables;
+ }
+
+ /**
* Builder for building {@link TextClassification} objects.
*
* <p>e.g.
@@ -358,9 +431,9 @@ public final class TextClassification {
* .setText(classifiedText)
* .setEntityType(TextClassifier.TYPE_EMAIL, 0.9)
* .setEntityType(TextClassifier.TYPE_OTHER, 0.1)
- * .setPrimaryAction(intent, label, icon, onClickListener)
- * .addSecondaryAction(intent1, label1, icon1, onClickListener1)
- * .addSecondaryAction(intent2, label2, icon2, onClickListener2)
+ * .setPrimaryAction(intent, label, icon)
+ * .addSecondaryAction(intent1, label1, icon1)
+ * .addSecondaryAction(intent2, label2, icon2)
* .build();
* }</pre>
*/
@@ -370,7 +443,6 @@ public final class TextClassification {
@NonNull private final List<Drawable> mSecondaryIcons = new ArrayList<>();
@NonNull private final List<String> mSecondaryLabels = new ArrayList<>();
@NonNull private final List<Intent> mSecondaryIntents = new ArrayList<>();
- @NonNull private final List<OnClickListener> mSecondaryOnClickListeners = new ArrayList<>();
@NonNull private final Map<String, Float> mEntityConfidence = new ArrayMap<>();
@Nullable Drawable mPrimaryIcon;
@Nullable String mPrimaryLabel;
@@ -413,16 +485,14 @@ public final class TextClassification {
* <p><stong>Note: </stong> If all input parameters are set to null, this method will be a
* no-op.
*
- * @see #setPrimaryAction(Intent, String, Drawable, OnClickListener)
+ * @see #setPrimaryAction(Intent, String, Drawable)
*/
public Builder addSecondaryAction(
- @Nullable Intent intent, @Nullable String label, @Nullable Drawable icon,
- @Nullable OnClickListener onClickListener) {
- if (intent != null || label != null || icon != null || onClickListener != null) {
+ @Nullable Intent intent, @Nullable String label, @Nullable Drawable icon) {
+ if (intent != null || label != null || icon != null) {
mSecondaryIntents.add(intent);
mSecondaryLabels.add(label);
mSecondaryIcons.add(icon);
- mSecondaryOnClickListeners.add(onClickListener);
}
return this;
}
@@ -432,7 +502,6 @@ public final class TextClassification {
*/
public Builder clearSecondaryActions() {
mSecondaryIntents.clear();
- mSecondaryOnClickListeners.clear();
mSecondaryLabels.clear();
mSecondaryIcons.clear();
return this;
@@ -440,26 +509,23 @@ public final class TextClassification {
/**
* Sets the <i>primary</i> action that may be performed on the classified text. This is
- * equivalent to calling {@code
- * setIntent(intent).setLabel(label).setIcon(icon).setOnClickListener(onClickListener)}.
+ * equivalent to calling {@code setIntent(intent).setLabel(label).setIcon(icon)}.
*
* <p><strong>Note: </strong>If all input parameters are null, there will be no
* <i>primary</i> action but there may still be <i>secondary</i> actions.
*
- * @see #addSecondaryAction(Intent, String, Drawable, OnClickListener)
+ * @see #addSecondaryAction(Intent, String, Drawable)
*/
public Builder setPrimaryAction(
- @Nullable Intent intent, @Nullable String label, @Nullable Drawable icon,
- @Nullable OnClickListener onClickListener) {
- return setIntent(intent).setLabel(label).setIcon(icon)
- .setOnClickListener(onClickListener);
+ @Nullable Intent intent, @Nullable String label, @Nullable Drawable icon) {
+ return setIntent(intent).setLabel(label).setIcon(icon);
}
/**
* Sets the icon for the <i>primary</i> action that may be rendered on a widget used to act
* on the classified text.
*
- * @see #setPrimaryAction(Intent, String, Drawable, OnClickListener)
+ * @see #setPrimaryAction(Intent, String, Drawable)
*/
public Builder setIcon(@Nullable Drawable icon) {
mPrimaryIcon = icon;
@@ -470,7 +536,7 @@ public final class TextClassification {
* Sets the label for the <i>primary</i> action that may be rendered on a widget used to
* act on the classified text.
*
- * @see #setPrimaryAction(Intent, String, Drawable, OnClickListener)
+ * @see #setPrimaryAction(Intent, String, Drawable)
*/
public Builder setLabel(@Nullable String label) {
mPrimaryLabel = label;
@@ -481,7 +547,7 @@ public final class TextClassification {
* Sets the intent for the <i>primary</i> action that may be fired to act on the classified
* text.
*
- * @see #setPrimaryAction(Intent, String, Drawable, OnClickListener)
+ * @see #setPrimaryAction(Intent, String, Drawable)
*/
public Builder setIntent(@Nullable Intent intent) {
mPrimaryIntent = intent;
@@ -490,9 +556,8 @@ public final class TextClassification {
/**
* Sets the OnClickListener for the <i>primary</i> action that may be triggered to act on
- * the classified text.
- *
- * @see #setPrimaryAction(Intent, String, Drawable, OnClickListener)
+ * the classified text. This field is not parcelable and will always be null when the
+ * object is read from a parcel.
*/
public Builder setOnClickListener(@Nullable OnClickListener onClickListener) {
mPrimaryOnClickListener = onClickListener;
@@ -515,10 +580,8 @@ public final class TextClassification {
public TextClassification build() {
return new TextClassification(
mText,
- mPrimaryIcon, mPrimaryLabel,
- mPrimaryIntent, mPrimaryOnClickListener,
- mSecondaryIcons, mSecondaryLabels,
- mSecondaryIntents, mSecondaryOnClickListeners,
+ mPrimaryIcon, mPrimaryLabel, mPrimaryIntent, mPrimaryOnClickListener,
+ mSecondaryIcons, mSecondaryLabels, mSecondaryIntents,
mEntityConfidence, mSignature);
}
}
@@ -526,9 +589,11 @@ public final class TextClassification {
/**
* Optional input parameters for generating TextClassification.
*/
- public static final class Options {
+ public static final class Options implements Parcelable {
+
+ private @Nullable LocaleList mDefaultLocales;
- private LocaleList mDefaultLocales;
+ public Options() {}
/**
* @param defaultLocales ordered list of locale preferences that may be used to disambiguate
@@ -548,5 +613,80 @@ public final class TextClassification {
public LocaleList getDefaultLocales() {
return mDefaultLocales;
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mDefaultLocales != null ? 1 : 0);
+ if (mDefaultLocales != null) {
+ mDefaultLocales.writeToParcel(dest, flags);
+ }
+ }
+
+ public static final Parcelable.Creator<Options> CREATOR =
+ new Parcelable.Creator<Options>() {
+ @Override
+ public Options createFromParcel(Parcel in) {
+ return new Options(in);
+ }
+
+ @Override
+ public Options[] newArray(int size) {
+ return new Options[size];
+ }
+ };
+
+ private Options(Parcel in) {
+ if (in.readInt() > 0) {
+ mDefaultLocales = LocaleList.CREATOR.createFromParcel(in);
+ }
+ }
+ }
+
+ /**
+ * Parcelable wrapper for TextClassification objects.
+ * @hide
+ */
+ public static final class ParcelableWrapper implements Parcelable {
+
+ @NonNull private TextClassification mTextClassification;
+
+ public ParcelableWrapper(@NonNull TextClassification textClassification) {
+ Preconditions.checkNotNull(textClassification);
+ mTextClassification = textClassification;
+ }
+
+ @NonNull
+ public TextClassification getTextClassification() {
+ return mTextClassification;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ mTextClassification.writeToParcel(dest, flags);
+ }
+
+ public static final Parcelable.Creator<ParcelableWrapper> CREATOR =
+ new Parcelable.Creator<ParcelableWrapper>() {
+ @Override
+ public ParcelableWrapper createFromParcel(Parcel in) {
+ return new ParcelableWrapper(new TextClassification(in));
+ }
+
+ @Override
+ public ParcelableWrapper[] newArray(int size) {
+ return new ParcelableWrapper[size];
+ }
+ };
+
}
}
diff --git a/android/view/textclassifier/TextClassifier.java b/android/view/textclassifier/TextClassifier.java
index ed604303..e9715c51 100644
--- a/android/view/textclassifier/TextClassifier.java
+++ b/android/view/textclassifier/TextClassifier.java
@@ -23,6 +23,8 @@ import android.annotation.Nullable;
import android.annotation.StringDef;
import android.annotation.WorkerThread;
import android.os.LocaleList;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.ArraySet;
import com.android.internal.util.Preconditions;
@@ -275,8 +277,8 @@ public interface TextClassifier {
/**
* Returns a {@link Collection} of the entity types in the specified preset.
*
- * @see #ENTITIES_ALL
- * @see #ENTITIES_NONE
+ * @see #ENTITY_PRESET_ALL
+ * @see #ENTITY_PRESET_NONE
*/
default Collection<String> getEntitiesForPreset(@EntityPreset int entityPreset) {
return Collections.EMPTY_LIST;
@@ -305,7 +307,7 @@ public interface TextClassifier {
*
* Configs are initially based on a predefined preset, and can be modified from there.
*/
- final class EntityConfig {
+ final class EntityConfig implements Parcelable {
private final @TextClassifier.EntityPreset int mEntityPreset;
private final Collection<String> mExcludedEntityTypes;
private final Collection<String> mIncludedEntityTypes;
@@ -355,6 +357,37 @@ public interface TextClassifier {
}
return Collections.unmodifiableList(entities);
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mEntityPreset);
+ dest.writeStringList(new ArrayList<>(mExcludedEntityTypes));
+ dest.writeStringList(new ArrayList<>(mIncludedEntityTypes));
+ }
+
+ public static final Parcelable.Creator<EntityConfig> CREATOR =
+ new Parcelable.Creator<EntityConfig>() {
+ @Override
+ public EntityConfig createFromParcel(Parcel in) {
+ return new EntityConfig(in);
+ }
+
+ @Override
+ public EntityConfig[] newArray(int size) {
+ return new EntityConfig[size];
+ }
+ };
+
+ private EntityConfig(Parcel in) {
+ mEntityPreset = in.readInt();
+ mExcludedEntityTypes = new ArraySet<>(in.createStringArrayList());
+ mIncludedEntityTypes = new ArraySet<>(in.createStringArrayList());
+ }
}
/**
diff --git a/android/view/textclassifier/TextClassifierConstants.java b/android/view/textclassifier/TextClassifierConstants.java
index 51e6168e..00695b79 100644
--- a/android/view/textclassifier/TextClassifierConstants.java
+++ b/android/view/textclassifier/TextClassifierConstants.java
@@ -45,19 +45,24 @@ public final class TextClassifierConstants {
"smart_selection_dark_launch";
private static final String SMART_SELECTION_ENABLED_FOR_EDIT_TEXT =
"smart_selection_enabled_for_edit_text";
+ private static final String SMART_LINKIFY_ENABLED =
+ "smart_linkify_enabled";
private static final boolean SMART_SELECTION_DARK_LAUNCH_DEFAULT = false;
private static final boolean SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT = true;
+ private static final boolean SMART_LINKIFY_ENABLED_DEFAULT = true;
/** Default settings. */
static final TextClassifierConstants DEFAULT = new TextClassifierConstants();
private final boolean mDarkLaunch;
private final boolean mSuggestSelectionEnabledForEditableText;
+ private final boolean mSmartLinkifyEnabled;
private TextClassifierConstants() {
mDarkLaunch = SMART_SELECTION_DARK_LAUNCH_DEFAULT;
mSuggestSelectionEnabledForEditableText = SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT;
+ mSmartLinkifyEnabled = SMART_LINKIFY_ENABLED_DEFAULT;
}
private TextClassifierConstants(@Nullable String settings) {
@@ -74,6 +79,9 @@ public final class TextClassifierConstants {
mSuggestSelectionEnabledForEditableText = parser.getBoolean(
SMART_SELECTION_ENABLED_FOR_EDIT_TEXT,
SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT);
+ mSmartLinkifyEnabled = parser.getBoolean(
+ SMART_LINKIFY_ENABLED,
+ SMART_LINKIFY_ENABLED_DEFAULT);
}
static TextClassifierConstants loadFromString(String settings) {
@@ -87,4 +95,8 @@ public final class TextClassifierConstants {
public boolean isSuggestSelectionEnabledForEditableText() {
return mSuggestSelectionEnabledForEditableText;
}
+
+ public boolean isSmartLinkifyEnabled() {
+ return mSmartLinkifyEnabled;
+ }
}
diff --git a/android/view/textclassifier/TextClassifierImpl.java b/android/view/textclassifier/TextClassifierImpl.java
index aea3cb06..7db0e76d 100644
--- a/android/view/textclassifier/TextClassifierImpl.java
+++ b/android/view/textclassifier/TextClassifierImpl.java
@@ -32,7 +32,6 @@ import android.provider.ContactsContract;
import android.provider.Settings;
import android.text.util.Linkify;
import android.util.Patterns;
-import android.view.View.OnClickListener;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
@@ -187,6 +186,11 @@ final class TextClassifierImpl implements TextClassifier {
Utils.validateInput(text);
final String textString = text.toString();
final TextLinks.Builder builder = new TextLinks.Builder(textString);
+
+ if (!getSettings().isSmartLinkifyEnabled()) {
+ return builder.build();
+ }
+
try {
final LocaleList defaultLocales = options != null ? options.getDefaultLocales() : null;
final Collection<String> entitiesToIdentify =
@@ -457,12 +461,10 @@ final class TextClassifierImpl implements TextClassifier {
}
}
final String labelString = (label != null) ? label.toString() : null;
- final OnClickListener onClickListener =
- TextClassification.createStartActivityOnClickListener(mContext, intent);
if (i == 0) {
- builder.setPrimaryAction(intent, labelString, icon, onClickListener);
+ builder.setPrimaryAction(intent, labelString, icon);
} else {
- builder.addSecondaryAction(intent, labelString, icon, onClickListener);
+ builder.addSecondaryAction(intent, labelString, icon);
}
}
}
diff --git a/android/view/textclassifier/TextLinks.java b/android/view/textclassifier/TextLinks.java
index 6c587cf9..ba854e04 100644
--- a/android/view/textclassifier/TextLinks.java
+++ b/android/view/textclassifier/TextLinks.java
@@ -20,6 +20,8 @@ import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.LocaleList;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.text.SpannableString;
import android.text.style.ClickableSpan;
import android.view.View;
@@ -38,7 +40,7 @@ import java.util.function.Function;
* A collection of links, representing subsequences of text and the entity types (phone number,
* address, url, etc) they may be.
*/
-public final class TextLinks {
+public final class TextLinks implements Parcelable {
private final String mFullText;
private final List<TextLink> mLinks;
@@ -83,11 +85,40 @@ public final class TextLinks {
return true;
}
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mFullText);
+ dest.writeTypedList(mLinks);
+ }
+
+ public static final Parcelable.Creator<TextLinks> CREATOR =
+ new Parcelable.Creator<TextLinks>() {
+ @Override
+ public TextLinks createFromParcel(Parcel in) {
+ return new TextLinks(in);
+ }
+
+ @Override
+ public TextLinks[] newArray(int size) {
+ return new TextLinks[size];
+ }
+ };
+
+ private TextLinks(Parcel in) {
+ mFullText = in.readString();
+ mLinks = in.createTypedArrayList(TextLink.CREATOR);
+ }
+
/**
* A link, identifying a substring of text and possible entity types for it.
*/
- public static final class TextLink {
- private final EntityConfidence<String> mEntityScores;
+ public static final class TextLink implements Parcelable {
+ private final EntityConfidence mEntityScores;
private final String mOriginalText;
private final int mStart;
private final int mEnd;
@@ -105,7 +136,7 @@ public final class TextLinks {
mOriginalText = originalText;
mStart = start;
mEnd = end;
- mEntityScores = new EntityConfidence<>(entityScores);
+ mEntityScores = new EntityConfidence(entityScores);
}
/**
@@ -153,16 +184,51 @@ public final class TextLinks {
@TextClassifier.EntityType String entityType) {
return mEntityScores.getConfidenceScore(entityType);
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ mEntityScores.writeToParcel(dest, flags);
+ dest.writeString(mOriginalText);
+ dest.writeInt(mStart);
+ dest.writeInt(mEnd);
+ }
+
+ public static final Parcelable.Creator<TextLink> CREATOR =
+ new Parcelable.Creator<TextLink>() {
+ @Override
+ public TextLink createFromParcel(Parcel in) {
+ return new TextLink(in);
+ }
+
+ @Override
+ public TextLink[] newArray(int size) {
+ return new TextLink[size];
+ }
+ };
+
+ private TextLink(Parcel in) {
+ mEntityScores = EntityConfidence.CREATOR.createFromParcel(in);
+ mOriginalText = in.readString();
+ mStart = in.readInt();
+ mEnd = in.readInt();
+ }
}
/**
* Optional input parameters for generating TextLinks.
*/
- public static final class Options {
+ public static final class Options implements Parcelable {
private LocaleList mDefaultLocales;
private TextClassifier.EntityConfig mEntityConfig;
+ public Options() {}
+
/**
* @param defaultLocales ordered list of locale preferences that may be used to
* disambiguate the provided text. If no locale preferences exist,
@@ -201,6 +267,45 @@ public final class TextLinks {
public TextClassifier.EntityConfig getEntityConfig() {
return mEntityConfig;
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mDefaultLocales != null ? 1 : 0);
+ if (mDefaultLocales != null) {
+ mDefaultLocales.writeToParcel(dest, flags);
+ }
+ dest.writeInt(mEntityConfig != null ? 1 : 0);
+ if (mEntityConfig != null) {
+ mEntityConfig.writeToParcel(dest, flags);
+ }
+ }
+
+ public static final Parcelable.Creator<Options> CREATOR =
+ new Parcelable.Creator<Options>() {
+ @Override
+ public Options createFromParcel(Parcel in) {
+ return new Options(in);
+ }
+
+ @Override
+ public Options[] newArray(int size) {
+ return new Options[size];
+ }
+ };
+
+ private Options(Parcel in) {
+ if (in.readInt() > 0) {
+ mDefaultLocales = LocaleList.CREATOR.createFromParcel(in);
+ }
+ if (in.readInt() > 0) {
+ mEntityConfig = TextClassifier.EntityConfig.CREATOR.createFromParcel(in);
+ }
+ }
}
/**
diff --git a/android/view/textclassifier/TextSelection.java b/android/view/textclassifier/TextSelection.java
index 25e9e7ec..774d42db 100644
--- a/android/view/textclassifier/TextSelection.java
+++ b/android/view/textclassifier/TextSelection.java
@@ -21,6 +21,8 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.LocaleList;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.ArrayMap;
import android.view.textclassifier.TextClassifier.EntityType;
@@ -36,7 +38,7 @@ public final class TextSelection {
private final int mStartIndex;
private final int mEndIndex;
- @NonNull private final EntityConfidence<String> mEntityConfidence;
+ @NonNull private final EntityConfidence mEntityConfidence;
@NonNull private final String mSignature;
private TextSelection(
@@ -44,7 +46,7 @@ public final class TextSelection {
@NonNull String signature) {
mStartIndex = startIndex;
mEndIndex = endIndex;
- mEntityConfidence = new EntityConfidence<>(entityConfidence);
+ mEntityConfidence = new EntityConfidence(entityConfidence);
mSignature = signature;
}
@@ -110,6 +112,22 @@ public final class TextSelection {
mStartIndex, mEndIndex, mEntityConfidence, mSignature);
}
+ /** Helper for parceling via #ParcelableWrapper. */
+ private void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mStartIndex);
+ dest.writeInt(mEndIndex);
+ mEntityConfidence.writeToParcel(dest, flags);
+ dest.writeString(mSignature);
+ }
+
+ /** Helper for unparceling via #ParcelableWrapper. */
+ private TextSelection(Parcel in) {
+ mStartIndex = in.readInt();
+ mEndIndex = in.readInt();
+ mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
+ mSignature = in.readString();
+ }
+
/**
* Builder used to build {@link TextSelection} objects.
*/
@@ -170,11 +188,13 @@ public final class TextSelection {
/**
* Optional input parameters for generating TextSelection.
*/
- public static final class Options {
+ public static final class Options implements Parcelable {
- private LocaleList mDefaultLocales;
+ private @Nullable LocaleList mDefaultLocales;
private boolean mDarkLaunchAllowed;
+ public Options() {}
+
/**
* @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
@@ -216,5 +236,82 @@ public final class TextSelection {
public boolean isDarkLaunchAllowed() {
return mDarkLaunchAllowed;
}
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mDefaultLocales != null ? 1 : 0);
+ if (mDefaultLocales != null) {
+ mDefaultLocales.writeToParcel(dest, flags);
+ }
+ dest.writeInt(mDarkLaunchAllowed ? 1 : 0);
+ }
+
+ public static final Parcelable.Creator<Options> CREATOR =
+ new Parcelable.Creator<Options>() {
+ @Override
+ public Options createFromParcel(Parcel in) {
+ return new Options(in);
+ }
+
+ @Override
+ public Options[] newArray(int size) {
+ return new Options[size];
+ }
+ };
+
+ private Options(Parcel in) {
+ if (in.readInt() > 0) {
+ mDefaultLocales = LocaleList.CREATOR.createFromParcel(in);
+ }
+ mDarkLaunchAllowed = in.readInt() != 0;
+ }
+ }
+
+ /**
+ * Parcelable wrapper for TextSelection objects.
+ * @hide
+ */
+ public static final class ParcelableWrapper implements Parcelable {
+
+ @NonNull private TextSelection mTextSelection;
+
+ public ParcelableWrapper(@NonNull TextSelection textSelection) {
+ Preconditions.checkNotNull(textSelection);
+ mTextSelection = textSelection;
+ }
+
+ @NonNull
+ public TextSelection getTextSelection() {
+ return mTextSelection;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ mTextSelection.writeToParcel(dest, flags);
+ }
+
+ public static final Parcelable.Creator<ParcelableWrapper> CREATOR =
+ new Parcelable.Creator<ParcelableWrapper>() {
+ @Override
+ public ParcelableWrapper createFromParcel(Parcel in) {
+ return new ParcelableWrapper(new TextSelection(in));
+ }
+
+ @Override
+ public ParcelableWrapper[] newArray(int size) {
+ return new ParcelableWrapper[size];
+ }
+ };
+
}
}