Loading api/current.txt +13 −0 Original line number Diff line number Diff line Loading @@ -49119,9 +49119,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.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 android.view.textclassifier.TextClassifier NO_OP; field public static final java.lang.String TYPE_ADDRESS = "address"; field public static final java.lang.String TYPE_EMAIL = "email"; Loading @@ -49131,6 +49135,13 @@ package android.view.textclassifier { field public static final java.lang.String TYPE_URL = "url"; } public static final class TextClassifier.EntityConfig { ctor public TextClassifier.EntityConfig(int); 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...); } public final class TextLinks { method public boolean apply(android.text.SpannableString, java.util.function.Function<android.view.textclassifier.TextLinks.TextLink, android.text.style.ClickableSpan>); method public java.util.Collection<android.view.textclassifier.TextLinks.TextLink> getLinks(); Loading @@ -49145,7 +49156,9 @@ package android.view.textclassifier { public static final class TextLinks.Options { ctor public TextLinks.Options(); method public android.os.LocaleList getDefaultLocales(); method public android.view.textclassifier.TextClassifier.EntityConfig getEntityConfig(); method public android.view.textclassifier.TextLinks.Options setDefaultLocales(android.os.LocaleList); method public android.view.textclassifier.TextLinks.Options setEntityConfig(android.view.textclassifier.TextClassifier.EntityConfig); } public static final class TextLinks.TextLink { core/java/android/view/textclassifier/TextClassifier.java +88 −0 Original line number Diff line number Diff line Loading @@ -16,17 +16,23 @@ package android.view.textclassifier; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; import android.annotation.WorkerThread; import android.os.LocaleList; import android.util.ArraySet; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; /** * Interface for providing text classification related features. Loading Loading @@ -58,6 +64,20 @@ 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; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "ENTITY_CONFIG_" }, value = {ENTITY_PRESET_ALL, ENTITY_PRESET_NONE, ENTITY_PRESET_BASE}) @interface EntityPreset {} /** * No-op TextClassifier. * This may be used to turn off TextClassifier features. Loading Loading @@ -217,6 +237,8 @@ public interface TextClassifier { * Returns a {@link TextLinks} that may be applied to the text to annotate it with links * information. * * If no options are supplied, default values will be used, determined by the TextClassifier. * * @param text the text to generate annotations for * @param options configuration for link generation * Loading Loading @@ -250,6 +272,16 @@ public interface TextClassifier { return generateLinks(text, null); } /** * Returns a {@link Collection} of the entity types in the specified preset. * * @see #ENTITIES_ALL * @see #ENTITIES_NONE */ default Collection<String> getEntitiesForPreset(@EntityPreset int entityPreset) { return Collections.EMPTY_LIST; } /** * Logs a TextClassifier event. * Loading @@ -268,6 +300,62 @@ public interface TextClassifier { return TextClassifierConstants.DEFAULT; } /** * Configuration object for specifying what entities to identify. * * Configs are initially based on a predefined preset, and can be modified from there. */ final class EntityConfig { private final @TextClassifier.EntityPreset int mEntityPreset; private final Collection<String> mExcludedEntityTypes; private final Collection<String> mIncludedEntityTypes; public EntityConfig(@TextClassifier.EntityPreset int mEntityPreset) { this.mEntityPreset = mEntityPreset; mExcludedEntityTypes = new ArraySet<>(); mIncludedEntityTypes = new ArraySet<>(); } /** * Specifies an entity to include in addition to any specified by the enity preset. * * 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; } /** * Specifies an entity to be excluded. */ public EntityConfig excludeEntities(String... entities) { for (String entity : entities) { mExcludedEntityTypes.add(entity); } return this; } /** * Returns an unmodifiable list of the final set of entities to find. */ public List<String> getEntities(TextClassifier textClassifier) { ArrayList<String> entities = new ArrayList<>(); for (String entity : textClassifier.getEntitiesForPreset(mEntityPreset)) { if (!mExcludedEntityTypes.contains(entity)) { entities.add(entity); } } for (String entity : mIncludedEntityTypes) { if (!mExcludedEntityTypes.contains(entity) && !entities.contains(entity)) { entities.add(entity); } } return Collections.unmodifiableList(entities); } } /** * Utility functions for TextClassifier methods. Loading core/java/android/view/textclassifier/TextClassifierImpl.java +38 −3 Original line number Diff line number Diff line Loading @@ -42,6 +42,9 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; Loading @@ -66,6 +69,18 @@ final class TextClassifierImpl implements TextClassifier { private static final String MODEL_FILE_REGEX = "textclassifier\\.smartselection\\.(.*)\\.model"; private static final String UPDATED_MODEL_FILE_PATH = "/data/misc/textclassifier/textclassifier.smartselection.model"; private static final List<String> ENTITY_TYPES_ALL = Collections.unmodifiableList(Arrays.asList( TextClassifier.TYPE_ADDRESS, TextClassifier.TYPE_EMAIL, TextClassifier.TYPE_PHONE, TextClassifier.TYPE_URL)); 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; Loading Loading @@ -168,17 +183,23 @@ final class TextClassifierImpl implements TextClassifier { @Override public TextLinks generateLinks( @NonNull CharSequence text, @NonNull TextLinks.Options options) { @NonNull CharSequence text, @Nullable TextLinks.Options options) { Utils.validateInput(text); final String textString = text.toString(); final TextLinks.Builder builder = new TextLinks.Builder(textString); try { LocaleList defaultLocales = options != null ? options.getDefaultLocales() : null; final LocaleList defaultLocales = options != null ? options.getDefaultLocales() : null; final Collection<String> entitiesToIdentify = options != null && options.getEntityConfig() != null ? options.getEntityConfig().getEntities(this) : ENTITY_TYPES_ALL; final SmartSelection smartSelection = getSmartSelection(defaultLocales); final SmartSelection.AnnotatedSpan[] annotations = smartSelection.annotate(textString); for (SmartSelection.AnnotatedSpan span : annotations) { final Map<String, Float> entityScores = new HashMap<>(); final SmartSelection.ClassificationResult[] results = span.getClassification(); if (results.length == 0 || !entitiesToIdentify.contains(results[0].mCollection)) { continue; } final Map<String, Float> entityScores = new HashMap<>(); for (int i = 0; i < results.length; i++) { entityScores.put(results[i].mCollection, results[i].mScore); } Loading @@ -192,6 +213,20 @@ final class TextClassifierImpl implements TextClassifier { return builder.build(); } @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; } } @Override public void logEvent(String source, String event) { if (LOG_TAG.equals(source)) { Loading core/java/android/view/textclassifier/TextLinks.java +24 −3 Original line number Diff line number Diff line Loading @@ -161,17 +161,29 @@ public final class TextLinks { public static final class Options { private LocaleList mDefaultLocales; private TextClassifier.EntityConfig mEntityConfig; /** * @param defaultLocales ordered list of locale preferences that may be used to disambiguate * the provided text. If no locale preferences exist, set this to null or an empty * locale list. * @param defaultLocales ordered list of locale preferences that may be used to * disambiguate the provided text. If no locale preferences exist, * set this to null or an empty locale list. */ public Options setDefaultLocales(@Nullable LocaleList defaultLocales) { mDefaultLocales = defaultLocales; return this; } /** * Sets the entity configuration to use. This determines what types of entities the * TextClassifier will look for. * * @param entityConfig EntityConfig to use */ public Options setEntityConfig(@Nullable TextClassifier.EntityConfig entityConfig) { mEntityConfig = entityConfig; return this; } /** * @return ordered list of locale preferences that can be used to disambiguate * the provided text. Loading @@ -180,6 +192,15 @@ public final class TextLinks { public LocaleList getDefaultLocales() { return mDefaultLocales; } /** * @return The config representing the set of entities to look for. * @see #setEntityConfig(TextClassifier.EntityConfig) */ @Nullable public TextClassifier.EntityConfig getEntityConfig() { return mEntityConfig; } } /** Loading core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java +66 −33 Original line number Diff line number Diff line Loading @@ -16,10 +16,9 @@ package android.view.textclassifier; import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import android.os.LocaleList; Loading @@ -34,8 +33,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Collection; @SmallTest @RunWith(AndroidJUnit4.class) public class TextClassificationManagerTest { Loading Loading @@ -166,20 +163,50 @@ public class TextClassificationManagerTest { } @Test public void testGenerateLinks() { public void testGenerateLinks_phone() { if (isTextClassifierDisabled()) return; String text = "The number is +12122537077. See you tonight!"; assertThat(mClassifier.generateLinks(text, null), isTextLinksContaining(text, "+12122537077", TextClassifier.TYPE_PHONE)); } checkGenerateLinksFindsLink( "The number is +12122537077. See you tonight!", "+12122537077", TextClassifier.TYPE_PHONE); @Test public void testGenerateLinks_exclude() { 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))), not(isTextLinksContaining(text, "+12122537077", TextClassifier.TYPE_PHONE))); } checkGenerateLinksFindsLink( "The address is 1600 Amphitheater Parkway, Mountain View, CA. See you tonight!", "1600 Amphitheater Parkway, Mountain View, CA", TextClassifier.TYPE_ADDRESS); @Test public void testGenerateLinks_none_config() { 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))), not(isTextLinksContaining(text, "+12122537077", TextClassifier.TYPE_PHONE))); } // TODO: Add more entity types when the model supports them. @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 Loading @@ -193,25 +220,6 @@ public class TextClassificationManagerTest { return mClassifier == TextClassifier.NO_OP; } private void checkGenerateLinksFindsLink(String text, String classifiedText, String type) { assertTrue(text.contains(classifiedText)); int startIndex = text.indexOf(classifiedText); int endIndex = startIndex + classifiedText.length(); Collection<TextLinks.TextLink> links = mClassifier.generateLinks(text, mLinksOptions) .getLinks(); for (TextLinks.TextLink link : links) { if (text.subSequence(link.getStart(), link.getEnd()).equals(classifiedText)) { assertEquals(type, link.getEntity(0)); assertEquals(startIndex, link.getStart()); assertEquals(endIndex, link.getEnd()); assertTrue(link.getConfidenceScore(type) > 0); return; } } fail(); // Subsequence was not identified. } private static Matcher<TextSelection> isTextSelection( final int startIndex, final int endIndex, final String type) { return new BaseMatcher<TextSelection>() { Loading Loading @@ -240,6 +248,31 @@ public class TextClassificationManagerTest { }; } private static Matcher<TextLinks> isTextLinksContaining( final String text, final String substring, final String type) { return new BaseMatcher<TextLinks>() { @Override public void describeTo(Description description) { description.appendText("text=").appendValue(text) .appendText(", substring=").appendValue(substring) .appendText(", type=").appendValue(type); } @Override public boolean matches(Object o) { if (o instanceof TextLinks) { for (TextLinks.TextLink link : ((TextLinks) o).getLinks()) { if (text.subSequence(link.getStart(), link.getEnd()).equals(substring)) { return type.equals(link.getEntity(0)); } } } return false; } }; } private static Matcher<TextClassification> isTextClassification( final String text, final String type, final String intentUri) { return new BaseMatcher<TextClassification>() { Loading Loading
api/current.txt +13 −0 Original line number Diff line number Diff line Loading @@ -49119,9 +49119,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.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 android.view.textclassifier.TextClassifier NO_OP; field public static final java.lang.String TYPE_ADDRESS = "address"; field public static final java.lang.String TYPE_EMAIL = "email"; Loading @@ -49131,6 +49135,13 @@ package android.view.textclassifier { field public static final java.lang.String TYPE_URL = "url"; } public static final class TextClassifier.EntityConfig { ctor public TextClassifier.EntityConfig(int); 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...); } public final class TextLinks { method public boolean apply(android.text.SpannableString, java.util.function.Function<android.view.textclassifier.TextLinks.TextLink, android.text.style.ClickableSpan>); method public java.util.Collection<android.view.textclassifier.TextLinks.TextLink> getLinks(); Loading @@ -49145,7 +49156,9 @@ package android.view.textclassifier { public static final class TextLinks.Options { ctor public TextLinks.Options(); method public android.os.LocaleList getDefaultLocales(); method public android.view.textclassifier.TextClassifier.EntityConfig getEntityConfig(); method public android.view.textclassifier.TextLinks.Options setDefaultLocales(android.os.LocaleList); method public android.view.textclassifier.TextLinks.Options setEntityConfig(android.view.textclassifier.TextClassifier.EntityConfig); } public static final class TextLinks.TextLink {
core/java/android/view/textclassifier/TextClassifier.java +88 −0 Original line number Diff line number Diff line Loading @@ -16,17 +16,23 @@ package android.view.textclassifier; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; import android.annotation.WorkerThread; import android.os.LocaleList; import android.util.ArraySet; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; /** * Interface for providing text classification related features. Loading Loading @@ -58,6 +64,20 @@ 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; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "ENTITY_CONFIG_" }, value = {ENTITY_PRESET_ALL, ENTITY_PRESET_NONE, ENTITY_PRESET_BASE}) @interface EntityPreset {} /** * No-op TextClassifier. * This may be used to turn off TextClassifier features. Loading Loading @@ -217,6 +237,8 @@ public interface TextClassifier { * Returns a {@link TextLinks} that may be applied to the text to annotate it with links * information. * * If no options are supplied, default values will be used, determined by the TextClassifier. * * @param text the text to generate annotations for * @param options configuration for link generation * Loading Loading @@ -250,6 +272,16 @@ public interface TextClassifier { return generateLinks(text, null); } /** * Returns a {@link Collection} of the entity types in the specified preset. * * @see #ENTITIES_ALL * @see #ENTITIES_NONE */ default Collection<String> getEntitiesForPreset(@EntityPreset int entityPreset) { return Collections.EMPTY_LIST; } /** * Logs a TextClassifier event. * Loading @@ -268,6 +300,62 @@ public interface TextClassifier { return TextClassifierConstants.DEFAULT; } /** * Configuration object for specifying what entities to identify. * * Configs are initially based on a predefined preset, and can be modified from there. */ final class EntityConfig { private final @TextClassifier.EntityPreset int mEntityPreset; private final Collection<String> mExcludedEntityTypes; private final Collection<String> mIncludedEntityTypes; public EntityConfig(@TextClassifier.EntityPreset int mEntityPreset) { this.mEntityPreset = mEntityPreset; mExcludedEntityTypes = new ArraySet<>(); mIncludedEntityTypes = new ArraySet<>(); } /** * Specifies an entity to include in addition to any specified by the enity preset. * * 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; } /** * Specifies an entity to be excluded. */ public EntityConfig excludeEntities(String... entities) { for (String entity : entities) { mExcludedEntityTypes.add(entity); } return this; } /** * Returns an unmodifiable list of the final set of entities to find. */ public List<String> getEntities(TextClassifier textClassifier) { ArrayList<String> entities = new ArrayList<>(); for (String entity : textClassifier.getEntitiesForPreset(mEntityPreset)) { if (!mExcludedEntityTypes.contains(entity)) { entities.add(entity); } } for (String entity : mIncludedEntityTypes) { if (!mExcludedEntityTypes.contains(entity) && !entities.contains(entity)) { entities.add(entity); } } return Collections.unmodifiableList(entities); } } /** * Utility functions for TextClassifier methods. Loading
core/java/android/view/textclassifier/TextClassifierImpl.java +38 −3 Original line number Diff line number Diff line Loading @@ -42,6 +42,9 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; Loading @@ -66,6 +69,18 @@ final class TextClassifierImpl implements TextClassifier { private static final String MODEL_FILE_REGEX = "textclassifier\\.smartselection\\.(.*)\\.model"; private static final String UPDATED_MODEL_FILE_PATH = "/data/misc/textclassifier/textclassifier.smartselection.model"; private static final List<String> ENTITY_TYPES_ALL = Collections.unmodifiableList(Arrays.asList( TextClassifier.TYPE_ADDRESS, TextClassifier.TYPE_EMAIL, TextClassifier.TYPE_PHONE, TextClassifier.TYPE_URL)); 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; Loading Loading @@ -168,17 +183,23 @@ final class TextClassifierImpl implements TextClassifier { @Override public TextLinks generateLinks( @NonNull CharSequence text, @NonNull TextLinks.Options options) { @NonNull CharSequence text, @Nullable TextLinks.Options options) { Utils.validateInput(text); final String textString = text.toString(); final TextLinks.Builder builder = new TextLinks.Builder(textString); try { LocaleList defaultLocales = options != null ? options.getDefaultLocales() : null; final LocaleList defaultLocales = options != null ? options.getDefaultLocales() : null; final Collection<String> entitiesToIdentify = options != null && options.getEntityConfig() != null ? options.getEntityConfig().getEntities(this) : ENTITY_TYPES_ALL; final SmartSelection smartSelection = getSmartSelection(defaultLocales); final SmartSelection.AnnotatedSpan[] annotations = smartSelection.annotate(textString); for (SmartSelection.AnnotatedSpan span : annotations) { final Map<String, Float> entityScores = new HashMap<>(); final SmartSelection.ClassificationResult[] results = span.getClassification(); if (results.length == 0 || !entitiesToIdentify.contains(results[0].mCollection)) { continue; } final Map<String, Float> entityScores = new HashMap<>(); for (int i = 0; i < results.length; i++) { entityScores.put(results[i].mCollection, results[i].mScore); } Loading @@ -192,6 +213,20 @@ final class TextClassifierImpl implements TextClassifier { return builder.build(); } @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; } } @Override public void logEvent(String source, String event) { if (LOG_TAG.equals(source)) { Loading
core/java/android/view/textclassifier/TextLinks.java +24 −3 Original line number Diff line number Diff line Loading @@ -161,17 +161,29 @@ public final class TextLinks { public static final class Options { private LocaleList mDefaultLocales; private TextClassifier.EntityConfig mEntityConfig; /** * @param defaultLocales ordered list of locale preferences that may be used to disambiguate * the provided text. If no locale preferences exist, set this to null or an empty * locale list. * @param defaultLocales ordered list of locale preferences that may be used to * disambiguate the provided text. If no locale preferences exist, * set this to null or an empty locale list. */ public Options setDefaultLocales(@Nullable LocaleList defaultLocales) { mDefaultLocales = defaultLocales; return this; } /** * Sets the entity configuration to use. This determines what types of entities the * TextClassifier will look for. * * @param entityConfig EntityConfig to use */ public Options setEntityConfig(@Nullable TextClassifier.EntityConfig entityConfig) { mEntityConfig = entityConfig; return this; } /** * @return ordered list of locale preferences that can be used to disambiguate * the provided text. Loading @@ -180,6 +192,15 @@ public final class TextLinks { public LocaleList getDefaultLocales() { return mDefaultLocales; } /** * @return The config representing the set of entities to look for. * @see #setEntityConfig(TextClassifier.EntityConfig) */ @Nullable public TextClassifier.EntityConfig getEntityConfig() { return mEntityConfig; } } /** Loading
core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java +66 −33 Original line number Diff line number Diff line Loading @@ -16,10 +16,9 @@ package android.view.textclassifier; import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import android.os.LocaleList; Loading @@ -34,8 +33,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Collection; @SmallTest @RunWith(AndroidJUnit4.class) public class TextClassificationManagerTest { Loading Loading @@ -166,20 +163,50 @@ public class TextClassificationManagerTest { } @Test public void testGenerateLinks() { public void testGenerateLinks_phone() { if (isTextClassifierDisabled()) return; String text = "The number is +12122537077. See you tonight!"; assertThat(mClassifier.generateLinks(text, null), isTextLinksContaining(text, "+12122537077", TextClassifier.TYPE_PHONE)); } checkGenerateLinksFindsLink( "The number is +12122537077. See you tonight!", "+12122537077", TextClassifier.TYPE_PHONE); @Test public void testGenerateLinks_exclude() { 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))), not(isTextLinksContaining(text, "+12122537077", TextClassifier.TYPE_PHONE))); } checkGenerateLinksFindsLink( "The address is 1600 Amphitheater Parkway, Mountain View, CA. See you tonight!", "1600 Amphitheater Parkway, Mountain View, CA", TextClassifier.TYPE_ADDRESS); @Test public void testGenerateLinks_none_config() { 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))), not(isTextLinksContaining(text, "+12122537077", TextClassifier.TYPE_PHONE))); } // TODO: Add more entity types when the model supports them. @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 Loading @@ -193,25 +220,6 @@ public class TextClassificationManagerTest { return mClassifier == TextClassifier.NO_OP; } private void checkGenerateLinksFindsLink(String text, String classifiedText, String type) { assertTrue(text.contains(classifiedText)); int startIndex = text.indexOf(classifiedText); int endIndex = startIndex + classifiedText.length(); Collection<TextLinks.TextLink> links = mClassifier.generateLinks(text, mLinksOptions) .getLinks(); for (TextLinks.TextLink link : links) { if (text.subSequence(link.getStart(), link.getEnd()).equals(classifiedText)) { assertEquals(type, link.getEntity(0)); assertEquals(startIndex, link.getStart()); assertEquals(endIndex, link.getEnd()); assertTrue(link.getConfidenceScore(type) > 0); return; } } fail(); // Subsequence was not identified. } private static Matcher<TextSelection> isTextSelection( final int startIndex, final int endIndex, final String type) { return new BaseMatcher<TextSelection>() { Loading Loading @@ -240,6 +248,31 @@ public class TextClassificationManagerTest { }; } private static Matcher<TextLinks> isTextLinksContaining( final String text, final String substring, final String type) { return new BaseMatcher<TextLinks>() { @Override public void describeTo(Description description) { description.appendText("text=").appendValue(text) .appendText(", substring=").appendValue(substring) .appendText(", type=").appendValue(type); } @Override public boolean matches(Object o) { if (o instanceof TextLinks) { for (TextLinks.TextLink link : ((TextLinks) o).getLinks()) { if (text.subSequence(link.getStart(), link.getEnd()).equals(substring)) { return type.equals(link.getEntity(0)); } } } return false; } }; } private static Matcher<TextClassification> isTextClassification( final String text, final String type, final String intentUri) { return new BaseMatcher<TextClassification>() { Loading