Loading api/current.txt +5 −0 Original line number Diff line number Diff line Loading @@ -49896,7 +49896,9 @@ package android.view.textclassifier { ctor public TextClassification.Options(); method public int describeContents(); method public android.os.LocaleList getDefaultLocales(); method public java.util.Calendar getReferenceTime(); method public android.view.textclassifier.TextClassification.Options setDefaultLocales(android.os.LocaleList); method public android.view.textclassifier.TextClassification.Options setReferenceTime(java.util.Calendar); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassification.Options> CREATOR; } Loading @@ -49921,7 +49923,10 @@ package android.view.textclassifier { 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_DATE = "date"; field public static final java.lang.String TYPE_DATE_TIME = "datetime"; field public static final java.lang.String TYPE_EMAIL = "email"; field public static final java.lang.String TYPE_FLIGHT_NUMBER = "flight"; field public static final java.lang.String TYPE_OTHER = "other"; field public static final java.lang.String TYPE_PHONE = "phone"; field public static final java.lang.String TYPE_UNKNOWN = ""; core/java/android/view/textclassifier/SmartSelection.java +14 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.view.textclassifier; import android.annotation.Nullable; import android.content.res.AssetFileDescriptor; /** Loading Loading @@ -146,11 +147,24 @@ final class SmartSelection { final String mCollection; /** float range: 0 - 1 */ final float mScore; @Nullable final DatetimeParseResult mDatetime; ClassificationResult(String collection, float score) { mCollection = collection; mScore = score; mDatetime = null; } ClassificationResult(String collection, float score, DatetimeParseResult datetime) { mCollection = collection; mScore = score; mDatetime = datetime; } } /** Parsed date information for the classification result. */ static final class DatetimeParseResult { long mMsSinceEpoch; } /** Represents a result of Annotate call. */ Loading core/java/android/view/textclassifier/TextClassification.java +28 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.view.textclassifier.TextClassifier.EntityType; import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import java.util.Locale; import java.util.Map; Loading Loading @@ -592,6 +593,7 @@ public final class TextClassification { public static final class Options implements Parcelable { private @Nullable LocaleList mDefaultLocales; private @Nullable Calendar mReferenceTime; public Options() {} Loading @@ -605,6 +607,16 @@ public final class TextClassification { return this; } /** * @param referenceTime reference time based on which relative dates (e.g. "tomorrow" should * be interpreted. This should usually be the time when the text was originally * composed. If no reference time is set, now is used. */ public Options setReferenceTime(Calendar referenceTime) { mReferenceTime = referenceTime; return this; } /** * @return ordered list of locale preferences that can be used to disambiguate * the provided text. Loading @@ -614,6 +626,15 @@ public final class TextClassification { return mDefaultLocales; } /** * @return reference time based on which relative dates (e.g. "tomorrow") should be * interpreted. */ @Nullable public Calendar getReferenceTime() { return mReferenceTime; } @Override public int describeContents() { return 0; Loading @@ -625,6 +646,10 @@ public final class TextClassification { if (mDefaultLocales != null) { mDefaultLocales.writeToParcel(dest, flags); } dest.writeInt(mReferenceTime != null ? 1 : 0); if (mReferenceTime != null) { dest.writeSerializable(mReferenceTime); } } public static final Parcelable.Creator<Options> CREATOR = Loading @@ -644,6 +669,9 @@ public final class TextClassification { if (in.readInt() > 0) { mDefaultLocales = LocaleList.CREATOR.createFromParcel(in); } if (in.readInt() > 0) { mReferenceTime = (Calendar) in.readSerializable(); } } } Loading core/java/android/view/textclassifier/TextClassifier.java +17 −0 Original line number Diff line number Diff line Loading @@ -47,12 +47,26 @@ public interface TextClassifier { /** @hide */ String DEFAULT_LOG_TAG = "androidtc"; /** The TextClassifier failed to run. */ String TYPE_UNKNOWN = ""; /** The classifier ran, but didn't recognize a known entity. */ String TYPE_OTHER = "other"; /** E-mail address (e.g. "noreply@android.com"). */ String TYPE_EMAIL = "email"; /** Phone number (e.g. "555-123 456"). */ String TYPE_PHONE = "phone"; /** Physical address. */ String TYPE_ADDRESS = "address"; /** Web URL. */ String TYPE_URL = "url"; /** Time reference that is no more specific than a date. May be absolute such as "01/01/2000" or * relative like "tomorrow". **/ String TYPE_DATE = "date"; /** Time reference that includes a specific time. May be absolute such as "01/01/2000 5:30pm" or * relative like "tomorrow at 5:30pm". **/ String TYPE_DATE_TIME = "datetime"; /** Flight number in IATA format. */ String TYPE_FLIGHT_NUMBER = "flight"; /** @hide */ @Retention(RetentionPolicy.SOURCE) Loading @@ -63,6 +77,9 @@ public interface TextClassifier { TYPE_PHONE, TYPE_ADDRESS, TYPE_URL, TYPE_DATE, TYPE_DATE_TIME, TYPE_FLIGHT_NUMBER, }) @interface EntityType {} Loading core/java/android/view/textclassifier/TextClassifierImpl.java +145 −64 Original line number Diff line number Diff line Loading @@ -18,7 +18,9 @@ package android.view.textclassifier; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.SearchManager; import android.content.ComponentName; import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; Loading @@ -28,6 +30,7 @@ import android.net.Uri; import android.os.LocaleList; import android.os.ParcelFileDescriptor; import android.provider.Browser; import android.provider.CalendarContract; import android.provider.ContactsContract; import android.provider.Settings; import android.text.util.Linkify; Loading @@ -42,6 +45,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.HashMap; Loading @@ -49,6 +53,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; Loading @@ -73,7 +78,10 @@ final class TextClassifierImpl implements TextClassifier { TextClassifier.TYPE_ADDRESS, TextClassifier.TYPE_EMAIL, TextClassifier.TYPE_PHONE, TextClassifier.TYPE_URL)); TextClassifier.TYPE_URL, 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, Loading Loading @@ -167,9 +175,8 @@ final class TextClassifierImpl implements TextClassifier { .classifyText(string, startIndex, endIndex, getHintFlags(string, startIndex, endIndex)); if (results.length > 0) { final TextClassification classificationResult = createClassificationResult(results, string, startIndex, endIndex); return classificationResult; return createClassificationResult( results, string, startIndex, endIndex, options.getReferenceTime()); } } } catch (Throwable t) { Loading Loading @@ -410,18 +417,24 @@ final class TextClassifierImpl implements TextClassifier { private TextClassification createClassificationResult( SmartSelection.ClassificationResult[] classifications, String text, int start, int end) { String text, int start, int end, @Nullable Calendar referenceTime) { final String classifiedText = text.substring(start, end); final TextClassification.Builder builder = new TextClassification.Builder() .setText(classifiedText); final int size = classifications.length; SmartSelection.ClassificationResult highestScoringResult = null; float highestScore = Float.MIN_VALUE; for (int i = 0; i < size; i++) { builder.setEntityType(classifications[i].mCollection, classifications[i].mScore); if (classifications[i].mScore > highestScore) { highestScoringResult = classifications[i]; highestScore = classifications[i].mScore; } } final String type = getHighestScoringType(classifications); addActions(builder, IntentFactory.create(mContext, type, classifiedText)); addActions(builder, IntentFactory.create( mContext, referenceTime, highestScoringResult, classifiedText)); return builder.setSignature(getSignature(text, start, end)).build(); } Loading @@ -441,11 +454,10 @@ final class TextClassifierImpl implements TextClassifier { } if (resolveInfo != null && resolveInfo.activityInfo != null) { final String packageName = resolveInfo.activityInfo.packageName; CharSequence label; final String label = IntentFactory.getLabel(mContext, intent); Drawable icon; if ("android".equals(packageName)) { // Requires the chooser to find an activity to handle the intent. label = IntentFactory.getLabel(mContext, intent); icon = null; } else { // A default activity will handle the intent. Loading @@ -455,16 +467,11 @@ final class TextClassifierImpl implements TextClassifier { if (icon == null) { icon = resolveInfo.loadIcon(pm); } label = resolveInfo.activityInfo.loadLabel(pm); if (label == null) { label = resolveInfo.loadLabel(pm); } } final String labelString = (label != null) ? label.toString() : null; if (i == 0) { builder.setPrimaryAction(intent, labelString, icon); builder.setPrimaryAction(intent, label, icon); } else { builder.addSecondaryAction(intent, labelString, icon); builder.addSecondaryAction(intent, label, icon); } } } Loading @@ -483,23 +490,6 @@ final class TextClassifierImpl implements TextClassifier { return flag; } private static String getHighestScoringType(SmartSelection.ClassificationResult[] types) { if (types.length < 1) { return ""; } String type = types[0].mCollection; float highestScore = types[0].mScore; final int size = types.length; for (int i = 1; i < size; i++) { if (types[i].mScore > highestScore) { type = types[i].mCollection; highestScore = types[i].mScore; } } return type; } /** * Closes the ParcelFileDescriptor and logs any errors that occur. */ Loading @@ -514,37 +504,76 @@ final class TextClassifierImpl implements TextClassifier { /** * Creates intents based on the classification type. */ private static final class IntentFactory { static final class IntentFactory { private static final long MIN_EVENT_FUTURE_MILLIS = TimeUnit.MINUTES.toMillis(5); private static final long DEFAULT_EVENT_DURATION = TimeUnit.HOURS.toMillis(1); private IntentFactory() {} @NonNull public static List<Intent> create(Context context, String type, String text) { final List<Intent> intents = new ArrayList<>(); type = type.trim().toLowerCase(Locale.ENGLISH); public static List<Intent> create( Context context, @Nullable Calendar referenceTime, SmartSelection.ClassificationResult classification, String text) { final String type = classification.mCollection.trim().toLowerCase(Locale.ENGLISH); text = text.trim(); switch (type) { case TextClassifier.TYPE_EMAIL: intents.add(new Intent(Intent.ACTION_SENDTO) .setData(Uri.parse(String.format("mailto:%s", text)))); intents.add(new Intent(Intent.ACTION_INSERT_OR_EDIT) return createForEmail(text); case TextClassifier.TYPE_PHONE: return createForPhone(text); case TextClassifier.TYPE_ADDRESS: return createForAddress(text); case TextClassifier.TYPE_URL: return createForUrl(context, text); case TextClassifier.TYPE_DATE: case TextClassifier.TYPE_DATE_TIME: if (classification.mDatetime != null) { Calendar eventTime = Calendar.getInstance(); eventTime.setTimeInMillis(classification.mDatetime.mMsSinceEpoch); return createForDatetime(type, referenceTime, eventTime); } else { return new ArrayList<>(); } case TextClassifier.TYPE_FLIGHT_NUMBER: return createForFlight(text); default: return new ArrayList<>(); } } @NonNull private static List<Intent> createForEmail(String text) { return Arrays.asList( new Intent(Intent.ACTION_SENDTO) .setData(Uri.parse(String.format("mailto:%s", text))), new Intent(Intent.ACTION_INSERT_OR_EDIT) .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE) .putExtra(ContactsContract.Intents.Insert.EMAIL, text)); break; case TextClassifier.TYPE_PHONE: intents.add(new Intent(Intent.ACTION_DIAL) .setData(Uri.parse(String.format("tel:%s", text)))); intents.add(new Intent(Intent.ACTION_INSERT_OR_EDIT) } @NonNull private static List<Intent> createForPhone(String text) { return Arrays.asList( new Intent(Intent.ACTION_DIAL) .setData(Uri.parse(String.format("tel:%s", text))), 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) .putExtra(ContactsContract.Intents.Insert.PHONE, text), new Intent(Intent.ACTION_SENDTO) .setData(Uri.parse(String.format("smsto:%s", text)))); break; case TextClassifier.TYPE_ADDRESS: intents.add(new Intent(Intent.ACTION_VIEW) } @NonNull private static List<Intent> createForAddress(String text) { return Arrays.asList(new Intent(Intent.ACTION_VIEW) .setData(Uri.parse(String.format("geo:0,0?q=%s", text)))); break; case TextClassifier.TYPE_URL: } @NonNull private static List<Intent> createForUrl(Context context, String text) { final String httpPrefix = "http://"; final String httpsPrefix = "https://"; if (text.toLowerCase().startsWith(httpPrefix)) { Loading @@ -554,18 +583,60 @@ final class TextClassifierImpl implements TextClassifier { } else { text = httpPrefix + text; } intents.add(new Intent(Intent.ACTION_VIEW, Uri.parse(text)) return Arrays.asList(new Intent(Intent.ACTION_VIEW, Uri.parse(text)) .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName())); break; } @NonNull private static List<Intent> createForDatetime( String type, @Nullable Calendar referenceTime, Calendar eventTime) { if (referenceTime == null) { // If no reference time was given, use now. referenceTime = Calendar.getInstance(); } List<Intent> intents = new ArrayList<>(); intents.add(createCalendarViewIntent(eventTime)); final long millisSinceReference = eventTime.getTimeInMillis() - referenceTime.getTimeInMillis(); if (millisSinceReference > MIN_EVENT_FUTURE_MILLIS) { intents.add(createCalendarCreateEventIntent(eventTime, type)); } return intents; } @NonNull private static List<Intent> createForFlight(String text) { return Arrays.asList(new Intent(Intent.ACTION_WEB_SEARCH) .putExtra(SearchManager.QUERY, text)); } @NonNull private static Intent createCalendarViewIntent(Calendar eventTime) { Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon(); builder.appendPath("time"); ContentUris.appendId(builder, eventTime.getTimeInMillis()); return new Intent(Intent.ACTION_VIEW).setData(builder.build()); } @NonNull private static Intent createCalendarCreateEventIntent( Calendar eventTime, @EntityType String type) { final boolean isAllDay = TextClassifier.TYPE_DATE.equals(type); return new Intent(Intent.ACTION_INSERT) .setData(CalendarContract.Events.CONTENT_URI) .putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, isAllDay) .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, eventTime.getTimeInMillis()) .putExtra(CalendarContract.EXTRA_EVENT_END_TIME, eventTime.getTimeInMillis() + DEFAULT_EVENT_DURATION); } @Nullable public static String getLabel(Context context, @Nullable Intent intent) { if (intent == null || intent.getAction() == null) { return null; } final String authority = intent.getData() == null ? null : intent.getData().getAuthority(); switch (intent.getAction()) { case Intent.ACTION_DIAL: return context.getString(com.android.internal.R.string.dial); Loading @@ -578,6 +649,11 @@ final class TextClassifierImpl implements TextClassifier { default: return null; } case Intent.ACTION_INSERT: if (CalendarContract.AUTHORITY.equals(authority)) { return context.getString(com.android.internal.R.string.add_calendar_event); } return null; case Intent.ACTION_INSERT_OR_EDIT: switch (intent.getDataString()) { case ContactsContract.Contacts.CONTENT_ITEM_TYPE: Loading @@ -586,6 +662,9 @@ final class TextClassifierImpl implements TextClassifier { return null; } case Intent.ACTION_VIEW: if (CalendarContract.AUTHORITY.equals(authority)) { return context.getString(com.android.internal.R.string.view_calendar); } switch (intent.getScheme()) { case "geo": return context.getString(com.android.internal.R.string.map); Loading @@ -595,6 +674,8 @@ final class TextClassifierImpl implements TextClassifier { default: return null; } case Intent.ACTION_WEB_SEARCH: return context.getString(com.android.internal.R.string.view_flight); default: return null; } Loading Loading
api/current.txt +5 −0 Original line number Diff line number Diff line Loading @@ -49896,7 +49896,9 @@ package android.view.textclassifier { ctor public TextClassification.Options(); method public int describeContents(); method public android.os.LocaleList getDefaultLocales(); method public java.util.Calendar getReferenceTime(); method public android.view.textclassifier.TextClassification.Options setDefaultLocales(android.os.LocaleList); method public android.view.textclassifier.TextClassification.Options setReferenceTime(java.util.Calendar); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassification.Options> CREATOR; } Loading @@ -49921,7 +49923,10 @@ package android.view.textclassifier { 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_DATE = "date"; field public static final java.lang.String TYPE_DATE_TIME = "datetime"; field public static final java.lang.String TYPE_EMAIL = "email"; field public static final java.lang.String TYPE_FLIGHT_NUMBER = "flight"; field public static final java.lang.String TYPE_OTHER = "other"; field public static final java.lang.String TYPE_PHONE = "phone"; field public static final java.lang.String TYPE_UNKNOWN = "";
core/java/android/view/textclassifier/SmartSelection.java +14 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.view.textclassifier; import android.annotation.Nullable; import android.content.res.AssetFileDescriptor; /** Loading Loading @@ -146,11 +147,24 @@ final class SmartSelection { final String mCollection; /** float range: 0 - 1 */ final float mScore; @Nullable final DatetimeParseResult mDatetime; ClassificationResult(String collection, float score) { mCollection = collection; mScore = score; mDatetime = null; } ClassificationResult(String collection, float score, DatetimeParseResult datetime) { mCollection = collection; mScore = score; mDatetime = datetime; } } /** Parsed date information for the classification result. */ static final class DatetimeParseResult { long mMsSinceEpoch; } /** Represents a result of Annotate call. */ Loading
core/java/android/view/textclassifier/TextClassification.java +28 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.view.textclassifier.TextClassifier.EntityType; import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import java.util.Locale; import java.util.Map; Loading Loading @@ -592,6 +593,7 @@ public final class TextClassification { public static final class Options implements Parcelable { private @Nullable LocaleList mDefaultLocales; private @Nullable Calendar mReferenceTime; public Options() {} Loading @@ -605,6 +607,16 @@ public final class TextClassification { return this; } /** * @param referenceTime reference time based on which relative dates (e.g. "tomorrow" should * be interpreted. This should usually be the time when the text was originally * composed. If no reference time is set, now is used. */ public Options setReferenceTime(Calendar referenceTime) { mReferenceTime = referenceTime; return this; } /** * @return ordered list of locale preferences that can be used to disambiguate * the provided text. Loading @@ -614,6 +626,15 @@ public final class TextClassification { return mDefaultLocales; } /** * @return reference time based on which relative dates (e.g. "tomorrow") should be * interpreted. */ @Nullable public Calendar getReferenceTime() { return mReferenceTime; } @Override public int describeContents() { return 0; Loading @@ -625,6 +646,10 @@ public final class TextClassification { if (mDefaultLocales != null) { mDefaultLocales.writeToParcel(dest, flags); } dest.writeInt(mReferenceTime != null ? 1 : 0); if (mReferenceTime != null) { dest.writeSerializable(mReferenceTime); } } public static final Parcelable.Creator<Options> CREATOR = Loading @@ -644,6 +669,9 @@ public final class TextClassification { if (in.readInt() > 0) { mDefaultLocales = LocaleList.CREATOR.createFromParcel(in); } if (in.readInt() > 0) { mReferenceTime = (Calendar) in.readSerializable(); } } } Loading
core/java/android/view/textclassifier/TextClassifier.java +17 −0 Original line number Diff line number Diff line Loading @@ -47,12 +47,26 @@ public interface TextClassifier { /** @hide */ String DEFAULT_LOG_TAG = "androidtc"; /** The TextClassifier failed to run. */ String TYPE_UNKNOWN = ""; /** The classifier ran, but didn't recognize a known entity. */ String TYPE_OTHER = "other"; /** E-mail address (e.g. "noreply@android.com"). */ String TYPE_EMAIL = "email"; /** Phone number (e.g. "555-123 456"). */ String TYPE_PHONE = "phone"; /** Physical address. */ String TYPE_ADDRESS = "address"; /** Web URL. */ String TYPE_URL = "url"; /** Time reference that is no more specific than a date. May be absolute such as "01/01/2000" or * relative like "tomorrow". **/ String TYPE_DATE = "date"; /** Time reference that includes a specific time. May be absolute such as "01/01/2000 5:30pm" or * relative like "tomorrow at 5:30pm". **/ String TYPE_DATE_TIME = "datetime"; /** Flight number in IATA format. */ String TYPE_FLIGHT_NUMBER = "flight"; /** @hide */ @Retention(RetentionPolicy.SOURCE) Loading @@ -63,6 +77,9 @@ public interface TextClassifier { TYPE_PHONE, TYPE_ADDRESS, TYPE_URL, TYPE_DATE, TYPE_DATE_TIME, TYPE_FLIGHT_NUMBER, }) @interface EntityType {} Loading
core/java/android/view/textclassifier/TextClassifierImpl.java +145 −64 Original line number Diff line number Diff line Loading @@ -18,7 +18,9 @@ package android.view.textclassifier; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.SearchManager; import android.content.ComponentName; import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; Loading @@ -28,6 +30,7 @@ import android.net.Uri; import android.os.LocaleList; import android.os.ParcelFileDescriptor; import android.provider.Browser; import android.provider.CalendarContract; import android.provider.ContactsContract; import android.provider.Settings; import android.text.util.Linkify; Loading @@ -42,6 +45,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.HashMap; Loading @@ -49,6 +53,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; Loading @@ -73,7 +78,10 @@ final class TextClassifierImpl implements TextClassifier { TextClassifier.TYPE_ADDRESS, TextClassifier.TYPE_EMAIL, TextClassifier.TYPE_PHONE, TextClassifier.TYPE_URL)); TextClassifier.TYPE_URL, 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, Loading Loading @@ -167,9 +175,8 @@ final class TextClassifierImpl implements TextClassifier { .classifyText(string, startIndex, endIndex, getHintFlags(string, startIndex, endIndex)); if (results.length > 0) { final TextClassification classificationResult = createClassificationResult(results, string, startIndex, endIndex); return classificationResult; return createClassificationResult( results, string, startIndex, endIndex, options.getReferenceTime()); } } } catch (Throwable t) { Loading Loading @@ -410,18 +417,24 @@ final class TextClassifierImpl implements TextClassifier { private TextClassification createClassificationResult( SmartSelection.ClassificationResult[] classifications, String text, int start, int end) { String text, int start, int end, @Nullable Calendar referenceTime) { final String classifiedText = text.substring(start, end); final TextClassification.Builder builder = new TextClassification.Builder() .setText(classifiedText); final int size = classifications.length; SmartSelection.ClassificationResult highestScoringResult = null; float highestScore = Float.MIN_VALUE; for (int i = 0; i < size; i++) { builder.setEntityType(classifications[i].mCollection, classifications[i].mScore); if (classifications[i].mScore > highestScore) { highestScoringResult = classifications[i]; highestScore = classifications[i].mScore; } } final String type = getHighestScoringType(classifications); addActions(builder, IntentFactory.create(mContext, type, classifiedText)); addActions(builder, IntentFactory.create( mContext, referenceTime, highestScoringResult, classifiedText)); return builder.setSignature(getSignature(text, start, end)).build(); } Loading @@ -441,11 +454,10 @@ final class TextClassifierImpl implements TextClassifier { } if (resolveInfo != null && resolveInfo.activityInfo != null) { final String packageName = resolveInfo.activityInfo.packageName; CharSequence label; final String label = IntentFactory.getLabel(mContext, intent); Drawable icon; if ("android".equals(packageName)) { // Requires the chooser to find an activity to handle the intent. label = IntentFactory.getLabel(mContext, intent); icon = null; } else { // A default activity will handle the intent. Loading @@ -455,16 +467,11 @@ final class TextClassifierImpl implements TextClassifier { if (icon == null) { icon = resolveInfo.loadIcon(pm); } label = resolveInfo.activityInfo.loadLabel(pm); if (label == null) { label = resolveInfo.loadLabel(pm); } } final String labelString = (label != null) ? label.toString() : null; if (i == 0) { builder.setPrimaryAction(intent, labelString, icon); builder.setPrimaryAction(intent, label, icon); } else { builder.addSecondaryAction(intent, labelString, icon); builder.addSecondaryAction(intent, label, icon); } } } Loading @@ -483,23 +490,6 @@ final class TextClassifierImpl implements TextClassifier { return flag; } private static String getHighestScoringType(SmartSelection.ClassificationResult[] types) { if (types.length < 1) { return ""; } String type = types[0].mCollection; float highestScore = types[0].mScore; final int size = types.length; for (int i = 1; i < size; i++) { if (types[i].mScore > highestScore) { type = types[i].mCollection; highestScore = types[i].mScore; } } return type; } /** * Closes the ParcelFileDescriptor and logs any errors that occur. */ Loading @@ -514,37 +504,76 @@ final class TextClassifierImpl implements TextClassifier { /** * Creates intents based on the classification type. */ private static final class IntentFactory { static final class IntentFactory { private static final long MIN_EVENT_FUTURE_MILLIS = TimeUnit.MINUTES.toMillis(5); private static final long DEFAULT_EVENT_DURATION = TimeUnit.HOURS.toMillis(1); private IntentFactory() {} @NonNull public static List<Intent> create(Context context, String type, String text) { final List<Intent> intents = new ArrayList<>(); type = type.trim().toLowerCase(Locale.ENGLISH); public static List<Intent> create( Context context, @Nullable Calendar referenceTime, SmartSelection.ClassificationResult classification, String text) { final String type = classification.mCollection.trim().toLowerCase(Locale.ENGLISH); text = text.trim(); switch (type) { case TextClassifier.TYPE_EMAIL: intents.add(new Intent(Intent.ACTION_SENDTO) .setData(Uri.parse(String.format("mailto:%s", text)))); intents.add(new Intent(Intent.ACTION_INSERT_OR_EDIT) return createForEmail(text); case TextClassifier.TYPE_PHONE: return createForPhone(text); case TextClassifier.TYPE_ADDRESS: return createForAddress(text); case TextClassifier.TYPE_URL: return createForUrl(context, text); case TextClassifier.TYPE_DATE: case TextClassifier.TYPE_DATE_TIME: if (classification.mDatetime != null) { Calendar eventTime = Calendar.getInstance(); eventTime.setTimeInMillis(classification.mDatetime.mMsSinceEpoch); return createForDatetime(type, referenceTime, eventTime); } else { return new ArrayList<>(); } case TextClassifier.TYPE_FLIGHT_NUMBER: return createForFlight(text); default: return new ArrayList<>(); } } @NonNull private static List<Intent> createForEmail(String text) { return Arrays.asList( new Intent(Intent.ACTION_SENDTO) .setData(Uri.parse(String.format("mailto:%s", text))), new Intent(Intent.ACTION_INSERT_OR_EDIT) .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE) .putExtra(ContactsContract.Intents.Insert.EMAIL, text)); break; case TextClassifier.TYPE_PHONE: intents.add(new Intent(Intent.ACTION_DIAL) .setData(Uri.parse(String.format("tel:%s", text)))); intents.add(new Intent(Intent.ACTION_INSERT_OR_EDIT) } @NonNull private static List<Intent> createForPhone(String text) { return Arrays.asList( new Intent(Intent.ACTION_DIAL) .setData(Uri.parse(String.format("tel:%s", text))), 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) .putExtra(ContactsContract.Intents.Insert.PHONE, text), new Intent(Intent.ACTION_SENDTO) .setData(Uri.parse(String.format("smsto:%s", text)))); break; case TextClassifier.TYPE_ADDRESS: intents.add(new Intent(Intent.ACTION_VIEW) } @NonNull private static List<Intent> createForAddress(String text) { return Arrays.asList(new Intent(Intent.ACTION_VIEW) .setData(Uri.parse(String.format("geo:0,0?q=%s", text)))); break; case TextClassifier.TYPE_URL: } @NonNull private static List<Intent> createForUrl(Context context, String text) { final String httpPrefix = "http://"; final String httpsPrefix = "https://"; if (text.toLowerCase().startsWith(httpPrefix)) { Loading @@ -554,18 +583,60 @@ final class TextClassifierImpl implements TextClassifier { } else { text = httpPrefix + text; } intents.add(new Intent(Intent.ACTION_VIEW, Uri.parse(text)) return Arrays.asList(new Intent(Intent.ACTION_VIEW, Uri.parse(text)) .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName())); break; } @NonNull private static List<Intent> createForDatetime( String type, @Nullable Calendar referenceTime, Calendar eventTime) { if (referenceTime == null) { // If no reference time was given, use now. referenceTime = Calendar.getInstance(); } List<Intent> intents = new ArrayList<>(); intents.add(createCalendarViewIntent(eventTime)); final long millisSinceReference = eventTime.getTimeInMillis() - referenceTime.getTimeInMillis(); if (millisSinceReference > MIN_EVENT_FUTURE_MILLIS) { intents.add(createCalendarCreateEventIntent(eventTime, type)); } return intents; } @NonNull private static List<Intent> createForFlight(String text) { return Arrays.asList(new Intent(Intent.ACTION_WEB_SEARCH) .putExtra(SearchManager.QUERY, text)); } @NonNull private static Intent createCalendarViewIntent(Calendar eventTime) { Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon(); builder.appendPath("time"); ContentUris.appendId(builder, eventTime.getTimeInMillis()); return new Intent(Intent.ACTION_VIEW).setData(builder.build()); } @NonNull private static Intent createCalendarCreateEventIntent( Calendar eventTime, @EntityType String type) { final boolean isAllDay = TextClassifier.TYPE_DATE.equals(type); return new Intent(Intent.ACTION_INSERT) .setData(CalendarContract.Events.CONTENT_URI) .putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, isAllDay) .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, eventTime.getTimeInMillis()) .putExtra(CalendarContract.EXTRA_EVENT_END_TIME, eventTime.getTimeInMillis() + DEFAULT_EVENT_DURATION); } @Nullable public static String getLabel(Context context, @Nullable Intent intent) { if (intent == null || intent.getAction() == null) { return null; } final String authority = intent.getData() == null ? null : intent.getData().getAuthority(); switch (intent.getAction()) { case Intent.ACTION_DIAL: return context.getString(com.android.internal.R.string.dial); Loading @@ -578,6 +649,11 @@ final class TextClassifierImpl implements TextClassifier { default: return null; } case Intent.ACTION_INSERT: if (CalendarContract.AUTHORITY.equals(authority)) { return context.getString(com.android.internal.R.string.add_calendar_event); } return null; case Intent.ACTION_INSERT_OR_EDIT: switch (intent.getDataString()) { case ContactsContract.Contacts.CONTENT_ITEM_TYPE: Loading @@ -586,6 +662,9 @@ final class TextClassifierImpl implements TextClassifier { return null; } case Intent.ACTION_VIEW: if (CalendarContract.AUTHORITY.equals(authority)) { return context.getString(com.android.internal.R.string.view_calendar); } switch (intent.getScheme()) { case "geo": return context.getString(com.android.internal.R.string.map); Loading @@ -595,6 +674,8 @@ final class TextClassifierImpl implements TextClassifier { default: return null; } case Intent.ACTION_WEB_SEARCH: return context.getString(com.android.internal.R.string.view_flight); default: return null; } Loading