blob: 3aed32adea1cd7cfa86033b50f695fcd40be6ac3 [file] [log] [blame]
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +00001/*
2 * Copyright (C) 2017 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.FloatRange;
Abodunrinwa Toki2f19b922018-02-12 19:59:28 +000020import android.annotation.IntDef;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +000021import android.annotation.IntRange;
22import android.annotation.NonNull;
23import android.annotation.Nullable;
Jan Althaus20d346e2018-03-23 14:03:52 +010024import android.app.PendingIntent;
25import android.app.RemoteAction;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +000026import android.content.Context;
27import android.content.Intent;
Abodunrinwa Tokief7cb2c2018-02-07 22:21:08 +000028import android.content.res.Resources;
Abodunrinwa Tokiba196c52018-04-20 19:52:21 +010029import android.graphics.BitmapFactory;
30import android.graphics.drawable.AdaptiveIconDrawable;
Jan Althaus0d9fbb92017-11-28 12:19:33 +010031import android.graphics.drawable.BitmapDrawable;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +000032import android.graphics.drawable.Drawable;
Abodunrinwa Tokiba196c52018-04-20 19:52:21 +010033import android.graphics.drawable.Icon;
Tony Makd6f3fb42018-10-26 15:42:49 +010034import android.os.Bundle;
Abodunrinwa Toki2b6020f2017-10-28 02:28:45 +010035import android.os.LocaleList;
Jan Althaus0d9fbb92017-11-28 12:19:33 +010036import android.os.Parcel;
37import android.os.Parcelable;
Abodunrinwa Toki0f138962018-12-03 22:35:10 +000038import android.text.SpannedString;
Jan Althausbbe43df2017-11-30 15:01:40 +010039import android.util.ArrayMap;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +000040import android.view.View.OnClickListener;
41import android.view.textclassifier.TextClassifier.EntityType;
Abodunrinwa Toki080c8542018-03-27 00:04:06 +010042import android.view.textclassifier.TextClassifier.Utils;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +000043
Abodunrinwa Toki0f138962018-12-03 22:35:10 +000044import com.android.internal.annotations.VisibleForTesting;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +000045import com.android.internal.util.Preconditions;
46
Abodunrinwa Toki2f19b922018-02-12 19:59:28 +000047import java.lang.annotation.Retention;
48import java.lang.annotation.RetentionPolicy;
Jan Althausa1652cf2018-03-29 17:51:57 +020049import java.time.ZonedDateTime;
Jan Althaus92d76832017-09-27 18:14:35 +020050import java.util.ArrayList;
Jan Althaus20d346e2018-03-23 14:03:52 +010051import java.util.Collections;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +000052import java.util.List;
Abodunrinwa Toki4d232d62017-11-23 12:22:45 +000053import java.util.Locale;
Jan Althausbbe43df2017-11-30 15:01:40 +010054import java.util.Map;
Abodunrinwa Toki25f7fdc2019-02-19 23:42:30 +000055import java.util.Objects;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +000056
57/**
58 * Information for generating a widget to handle classified text.
Abodunrinwa Toki33ff2002017-10-24 00:49:27 +010059 *
60 * <p>A TextClassification object contains icons, labels, onClickListeners and intents that may
Abodunrinwa Tokiba385622017-11-29 19:30:32 +000061 * be used to build a widget that can be used to act on classified text. There is the concept of a
62 * <i>primary action</i> and other <i>secondary actions</i>.
Abodunrinwa Toki33ff2002017-10-24 00:49:27 +010063 *
64 * <p>e.g. building a view that, when clicked, shares the classified text with the preferred app:
65 *
66 * <pre>{@code
67 * // Called preferably outside the UiThread.
Abodunrinwa Toki4d232d62017-11-23 12:22:45 +000068 * TextClassification classification = textClassifier.classifyText(allText, 10, 25);
Abodunrinwa Toki33ff2002017-10-24 00:49:27 +010069 *
70 * // Called on the UiThread.
71 * Button button = new Button(context);
72 * button.setCompoundDrawablesWithIntrinsicBounds(classification.getIcon(), null, null, null);
73 * button.setText(classification.getLabel());
Abodunrinwa Tokiba196c52018-04-20 19:52:21 +010074 * button.setOnClickListener(v -> classification.getActions().get(0).getActionIntent().send());
Abodunrinwa Toki33ff2002017-10-24 00:49:27 +010075 * }</pre>
76 *
77 * <p>e.g. starting an action mode with menu items that can handle the classified text:
78 *
79 * <pre>{@code
80 * // Called preferably outside the UiThread.
Abodunrinwa Toki4d232d62017-11-23 12:22:45 +000081 * final TextClassification classification = textClassifier.classifyText(allText, 10, 25);
Abodunrinwa Toki33ff2002017-10-24 00:49:27 +010082 *
83 * // Called on the UiThread.
84 * view.startActionMode(new ActionMode.Callback() {
85 *
86 * public boolean onCreateActionMode(ActionMode mode, Menu menu) {
Jan Althaus20d346e2018-03-23 14:03:52 +010087 * for (int i = 0; i < classification.getActions().size(); ++i) {
88 * RemoteAction action = classification.getActions().get(i);
89 * menu.add(Menu.NONE, i, 20, action.getTitle())
90 * .setIcon(action.getIcon());
Abodunrinwa Toki33ff2002017-10-24 00:49:27 +010091 * }
92 * return true;
93 * }
94 *
95 * public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
Jan Althaus20d346e2018-03-23 14:03:52 +010096 * classification.getActions().get(item.getItemId()).getActionIntent().send();
Abodunrinwa Toki33ff2002017-10-24 00:49:27 +010097 * return true;
98 * }
99 *
100 * ...
101 * });
102 * }</pre>
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000103 */
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800104public final class TextClassification implements Parcelable {
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000105
106 /**
107 * @hide
108 */
Abodunrinwa Tokiadc19402018-11-22 17:10:25 +0000109 public static final TextClassification EMPTY = new TextClassification.Builder().build();
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000110
Jan Althaus20d346e2018-03-23 14:03:52 +0100111 private static final String LOG_TAG = "TextClassification";
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100112 // TODO(toki): investigate a way to derive this based on device properties.
Jan Althaus20d346e2018-03-23 14:03:52 +0100113 private static final int MAX_LEGACY_ICON_SIZE = 192;
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100114
Abodunrinwa Toki2f19b922018-02-12 19:59:28 +0000115 @Retention(RetentionPolicy.SOURCE)
116 @IntDef(value = {IntentType.UNSUPPORTED, IntentType.ACTIVITY, IntentType.SERVICE})
117 private @interface IntentType {
118 int UNSUPPORTED = -1;
119 int ACTIVITY = 0;
120 int SERVICE = 1;
121 }
122
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000123 @NonNull private final String mText;
Jan Althaus20d346e2018-03-23 14:03:52 +0100124 @Nullable private final Drawable mLegacyIcon;
125 @Nullable private final String mLegacyLabel;
126 @Nullable private final Intent mLegacyIntent;
127 @Nullable private final OnClickListener mLegacyOnClickListener;
128 @NonNull private final List<RemoteAction> mActions;
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100129 @NonNull private final EntityConfidence mEntityConfidence;
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100130 @Nullable private final String mId;
Tony Makd6f3fb42018-10-26 15:42:49 +0100131 @NonNull private final Bundle mExtras;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000132
Abodunrinwa Tokie0b57892017-04-28 19:59:57 +0100133 private TextClassification(
Abodunrinwa Tokid44286f2017-07-12 19:38:54 +0100134 @Nullable String text,
Jan Althaus20d346e2018-03-23 14:03:52 +0100135 @Nullable Drawable legacyIcon,
136 @Nullable String legacyLabel,
137 @Nullable Intent legacyIntent,
138 @Nullable OnClickListener legacyOnClickListener,
139 @NonNull List<RemoteAction> actions,
Tony Makfdb35542019-03-22 12:01:50 +0000140 @NonNull EntityConfidence entityConfidence,
Tony Makd6f3fb42018-10-26 15:42:49 +0100141 @Nullable String id,
142 @NonNull Bundle extras) {
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000143 mText = text;
Jan Althaus20d346e2018-03-23 14:03:52 +0100144 mLegacyIcon = legacyIcon;
145 mLegacyLabel = legacyLabel;
146 mLegacyIntent = legacyIntent;
147 mLegacyOnClickListener = legacyOnClickListener;
148 mActions = Collections.unmodifiableList(actions);
Daulet Zhanguzine1559472019-12-18 14:17:56 +0000149 mEntityConfidence = Objects.requireNonNull(entityConfidence);
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100150 mId = id;
Tony Makd6f3fb42018-10-26 15:42:49 +0100151 mExtras = extras;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000152 }
153
154 /**
155 * Gets the classified text.
156 */
Abodunrinwa Tokid44286f2017-07-12 19:38:54 +0100157 @Nullable
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000158 public String getText() {
159 return mText;
160 }
161
162 /**
163 * Returns the number of entities found in the classified text.
164 */
165 @IntRange(from = 0)
166 public int getEntityCount() {
Jan Althausbbe43df2017-11-30 15:01:40 +0100167 return mEntityConfidence.getEntities().size();
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000168 }
169
170 /**
171 * Returns the entity at the specified index. Entities are ordered from high confidence
172 * to low confidence.
173 *
174 * @throws IndexOutOfBoundsException if the specified index is out of range.
175 * @see #getEntityCount() for the number of entities available.
176 */
177 @NonNull
178 public @EntityType String getEntity(int index) {
Jan Althausbbe43df2017-11-30 15:01:40 +0100179 return mEntityConfidence.getEntities().get(index);
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000180 }
181
182 /**
183 * Returns the confidence score for the specified entity. The value ranges from
184 * 0 (low confidence) to 1 (high confidence). 0 indicates that the entity was not found for the
185 * classified text.
186 */
187 @FloatRange(from = 0.0, to = 1.0)
188 public float getConfidenceScore(@EntityType String entity) {
189 return mEntityConfidence.getConfidenceScore(entity);
190 }
191
192 /**
Jan Althaus20d346e2018-03-23 14:03:52 +0100193 * Returns a list of actions that may be performed on the text. The list is ordered based on
194 * the likelihood that a user will use the action, with the most likely action appearing first.
Jan Althaus92d76832017-09-27 18:14:35 +0200195 */
Jan Althaus20d346e2018-03-23 14:03:52 +0100196 public List<RemoteAction> getActions() {
197 return mActions;
Jan Althaus92d76832017-09-27 18:14:35 +0200198 }
199
200 /**
Jan Althaus20d346e2018-03-23 14:03:52 +0100201 * Returns an icon that may be rendered on a widget used to act on the classified text.
Abodunrinwa Tokiba385622017-11-29 19:30:32 +0000202 *
Abodunrinwa Tokiba196c52018-04-20 19:52:21 +0100203 * <p><strong>NOTE: </strong>This field is not parcelable and only represents the icon of the
204 * first {@link RemoteAction} (if one exists) when this object is read from a parcel.
205 *
Jan Althaus20d346e2018-03-23 14:03:52 +0100206 * @deprecated Use {@link #getActions()} instead.
Jan Althaus92d76832017-09-27 18:14:35 +0200207 */
Jan Althaus20d346e2018-03-23 14:03:52 +0100208 @Deprecated
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000209 @Nullable
210 public Drawable getIcon() {
Jan Althaus20d346e2018-03-23 14:03:52 +0100211 return mLegacyIcon;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000212 }
213
214 /**
Jan Althaus20d346e2018-03-23 14:03:52 +0100215 * Returns a label that may be rendered on a widget used to act on the classified text.
Abodunrinwa Tokiba385622017-11-29 19:30:32 +0000216 *
Abodunrinwa Tokiba196c52018-04-20 19:52:21 +0100217 * <p><strong>NOTE: </strong>This field is not parcelable and only represents the label of the
218 * first {@link RemoteAction} (if one exists) when this object is read from a parcel.
219 *
Jan Althaus20d346e2018-03-23 14:03:52 +0100220 * @deprecated Use {@link #getActions()} instead.
Jan Althaus92d76832017-09-27 18:14:35 +0200221 */
Jan Althaus20d346e2018-03-23 14:03:52 +0100222 @Deprecated
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000223 @Nullable
224 public CharSequence getLabel() {
Jan Althaus20d346e2018-03-23 14:03:52 +0100225 return mLegacyLabel;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000226 }
227
228 /**
Jan Althaus20d346e2018-03-23 14:03:52 +0100229 * Returns an intent that may be fired to act on the classified text.
Abodunrinwa Tokiba385622017-11-29 19:30:32 +0000230 *
Abodunrinwa Tokiba196c52018-04-20 19:52:21 +0100231 * <p><strong>NOTE: </strong>This field is not parcelled and will always return null when this
232 * object is read from a parcel.
233 *
Jan Althaus20d346e2018-03-23 14:03:52 +0100234 * @deprecated Use {@link #getActions()} instead.
Jan Althaus92d76832017-09-27 18:14:35 +0200235 */
Jan Althaus20d346e2018-03-23 14:03:52 +0100236 @Deprecated
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000237 @Nullable
238 public Intent getIntent() {
Jan Althaus20d346e2018-03-23 14:03:52 +0100239 return mLegacyIntent;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000240 }
241
242 /**
Abodunrinwa Tokiba196c52018-04-20 19:52:21 +0100243 * Returns the OnClickListener that may be triggered to act on the classified text.
244 *
245 * <p><strong>NOTE: </strong>This field is not parcelable and only represents the first
246 * {@link RemoteAction} (if one exists) when this object is read from a parcel.
Jan Althaus20d346e2018-03-23 14:03:52 +0100247 *
248 * @deprecated Use {@link #getActions()} instead.
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000249 */
250 @Nullable
251 public OnClickListener getOnClickListener() {
Jan Althaus20d346e2018-03-23 14:03:52 +0100252 return mLegacyOnClickListener;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000253 }
254
Abodunrinwa Toki54486c12017-04-19 21:02:36 +0100255 /**
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100256 * Returns the id, if one exists, for this object.
Abodunrinwa Toki692b1962017-08-15 15:05:11 +0100257 */
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100258 @Nullable
259 public String getId() {
260 return mId;
Abodunrinwa Toki692b1962017-08-15 15:05:11 +0100261 }
262
Tony Makd6f3fb42018-10-26 15:42:49 +0100263 /**
264 * Returns the extended data.
265 *
Tony Mak5a8d8272019-04-08 15:46:13 +0100266 * <p><b>NOTE: </b>Do not modify this bundle.
Tony Makd6f3fb42018-10-26 15:42:49 +0100267 */
268 @NonNull
269 public Bundle getExtras() {
Tony Mak5a8d8272019-04-08 15:46:13 +0100270 return mExtras;
Tony Makd6f3fb42018-10-26 15:42:49 +0100271 }
272
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000273 @Override
274 public String toString() {
Jan Althaus20d346e2018-03-23 14:03:52 +0100275 return String.format(Locale.US,
Abodunrinwa Toki25f7fdc2019-02-19 23:42:30 +0000276 "TextClassification {text=%s, entities=%s, actions=%s, id=%s, extras=%s}",
277 mText, mEntityConfidence, mActions, mId, mExtras);
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000278 }
279
280 /**
Jan Althaus20d346e2018-03-23 14:03:52 +0100281 * Creates an OnClickListener that triggers the specified PendingIntent.
282 *
283 * @hide
284 */
285 public static OnClickListener createIntentOnClickListener(@NonNull final PendingIntent intent) {
Daulet Zhanguzine1559472019-12-18 14:17:56 +0000286 Objects.requireNonNull(intent);
Jan Althaus20d346e2018-03-23 14:03:52 +0100287 return v -> {
288 try {
289 intent.send();
290 } catch (PendingIntent.CanceledException e) {
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100291 Log.e(LOG_TAG, "Error sending PendingIntent", e);
Jan Althaus20d346e2018-03-23 14:03:52 +0100292 }
293 };
294 }
295
296 /**
297 * Creates a PendingIntent for the specified intent.
Abodunrinwa Toki2f19b922018-02-12 19:59:28 +0000298 * Returns null if the intent is not supported for the specified context.
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000299 *
300 * @throws IllegalArgumentException if context or intent is null
301 * @hide
302 */
Jan Althaus20d346e2018-03-23 14:03:52 +0100303 public static PendingIntent createPendingIntent(
Abodunrinwa Toki904a9312018-04-18 21:21:27 +0100304 @NonNull final Context context, @NonNull final Intent intent, int requestCode) {
Tony Makc12035e2019-02-26 17:45:34 +0000305 return PendingIntent.getActivity(
306 context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000307 }
308
309 /**
Abodunrinwa Tokie0b57892017-04-28 19:59:57 +0100310 * Builder for building {@link TextClassification} objects.
Abodunrinwa Tokiba385622017-11-29 19:30:32 +0000311 *
312 * <p>e.g.
313 *
314 * <pre>{@code
315 * TextClassification classification = new TextClassification.Builder()
316 * .setText(classifiedText)
317 * .setEntityType(TextClassifier.TYPE_EMAIL, 0.9)
318 * .setEntityType(TextClassifier.TYPE_OTHER, 0.1)
Jan Althaus20d346e2018-03-23 14:03:52 +0100319 * .addAction(remoteAction1)
320 * .addAction(remoteAction2)
Abodunrinwa Tokiba385622017-11-29 19:30:32 +0000321 * .build();
322 * }</pre>
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000323 */
324 public static final class Builder {
325
Jan Althaus20d346e2018-03-23 14:03:52 +0100326 @NonNull private List<RemoteAction> mActions = new ArrayList<>();
Tony Makfdb35542019-03-22 12:01:50 +0000327 @NonNull private final Map<String, Float> mTypeScoreMap = new ArrayMap<>();
Jan Althaus13a89c92018-04-09 16:14:36 +0200328 @Nullable private String mText;
329 @Nullable private Drawable mLegacyIcon;
330 @Nullable private String mLegacyLabel;
331 @Nullable private Intent mLegacyIntent;
332 @Nullable private OnClickListener mLegacyOnClickListener;
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100333 @Nullable private String mId;
Tony Makd6f3fb42018-10-26 15:42:49 +0100334 @Nullable private Bundle mExtras;
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +0000335 @NonNull private final ArrayList<Intent> mActionIntents = new ArrayList<>();
336 @Nullable private Bundle mForeignLanguageExtra;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000337
338 /**
339 * Sets the classified text.
340 */
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100341 @NonNull
Abodunrinwa Tokid44286f2017-07-12 19:38:54 +0100342 public Builder setText(@Nullable String text) {
343 mText = text;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000344 return this;
345 }
346
347 /**
348 * Sets an entity type for the classification result and assigns a confidence score.
Abodunrinwa Tokiba385622017-11-29 19:30:32 +0000349 * If a confidence score had already been set for the specified entity type, this will
350 * override that score.
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000351 *
352 * @param confidenceScore a value from 0 (low confidence) to 1 (high confidence).
353 * 0 implies the entity does not exist for the classified text.
354 * Values greater than 1 are clamped to 1.
355 */
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100356 @NonNull
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000357 public Builder setEntityType(
358 @NonNull @EntityType String type,
Abodunrinwa Tokid44286f2017-07-12 19:38:54 +0100359 @FloatRange(from = 0.0, to = 1.0) float confidenceScore) {
Tony Makfdb35542019-03-22 12:01:50 +0000360 mTypeScoreMap.put(type, confidenceScore);
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000361 return this;
362 }
363
364 /**
Jan Althaus20d346e2018-03-23 14:03:52 +0100365 * Adds an action that may be performed on the classified text. Actions should be added in
366 * order of likelihood that the user will use them, with the most likely action being added
367 * first.
Jan Althaus92d76832017-09-27 18:14:35 +0200368 */
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100369 @NonNull
Jan Althaus20d346e2018-03-23 14:03:52 +0100370 public Builder addAction(@NonNull RemoteAction action) {
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +0000371 return addAction(action, null);
372 }
373
374 /**
375 * @param intent the intent in the remote action.
376 * @see #addAction(RemoteAction)
377 * @hide
378 */
379 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
380 public Builder addAction(RemoteAction action, @Nullable Intent intent) {
Jan Althaus20d346e2018-03-23 14:03:52 +0100381 Preconditions.checkArgument(action != null);
382 mActions.add(action);
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +0000383 mActionIntents.add(intent);
Jan Althaus92d76832017-09-27 18:14:35 +0200384 return this;
385 }
386
387 /**
Abodunrinwa Tokiba385622017-11-29 19:30:32 +0000388 * Sets the icon for the <i>primary</i> action that may be rendered on a widget used to act
389 * on the classified text.
390 *
Abodunrinwa Tokiba196c52018-04-20 19:52:21 +0100391 * <p><strong>NOTE: </strong>This field is not parcelled. If read from a parcel, the
392 * returned icon represents the icon of the first {@link RemoteAction} (if one exists).
393 *
Jan Althaus20d346e2018-03-23 14:03:52 +0100394 * @deprecated Use {@link #addAction(RemoteAction)} instead.
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000395 */
Jan Althaus20d346e2018-03-23 14:03:52 +0100396 @Deprecated
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100397 @NonNull
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000398 public Builder setIcon(@Nullable Drawable icon) {
Jan Althaus20d346e2018-03-23 14:03:52 +0100399 mLegacyIcon = icon;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000400 return this;
401 }
402
403 /**
Abodunrinwa Tokiba385622017-11-29 19:30:32 +0000404 * Sets the label for the <i>primary</i> action that may be rendered on a widget used to
405 * act on the classified text.
406 *
Abodunrinwa Tokiba196c52018-04-20 19:52:21 +0100407 * <p><strong>NOTE: </strong>This field is not parcelled. If read from a parcel, the
408 * returned label represents the label of the first {@link RemoteAction} (if one exists).
409 *
Jan Althaus20d346e2018-03-23 14:03:52 +0100410 * @deprecated Use {@link #addAction(RemoteAction)} instead.
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000411 */
Jan Althaus20d346e2018-03-23 14:03:52 +0100412 @Deprecated
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100413 @NonNull
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000414 public Builder setLabel(@Nullable String label) {
Jan Althaus20d346e2018-03-23 14:03:52 +0100415 mLegacyLabel = label;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000416 return this;
417 }
418
419 /**
Abodunrinwa Tokiba385622017-11-29 19:30:32 +0000420 * Sets the intent for the <i>primary</i> action that may be fired to act on the classified
421 * text.
422 *
Abodunrinwa Tokiba196c52018-04-20 19:52:21 +0100423 * <p><strong>NOTE: </strong>This field is not parcelled.
424 *
Jan Althaus20d346e2018-03-23 14:03:52 +0100425 * @deprecated Use {@link #addAction(RemoteAction)} instead.
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000426 */
Jan Althaus20d346e2018-03-23 14:03:52 +0100427 @Deprecated
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100428 @NonNull
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000429 public Builder setIntent(@Nullable Intent intent) {
Jan Althaus20d346e2018-03-23 14:03:52 +0100430 mLegacyIntent = intent;
Abodunrinwa Tokiba385622017-11-29 19:30:32 +0000431 return this;
432 }
433
434 /**
435 * Sets the OnClickListener for the <i>primary</i> action that may be triggered to act on
Abodunrinwa Tokiba196c52018-04-20 19:52:21 +0100436 * the classified text.
437 *
438 * <p><strong>NOTE: </strong>This field is not parcelable. If read from a parcel, the
439 * returned OnClickListener represents the first {@link RemoteAction} (if one exists).
Jan Althaus20d346e2018-03-23 14:03:52 +0100440 *
441 * @deprecated Use {@link #addAction(RemoteAction)} instead.
Abodunrinwa Tokiba385622017-11-29 19:30:32 +0000442 */
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100443 @Deprecated
444 @NonNull
Abodunrinwa Tokiba385622017-11-29 19:30:32 +0000445 public Builder setOnClickListener(@Nullable OnClickListener onClickListener) {
Jan Althaus20d346e2018-03-23 14:03:52 +0100446 mLegacyOnClickListener = onClickListener;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000447 return this;
448 }
449
450 /**
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100451 * Sets an id for the TextClassification object.
Abodunrinwa Toki54486c12017-04-19 21:02:36 +0100452 */
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100453 @NonNull
454 public Builder setId(@Nullable String id) {
455 mId = id;
Abodunrinwa Toki692b1962017-08-15 15:05:11 +0100456 return this;
457 }
458
459 /**
Tony Makd6f3fb42018-10-26 15:42:49 +0100460 * Sets the extended data.
461 */
462 @NonNull
463 public Builder setExtras(@Nullable Bundle extras) {
464 mExtras = extras;
465 return this;
466 }
467
468 /**
Abodunrinwa Toki520b2f82019-01-27 07:48:02 +0000469 * @see #setExtras(Bundle)
470 * @hide
471 */
472 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
473 public Builder setForeignLanguageExtra(@Nullable Bundle extra) {
474 mForeignLanguageExtra = extra;
475 return this;
476 }
477
478 /**
Abodunrinwa Tokie0b57892017-04-28 19:59:57 +0100479 * Builds and returns a {@link TextClassification} object.
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000480 */
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100481 @NonNull
Abodunrinwa Tokie0b57892017-04-28 19:59:57 +0100482 public TextClassification build() {
Tony Makfdb35542019-03-22 12:01:50 +0000483 EntityConfidence entityConfidence = new EntityConfidence(mTypeScoreMap);
Jan Althaus20d346e2018-03-23 14:03:52 +0100484 return new TextClassification(mText, mLegacyIcon, mLegacyLabel, mLegacyIntent,
Tony Makfdb35542019-03-22 12:01:50 +0000485 mLegacyOnClickListener, mActions, entityConfidence, mId,
Tony Mak293bdf32020-02-18 11:33:43 +0000486 mExtras == null ? Bundle.EMPTY : mExtras);
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000487 }
488 }
Abodunrinwa Toki2b6020f2017-10-28 02:28:45 +0100489
490 /**
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100491 * A request object for generating TextClassification.
Abodunrinwa Toki2b6020f2017-10-28 02:28:45 +0100492 */
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100493 public static final class Request implements Parcelable {
Abodunrinwa Toki2b6020f2017-10-28 02:28:45 +0100494
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100495 private final CharSequence mText;
496 private final int mStartIndex;
497 private final int mEndIndex;
498 @Nullable private final LocaleList mDefaultLocales;
499 @Nullable private final ZonedDateTime mReferenceTime;
Tony Makd6f3fb42018-10-26 15:42:49 +0100500 @NonNull private final Bundle mExtras;
Joanne Chung97d3a452020-03-04 19:03:11 +0800501 @Nullable private SystemTextClassifierMetadata mSystemTcMetadata;
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100502
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100503 private Request(
504 CharSequence text,
505 int startIndex,
506 int endIndex,
507 LocaleList defaultLocales,
Tony Makd6f3fb42018-10-26 15:42:49 +0100508 ZonedDateTime referenceTime,
509 Bundle extras) {
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100510 mText = text;
511 mStartIndex = startIndex;
512 mEndIndex = endIndex;
Abodunrinwa Toki2b6020f2017-10-28 02:28:45 +0100513 mDefaultLocales = defaultLocales;
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100514 mReferenceTime = referenceTime;
Tony Makd6f3fb42018-10-26 15:42:49 +0100515 mExtras = extras;
Abodunrinwa Toki2b6020f2017-10-28 02:28:45 +0100516 }
517
518 /**
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100519 * Returns the text providing context for the text to classify (which is specified
520 * by the sub sequence starting at startIndex and ending at endIndex)
Jan Althaus705b9e92018-01-22 18:22:29 +0100521 */
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100522 @NonNull
523 public CharSequence getText() {
524 return mText;
525 }
526
527 /**
528 * Returns start index of the text to classify.
529 */
530 @IntRange(from = 0)
531 public int getStartIndex() {
532 return mStartIndex;
533 }
534
535 /**
536 * Returns end index of the text to classify.
537 */
538 @IntRange(from = 0)
539 public int getEndIndex() {
540 return mEndIndex;
Jan Althaus705b9e92018-01-22 18:22:29 +0100541 }
542
543 /**
Abodunrinwa Toki2b6020f2017-10-28 02:28:45 +0100544 * @return ordered list of locale preferences that can be used to disambiguate
545 * the provided text.
546 */
547 @Nullable
548 public LocaleList getDefaultLocales() {
549 return mDefaultLocales;
550 }
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100551
Jan Althaus705b9e92018-01-22 18:22:29 +0100552 /**
553 * @return reference time based on which relative dates (e.g. "tomorrow") should be
554 * interpreted.
555 */
556 @Nullable
Jan Althausa1652cf2018-03-29 17:51:57 +0200557 public ZonedDateTime getReferenceTime() {
Jan Althaus705b9e92018-01-22 18:22:29 +0100558 return mReferenceTime;
559 }
560
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100561 /**
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000562 * Returns the name of the package that sent this request.
563 * This returns {@code null} if no calling package name is set.
564 */
565 @Nullable
566 public String getCallingPackageName() {
Joanne Chung97d3a452020-03-04 19:03:11 +0800567 return mSystemTcMetadata != null ? mSystemTcMetadata.getCallingPackageName() : null;
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000568 }
569
570 /**
Joanne Chung97d3a452020-03-04 19:03:11 +0800571 * Sets the information about the {@link SystemTextClassifier} that sent this request.
Tony Makc5a74322020-02-04 17:18:15 +0000572 *
573 * @hide
574 */
Joanne Chung97d3a452020-03-04 19:03:11 +0800575 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
576 public void setSystemTextClassifierMetadata(
577 @Nullable SystemTextClassifierMetadata systemTcMetadata) {
578 mSystemTcMetadata = systemTcMetadata;
Tony Makc5a74322020-02-04 17:18:15 +0000579 }
580
581 /**
Joanne Chung97d3a452020-03-04 19:03:11 +0800582 * Returns the information about the {@link SystemTextClassifier} that sent this request.
Tony Makc5a74322020-02-04 17:18:15 +0000583 *
584 * @hide
585 */
Joanne Chung97d3a452020-03-04 19:03:11 +0800586 @Nullable
587 public SystemTextClassifierMetadata getSystemTextClassifierMetadata() {
588 return mSystemTcMetadata;
Tony Makc5a74322020-02-04 17:18:15 +0000589 }
590
591 /**
Tony Makd6f3fb42018-10-26 15:42:49 +0100592 * Returns the extended data.
593 *
Tony Mak5a8d8272019-04-08 15:46:13 +0100594 * <p><b>NOTE: </b>Do not modify this bundle.
Tony Makd6f3fb42018-10-26 15:42:49 +0100595 */
596 @NonNull
597 public Bundle getExtras() {
Tony Mak5a8d8272019-04-08 15:46:13 +0100598 return mExtras;
Tony Makd6f3fb42018-10-26 15:42:49 +0100599 }
600
601 /**
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100602 * A builder for building TextClassification requests.
603 */
604 public static final class Builder {
605
606 private final CharSequence mText;
607 private final int mStartIndex;
608 private final int mEndIndex;
Tony Makd6f3fb42018-10-26 15:42:49 +0100609 private Bundle mExtras;
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100610
611 @Nullable private LocaleList mDefaultLocales;
612 @Nullable private ZonedDateTime mReferenceTime;
613
614 /**
615 * @param text text providing context for the text to classify (which is specified
616 * by the sub sequence starting at startIndex and ending at endIndex)
617 * @param startIndex start index of the text to classify
618 * @param endIndex end index of the text to classify
619 */
620 public Builder(
621 @NonNull CharSequence text,
622 @IntRange(from = 0) int startIndex,
623 @IntRange(from = 0) int endIndex) {
624 Utils.checkArgument(text, startIndex, endIndex);
625 mText = text;
626 mStartIndex = startIndex;
627 mEndIndex = endIndex;
628 }
629
630 /**
631 * @param defaultLocales ordered list of locale preferences that may be used to
632 * disambiguate the provided text. If no locale preferences exist, set this to null
633 * or an empty locale list.
634 *
635 * @return this builder
636 */
637 @NonNull
638 public Builder setDefaultLocales(@Nullable LocaleList defaultLocales) {
639 mDefaultLocales = defaultLocales;
640 return this;
641 }
642
643 /**
644 * @param referenceTime reference time based on which relative dates (e.g. "tomorrow"
645 * should be interpreted. This should usually be the time when the text was
646 * originally composed. If no reference time is set, now is used.
647 *
648 * @return this builder
649 */
650 @NonNull
651 public Builder setReferenceTime(@Nullable ZonedDateTime referenceTime) {
652 mReferenceTime = referenceTime;
653 return this;
654 }
655
656 /**
Tony Makd6f3fb42018-10-26 15:42:49 +0100657 * Sets the extended data.
658 *
659 * @return this builder
660 */
661 @NonNull
662 public Builder setExtras(@Nullable Bundle extras) {
663 mExtras = extras;
664 return this;
665 }
666
667 /**
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100668 * Builds and returns the request object.
669 */
670 @NonNull
671 public Request build() {
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000672 return new Request(new SpannedString(mText), mStartIndex, mEndIndex,
673 mDefaultLocales, mReferenceTime,
Tony Mak74828102019-04-10 17:53:13 +0100674 mExtras == null ? Bundle.EMPTY : mExtras);
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100675 }
676 }
677
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100678 @Override
679 public int describeContents() {
680 return 0;
681 }
682
683 @Override
684 public void writeToParcel(Parcel dest, int flags) {
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000685 dest.writeCharSequence(mText);
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100686 dest.writeInt(mStartIndex);
687 dest.writeInt(mEndIndex);
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000688 dest.writeParcelable(mDefaultLocales, flags);
689 dest.writeString(mReferenceTime == null ? null : mReferenceTime.toString());
Tony Makd6f3fb42018-10-26 15:42:49 +0100690 dest.writeBundle(mExtras);
Joanne Chung97d3a452020-03-04 19:03:11 +0800691 dest.writeParcelable(mSystemTcMetadata, flags);
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100692 }
693
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000694 private static Request readFromParcel(Parcel in) {
695 final CharSequence text = in.readCharSequence();
696 final int startIndex = in.readInt();
697 final int endIndex = in.readInt();
698 final LocaleList defaultLocales = in.readParcelable(null);
699 final String referenceTimeString = in.readString();
700 final ZonedDateTime referenceTime = referenceTimeString == null
701 ? null : ZonedDateTime.parse(referenceTimeString);
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000702 final Bundle extras = in.readBundle();
Joanne Chung97d3a452020-03-04 19:03:11 +0800703 final SystemTextClassifierMetadata systemTcMetadata = in.readParcelable(null);
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000704
705 final Request request = new Request(text, startIndex, endIndex,
706 defaultLocales, referenceTime, extras);
Joanne Chung97d3a452020-03-04 19:03:11 +0800707 request.setSystemTextClassifierMetadata(systemTcMetadata);
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000708 return request;
709 }
710
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700711 public static final @android.annotation.NonNull Parcelable.Creator<Request> CREATOR =
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100712 new Parcelable.Creator<Request>() {
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100713 @Override
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100714 public Request createFromParcel(Parcel in) {
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000715 return readFromParcel(in);
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100716 }
717
718 @Override
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100719 public Request[] newArray(int size) {
720 return new Request[size];
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100721 }
722 };
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100723 }
724
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800725 @Override
726 public int describeContents() {
727 return 0;
728 }
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100729
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800730 @Override
731 public void writeToParcel(Parcel dest, int flags) {
732 dest.writeString(mText);
Abodunrinwa Tokiba196c52018-04-20 19:52:21 +0100733 // NOTE: legacy fields are not parcelled.
Jan Althaus20d346e2018-03-23 14:03:52 +0100734 dest.writeTypedList(mActions);
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800735 mEntityConfidence.writeToParcel(dest, flags);
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100736 dest.writeString(mId);
Tony Makd6f3fb42018-10-26 15:42:49 +0100737 dest.writeBundle(mExtras);
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800738 }
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100739
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700740 public static final @android.annotation.NonNull Parcelable.Creator<TextClassification> CREATOR =
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800741 new Parcelable.Creator<TextClassification>() {
742 @Override
743 public TextClassification createFromParcel(Parcel in) {
744 return new TextClassification(in);
745 }
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100746
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800747 @Override
748 public TextClassification[] newArray(int size) {
749 return new TextClassification[size];
750 }
751 };
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100752
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800753 private TextClassification(Parcel in) {
754 mText = in.readString();
Jan Althaus20d346e2018-03-23 14:03:52 +0100755 mActions = in.createTypedArrayList(RemoteAction.CREATOR);
Abodunrinwa Tokiba196c52018-04-20 19:52:21 +0100756 if (!mActions.isEmpty()) {
757 final RemoteAction action = mActions.get(0);
758 mLegacyIcon = maybeLoadDrawable(action.getIcon());
759 mLegacyLabel = action.getTitle().toString();
760 mLegacyOnClickListener = createIntentOnClickListener(mActions.get(0).getActionIntent());
761 } else {
762 mLegacyIcon = null;
763 mLegacyLabel = null;
764 mLegacyOnClickListener = null;
765 }
766 mLegacyIntent = null; // mLegacyIntent is not parcelled.
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800767 mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100768 mId = in.readString();
Tony Makd6f3fb42018-10-26 15:42:49 +0100769 mExtras = in.readBundle();
Abodunrinwa Toki2b6020f2017-10-28 02:28:45 +0100770 }
Abodunrinwa Tokiae82e7a2018-04-03 23:49:16 +0100771
Abodunrinwa Tokiba196c52018-04-20 19:52:21 +0100772 // Best effort attempt to try to load a drawable from the provided icon.
773 @Nullable
774 private static Drawable maybeLoadDrawable(Icon icon) {
775 if (icon == null) {
776 return null;
777 }
778 switch (icon.getType()) {
779 case Icon.TYPE_BITMAP:
780 return new BitmapDrawable(Resources.getSystem(), icon.getBitmap());
781 case Icon.TYPE_ADAPTIVE_BITMAP:
782 return new AdaptiveIconDrawable(null,
783 new BitmapDrawable(Resources.getSystem(), icon.getBitmap()));
784 case Icon.TYPE_DATA:
785 return new BitmapDrawable(
786 Resources.getSystem(),
787 BitmapFactory.decodeByteArray(
788 icon.getDataBytes(), icon.getDataOffset(), icon.getDataLength()));
789 }
790 return null;
791 }
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000792}