Loading api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -10133,6 +10133,7 @@ package android.content { field public static final java.lang.String ACTION_TIMEZONE_CHANGED = "android.intent.action.TIMEZONE_CHANGED"; field public static final java.lang.String ACTION_TIME_CHANGED = "android.intent.action.TIME_SET"; field public static final java.lang.String ACTION_TIME_TICK = "android.intent.action.TIME_TICK"; field public static final java.lang.String ACTION_TRANSLATE = "android.intent.action.TRANSLATE"; field public static final java.lang.String ACTION_UID_REMOVED = "android.intent.action.UID_REMOVED"; field public static final deprecated java.lang.String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED"; field public static final deprecated java.lang.String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED"; core/java/android/content/Intent.java +11 −0 Original line number Diff line number Diff line Loading @@ -3589,6 +3589,17 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE"; /** * Activity Action: Perform text translation. * <p> * Input: {@link #EXTRA_TEXT getCharSequence(EXTRA_TEXT)} is the text to translate. * <p> * Output: nothing. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_TRANSLATE = "android.intent.action.TRANSLATE"; /** * Broadcast Action: List of dynamic sensor is changed due to new sensor being connected or * exisiting sensor being disconnected. Loading core/java/android/view/textclassifier/TextClassifierImpl.java +99 −31 Original line number Diff line number Diff line Loading @@ -58,7 +58,6 @@ import java.net.URLEncoder; import java.time.Instant; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; Loading Loading @@ -523,10 +522,10 @@ public final class TextClassifierImpl implements TextClassifier { final TextClassification.Builder builder = new TextClassification.Builder() .setText(classifiedText); final int size = classifications.length; final int typeCount = classifications.length; AnnotatorModel.ClassificationResult highestScoringResult = size > 0 ? classifications[0] : null; for (int i = 0; i < size; i++) { typeCount > 0 ? classifications[0] : null; for (int i = 0; i < typeCount; i++) { builder.setEntityType(classifications[i].getCollection(), classifications[i].getScore()); if (classifications[i].getScore() > highestScoringResult.getScore()) { Loading @@ -534,9 +533,12 @@ public final class TextClassifierImpl implements TextClassifier { } } // TODO: Make this configurable. final float foreignTextThreshold = typeCount == 0 ? 0.5f : 0.7f; boolean isPrimaryAction = true; for (LabeledIntent labeledIntent : IntentFactory.create( mContext, classifiedText, referenceTime, highestScoringResult)) { mContext, classifiedText, isForeignText(classifiedText, foreignTextThreshold), referenceTime, highestScoringResult)) { final RemoteAction action = labeledIntent.asRemoteAction(mContext); if (action == null) { continue; Loading @@ -558,6 +560,42 @@ public final class TextClassifierImpl implements TextClassifier { return builder.setId(createId(text, start, end)).build(); } private boolean isForeignText(String text, float threshold) { // TODO: Revisit this algorithm. try { final LangIdModel.LanguageResult[] langResults = getLangIdImpl().detectLanguages(text); if (langResults.length <= 0) { return false; } LangIdModel.LanguageResult highestScoringResult = langResults[0]; for (int i = 1; i < langResults.length; i++) { if (langResults[i].getScore() > highestScoringResult.getScore()) { highestScoringResult = langResults[i]; } } if (highestScoringResult.getScore() < threshold) { return false; } // TODO: Remove Log.d(LOG_TAG, String.format("Language detected: <%s:%s>", highestScoringResult.getLanguage(), highestScoringResult.getScore())); final Locale detected = new Locale(highestScoringResult.getLanguage()); final LocaleList deviceLocales = LocaleList.getDefault(); final int size = deviceLocales.size(); for (int i = 0; i < size; i++) { if (deviceLocales.get(i).getLanguage().equals(detected.getLanguage())) { return false; } } return true; } catch (Throwable t) { Log.e(LOG_TAG, "Error detecting foreign text. Ignored.", t); } return false; } @Override public void dump(@NonNull IndentingPrintWriter printWriter) { synchronized (mLock) { Loading Loading @@ -698,53 +736,67 @@ public final class TextClassifierImpl implements TextClassifier { public static List<LabeledIntent> create( Context context, String text, boolean foreignText, @Nullable Instant referenceTime, @Nullable AnnotatorModel.ClassificationResult classification) { final String type = classification != null ? classification.getCollection().trim().toLowerCase(Locale.ENGLISH) : null; : ""; text = text.trim(); final List<LabeledIntent> actions; switch (type) { case TextClassifier.TYPE_EMAIL: return createForEmail(context, text); actions = createForEmail(context, text); break; case TextClassifier.TYPE_PHONE: return createForPhone(context, text); actions = createForPhone(context, text); break; case TextClassifier.TYPE_ADDRESS: return createForAddress(context, text); actions = createForAddress(context, text); break; case TextClassifier.TYPE_URL: return createForUrl(context, text); case TextClassifier.TYPE_DATE: actions = createForUrl(context, text); break; case TextClassifier.TYPE_DATE: // fall through case TextClassifier.TYPE_DATE_TIME: if (classification.getDatetimeResult() != null) { final Instant parsedTime = Instant.ofEpochMilli( classification.getDatetimeResult().getTimeMsUtc()); return createForDatetime(context, type, referenceTime, parsedTime); actions = createForDatetime(context, type, referenceTime, parsedTime); } else { return new ArrayList<>(); actions = new ArrayList<>(); } break; case TextClassifier.TYPE_FLIGHT_NUMBER: return createForFlight(context, text); actions = createForFlight(context, text); break; default: return new ArrayList<>(); actions = new ArrayList<>(); break; } if (foreignText) { insertTranslateAction(actions, context, text); } return actions; } @NonNull private static List<LabeledIntent> createForEmail(Context context, String text) { return Arrays.asList( new LabeledIntent( final List<LabeledIntent> actions = new ArrayList<>(); actions.add(new LabeledIntent( context.getString(com.android.internal.R.string.email), context.getString(com.android.internal.R.string.email_desc), new Intent(Intent.ACTION_SENDTO) .setData(Uri.parse(String.format("mailto:%s", text))), LabeledIntent.DEFAULT_REQUEST_CODE), new LabeledIntent( LabeledIntent.DEFAULT_REQUEST_CODE)); actions.add(new LabeledIntent( context.getString(com.android.internal.R.string.add_contact), context.getString(com.android.internal.R.string.add_contact_desc), new Intent(Intent.ACTION_INSERT_OR_EDIT) .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE) .putExtra(ContactsContract.Intents.Insert.EMAIL, text), text.hashCode())); return actions; } @NonNull Loading Loading @@ -801,12 +853,14 @@ public final class TextClassifierImpl implements TextClassifier { if (Uri.parse(text).getScheme() == null) { text = "http://" + text; } return Arrays.asList(new LabeledIntent( final List<LabeledIntent> actions = new ArrayList<>(); actions.add(new LabeledIntent( context.getString(com.android.internal.R.string.browse), context.getString(com.android.internal.R.string.browse_desc), new Intent(Intent.ACTION_VIEW, Uri.parse(text)) .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()), LabeledIntent.DEFAULT_REQUEST_CODE)); return actions; } @NonNull Loading @@ -828,12 +882,14 @@ public final class TextClassifierImpl implements TextClassifier { @NonNull private static List<LabeledIntent> createForFlight(Context context, String text) { return Arrays.asList(new LabeledIntent( final List<LabeledIntent> actions = new ArrayList<>(); actions.add(new LabeledIntent( context.getString(com.android.internal.R.string.view_flight), context.getString(com.android.internal.R.string.view_flight_desc), new Intent(Intent.ACTION_WEB_SEARCH) .putExtra(SearchManager.QUERY, text), text.hashCode())); return actions; } @NonNull Loading Loading @@ -864,5 +920,17 @@ public final class TextClassifierImpl implements TextClassifier { parsedTime.toEpochMilli() + DEFAULT_EVENT_DURATION), parsedTime.hashCode()); } private static void insertTranslateAction( List<LabeledIntent> actions, Context context, String text) { actions.add(new LabeledIntent( context.getString(com.android.internal.R.string.translate), context.getString(com.android.internal.R.string.translate_desc), new Intent(Intent.ACTION_TRANSLATE) // TODO: Probably better to introduce a "translate" scheme instead of // using EXTRA_TEXT. .putExtra(Intent.EXTRA_TEXT, text), text.hashCode())); } } } core/res/res/values/strings.xml +6 −0 Original line number Diff line number Diff line Loading @@ -2989,6 +2989,12 @@ <!-- Accessibility description for an item in the text selection menu to track a flight [CHAR LIMIT=NONE] --> <string name="view_flight_desc">Track selected flight</string> <!-- Label for item in the text selection menu to translate selected text with a translation app. Should be a verb. [CHAR LIMIT=30] --> <string name="translate">Translate</string> <!-- Accessibility description for an item in the text selection menu to translate selected text with a translation app. [CHAR LIMIT=NONE] --> <string name="translate_desc">Translate selected text</string> <!-- If the device is getting low on internal storage, a notification is shown to the user. This is the title of that notification. --> <string name="low_internal_storage_view_title">Storage space running out</string> <!-- If the device is getting low on internal storage, a notification is shown to the user. This is the message of that notification. --> Loading core/res/res/values/symbols.xml +2 −0 Original line number Diff line number Diff line Loading @@ -574,6 +574,8 @@ <java-symbol type="string" name="add_calendar_event_desc" /> <java-symbol type="string" name="view_flight" /> <java-symbol type="string" name="view_flight_desc" /> <java-symbol type="string" name="translate" /> <java-symbol type="string" name="translate_desc" /> <java-symbol type="string" name="textSelectionCABTitle" /> <java-symbol type="string" name="BaMmi" /> <java-symbol type="string" name="CLIRDefaultOffNextCallOff" /> Loading Loading
api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -10133,6 +10133,7 @@ package android.content { field public static final java.lang.String ACTION_TIMEZONE_CHANGED = "android.intent.action.TIMEZONE_CHANGED"; field public static final java.lang.String ACTION_TIME_CHANGED = "android.intent.action.TIME_SET"; field public static final java.lang.String ACTION_TIME_TICK = "android.intent.action.TIME_TICK"; field public static final java.lang.String ACTION_TRANSLATE = "android.intent.action.TRANSLATE"; field public static final java.lang.String ACTION_UID_REMOVED = "android.intent.action.UID_REMOVED"; field public static final deprecated java.lang.String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED"; field public static final deprecated java.lang.String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED";
core/java/android/content/Intent.java +11 −0 Original line number Diff line number Diff line Loading @@ -3589,6 +3589,17 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE"; /** * Activity Action: Perform text translation. * <p> * Input: {@link #EXTRA_TEXT getCharSequence(EXTRA_TEXT)} is the text to translate. * <p> * Output: nothing. */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_TRANSLATE = "android.intent.action.TRANSLATE"; /** * Broadcast Action: List of dynamic sensor is changed due to new sensor being connected or * exisiting sensor being disconnected. Loading
core/java/android/view/textclassifier/TextClassifierImpl.java +99 −31 Original line number Diff line number Diff line Loading @@ -58,7 +58,6 @@ import java.net.URLEncoder; import java.time.Instant; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; Loading Loading @@ -523,10 +522,10 @@ public final class TextClassifierImpl implements TextClassifier { final TextClassification.Builder builder = new TextClassification.Builder() .setText(classifiedText); final int size = classifications.length; final int typeCount = classifications.length; AnnotatorModel.ClassificationResult highestScoringResult = size > 0 ? classifications[0] : null; for (int i = 0; i < size; i++) { typeCount > 0 ? classifications[0] : null; for (int i = 0; i < typeCount; i++) { builder.setEntityType(classifications[i].getCollection(), classifications[i].getScore()); if (classifications[i].getScore() > highestScoringResult.getScore()) { Loading @@ -534,9 +533,12 @@ public final class TextClassifierImpl implements TextClassifier { } } // TODO: Make this configurable. final float foreignTextThreshold = typeCount == 0 ? 0.5f : 0.7f; boolean isPrimaryAction = true; for (LabeledIntent labeledIntent : IntentFactory.create( mContext, classifiedText, referenceTime, highestScoringResult)) { mContext, classifiedText, isForeignText(classifiedText, foreignTextThreshold), referenceTime, highestScoringResult)) { final RemoteAction action = labeledIntent.asRemoteAction(mContext); if (action == null) { continue; Loading @@ -558,6 +560,42 @@ public final class TextClassifierImpl implements TextClassifier { return builder.setId(createId(text, start, end)).build(); } private boolean isForeignText(String text, float threshold) { // TODO: Revisit this algorithm. try { final LangIdModel.LanguageResult[] langResults = getLangIdImpl().detectLanguages(text); if (langResults.length <= 0) { return false; } LangIdModel.LanguageResult highestScoringResult = langResults[0]; for (int i = 1; i < langResults.length; i++) { if (langResults[i].getScore() > highestScoringResult.getScore()) { highestScoringResult = langResults[i]; } } if (highestScoringResult.getScore() < threshold) { return false; } // TODO: Remove Log.d(LOG_TAG, String.format("Language detected: <%s:%s>", highestScoringResult.getLanguage(), highestScoringResult.getScore())); final Locale detected = new Locale(highestScoringResult.getLanguage()); final LocaleList deviceLocales = LocaleList.getDefault(); final int size = deviceLocales.size(); for (int i = 0; i < size; i++) { if (deviceLocales.get(i).getLanguage().equals(detected.getLanguage())) { return false; } } return true; } catch (Throwable t) { Log.e(LOG_TAG, "Error detecting foreign text. Ignored.", t); } return false; } @Override public void dump(@NonNull IndentingPrintWriter printWriter) { synchronized (mLock) { Loading Loading @@ -698,53 +736,67 @@ public final class TextClassifierImpl implements TextClassifier { public static List<LabeledIntent> create( Context context, String text, boolean foreignText, @Nullable Instant referenceTime, @Nullable AnnotatorModel.ClassificationResult classification) { final String type = classification != null ? classification.getCollection().trim().toLowerCase(Locale.ENGLISH) : null; : ""; text = text.trim(); final List<LabeledIntent> actions; switch (type) { case TextClassifier.TYPE_EMAIL: return createForEmail(context, text); actions = createForEmail(context, text); break; case TextClassifier.TYPE_PHONE: return createForPhone(context, text); actions = createForPhone(context, text); break; case TextClassifier.TYPE_ADDRESS: return createForAddress(context, text); actions = createForAddress(context, text); break; case TextClassifier.TYPE_URL: return createForUrl(context, text); case TextClassifier.TYPE_DATE: actions = createForUrl(context, text); break; case TextClassifier.TYPE_DATE: // fall through case TextClassifier.TYPE_DATE_TIME: if (classification.getDatetimeResult() != null) { final Instant parsedTime = Instant.ofEpochMilli( classification.getDatetimeResult().getTimeMsUtc()); return createForDatetime(context, type, referenceTime, parsedTime); actions = createForDatetime(context, type, referenceTime, parsedTime); } else { return new ArrayList<>(); actions = new ArrayList<>(); } break; case TextClassifier.TYPE_FLIGHT_NUMBER: return createForFlight(context, text); actions = createForFlight(context, text); break; default: return new ArrayList<>(); actions = new ArrayList<>(); break; } if (foreignText) { insertTranslateAction(actions, context, text); } return actions; } @NonNull private static List<LabeledIntent> createForEmail(Context context, String text) { return Arrays.asList( new LabeledIntent( final List<LabeledIntent> actions = new ArrayList<>(); actions.add(new LabeledIntent( context.getString(com.android.internal.R.string.email), context.getString(com.android.internal.R.string.email_desc), new Intent(Intent.ACTION_SENDTO) .setData(Uri.parse(String.format("mailto:%s", text))), LabeledIntent.DEFAULT_REQUEST_CODE), new LabeledIntent( LabeledIntent.DEFAULT_REQUEST_CODE)); actions.add(new LabeledIntent( context.getString(com.android.internal.R.string.add_contact), context.getString(com.android.internal.R.string.add_contact_desc), new Intent(Intent.ACTION_INSERT_OR_EDIT) .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE) .putExtra(ContactsContract.Intents.Insert.EMAIL, text), text.hashCode())); return actions; } @NonNull Loading Loading @@ -801,12 +853,14 @@ public final class TextClassifierImpl implements TextClassifier { if (Uri.parse(text).getScheme() == null) { text = "http://" + text; } return Arrays.asList(new LabeledIntent( final List<LabeledIntent> actions = new ArrayList<>(); actions.add(new LabeledIntent( context.getString(com.android.internal.R.string.browse), context.getString(com.android.internal.R.string.browse_desc), new Intent(Intent.ACTION_VIEW, Uri.parse(text)) .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()), LabeledIntent.DEFAULT_REQUEST_CODE)); return actions; } @NonNull Loading @@ -828,12 +882,14 @@ public final class TextClassifierImpl implements TextClassifier { @NonNull private static List<LabeledIntent> createForFlight(Context context, String text) { return Arrays.asList(new LabeledIntent( final List<LabeledIntent> actions = new ArrayList<>(); actions.add(new LabeledIntent( context.getString(com.android.internal.R.string.view_flight), context.getString(com.android.internal.R.string.view_flight_desc), new Intent(Intent.ACTION_WEB_SEARCH) .putExtra(SearchManager.QUERY, text), text.hashCode())); return actions; } @NonNull Loading Loading @@ -864,5 +920,17 @@ public final class TextClassifierImpl implements TextClassifier { parsedTime.toEpochMilli() + DEFAULT_EVENT_DURATION), parsedTime.hashCode()); } private static void insertTranslateAction( List<LabeledIntent> actions, Context context, String text) { actions.add(new LabeledIntent( context.getString(com.android.internal.R.string.translate), context.getString(com.android.internal.R.string.translate_desc), new Intent(Intent.ACTION_TRANSLATE) // TODO: Probably better to introduce a "translate" scheme instead of // using EXTRA_TEXT. .putExtra(Intent.EXTRA_TEXT, text), text.hashCode())); } } }
core/res/res/values/strings.xml +6 −0 Original line number Diff line number Diff line Loading @@ -2989,6 +2989,12 @@ <!-- Accessibility description for an item in the text selection menu to track a flight [CHAR LIMIT=NONE] --> <string name="view_flight_desc">Track selected flight</string> <!-- Label for item in the text selection menu to translate selected text with a translation app. Should be a verb. [CHAR LIMIT=30] --> <string name="translate">Translate</string> <!-- Accessibility description for an item in the text selection menu to translate selected text with a translation app. [CHAR LIMIT=NONE] --> <string name="translate_desc">Translate selected text</string> <!-- If the device is getting low on internal storage, a notification is shown to the user. This is the title of that notification. --> <string name="low_internal_storage_view_title">Storage space running out</string> <!-- If the device is getting low on internal storage, a notification is shown to the user. This is the message of that notification. --> Loading
core/res/res/values/symbols.xml +2 −0 Original line number Diff line number Diff line Loading @@ -574,6 +574,8 @@ <java-symbol type="string" name="add_calendar_event_desc" /> <java-symbol type="string" name="view_flight" /> <java-symbol type="string" name="view_flight_desc" /> <java-symbol type="string" name="translate" /> <java-symbol type="string" name="translate_desc" /> <java-symbol type="string" name="textSelectionCABTitle" /> <java-symbol type="string" name="BaMmi" /> <java-symbol type="string" name="CLIRDefaultOffNextCallOff" /> Loading