diff options
author | Jeff Davidson <jpd@google.com> | 2018-02-08 15:30:06 -0800 |
---|---|---|
committer | Jeff Davidson <jpd@google.com> | 2018-02-08 15:30:06 -0800 |
commit | a192cc2a132cb0ee8588e2df755563ec7008c179 (patch) | |
tree | 380e4db22df19c819bd37df34bf06e7568916a50 /android/view/textclassifier | |
parent | 98fe7819c6d14f4f464a5cac047f9e82dee5da58 (diff) | |
download | android-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.java | 76 | ||||
-rw-r--r-- | android/view/textclassifier/TextClassification.java | 278 | ||||
-rw-r--r-- | android/view/textclassifier/TextClassifier.java | 39 | ||||
-rw-r--r-- | android/view/textclassifier/TextClassifierConstants.java | 12 | ||||
-rw-r--r-- | android/view/textclassifier/TextClassifierImpl.java | 12 | ||||
-rw-r--r-- | android/view/textclassifier/TextLinks.java | 115 | ||||
-rw-r--r-- | android/view/textclassifier/TextSelection.java | 105 |
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]; + } + }; + } } |