Merge "Replace Entity Presets with hints and an explicit factory."
This commit is contained in:
committed by
Android (Google) Code Review
commit
d173506597
@@ -50293,15 +50293,13 @@ package android.view.textclassifier {
|
||||
method public default android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int, android.os.LocaleList);
|
||||
method public default android.view.textclassifier.TextLinks generateLinks(java.lang.CharSequence, android.view.textclassifier.TextLinks.Options);
|
||||
method public default android.view.textclassifier.TextLinks generateLinks(java.lang.CharSequence);
|
||||
method public default java.util.Collection<java.lang.String> getEntitiesForPreset(int);
|
||||
method public default android.view.textclassifier.logging.Logger getLogger(android.view.textclassifier.logging.Logger.Config);
|
||||
method public default int getMaxGenerateLinksTextLength();
|
||||
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);
|
||||
method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList);
|
||||
field public static final int ENTITY_PRESET_ALL = 0; // 0x0
|
||||
field public static final int ENTITY_PRESET_BASE = 2; // 0x2
|
||||
field public static final int ENTITY_PRESET_NONE = 1; // 0x1
|
||||
field public static final java.lang.String HINT_TEXT_IS_EDITABLE = "android.text_is_editable";
|
||||
field public static final java.lang.String HINT_TEXT_IS_NOT_EDITABLE = "android.text_is_not_editable";
|
||||
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_DATE = "date";
|
||||
@@ -50315,11 +50313,12 @@ package android.view.textclassifier {
|
||||
}
|
||||
|
||||
public static final class TextClassifier.EntityConfig implements android.os.Parcelable {
|
||||
ctor public TextClassifier.EntityConfig(int);
|
||||
method public static android.view.textclassifier.TextClassifier.EntityConfig create(java.util.Collection<java.lang.String>);
|
||||
method public static android.view.textclassifier.TextClassifier.EntityConfig create(java.util.Collection<java.lang.String>, java.util.Collection<java.lang.String>, java.util.Collection<java.lang.String>);
|
||||
method public static android.view.textclassifier.TextClassifier.EntityConfig createWithEntityList(java.util.Collection<java.lang.String>);
|
||||
method public int describeContents();
|
||||
method public android.view.textclassifier.TextClassifier.EntityConfig excludeEntities(java.lang.String...);
|
||||
method public java.util.List<java.lang.String> getEntities(android.view.textclassifier.TextClassifier);
|
||||
method public android.view.textclassifier.TextClassifier.EntityConfig includeEntities(java.lang.String...);
|
||||
method public java.util.Collection<java.lang.String> getHints();
|
||||
method public java.util.List<java.lang.String> resolveEntityListModifications(java.util.Collection<java.lang.String>);
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifier.EntityConfig> CREATOR;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package android.view.textclassifier;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
@@ -86,19 +85,15 @@ public interface TextClassifier {
|
||||
})
|
||||
@interface EntityType {}
|
||||
|
||||
/** Designates that the TextClassifier should identify all entity types it can. **/
|
||||
int ENTITY_PRESET_ALL = 0;
|
||||
/** Designates that the TextClassifier should identify no entities. **/
|
||||
int ENTITY_PRESET_NONE = 1;
|
||||
/** Designates that the TextClassifier should identify a base set of entities determined by the
|
||||
* TextClassifier. **/
|
||||
int ENTITY_PRESET_BASE = 2;
|
||||
/** Designates that the text in question is editable. **/
|
||||
String HINT_TEXT_IS_EDITABLE = "android.text_is_editable";
|
||||
/** Designates that the text in question is not editable. **/
|
||||
String HINT_TEXT_IS_NOT_EDITABLE = "android.text_is_not_editable";
|
||||
|
||||
/** @hide */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(prefix = { "ENTITY_CONFIG_" },
|
||||
value = {ENTITY_PRESET_ALL, ENTITY_PRESET_NONE, ENTITY_PRESET_BASE})
|
||||
@interface EntityPreset {}
|
||||
@StringDef(prefix = { "HINT_" }, value = {HINT_TEXT_IS_EDITABLE, HINT_TEXT_IS_NOT_EDITABLE})
|
||||
@interface Hints {}
|
||||
|
||||
/**
|
||||
* No-op TextClassifier.
|
||||
@@ -322,17 +317,6 @@ public interface TextClassifier {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Collection} of the entity types in the specified preset.
|
||||
*
|
||||
* @see #ENTITY_PRESET_ALL
|
||||
* @see #ENTITY_PRESET_NONE
|
||||
* @see #ENTITY_PRESET_BASE
|
||||
*/
|
||||
default Collection<String> getEntitiesForPreset(@EntityPreset int entityPreset) {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a helper for logging TextClassifier related events.
|
||||
*
|
||||
@@ -358,54 +342,92 @@ public interface TextClassifier {
|
||||
* Configs are initially based on a predefined preset, and can be modified from there.
|
||||
*/
|
||||
final class EntityConfig implements Parcelable {
|
||||
private final @TextClassifier.EntityPreset int mEntityPreset;
|
||||
private final Collection<String> mHints;
|
||||
private final Collection<String> mExcludedEntityTypes;
|
||||
private final Collection<String> mIncludedEntityTypes;
|
||||
private final boolean mUseHints;
|
||||
|
||||
public EntityConfig(@TextClassifier.EntityPreset int mEntityPreset) {
|
||||
this.mEntityPreset = mEntityPreset;
|
||||
mExcludedEntityTypes = new ArraySet<>();
|
||||
mIncludedEntityTypes = new ArraySet<>();
|
||||
private EntityConfig(boolean useHints, Collection<String> hints,
|
||||
Collection<String> includedEntityTypes, Collection<String> excludedEntityTypes) {
|
||||
mHints = hints == null
|
||||
? Collections.EMPTY_LIST
|
||||
: Collections.unmodifiableCollection(new ArraySet<>(hints));
|
||||
mExcludedEntityTypes = excludedEntityTypes == null
|
||||
? Collections.EMPTY_LIST : new ArraySet<>(excludedEntityTypes);
|
||||
mIncludedEntityTypes = includedEntityTypes == null
|
||||
? Collections.EMPTY_LIST : new ArraySet<>(includedEntityTypes);
|
||||
mUseHints = useHints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies an entity to include in addition to any specified by the enity preset.
|
||||
* Creates an EntityConfig.
|
||||
*
|
||||
* @param hints Hints for the TextClassifier to determine what types of entities to find.
|
||||
*/
|
||||
public static EntityConfig create(@Nullable Collection<String> hints) {
|
||||
return new EntityConfig(/* useHints */ true, hints,
|
||||
/* includedEntityTypes */null, /* excludedEntityTypes */ null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an EntityConfig.
|
||||
*
|
||||
* @param hints Hints for the TextClassifier to determine what types of entities to find
|
||||
* @param includedEntityTypes Entity types, e.g. {@link #TYPE_EMAIL}, to explicitly include
|
||||
* @param excludedEntityTypes Entity types, e.g. {@link #TYPE_PHONE}, to explicitly exclude
|
||||
*
|
||||
*
|
||||
* Note that if an entity has been excluded, the exclusion will take precedence.
|
||||
*/
|
||||
public EntityConfig includeEntities(String... entities) {
|
||||
for (String entity : entities) {
|
||||
mIncludedEntityTypes.add(entity);
|
||||
}
|
||||
return this;
|
||||
public static EntityConfig create(@Nullable Collection<String> hints,
|
||||
@Nullable Collection<String> includedEntityTypes,
|
||||
@Nullable Collection<String> excludedEntityTypes) {
|
||||
return new EntityConfig(/* useHints */ true, hints,
|
||||
includedEntityTypes, excludedEntityTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies an entity to be excluded.
|
||||
* Creates an EntityConfig with an explicit entity list.
|
||||
*
|
||||
* @param entityTypes Complete set of entities, e.g. {@link #TYPE_URL} to find.
|
||||
*
|
||||
*/
|
||||
public EntityConfig excludeEntities(String... entities) {
|
||||
for (String entity : entities) {
|
||||
mExcludedEntityTypes.add(entity);
|
||||
}
|
||||
return this;
|
||||
public static EntityConfig createWithEntityList(@Nullable Collection<String> entityTypes) {
|
||||
return new EntityConfig(/* useHints */ false, /* hints */ null,
|
||||
/* includedEntityTypes */ entityTypes, /* excludedEntityTypes */ null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable list of the final set of entities to find.
|
||||
* Returns a list of the final set of entities to find.
|
||||
*
|
||||
* @param entities Entities we think should be found before factoring in includes/excludes
|
||||
*
|
||||
* This method is intended for use by TextClassifier implementations.
|
||||
*/
|
||||
public List<String> getEntities(TextClassifier textClassifier) {
|
||||
ArrayList<String> entities = new ArrayList<>();
|
||||
for (String entity : textClassifier.getEntitiesForPreset(mEntityPreset)) {
|
||||
if (!mExcludedEntityTypes.contains(entity)) {
|
||||
entities.add(entity);
|
||||
public List<String> resolveEntityListModifications(@NonNull Collection<String> entities) {
|
||||
final ArrayList<String> finalList = new ArrayList<>();
|
||||
if (mUseHints) {
|
||||
for (String entity : entities) {
|
||||
if (!mExcludedEntityTypes.contains(entity)) {
|
||||
finalList.add(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String entity : mIncludedEntityTypes) {
|
||||
if (!mExcludedEntityTypes.contains(entity) && !entities.contains(entity)) {
|
||||
entities.add(entity);
|
||||
if (!mExcludedEntityTypes.contains(entity) && !finalList.contains(entity)) {
|
||||
finalList.add(entity);
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableList(entities);
|
||||
return finalList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of hints.
|
||||
*
|
||||
* @return An unmodifiable collection of the hints.
|
||||
*/
|
||||
public Collection<String> getHints() {
|
||||
return mHints;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -415,9 +437,10 @@ public interface TextClassifier {
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(mEntityPreset);
|
||||
dest.writeStringList(new ArrayList<>(mHints));
|
||||
dest.writeStringList(new ArrayList<>(mExcludedEntityTypes));
|
||||
dest.writeStringList(new ArrayList<>(mIncludedEntityTypes));
|
||||
dest.writeInt(mUseHints ? 1 : 0);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<EntityConfig> CREATOR =
|
||||
@@ -434,9 +457,10 @@ public interface TextClassifier {
|
||||
};
|
||||
|
||||
private EntityConfig(Parcel in) {
|
||||
mEntityPreset = in.readInt();
|
||||
mHints = new ArraySet<>(in.createStringArrayList());
|
||||
mExcludedEntityTypes = new ArraySet<>(in.createStringArrayList());
|
||||
mIncludedEntityTypes = new ArraySet<>(in.createStringArrayList());
|
||||
mUseHints = in.readInt() == 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,12 +90,6 @@ public final class TextClassifierImpl implements TextClassifier {
|
||||
TextClassifier.TYPE_DATE,
|
||||
TextClassifier.TYPE_DATE_TIME,
|
||||
TextClassifier.TYPE_FLIGHT_NUMBER));
|
||||
private static final List<String> ENTITY_TYPES_BASE =
|
||||
Collections.unmodifiableList(Arrays.asList(
|
||||
TextClassifier.TYPE_ADDRESS,
|
||||
TextClassifier.TYPE_EMAIL,
|
||||
TextClassifier.TYPE_PHONE,
|
||||
TextClassifier.TYPE_URL));
|
||||
|
||||
private final Context mContext;
|
||||
private final TextClassifier mFallback;
|
||||
@@ -224,7 +218,9 @@ public final class TextClassifierImpl implements TextClassifier {
|
||||
final LocaleList defaultLocales = options != null ? options.getDefaultLocales() : null;
|
||||
final Collection<String> entitiesToIdentify =
|
||||
options != null && options.getEntityConfig() != null
|
||||
? options.getEntityConfig().getEntities(this) : ENTITY_TYPES_ALL;
|
||||
? options.getEntityConfig().resolveEntityListModifications(
|
||||
getEntitiesForHints(options.getEntityConfig().getHints()))
|
||||
: ENTITY_TYPES_ALL;
|
||||
final SmartSelection smartSelection = getSmartSelection(defaultLocales);
|
||||
final SmartSelection.AnnotatedSpan[] annotations = smartSelection.annotate(textString);
|
||||
for (SmartSelection.AnnotatedSpan span : annotations) {
|
||||
@@ -252,18 +248,8 @@ public final class TextClassifierImpl implements TextClassifier {
|
||||
return getSettings().getGenerateLinksMaxTextLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getEntitiesForPreset(@TextClassifier.EntityPreset int entityPreset) {
|
||||
switch (entityPreset) {
|
||||
case TextClassifier.ENTITY_PRESET_NONE:
|
||||
return Collections.emptyList();
|
||||
case TextClassifier.ENTITY_PRESET_BASE:
|
||||
return ENTITY_TYPES_BASE;
|
||||
case TextClassifier.ENTITY_PRESET_ALL:
|
||||
// fall through
|
||||
default:
|
||||
return ENTITY_TYPES_ALL;
|
||||
}
|
||||
private Collection<String> getEntitiesForHints(Collection<String> hints) {
|
||||
return ENTITY_TYPES_ALL;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -309,23 +309,23 @@ public final class TextLinks implements Parcelable {
|
||||
* Returns a new options object based on the specified link mask.
|
||||
*/
|
||||
public static Options fromLinkMask(@LinkifyMask int mask) {
|
||||
final TextClassifier.EntityConfig entityConfig =
|
||||
new TextClassifier.EntityConfig(TextClassifier.ENTITY_PRESET_NONE);
|
||||
final List<String> entitiesToFind = new ArrayList<>();
|
||||
|
||||
if ((mask & Linkify.WEB_URLS) != 0) {
|
||||
entityConfig.includeEntities(TextClassifier.TYPE_URL);
|
||||
entitiesToFind.add(TextClassifier.TYPE_URL);
|
||||
}
|
||||
if ((mask & Linkify.EMAIL_ADDRESSES) != 0) {
|
||||
entityConfig.includeEntities(TextClassifier.TYPE_EMAIL);
|
||||
entitiesToFind.add(TextClassifier.TYPE_EMAIL);
|
||||
}
|
||||
if ((mask & Linkify.PHONE_NUMBERS) != 0) {
|
||||
entityConfig.includeEntities(TextClassifier.TYPE_PHONE);
|
||||
entitiesToFind.add(TextClassifier.TYPE_PHONE);
|
||||
}
|
||||
if ((mask & Linkify.MAP_ADDRESSES) != 0) {
|
||||
entityConfig.includeEntities(TextClassifier.TYPE_ADDRESS);
|
||||
entitiesToFind.add(TextClassifier.TYPE_ADDRESS);
|
||||
}
|
||||
|
||||
return new Options().setEntityConfig(entityConfig);
|
||||
return new Options().setEntityConfig(
|
||||
TextClassifier.EntityConfig.createWithEntityList(entitiesToFind));
|
||||
}
|
||||
|
||||
public Options() {}
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@@ -192,41 +193,33 @@ public class TextClassificationManagerTest {
|
||||
if (isTextClassifierDisabled()) return;
|
||||
String text = "The number is +12122537077. See you tonight!";
|
||||
assertThat(mClassifier.generateLinks(text, mLinksOptions.setEntityConfig(
|
||||
new TextClassifier.EntityConfig(TextClassifier.ENTITY_PRESET_ALL)
|
||||
.excludeEntities(TextClassifier.TYPE_PHONE))),
|
||||
TextClassifier.EntityConfig.create(Collections.EMPTY_LIST,
|
||||
Collections.EMPTY_LIST, Arrays.asList(TextClassifier.TYPE_PHONE)))),
|
||||
not(isTextLinksContaining(text, "+12122537077", TextClassifier.TYPE_PHONE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateLinks_none_config() {
|
||||
public void testGenerateLinks_explicit_address() {
|
||||
if (isTextClassifierDisabled()) return;
|
||||
String text = "The address is 1600 Amphitheater Parkway, Mountain View, CA. See you!";
|
||||
assertThat(mClassifier.generateLinks(text, mLinksOptions.setEntityConfig(
|
||||
TextClassifier.EntityConfig.createWithEntityList(
|
||||
Arrays.asList(TextClassifier.TYPE_ADDRESS)))),
|
||||
isTextLinksContaining(text, "1600 Amphitheater Parkway, Mountain View, CA",
|
||||
TextClassifier.TYPE_ADDRESS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateLinks_exclude_override() {
|
||||
if (isTextClassifierDisabled()) return;
|
||||
String text = "The number is +12122537077. See you tonight!";
|
||||
assertThat(mClassifier.generateLinks(text, mLinksOptions.setEntityConfig(
|
||||
new TextClassifier.EntityConfig(TextClassifier.ENTITY_PRESET_NONE))),
|
||||
TextClassifier.EntityConfig.create(Collections.EMPTY_LIST,
|
||||
Arrays.asList(TextClassifier.TYPE_PHONE),
|
||||
Arrays.asList(TextClassifier.TYPE_PHONE)))),
|
||||
not(isTextLinksContaining(text, "+12122537077", TextClassifier.TYPE_PHONE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateLinks_address() {
|
||||
if (isTextClassifierDisabled()) return;
|
||||
String text = "The address is 1600 Amphitheater Parkway, Mountain View, CA. See you!";
|
||||
assertThat(mClassifier.generateLinks(text, null),
|
||||
isTextLinksContaining(text, "1600 Amphitheater Parkway, Mountain View, CA",
|
||||
TextClassifier.TYPE_ADDRESS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateLinks_include() {
|
||||
if (isTextClassifierDisabled()) return;
|
||||
String text = "The address is 1600 Amphitheater Parkway, Mountain View, CA. See you!";
|
||||
assertThat(mClassifier.generateLinks(text, mLinksOptions.setEntityConfig(
|
||||
new TextClassifier.EntityConfig(TextClassifier.ENTITY_PRESET_NONE)
|
||||
.includeEntities(TextClassifier.TYPE_ADDRESS))),
|
||||
isTextLinksContaining(text, "1600 Amphitheater Parkway, Mountain View, CA",
|
||||
TextClassifier.TYPE_ADDRESS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateLinks_maxLength() {
|
||||
if (isTextClassifierDisabled()) return;
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@@ -98,10 +99,10 @@ public class TextLinksTest {
|
||||
|
||||
@Test
|
||||
public void testParcelOptions() {
|
||||
TextClassifier.EntityConfig entityConfig = new TextClassifier.EntityConfig(
|
||||
TextClassifier.ENTITY_PRESET_NONE);
|
||||
entityConfig.includeEntities("a", "b", "c");
|
||||
entityConfig.excludeEntities("b");
|
||||
TextClassifier.EntityConfig entityConfig = TextClassifier.EntityConfig.create(
|
||||
Arrays.asList(TextClassifier.HINT_TEXT_IS_EDITABLE),
|
||||
Arrays.asList("a", "b", "c"),
|
||||
Arrays.asList("b"));
|
||||
TextLinks.Options reference = new TextLinks.Options();
|
||||
reference.setDefaultLocales(new LocaleList(Locale.US, Locale.GERMANY));
|
||||
reference.setEntityConfig(entityConfig);
|
||||
@@ -113,6 +114,9 @@ public class TextLinksTest {
|
||||
TextLinks.Options result = TextLinks.Options.CREATOR.createFromParcel(parcel);
|
||||
|
||||
assertEquals("en-US,de-DE", result.getDefaultLocales().toLanguageTags());
|
||||
assertEquals(Arrays.asList("a", "c"), result.getEntityConfig().getEntities(mClassifier));
|
||||
assertEquals(new String[]{TextClassifier.HINT_TEXT_IS_EDITABLE},
|
||||
result.getEntityConfig().getHints().toArray());
|
||||
assertEquals(Arrays.asList("a", "c"),
|
||||
result.getEntityConfig().resolveEntityListModifications(Collections.emptyList()));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user