Loading api/current.txt +7 −0 Original line number Diff line number Diff line Loading @@ -48693,19 +48693,26 @@ package android.view.inputmethod { package android.view.textclassifier { public final class TextClassification { method public int getActionCount(); method public float getConfidenceScore(java.lang.String); method public java.lang.String getEntity(int); method public int getEntityCount(); method public android.graphics.drawable.Drawable getIcon(int); method public android.graphics.drawable.Drawable getIcon(); method public android.content.Intent getIntent(int); method public android.content.Intent getIntent(); method public java.lang.CharSequence getLabel(int); method public java.lang.CharSequence getLabel(); method public android.view.View.OnClickListener getOnClickListener(int); method public android.view.View.OnClickListener getOnClickListener(); method public java.lang.String getText(); } public static final class TextClassification.Builder { ctor public TextClassification.Builder(); method public android.view.textclassifier.TextClassification.Builder addAction(android.content.Intent, java.lang.String, android.graphics.drawable.Drawable, android.view.View.OnClickListener); method public android.view.textclassifier.TextClassification build(); method public android.view.textclassifier.TextClassification.Builder clearActions(); method public android.view.textclassifier.TextClassification.Builder setEntityType(java.lang.String, float); method public android.view.textclassifier.TextClassification.Builder setIcon(android.graphics.drawable.Drawable); method public android.view.textclassifier.TextClassification.Builder setIntent(android.content.Intent); api/system-current.txt +7 −0 Original line number Diff line number Diff line Loading @@ -52389,19 +52389,26 @@ package android.view.inputmethod { package android.view.textclassifier { public final class TextClassification { method public int getActionCount(); method public float getConfidenceScore(java.lang.String); method public java.lang.String getEntity(int); method public int getEntityCount(); method public android.graphics.drawable.Drawable getIcon(int); method public android.graphics.drawable.Drawable getIcon(); method public android.content.Intent getIntent(int); method public android.content.Intent getIntent(); method public java.lang.CharSequence getLabel(int); method public java.lang.CharSequence getLabel(); method public android.view.View.OnClickListener getOnClickListener(int); method public android.view.View.OnClickListener getOnClickListener(); method public java.lang.String getText(); } public static final class TextClassification.Builder { ctor public TextClassification.Builder(); method public android.view.textclassifier.TextClassification.Builder addAction(android.content.Intent, java.lang.String, android.graphics.drawable.Drawable, android.view.View.OnClickListener); method public android.view.textclassifier.TextClassification build(); method public android.view.textclassifier.TextClassification.Builder clearActions(); method public android.view.textclassifier.TextClassification.Builder setEntityType(java.lang.String, float); method public android.view.textclassifier.TextClassification.Builder setIcon(android.graphics.drawable.Drawable); method public android.view.textclassifier.TextClassification.Builder setIntent(android.content.Intent); api/test-current.txt +7 −0 Original line number Diff line number Diff line Loading @@ -49249,19 +49249,26 @@ package android.view.inputmethod { package android.view.textclassifier { public final class TextClassification { method public int getActionCount(); method public float getConfidenceScore(java.lang.String); method public java.lang.String getEntity(int); method public int getEntityCount(); method public android.graphics.drawable.Drawable getIcon(int); method public android.graphics.drawable.Drawable getIcon(); method public android.content.Intent getIntent(int); method public android.content.Intent getIntent(); method public java.lang.CharSequence getLabel(int); method public java.lang.CharSequence getLabel(); method public android.view.View.OnClickListener getOnClickListener(int); method public android.view.View.OnClickListener getOnClickListener(); method public java.lang.String getText(); } public static final class TextClassification.Builder { ctor public TextClassification.Builder(); method public android.view.textclassifier.TextClassification.Builder addAction(android.content.Intent, java.lang.String, android.graphics.drawable.Drawable, android.view.View.OnClickListener); method public android.view.textclassifier.TextClassification build(); method public android.view.textclassifier.TextClassification.Builder clearActions(); method public android.view.textclassifier.TextClassification.Builder setEntityType(java.lang.String, float); method public android.view.textclassifier.TextClassification.Builder setIcon(android.graphics.drawable.Drawable); method public android.view.textclassifier.TextClassification.Builder setIntent(android.content.Intent); core/java/android/view/textclassifier/TextClassification.java +153 −35 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.view.textclassifier.TextClassifier.EntityType; import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.List; /** Loading @@ -41,10 +42,10 @@ public final class TextClassification { static final TextClassification EMPTY = new TextClassification.Builder().build(); @NonNull private final String mText; @Nullable private final Drawable mIcon; @Nullable private final String mLabel; @Nullable private final Intent mIntent; @Nullable private final OnClickListener mOnClickListener; @NonNull private final List<Drawable> mIcons; @NonNull private final List<String> mLabels; @NonNull private final List<Intent> mIntents; @NonNull private final List<OnClickListener> mOnClickListeners; @NonNull private final EntityConfidence<String> mEntityConfidence; @NonNull private final List<String> mEntities; private int mLogType; Loading @@ -52,18 +53,21 @@ public final class TextClassification { private TextClassification( @Nullable String text, @Nullable Drawable icon, @Nullable String label, @Nullable Intent intent, @Nullable OnClickListener onClickListener, @NonNull List<Drawable> icons, @NonNull List<String> labels, @NonNull List<Intent> intents, @NonNull List<OnClickListener> onClickListeners, @NonNull EntityConfidence<String> entityConfidence, int logType, @NonNull String versionInfo) { Preconditions.checkArgument(labels.size() == intents.size()); Preconditions.checkArgument(icons.size() == intents.size()); Preconditions.checkArgument(onClickListeners.size() == intents.size()); mText = text; mIcon = icon; mLabel = label; mIntent = intent; mOnClickListener = onClickListener; mIcons = icons; mLabels = labels; mIntents = intents; mOnClickListeners = onClickListeners; mEntityConfidence = new EntityConfidence<>(entityConfidence); mEntities = mEntityConfidence.getEntities(); mLogType = logType; Loading Loading @@ -109,35 +113,106 @@ public final class TextClassification { } /** * Returns an icon that may be rendered on a widget used to act on the classified text. * Returns the number of actions that are available to act on the classified text. * @see #getIntent(int) * @see #getLabel(int) * @see #getIcon(int) * @see #getOnClickListener(int) */ @IntRange(from = 0) public int getActionCount() { return mIntents.size(); } /** * Returns one of the icons that maybe rendered on a widget used to act on the classified text. * @param index Index of the action to get the icon for. * @throws IndexOutOfBoundsException if the specified index is out of range. * @see #getActionCount() for the number of entities available. * @see #getIntent(int) * @see #getLabel(int) * @see #getOnClickListener(int) */ @Nullable public Drawable getIcon(int index) { return mIcons.get(index); } /** * Returns an icon for the default intent that may be rendered on a widget used to act on the * classified text. */ @Nullable public Drawable getIcon() { return mIcon; return mIcons.isEmpty() ? null : mIcons.get(0); } /** * Returns a label that may be rendered on a widget used to act on the classified text. * Returns one of the labels that may be rendered on a widget used to act on 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 #getActionCount() * @see #getIntent(int) * @see #getIcon(int) * @see #getOnClickListener(int) */ @Nullable public CharSequence getLabel(int index) { return mLabels.get(index); } /** * Returns a label for the default intent that may be rendered on a widget used to act on the * classified text. */ @Nullable public CharSequence getLabel() { return mLabel; return mLabels.isEmpty() ? null : mLabels.get(0); } /** * Returns one of the 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 #getActionCount() * @see #getLabel(int) * @see #getIcon(int) * @see #getOnClickListener(int) */ @Nullable public Intent getIntent(int index) { return mIntents.get(index); } /** * Returns an intent that may be fired to act on the classified text. * Returns the default intent that may be fired to act on the classified text. */ @Nullable public Intent getIntent() { return mIntent; return mIntents.isEmpty() ? null : mIntents.get(0); } /** * Returns one of the 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 #getActionCount() * @see #getIntent(int) * @see #getLabel(int) * @see #getIcon(int) */ @Nullable public OnClickListener getOnClickListener(int index) { return mOnClickListeners.get(index); } /** * Returns an OnClickListener that may be triggered to act on the classified text. * Returns the default OnClickListener that may be triggered to act on the classified text. */ @Nullable public OnClickListener getOnClickListener() { return mOnClickListener; return mOnClickListeners.isEmpty() ? null : mOnClickListeners.get(0); } /** Loading @@ -160,8 +235,8 @@ public final class TextClassification { @Override public String toString() { return String.format("TextClassification {" + "text=%s, entities=%s, label=%s, intent=%s}", mText, mEntityConfidence, mLabel, mIntent); + "text=%s, entities=%s, labels=%s, intents=%s}", mText, mEntityConfidence, mLabels, mIntents); } /** Loading @@ -184,10 +259,10 @@ public final class TextClassification { public static final class Builder { @NonNull private String mText; @Nullable private Drawable mIcon; @Nullable private String mLabel; @Nullable private Intent mIntent; @Nullable private OnClickListener mOnClickListener; @NonNull private final List<Drawable> mIcons = new ArrayList<>(); @NonNull private final List<String> mLabels = new ArrayList<>(); @NonNull private final List<Intent> mIntents = new ArrayList<>(); @NonNull private final List<OnClickListener> mOnClickListeners = new ArrayList<>(); @NonNull private final EntityConfidence<String> mEntityConfidence = new EntityConfidence<>(); private int mLogType; Loading Loading @@ -216,26 +291,57 @@ public final class TextClassification { } /** * Sets an icon that may be rendered on a widget used to act on the classified text. * Adds an action that may be performed on the classified text. The label and icon are used * for rendering of widgets that offer the intent. Actions should be added in order of * priority and the first one will be treated as the default. */ public Builder addAction( Intent intent, @Nullable String label, @Nullable Drawable icon, @Nullable OnClickListener onClickListener) { mIntents.add(intent); mLabels.add(label); mIcons.add(icon); mOnClickListeners.add(onClickListener); return this; } /** * Removes all actions. */ public Builder clearActions() { mIntents.clear(); mOnClickListeners.clear(); mLabels.clear(); mIcons.clear(); return this; } /** * Sets the icon for the default action that may be rendered on a widget used to act on the * classified text. */ public Builder setIcon(@Nullable Drawable icon) { mIcon = icon; ensureDefaultActionAvailable(); mIcons.set(0, icon); return this; } /** * Sets a label that may be rendered on a widget used to act on the classified text. * Sets the label for the default action that may be rendered on a widget used to act on the * classified text. */ public Builder setLabel(@Nullable String label) { mLabel = label; ensureDefaultActionAvailable(); mLabels.set(0, label); return this; } /** * Sets an intent that may be fired to act on the classified text. * Sets the intent for the default action that may be fired to act on the classified text. */ public Builder setIntent(@Nullable Intent intent) { mIntent = intent; ensureDefaultActionAvailable(); mIntents.set(0, intent); return this; } Loading @@ -249,10 +355,12 @@ public final class TextClassification { } /** * Sets an OnClickListener that may be triggered to act on the classified text. * Sets the OnClickListener for the default action that may be triggered to act on the * classified text. */ public Builder setOnClickListener(@Nullable OnClickListener onClickListener) { mOnClickListener = onClickListener; ensureDefaultActionAvailable(); mOnClickListeners.set(0, onClickListener); return this; } Loading @@ -265,12 +373,22 @@ public final class TextClassification { return this; } /** * Ensures that we have at we have storage for the default action. */ private void ensureDefaultActionAvailable() { if (mIntents.isEmpty()) mIntents.add(null); if (mLabels.isEmpty()) mLabels.add(null); if (mIcons.isEmpty()) mIcons.add(null); if (mOnClickListeners.isEmpty()) mOnClickListeners.add(null); } /** * Builds and returns a {@link TextClassification} object. */ public TextClassification build() { return new TextClassification( mText, mIcon, mLabel, mIntent, mOnClickListener, mEntityConfidence, mText, mIcons, mLabels, mIntents, mOnClickListeners, mEntityConfidence, mLogType, mVersionInfo); } } Loading core/java/android/view/textclassifier/TextClassifierImpl.java +81 −37 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.net.Uri; import android.os.LocaleList; import android.os.ParcelFileDescriptor; import android.provider.Browser; import android.provider.ContactsContract; import android.text.Spannable; import android.text.TextUtils; import android.text.method.WordIterator; Loading Loading @@ -356,7 +357,16 @@ final class TextClassifierImpl implements TextClassifier { final String type = getHighestScoringType(classifications); builder.setLogType(IntentFactory.getLogType(type)); final Intent intent = IntentFactory.create(mContext, type, text.toString()); final List<Intent> intents = IntentFactory.create(mContext, type, text.toString()); for (Intent intent : intents) { extendClassificationWithIntent(intent, builder); } return builder.setVersionInfo(getVersionInfo()).build(); } /** Extends the classification with the intent if it can be resolved. */ private void extendClassificationWithIntent(Intent intent, TextClassification.Builder builder) { final PackageManager pm; final ResolveInfo resolveInfo; if (intent != null) { Loading @@ -367,30 +377,29 @@ final class TextClassifierImpl implements TextClassifier { resolveInfo = null; } if (resolveInfo != null && resolveInfo.activityInfo != null) { builder.setIntent(intent) .setOnClickListener(TextClassification.createStartActivityOnClickListener( mContext, intent)); final String packageName = resolveInfo.activityInfo.packageName; CharSequence label; Drawable icon; if ("android".equals(packageName)) { // Requires the chooser to find an activity to handle the intent. builder.setLabel(IntentFactory.getLabel(mContext, type)); label = IntentFactory.getLabel(mContext, intent); icon = null; } else { // A default activity will handle the intent. intent.setComponent(new ComponentName(packageName, resolveInfo.activityInfo.name)); Drawable icon = resolveInfo.activityInfo.loadIcon(pm); icon = resolveInfo.activityInfo.loadIcon(pm); if (icon == null) { icon = resolveInfo.loadIcon(pm); } builder.setIcon(icon); CharSequence label = resolveInfo.activityInfo.loadLabel(pm); label = resolveInfo.activityInfo.loadLabel(pm); if (label == null) { label = resolveInfo.loadLabel(pm); } builder.setLabel(label != null ? label.toString() : null); } builder.addAction( intent, label != null ? label.toString() : null, icon, TextClassification.createStartActivityOnClickListener(mContext, intent)); } return builder.setVersionInfo(getVersionInfo()).build(); } private static int getHintFlags(CharSequence text, int start, int end) { Loading Loading @@ -477,10 +486,11 @@ final class TextClassifierImpl implements TextClassifier { if (results.length > 0) { final String type = getHighestScoringType(results); if (matches(type, linkMask)) { final Intent intent = IntentFactory.create( // For links without disambiguation, we simply use the default intent. final List<Intent> intents = IntentFactory.create( context, type, text.substring(selectionStart, selectionEnd)); if (hasActivityHandler(context, intent)) { final ClickableSpan span = createSpan(context, intent); if (!intents.isEmpty() && hasActivityHandler(context, intents.get(0))) { final ClickableSpan span = createSpan(context, intents.get(0)); spans.add(new SpanSpec(selectionStart, selectionEnd, span)); } } Loading Loading @@ -564,7 +574,7 @@ final class TextClassifierImpl implements TextClassifier { }; } private static boolean hasActivityHandler(Context context, @Nullable Intent intent) { private static boolean hasActivityHandler(Context context, Intent intent) { if (intent == null) { return false; } Loading Loading @@ -625,20 +635,32 @@ final class TextClassifierImpl implements TextClassifier { private IntentFactory() {} @Nullable public static Intent create(Context context, String type, String text) { @NonNull public static List<Intent> create(Context context, String type, String text) { final List<Intent> intents = new ArrayList<>(); type = type.trim().toLowerCase(Locale.ENGLISH); text = text.trim(); switch (type) { case TextClassifier.TYPE_EMAIL: return new Intent(Intent.ACTION_SENDTO) .setData(Uri.parse(String.format("mailto:%s", text))); intents.add(new Intent(Intent.ACTION_SENDTO) .setData(Uri.parse(String.format("mailto:%s", text)))); intents.add(new Intent(Intent.ACTION_INSERT_OR_EDIT) .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE) .putExtra(ContactsContract.Intents.Insert.EMAIL, text)); break; case TextClassifier.TYPE_PHONE: return new Intent(Intent.ACTION_DIAL) .setData(Uri.parse(String.format("tel:%s", text))); intents.add(new Intent(Intent.ACTION_DIAL) .setData(Uri.parse(String.format("tel:%s", text)))); intents.add(new Intent(Intent.ACTION_INSERT_OR_EDIT) .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE) .putExtra(ContactsContract.Intents.Insert.PHONE, text)); intents.add(new Intent(Intent.ACTION_SENDTO) .setData(Uri.parse(String.format("smsto:%s", text)))); break; case TextClassifier.TYPE_ADDRESS: return new Intent(Intent.ACTION_VIEW) .setData(Uri.parse(String.format("geo:0,0?q=%s", text))); intents.add(new Intent(Intent.ACTION_VIEW) .setData(Uri.parse(String.format("geo:0,0?q=%s", text)))); break; case TextClassifier.TYPE_URL: final String httpPrefix = "http://"; final String httpsPrefix = "https://"; Loading @@ -649,28 +671,50 @@ final class TextClassifierImpl implements TextClassifier { } else { text = httpPrefix + text; } return new Intent(Intent.ACTION_VIEW, Uri.parse(text)) .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()); default: return null; intents.add(new Intent(Intent.ACTION_VIEW, Uri.parse(text)) .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName())); break; } return intents; } @Nullable public static String getLabel(Context context, String type) { type = type.trim().toLowerCase(Locale.ENGLISH); switch (type) { case TextClassifier.TYPE_EMAIL: return context.getString(com.android.internal.R.string.email); case TextClassifier.TYPE_PHONE: public static String getLabel(Context context, @Nullable Intent intent) { if (intent == null || intent.getAction() == null) { return null; } switch (intent.getAction()) { case Intent.ACTION_DIAL: return context.getString(com.android.internal.R.string.dial); case TextClassifier.TYPE_ADDRESS: case Intent.ACTION_SENDTO: switch (intent.getScheme()) { case "mailto": return context.getString(com.android.internal.R.string.email); case "smsto": return context.getString(com.android.internal.R.string.sms); default: return null; } case Intent.ACTION_INSERT_OR_EDIT: switch (intent.getDataString()) { case ContactsContract.Contacts.CONTENT_ITEM_TYPE: return context.getString(com.android.internal.R.string.add_contact); default: return null; } case Intent.ACTION_VIEW: switch (intent.getScheme()) { case "geo": return context.getString(com.android.internal.R.string.map); case TextClassifier.TYPE_URL: case "http": // fall through case "https": return context.getString(com.android.internal.R.string.browse); default: return null; } default: return null; } } @Nullable Loading Loading
api/current.txt +7 −0 Original line number Diff line number Diff line Loading @@ -48693,19 +48693,26 @@ package android.view.inputmethod { package android.view.textclassifier { public final class TextClassification { method public int getActionCount(); method public float getConfidenceScore(java.lang.String); method public java.lang.String getEntity(int); method public int getEntityCount(); method public android.graphics.drawable.Drawable getIcon(int); method public android.graphics.drawable.Drawable getIcon(); method public android.content.Intent getIntent(int); method public android.content.Intent getIntent(); method public java.lang.CharSequence getLabel(int); method public java.lang.CharSequence getLabel(); method public android.view.View.OnClickListener getOnClickListener(int); method public android.view.View.OnClickListener getOnClickListener(); method public java.lang.String getText(); } public static final class TextClassification.Builder { ctor public TextClassification.Builder(); method public android.view.textclassifier.TextClassification.Builder addAction(android.content.Intent, java.lang.String, android.graphics.drawable.Drawable, android.view.View.OnClickListener); method public android.view.textclassifier.TextClassification build(); method public android.view.textclassifier.TextClassification.Builder clearActions(); method public android.view.textclassifier.TextClassification.Builder setEntityType(java.lang.String, float); method public android.view.textclassifier.TextClassification.Builder setIcon(android.graphics.drawable.Drawable); method public android.view.textclassifier.TextClassification.Builder setIntent(android.content.Intent);
api/system-current.txt +7 −0 Original line number Diff line number Diff line Loading @@ -52389,19 +52389,26 @@ package android.view.inputmethod { package android.view.textclassifier { public final class TextClassification { method public int getActionCount(); method public float getConfidenceScore(java.lang.String); method public java.lang.String getEntity(int); method public int getEntityCount(); method public android.graphics.drawable.Drawable getIcon(int); method public android.graphics.drawable.Drawable getIcon(); method public android.content.Intent getIntent(int); method public android.content.Intent getIntent(); method public java.lang.CharSequence getLabel(int); method public java.lang.CharSequence getLabel(); method public android.view.View.OnClickListener getOnClickListener(int); method public android.view.View.OnClickListener getOnClickListener(); method public java.lang.String getText(); } public static final class TextClassification.Builder { ctor public TextClassification.Builder(); method public android.view.textclassifier.TextClassification.Builder addAction(android.content.Intent, java.lang.String, android.graphics.drawable.Drawable, android.view.View.OnClickListener); method public android.view.textclassifier.TextClassification build(); method public android.view.textclassifier.TextClassification.Builder clearActions(); method public android.view.textclassifier.TextClassification.Builder setEntityType(java.lang.String, float); method public android.view.textclassifier.TextClassification.Builder setIcon(android.graphics.drawable.Drawable); method public android.view.textclassifier.TextClassification.Builder setIntent(android.content.Intent);
api/test-current.txt +7 −0 Original line number Diff line number Diff line Loading @@ -49249,19 +49249,26 @@ package android.view.inputmethod { package android.view.textclassifier { public final class TextClassification { method public int getActionCount(); method public float getConfidenceScore(java.lang.String); method public java.lang.String getEntity(int); method public int getEntityCount(); method public android.graphics.drawable.Drawable getIcon(int); method public android.graphics.drawable.Drawable getIcon(); method public android.content.Intent getIntent(int); method public android.content.Intent getIntent(); method public java.lang.CharSequence getLabel(int); method public java.lang.CharSequence getLabel(); method public android.view.View.OnClickListener getOnClickListener(int); method public android.view.View.OnClickListener getOnClickListener(); method public java.lang.String getText(); } public static final class TextClassification.Builder { ctor public TextClassification.Builder(); method public android.view.textclassifier.TextClassification.Builder addAction(android.content.Intent, java.lang.String, android.graphics.drawable.Drawable, android.view.View.OnClickListener); method public android.view.textclassifier.TextClassification build(); method public android.view.textclassifier.TextClassification.Builder clearActions(); method public android.view.textclassifier.TextClassification.Builder setEntityType(java.lang.String, float); method public android.view.textclassifier.TextClassification.Builder setIcon(android.graphics.drawable.Drawable); method public android.view.textclassifier.TextClassification.Builder setIntent(android.content.Intent);
core/java/android/view/textclassifier/TextClassification.java +153 −35 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import android.view.textclassifier.TextClassifier.EntityType; import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.List; /** Loading @@ -41,10 +42,10 @@ public final class TextClassification { static final TextClassification EMPTY = new TextClassification.Builder().build(); @NonNull private final String mText; @Nullable private final Drawable mIcon; @Nullable private final String mLabel; @Nullable private final Intent mIntent; @Nullable private final OnClickListener mOnClickListener; @NonNull private final List<Drawable> mIcons; @NonNull private final List<String> mLabels; @NonNull private final List<Intent> mIntents; @NonNull private final List<OnClickListener> mOnClickListeners; @NonNull private final EntityConfidence<String> mEntityConfidence; @NonNull private final List<String> mEntities; private int mLogType; Loading @@ -52,18 +53,21 @@ public final class TextClassification { private TextClassification( @Nullable String text, @Nullable Drawable icon, @Nullable String label, @Nullable Intent intent, @Nullable OnClickListener onClickListener, @NonNull List<Drawable> icons, @NonNull List<String> labels, @NonNull List<Intent> intents, @NonNull List<OnClickListener> onClickListeners, @NonNull EntityConfidence<String> entityConfidence, int logType, @NonNull String versionInfo) { Preconditions.checkArgument(labels.size() == intents.size()); Preconditions.checkArgument(icons.size() == intents.size()); Preconditions.checkArgument(onClickListeners.size() == intents.size()); mText = text; mIcon = icon; mLabel = label; mIntent = intent; mOnClickListener = onClickListener; mIcons = icons; mLabels = labels; mIntents = intents; mOnClickListeners = onClickListeners; mEntityConfidence = new EntityConfidence<>(entityConfidence); mEntities = mEntityConfidence.getEntities(); mLogType = logType; Loading Loading @@ -109,35 +113,106 @@ public final class TextClassification { } /** * Returns an icon that may be rendered on a widget used to act on the classified text. * Returns the number of actions that are available to act on the classified text. * @see #getIntent(int) * @see #getLabel(int) * @see #getIcon(int) * @see #getOnClickListener(int) */ @IntRange(from = 0) public int getActionCount() { return mIntents.size(); } /** * Returns one of the icons that maybe rendered on a widget used to act on the classified text. * @param index Index of the action to get the icon for. * @throws IndexOutOfBoundsException if the specified index is out of range. * @see #getActionCount() for the number of entities available. * @see #getIntent(int) * @see #getLabel(int) * @see #getOnClickListener(int) */ @Nullable public Drawable getIcon(int index) { return mIcons.get(index); } /** * Returns an icon for the default intent that may be rendered on a widget used to act on the * classified text. */ @Nullable public Drawable getIcon() { return mIcon; return mIcons.isEmpty() ? null : mIcons.get(0); } /** * Returns a label that may be rendered on a widget used to act on the classified text. * Returns one of the labels that may be rendered on a widget used to act on 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 #getActionCount() * @see #getIntent(int) * @see #getIcon(int) * @see #getOnClickListener(int) */ @Nullable public CharSequence getLabel(int index) { return mLabels.get(index); } /** * Returns a label for the default intent that may be rendered on a widget used to act on the * classified text. */ @Nullable public CharSequence getLabel() { return mLabel; return mLabels.isEmpty() ? null : mLabels.get(0); } /** * Returns one of the 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 #getActionCount() * @see #getLabel(int) * @see #getIcon(int) * @see #getOnClickListener(int) */ @Nullable public Intent getIntent(int index) { return mIntents.get(index); } /** * Returns an intent that may be fired to act on the classified text. * Returns the default intent that may be fired to act on the classified text. */ @Nullable public Intent getIntent() { return mIntent; return mIntents.isEmpty() ? null : mIntents.get(0); } /** * Returns one of the 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 #getActionCount() * @see #getIntent(int) * @see #getLabel(int) * @see #getIcon(int) */ @Nullable public OnClickListener getOnClickListener(int index) { return mOnClickListeners.get(index); } /** * Returns an OnClickListener that may be triggered to act on the classified text. * Returns the default OnClickListener that may be triggered to act on the classified text. */ @Nullable public OnClickListener getOnClickListener() { return mOnClickListener; return mOnClickListeners.isEmpty() ? null : mOnClickListeners.get(0); } /** Loading @@ -160,8 +235,8 @@ public final class TextClassification { @Override public String toString() { return String.format("TextClassification {" + "text=%s, entities=%s, label=%s, intent=%s}", mText, mEntityConfidence, mLabel, mIntent); + "text=%s, entities=%s, labels=%s, intents=%s}", mText, mEntityConfidence, mLabels, mIntents); } /** Loading @@ -184,10 +259,10 @@ public final class TextClassification { public static final class Builder { @NonNull private String mText; @Nullable private Drawable mIcon; @Nullable private String mLabel; @Nullable private Intent mIntent; @Nullable private OnClickListener mOnClickListener; @NonNull private final List<Drawable> mIcons = new ArrayList<>(); @NonNull private final List<String> mLabels = new ArrayList<>(); @NonNull private final List<Intent> mIntents = new ArrayList<>(); @NonNull private final List<OnClickListener> mOnClickListeners = new ArrayList<>(); @NonNull private final EntityConfidence<String> mEntityConfidence = new EntityConfidence<>(); private int mLogType; Loading Loading @@ -216,26 +291,57 @@ public final class TextClassification { } /** * Sets an icon that may be rendered on a widget used to act on the classified text. * Adds an action that may be performed on the classified text. The label and icon are used * for rendering of widgets that offer the intent. Actions should be added in order of * priority and the first one will be treated as the default. */ public Builder addAction( Intent intent, @Nullable String label, @Nullable Drawable icon, @Nullable OnClickListener onClickListener) { mIntents.add(intent); mLabels.add(label); mIcons.add(icon); mOnClickListeners.add(onClickListener); return this; } /** * Removes all actions. */ public Builder clearActions() { mIntents.clear(); mOnClickListeners.clear(); mLabels.clear(); mIcons.clear(); return this; } /** * Sets the icon for the default action that may be rendered on a widget used to act on the * classified text. */ public Builder setIcon(@Nullable Drawable icon) { mIcon = icon; ensureDefaultActionAvailable(); mIcons.set(0, icon); return this; } /** * Sets a label that may be rendered on a widget used to act on the classified text. * Sets the label for the default action that may be rendered on a widget used to act on the * classified text. */ public Builder setLabel(@Nullable String label) { mLabel = label; ensureDefaultActionAvailable(); mLabels.set(0, label); return this; } /** * Sets an intent that may be fired to act on the classified text. * Sets the intent for the default action that may be fired to act on the classified text. */ public Builder setIntent(@Nullable Intent intent) { mIntent = intent; ensureDefaultActionAvailable(); mIntents.set(0, intent); return this; } Loading @@ -249,10 +355,12 @@ public final class TextClassification { } /** * Sets an OnClickListener that may be triggered to act on the classified text. * Sets the OnClickListener for the default action that may be triggered to act on the * classified text. */ public Builder setOnClickListener(@Nullable OnClickListener onClickListener) { mOnClickListener = onClickListener; ensureDefaultActionAvailable(); mOnClickListeners.set(0, onClickListener); return this; } Loading @@ -265,12 +373,22 @@ public final class TextClassification { return this; } /** * Ensures that we have at we have storage for the default action. */ private void ensureDefaultActionAvailable() { if (mIntents.isEmpty()) mIntents.add(null); if (mLabels.isEmpty()) mLabels.add(null); if (mIcons.isEmpty()) mIcons.add(null); if (mOnClickListeners.isEmpty()) mOnClickListeners.add(null); } /** * Builds and returns a {@link TextClassification} object. */ public TextClassification build() { return new TextClassification( mText, mIcon, mLabel, mIntent, mOnClickListener, mEntityConfidence, mText, mIcons, mLabels, mIntents, mOnClickListeners, mEntityConfidence, mLogType, mVersionInfo); } } Loading
core/java/android/view/textclassifier/TextClassifierImpl.java +81 −37 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.net.Uri; import android.os.LocaleList; import android.os.ParcelFileDescriptor; import android.provider.Browser; import android.provider.ContactsContract; import android.text.Spannable; import android.text.TextUtils; import android.text.method.WordIterator; Loading Loading @@ -356,7 +357,16 @@ final class TextClassifierImpl implements TextClassifier { final String type = getHighestScoringType(classifications); builder.setLogType(IntentFactory.getLogType(type)); final Intent intent = IntentFactory.create(mContext, type, text.toString()); final List<Intent> intents = IntentFactory.create(mContext, type, text.toString()); for (Intent intent : intents) { extendClassificationWithIntent(intent, builder); } return builder.setVersionInfo(getVersionInfo()).build(); } /** Extends the classification with the intent if it can be resolved. */ private void extendClassificationWithIntent(Intent intent, TextClassification.Builder builder) { final PackageManager pm; final ResolveInfo resolveInfo; if (intent != null) { Loading @@ -367,30 +377,29 @@ final class TextClassifierImpl implements TextClassifier { resolveInfo = null; } if (resolveInfo != null && resolveInfo.activityInfo != null) { builder.setIntent(intent) .setOnClickListener(TextClassification.createStartActivityOnClickListener( mContext, intent)); final String packageName = resolveInfo.activityInfo.packageName; CharSequence label; Drawable icon; if ("android".equals(packageName)) { // Requires the chooser to find an activity to handle the intent. builder.setLabel(IntentFactory.getLabel(mContext, type)); label = IntentFactory.getLabel(mContext, intent); icon = null; } else { // A default activity will handle the intent. intent.setComponent(new ComponentName(packageName, resolveInfo.activityInfo.name)); Drawable icon = resolveInfo.activityInfo.loadIcon(pm); icon = resolveInfo.activityInfo.loadIcon(pm); if (icon == null) { icon = resolveInfo.loadIcon(pm); } builder.setIcon(icon); CharSequence label = resolveInfo.activityInfo.loadLabel(pm); label = resolveInfo.activityInfo.loadLabel(pm); if (label == null) { label = resolveInfo.loadLabel(pm); } builder.setLabel(label != null ? label.toString() : null); } builder.addAction( intent, label != null ? label.toString() : null, icon, TextClassification.createStartActivityOnClickListener(mContext, intent)); } return builder.setVersionInfo(getVersionInfo()).build(); } private static int getHintFlags(CharSequence text, int start, int end) { Loading Loading @@ -477,10 +486,11 @@ final class TextClassifierImpl implements TextClassifier { if (results.length > 0) { final String type = getHighestScoringType(results); if (matches(type, linkMask)) { final Intent intent = IntentFactory.create( // For links without disambiguation, we simply use the default intent. final List<Intent> intents = IntentFactory.create( context, type, text.substring(selectionStart, selectionEnd)); if (hasActivityHandler(context, intent)) { final ClickableSpan span = createSpan(context, intent); if (!intents.isEmpty() && hasActivityHandler(context, intents.get(0))) { final ClickableSpan span = createSpan(context, intents.get(0)); spans.add(new SpanSpec(selectionStart, selectionEnd, span)); } } Loading Loading @@ -564,7 +574,7 @@ final class TextClassifierImpl implements TextClassifier { }; } private static boolean hasActivityHandler(Context context, @Nullable Intent intent) { private static boolean hasActivityHandler(Context context, Intent intent) { if (intent == null) { return false; } Loading Loading @@ -625,20 +635,32 @@ final class TextClassifierImpl implements TextClassifier { private IntentFactory() {} @Nullable public static Intent create(Context context, String type, String text) { @NonNull public static List<Intent> create(Context context, String type, String text) { final List<Intent> intents = new ArrayList<>(); type = type.trim().toLowerCase(Locale.ENGLISH); text = text.trim(); switch (type) { case TextClassifier.TYPE_EMAIL: return new Intent(Intent.ACTION_SENDTO) .setData(Uri.parse(String.format("mailto:%s", text))); intents.add(new Intent(Intent.ACTION_SENDTO) .setData(Uri.parse(String.format("mailto:%s", text)))); intents.add(new Intent(Intent.ACTION_INSERT_OR_EDIT) .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE) .putExtra(ContactsContract.Intents.Insert.EMAIL, text)); break; case TextClassifier.TYPE_PHONE: return new Intent(Intent.ACTION_DIAL) .setData(Uri.parse(String.format("tel:%s", text))); intents.add(new Intent(Intent.ACTION_DIAL) .setData(Uri.parse(String.format("tel:%s", text)))); intents.add(new Intent(Intent.ACTION_INSERT_OR_EDIT) .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE) .putExtra(ContactsContract.Intents.Insert.PHONE, text)); intents.add(new Intent(Intent.ACTION_SENDTO) .setData(Uri.parse(String.format("smsto:%s", text)))); break; case TextClassifier.TYPE_ADDRESS: return new Intent(Intent.ACTION_VIEW) .setData(Uri.parse(String.format("geo:0,0?q=%s", text))); intents.add(new Intent(Intent.ACTION_VIEW) .setData(Uri.parse(String.format("geo:0,0?q=%s", text)))); break; case TextClassifier.TYPE_URL: final String httpPrefix = "http://"; final String httpsPrefix = "https://"; Loading @@ -649,28 +671,50 @@ final class TextClassifierImpl implements TextClassifier { } else { text = httpPrefix + text; } return new Intent(Intent.ACTION_VIEW, Uri.parse(text)) .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()); default: return null; intents.add(new Intent(Intent.ACTION_VIEW, Uri.parse(text)) .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName())); break; } return intents; } @Nullable public static String getLabel(Context context, String type) { type = type.trim().toLowerCase(Locale.ENGLISH); switch (type) { case TextClassifier.TYPE_EMAIL: return context.getString(com.android.internal.R.string.email); case TextClassifier.TYPE_PHONE: public static String getLabel(Context context, @Nullable Intent intent) { if (intent == null || intent.getAction() == null) { return null; } switch (intent.getAction()) { case Intent.ACTION_DIAL: return context.getString(com.android.internal.R.string.dial); case TextClassifier.TYPE_ADDRESS: case Intent.ACTION_SENDTO: switch (intent.getScheme()) { case "mailto": return context.getString(com.android.internal.R.string.email); case "smsto": return context.getString(com.android.internal.R.string.sms); default: return null; } case Intent.ACTION_INSERT_OR_EDIT: switch (intent.getDataString()) { case ContactsContract.Contacts.CONTENT_ITEM_TYPE: return context.getString(com.android.internal.R.string.add_contact); default: return null; } case Intent.ACTION_VIEW: switch (intent.getScheme()) { case "geo": return context.getString(com.android.internal.R.string.map); case TextClassifier.TYPE_URL: case "http": // fall through case "https": return context.getString(com.android.internal.R.string.browse); default: return null; } default: return null; } } @Nullable Loading