Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4bad09cd authored by Abodunrinwa Toki's avatar Abodunrinwa Toki
Browse files

Introduce Translate intent.

Action: ACTION_TRANSLATE
Text: EXTRA_TEXT

TODO: Consider introducing a "translate" schema to pass the text to
translate instead of using a string extra to define this.

Bug: 116020587
Test: manually tested by selecting foreign language text and watching
      for a "Translate" action.
Change-Id: I046157f4b3e1aa60afaf3f123fdeecf406f712dc
parent abbcfb2d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -10116,6 +10116,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";
+11 −0
Original line number Diff line number Diff line
@@ -3586,6 +3586,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.
+99 −31
Original line number Diff line number Diff line
@@ -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;
@@ -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()) {
@@ -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;
@@ -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) {
@@ -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
@@ -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
@@ -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
@@ -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()));
        }
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -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. -->
+2 −0
Original line number Diff line number Diff line
@@ -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" />