Merge "Replace Entity Presets with hints and an explicit factory."

This commit is contained in:
Richard Ledley
2018-02-21 09:33:05 +00:00
committed by Android (Google) Code Review
6 changed files with 119 additions and 113 deletions

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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() {}

View File

@@ -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;

View File

@@ -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()));
}
}