blob: 11e0e2ca072c0b836edd172d6aeae49ace5a02d9 [file] [log] [blame]
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +00001/*
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
17package android.view.textclassifier;
18
19import android.annotation.Nullable;
20import android.app.RemoteAction;
21import android.content.Intent;
Tony Mak72e17972019-03-16 10:28:42 +000022import android.icu.util.ULocale;
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +000023import android.os.Bundle;
24
Tony Makfdb35542019-03-22 12:01:50 +000025import com.android.internal.util.ArrayUtils;
26
27import com.google.android.textclassifier.AnnotatorModel;
28
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +000029import java.util.ArrayList;
Tony Makfdb35542019-03-22 12:01:50 +000030import java.util.List;
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +000031
32/**
33 * Utility class for inserting and retrieving data in TextClassifier request/response extras.
34 * @hide
35 */
Tony Mak72e17972019-03-16 10:28:42 +000036// TODO: Make this a TestApi for CTS testing.
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +000037public final class ExtrasUtils {
38
Tony Makb6afd3c2019-04-05 15:45:18 +010039 // Keys for response objects.
Tony Makfdb35542019-03-22 12:01:50 +000040 private static final String SERIALIZED_ENTITIES_DATA = "serialized-entities-data";
Tony Mak09214422019-03-01 18:25:23 +000041 private static final String ENTITIES_EXTRAS = "entities-extras";
Tony Makc12035e2019-02-26 17:45:34 +000042 private static final String ACTION_INTENT = "action-intent";
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +000043 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";
Tony Mak72e17972019-03-16 10:28:42 +000049 private static final String TEXT_LANGUAGES = "text-languages";
Tony Makfdb35542019-03-22 12:01:50 +000050 private static final String ENTITIES = "entities";
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +000051
Tony Makb6afd3c2019-04-05 15:45:18 +010052 // Keys for request objects.
53 private static final String IS_SERIALIZED_ENTITY_DATA_ENABLED =
54 "is-serialized-entity-data-enabled";
55
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +000056 private ExtrasUtils() {}
57
58 /**
59 * Bundles and returns foreign language detection information for TextClassifier responses.
60 */
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}.
Tony Mak72e17972019-03-16 10:28:42 +000074 *
75 * @see #getForeignLanguageExtra(TextClassification)
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +000076 */
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.
Tony Mak72e17972019-03-16 10:28:42 +000084 *
85 * @see #putForeignLanguageExtra(Bundle, Bundle)
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +000086 */
87 @Nullable
Tony Mak72e17972019-03-16 10:28:42 +000088 public static Bundle getForeignLanguageExtra(@Nullable TextClassification classification) {
89 if (classification == null) {
90 return null;
91 }
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +000092 return classification.getExtras().getBundle(FOREIGN_LANGUAGE);
93 }
94
95 /**
Tony Mak72e17972019-03-16 10:28:42 +000096 * @see #getTopLanguage(Intent)
97 */
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
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
141 public static void putTextLanguagesExtra(Bundle container, Bundle extra) {
142 container.putBundle(TEXT_LANGUAGES, extra);
143 }
144
145 /**
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +0000146 * Stores {@code actionIntents} information in TextClassifier response object's extras
147 * {@code container}.
148 */
149 static void putActionsIntents(Bundle container, ArrayList<Intent> actionsIntents) {
150 container.putParcelableArrayList(ACTIONS_INTENTS, actionsIntents);
151 }
152
153 /**
Tony Makc12035e2019-02-26 17:45:34 +0000154 * Stores {@code actionIntents} information in TextClassifier response object's extras
155 * {@code container}.
156 */
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
165 public static Intent getActionIntent(Bundle container) {
166 return container.getParcelable(ACTION_INTENT);
167 }
168
169 /**
Tony Makfdb35542019-03-22 12:01:50 +0000170 * Stores serialized entity data information in TextClassifier response object's extras
171 * {@code container}.
172 */
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
183 public static byte[] getSerializedEntityData(Bundle container) {
184 return container.getByteArray(SERIALIZED_ENTITIES_DATA);
185 }
186
187 /**
Tony Mak09214422019-03-01 18:25:23 +0000188 * Stores {@code entities} information in TextClassifier response object's extras
189 * {@code container}.
190 *
191 * @see {@link #getCopyText(Bundle)}
192 */
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
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 /**
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +0000212 * Returns {@code actionIntents} information contained in the TextClassification object.
213 */
214 @Nullable
Tony Mak72e17972019-03-16 10:28:42 +0000215 public static ArrayList<Intent> getActionsIntents(@Nullable TextClassification classification) {
216 if (classification == null) {
217 return null;
218 }
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +0000219 return classification.getExtras().getParcelableArrayList(ACTIONS_INTENTS);
220 }
221
222 /**
Abodunrinwa Tokic33fc772019-02-06 01:17:10 +0000223 * Returns the first action found in the {@code classification} object with an intent
224 * action string, {@code intentAction}.
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +0000225 */
226 @Nullable
Tony Mak72e17972019-03-16 10:28:42 +0000227 public static RemoteAction findAction(
228 @Nullable TextClassification classification, @Nullable String intentAction) {
229 if (classification == null || intentAction == null) {
230 return null;
231 }
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +0000232 final ArrayList<Intent> actionIntents = getActionsIntents(classification);
233 if (actionIntents != null) {
234 final int size = actionIntents.size();
235 for (int i = 0; i < size; i++) {
Abodunrinwa Toki25f7fdc2019-02-19 23:42:30 +0000236 final Intent intent = actionIntents.get(i);
237 if (intent != null && intentAction.equals(intent.getAction())) {
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +0000238 return classification.getActions().get(i);
239 }
240 }
241 }
242 return null;
243 }
244
245 /**
Abodunrinwa Tokic33fc772019-02-06 01:17:10 +0000246 * Returns the first "translate" action found in the {@code classification} object.
247 */
248 @Nullable
Tony Mak72e17972019-03-16 10:28:42 +0000249 public static RemoteAction findTranslateAction(@Nullable TextClassification classification) {
Abodunrinwa Tokic33fc772019-02-06 01:17:10 +0000250 return findAction(classification, Intent.ACTION_TRANSLATE);
251 }
252
253 /**
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +0000254 * Returns the entity type contained in the {@code extra}.
255 */
256 @Nullable
Tony Mak72e17972019-03-16 10:28:42 +0000257 public static String getEntityType(@Nullable Bundle extra) {
258 if (extra == null) {
259 return null;
260 }
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +0000261 return extra.getString(ENTITY_TYPE);
262 }
263
264 /**
265 * Returns the score contained in the {@code extra}.
266 */
267 @Nullable
268 public static float getScore(Bundle extra) {
Tony Mak72e17972019-03-16 10:28:42 +0000269 final int defaultValue = -1;
270 if (extra == null) {
271 return defaultValue;
272 }
273 return extra.getFloat(SCORE, defaultValue);
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +0000274 }
275
276 /**
277 * Returns the model name contained in the {@code extra}.
278 */
279 @Nullable
Tony Mak72e17972019-03-16 10:28:42 +0000280 public static String getModelName(@Nullable Bundle extra) {
281 if (extra == null) {
282 return null;
283 }
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +0000284 return extra.getString(MODEL_NAME);
285 }
Tony Makfdb35542019-03-22 12:01:50 +0000286
287 /**
288 * Stores the entities from {@link AnnotatorModel.ClassificationResult} in {@code container}.
289 */
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 */
Tony Makb6afd3c2019-04-05 15:45:18 +0100316 @Nullable
Tony Makfdb35542019-03-22 12:01:50 +0000317 public static List<Bundle> getEntities(Bundle container) {
318 return container.getParcelableArrayList(ENTITIES);
319 }
Tony Makb6afd3c2019-04-05 15:45:18 +0100320
321 /**
322 * Whether the annotator should populate serialized entity data into the result object.
323 */
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 */
332 public static void putIsSerializedEntityDataEnabled(Bundle bundle, boolean isEnabled) {
333 bundle.putBoolean(IS_SERIALIZED_ENTITY_DATA_ENABLED, isEnabled);
334 }
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +0000335}