• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
2   * Copyright (C) 2019 The Android Open Source Project
3   *
4   * Licensed under the Apache License, Version 2.0 (the "License");
5   * you may not use this file except in compliance with the License.
6   * You may obtain a copy of the License at
7   *
8   *      http://www.apache.org/licenses/LICENSE-2.0
9   *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package android.view.textclassifier;
18  
19  import android.annotation.Nullable;
20  import android.app.RemoteAction;
21  import android.content.Intent;
22  import android.icu.util.ULocale;
23  import android.os.Bundle;
24  
25  import com.android.internal.util.ArrayUtils;
26  
27  import com.google.android.textclassifier.AnnotatorModel;
28  
29  import java.util.ArrayList;
30  import java.util.List;
31  
32  /**
33   * Utility class for inserting and retrieving data in TextClassifier request/response extras.
34   * @hide
35   */
36  // TODO: Make this a TestApi for CTS testing.
37  public final class ExtrasUtils {
38  
39      // Keys for response objects.
40      private static final String SERIALIZED_ENTITIES_DATA = "serialized-entities-data";
41      private static final String ENTITIES_EXTRAS = "entities-extras";
42      private static final String ACTION_INTENT = "action-intent";
43      private static final String ACTIONS_INTENTS = "actions-intents";
44      private static final String FOREIGN_LANGUAGE = "foreign-language";
45      private static final String ENTITY_TYPE = "entity-type";
46      private static final String SCORE = "score";
47      private static final String MODEL_VERSION = "model-version";
48      private static final String MODEL_NAME = "model-name";
49      private static final String TEXT_LANGUAGES = "text-languages";
50      private static final String ENTITIES = "entities";
51  
52      // Keys for request objects.
53      private static final String IS_SERIALIZED_ENTITY_DATA_ENABLED =
54              "is-serialized-entity-data-enabled";
55  
ExtrasUtils()56      private ExtrasUtils() {}
57  
58      /**
59       * Bundles and returns foreign language detection information for TextClassifier responses.
60       */
createForeignLanguageExtra( String language, float score, int modelVersion)61      static Bundle createForeignLanguageExtra(
62              String language, float score, int modelVersion) {
63          final Bundle bundle = new Bundle();
64          bundle.putString(ENTITY_TYPE, language);
65          bundle.putFloat(SCORE, score);
66          bundle.putInt(MODEL_VERSION, modelVersion);
67          bundle.putString(MODEL_NAME, "langId_v" + modelVersion);
68          return bundle;
69      }
70  
71      /**
72       * Stores {@code extra} as foreign language information in TextClassifier response object's
73       * extras {@code container}.
74       *
75       * @see #getForeignLanguageExtra(TextClassification)
76       */
putForeignLanguageExtra(Bundle container, Bundle extra)77      static void putForeignLanguageExtra(Bundle container, Bundle extra) {
78          container.putParcelable(FOREIGN_LANGUAGE, extra);
79      }
80  
81      /**
82       * Returns foreign language detection information contained in the TextClassification object.
83       * responses.
84       *
85       * @see #putForeignLanguageExtra(Bundle, Bundle)
86       */
87      @Nullable
getForeignLanguageExtra(@ullable TextClassification classification)88      public static Bundle getForeignLanguageExtra(@Nullable TextClassification classification) {
89          if (classification == null) {
90              return null;
91          }
92          return classification.getExtras().getBundle(FOREIGN_LANGUAGE);
93      }
94  
95      /**
96       * @see #getTopLanguage(Intent)
97       */
putTopLanguageScores(Bundle container, EntityConfidence languageScores)98      static void putTopLanguageScores(Bundle container, EntityConfidence languageScores) {
99          final int maxSize = Math.min(3, languageScores.getEntities().size());
100          final String[] languages = languageScores.getEntities().subList(0, maxSize)
101                  .toArray(new String[0]);
102          final float[] scores = new float[languages.length];
103          for (int i = 0; i < languages.length; i++) {
104              scores[i] = languageScores.getConfidenceScore(languages[i]);
105          }
106          container.putStringArray(ENTITY_TYPE, languages);
107          container.putFloatArray(SCORE, scores);
108      }
109  
110      /**
111       * @see #putTopLanguageScores(Bundle, EntityConfidence)
112       */
113      @Nullable
getTopLanguage(@ullable Intent intent)114      public static ULocale getTopLanguage(@Nullable Intent intent) {
115          if (intent == null) {
116              return null;
117          }
118          final Bundle tcBundle = intent.getBundleExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER);
119          if (tcBundle == null) {
120              return null;
121          }
122          final Bundle textLanguagesExtra = tcBundle.getBundle(TEXT_LANGUAGES);
123          if (textLanguagesExtra == null) {
124              return null;
125          }
126          final String[] languages = textLanguagesExtra.getStringArray(ENTITY_TYPE);
127          final float[] scores = textLanguagesExtra.getFloatArray(SCORE);
128          if (languages == null || scores == null
129                  || languages.length == 0 || languages.length != scores.length) {
130              return null;
131          }
132          int highestScoringIndex = 0;
133          for (int i = 1; i < languages.length; i++) {
134              if (scores[highestScoringIndex] < scores[i]) {
135                  highestScoringIndex = i;
136              }
137          }
138          return ULocale.forLanguageTag(languages[highestScoringIndex]);
139      }
140  
putTextLanguagesExtra(Bundle container, Bundle extra)141      public static void putTextLanguagesExtra(Bundle container, Bundle extra) {
142          container.putBundle(TEXT_LANGUAGES, extra);
143      }
144  
145      /**
146       * Stores {@code actionIntents} information in TextClassifier response object's extras
147       * {@code container}.
148       */
putActionsIntents(Bundle container, ArrayList<Intent> actionsIntents)149      static void putActionsIntents(Bundle container, ArrayList<Intent> actionsIntents) {
150          container.putParcelableArrayList(ACTIONS_INTENTS, actionsIntents);
151      }
152  
153      /**
154       * Stores {@code actionIntents} information in TextClassifier response object's extras
155       * {@code container}.
156       */
putActionIntent(Bundle container, @Nullable Intent actionIntent)157      public static void putActionIntent(Bundle container, @Nullable Intent actionIntent) {
158          container.putParcelable(ACTION_INTENT, actionIntent);
159      }
160  
161      /**
162       * Returns {@code actionIntent} information contained in a TextClassifier response object.
163       */
164      @Nullable
getActionIntent(Bundle container)165      public static Intent getActionIntent(Bundle container) {
166          return container.getParcelable(ACTION_INTENT);
167      }
168  
169      /**
170       * Stores serialized entity data information in TextClassifier response object's extras
171       * {@code container}.
172       */
putSerializedEntityData( Bundle container, @Nullable byte[] serializedEntityData)173      public static void putSerializedEntityData(
174              Bundle container, @Nullable byte[] serializedEntityData) {
175          container.putByteArray(SERIALIZED_ENTITIES_DATA, serializedEntityData);
176      }
177  
178      /**
179       * Returns serialized entity data information contained in a TextClassifier response
180       * object.
181       */
182      @Nullable
getSerializedEntityData(Bundle container)183      public static byte[] getSerializedEntityData(Bundle container) {
184          return container.getByteArray(SERIALIZED_ENTITIES_DATA);
185      }
186  
187      /**
188       * Stores {@code entities} information in TextClassifier response object's extras
189       * {@code container}.
190       *
191       * @see {@link #getCopyText(Bundle)}
192       */
putEntitiesExtras(Bundle container, @Nullable Bundle entitiesExtras)193      public static void putEntitiesExtras(Bundle container, @Nullable Bundle entitiesExtras) {
194          container.putParcelable(ENTITIES_EXTRAS, entitiesExtras);
195      }
196  
197      /**
198       * Returns {@code entities} information contained in a TextClassifier response object.
199       *
200       * @see {@link #putEntitiesExtras(Bundle, Bundle)}
201       */
202      @Nullable
getCopyText(Bundle container)203      public static String getCopyText(Bundle container) {
204          Bundle entitiesExtras = container.getParcelable(ENTITIES_EXTRAS);
205          if (entitiesExtras == null) {
206              return null;
207          }
208          return entitiesExtras.getString("text");
209      }
210  
211      /**
212       * Returns {@code actionIntents} information contained in the TextClassification object.
213       */
214      @Nullable
getActionsIntents(@ullable TextClassification classification)215      public static ArrayList<Intent> getActionsIntents(@Nullable TextClassification classification) {
216          if (classification == null) {
217              return null;
218          }
219          return classification.getExtras().getParcelableArrayList(ACTIONS_INTENTS);
220      }
221  
222      /**
223       * Returns the first action found in the {@code classification} object with an intent
224       * action string, {@code intentAction}.
225       */
226      @Nullable
findAction( @ullable TextClassification classification, @Nullable String intentAction)227      public static RemoteAction findAction(
228              @Nullable TextClassification classification, @Nullable String intentAction) {
229          if (classification == null || intentAction == null) {
230              return null;
231          }
232          final ArrayList<Intent> actionIntents = getActionsIntents(classification);
233          if (actionIntents != null) {
234              final int size = actionIntents.size();
235              for (int i = 0; i < size; i++) {
236                  final Intent intent = actionIntents.get(i);
237                  if (intent != null && intentAction.equals(intent.getAction())) {
238                      return classification.getActions().get(i);
239                  }
240              }
241          }
242          return null;
243      }
244  
245      /**
246       * Returns the first "translate" action found in the {@code classification} object.
247       */
248      @Nullable
findTranslateAction(@ullable TextClassification classification)249      public static RemoteAction findTranslateAction(@Nullable TextClassification classification) {
250          return findAction(classification, Intent.ACTION_TRANSLATE);
251      }
252  
253      /**
254       * Returns the entity type contained in the {@code extra}.
255       */
256      @Nullable
getEntityType(@ullable Bundle extra)257      public static String getEntityType(@Nullable Bundle extra) {
258          if (extra == null) {
259              return null;
260          }
261          return extra.getString(ENTITY_TYPE);
262      }
263  
264      /**
265       * Returns the score contained in the {@code extra}.
266       */
267      @Nullable
getScore(Bundle extra)268      public static float getScore(Bundle extra) {
269          final int defaultValue = -1;
270          if (extra == null) {
271              return defaultValue;
272          }
273          return extra.getFloat(SCORE, defaultValue);
274      }
275  
276      /**
277       * Returns the model name contained in the {@code extra}.
278       */
279      @Nullable
getModelName(@ullable Bundle extra)280      public static String getModelName(@Nullable Bundle extra) {
281          if (extra == null) {
282              return null;
283          }
284          return extra.getString(MODEL_NAME);
285      }
286  
287      /**
288       * Stores the entities from {@link AnnotatorModel.ClassificationResult} in {@code container}.
289       */
putEntities( Bundle container, @Nullable AnnotatorModel.ClassificationResult[] classifications)290      public static void putEntities(
291              Bundle container,
292              @Nullable AnnotatorModel.ClassificationResult[] classifications) {
293          if (ArrayUtils.isEmpty(classifications)) {
294              return;
295          }
296          ArrayList<Bundle> entitiesBundle = new ArrayList<>();
297          for (AnnotatorModel.ClassificationResult classification : classifications) {
298              if (classification == null) {
299                  continue;
300              }
301              Bundle entityBundle = new Bundle();
302              entityBundle.putString(ENTITY_TYPE, classification.getCollection());
303              entityBundle.putByteArray(
304                      SERIALIZED_ENTITIES_DATA,
305                      classification.getSerializedEntityData());
306              entitiesBundle.add(entityBundle);
307          }
308          if (!entitiesBundle.isEmpty()) {
309              container.putParcelableArrayList(ENTITIES, entitiesBundle);
310          }
311      }
312  
313      /**
314       * Returns a list of entities contained in the {@code extra}.
315       */
316      @Nullable
getEntities(Bundle container)317      public static List<Bundle> getEntities(Bundle container) {
318          return container.getParcelableArrayList(ENTITIES);
319      }
320  
321      /**
322       * Whether the annotator should populate serialized entity data into the result object.
323       */
isSerializedEntityDataEnabled(TextLinks.Request request)324      public static boolean isSerializedEntityDataEnabled(TextLinks.Request request) {
325          return request.getExtras().getBoolean(IS_SERIALIZED_ENTITY_DATA_ENABLED);
326      }
327  
328      /**
329       * To indicate whether the annotator should populate serialized entity data in the result
330       * object.
331       */
putIsSerializedEntityDataEnabled(Bundle bundle, boolean isEnabled)332      public static void putIsSerializedEntityDataEnabled(Bundle bundle, boolean isEnabled) {
333          bundle.putBoolean(IS_SERIALIZED_ENTITY_DATA_ENABLED, isEnabled);
334      }
335  }
336