blob: 52989397e4730b60cebe5dec578ceb7a3e03e96b [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;
20import android.annotation.IntRange;
21import android.annotation.NonNull;
Abodunrinwa Toki2b6020f2017-10-28 02:28:45 +010022import android.annotation.Nullable;
Tony Mak916bb9e2018-11-08 20:45:20 +000023import android.os.Bundle;
Abodunrinwa Toki2b6020f2017-10-28 02:28:45 +010024import android.os.LocaleList;
Jan Althaus0d9fbb92017-11-28 12:19:33 +010025import android.os.Parcel;
26import android.os.Parcelable;
Abodunrinwa Toki0f138962018-12-03 22:35:10 +000027import android.text.SpannedString;
Jan Althausbbe43df2017-11-30 15:01:40 +010028import android.util.ArrayMap;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +000029import android.view.textclassifier.TextClassifier.EntityType;
Abodunrinwa Toki080c8542018-03-27 00:04:06 +010030import android.view.textclassifier.TextClassifier.Utils;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +000031
Abodunrinwa Toki0f138962018-12-03 22:35:10 +000032import com.android.internal.annotations.VisibleForTesting;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +000033import com.android.internal.util.Preconditions;
34
Abodunrinwa Toki4d232d62017-11-23 12:22:45 +000035import java.util.Locale;
Jan Althausbbe43df2017-11-30 15:01:40 +010036import java.util.Map;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +000037
38/**
39 * Information about where text selection should be.
40 */
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -080041public final class TextSelection implements Parcelable {
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +000042
43 private final int mStartIndex;
44 private final int mEndIndex;
Abodunrinwa Toki080c8542018-03-27 00:04:06 +010045 private final EntityConfidence mEntityConfidence;
46 @Nullable private final String mId;
Tony Mak916bb9e2018-11-08 20:45:20 +000047 private final Bundle mExtras;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +000048
49 private TextSelection(
Tony Mak916bb9e2018-11-08 20:45:20 +000050 int startIndex, int endIndex, Map<String, Float> entityConfidence, String id,
51 Bundle extras) {
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +000052 mStartIndex = startIndex;
53 mEndIndex = endIndex;
Jan Althaus0d9fbb92017-11-28 12:19:33 +010054 mEntityConfidence = new EntityConfidence(entityConfidence);
Abodunrinwa Toki080c8542018-03-27 00:04:06 +010055 mId = id;
Tony Mak916bb9e2018-11-08 20:45:20 +000056 mExtras = extras;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +000057 }
58
59 /**
60 * Returns the start index of the text selection.
61 */
62 public int getSelectionStartIndex() {
63 return mStartIndex;
64 }
65
66 /**
67 * Returns the end index of the text selection.
68 */
69 public int getSelectionEndIndex() {
70 return mEndIndex;
71 }
72
73 /**
74 * Returns the number of entities found in the classified text.
75 */
76 @IntRange(from = 0)
77 public int getEntityCount() {
Jan Althausbbe43df2017-11-30 15:01:40 +010078 return mEntityConfidence.getEntities().size();
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +000079 }
80
81 /**
82 * Returns the entity at the specified index. Entities are ordered from high confidence
83 * to low confidence.
84 *
85 * @throws IndexOutOfBoundsException if the specified index is out of range.
86 * @see #getEntityCount() for the number of entities available.
87 */
88 @NonNull
Abodunrinwa Toki080c8542018-03-27 00:04:06 +010089 @EntityType
90 public String getEntity(int index) {
Jan Althausbbe43df2017-11-30 15:01:40 +010091 return mEntityConfidence.getEntities().get(index);
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +000092 }
93
94 /**
95 * Returns the confidence score for the specified entity. The value ranges from
96 * 0 (low confidence) to 1 (high confidence). 0 indicates that the entity was not found for the
97 * classified text.
98 */
99 @FloatRange(from = 0.0, to = 1.0)
100 public float getConfidenceScore(@EntityType String entity) {
101 return mEntityConfidence.getConfidenceScore(entity);
102 }
103
Abodunrinwa Toki1d775572017-05-08 16:03:01 +0100104 /**
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100105 * Returns the id, if one exists, for this object.
Abodunrinwa Toki1d775572017-05-08 16:03:01 +0100106 */
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100107 @Nullable
108 public String getId() {
109 return mId;
Abodunrinwa Toki692b1962017-08-15 15:05:11 +0100110 }
111
Tony Mak916bb9e2018-11-08 20:45:20 +0000112 /**
113 * Returns the extended data.
114 *
115 * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
116 * prefer to hold a reference to the returned bundle rather than frequently calling this
117 * method.
118 */
119 @NonNull
120 public Bundle getExtras() {
121 return mExtras.deepCopy();
122 }
123
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000124 @Override
125 public String toString() {
Abodunrinwa Toki008f3872017-11-27 19:32:35 +0000126 return String.format(
127 Locale.US,
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100128 "TextSelection {id=%s, startIndex=%d, endIndex=%d, entities=%s}",
129 mId, mStartIndex, mEndIndex, mEntityConfidence);
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000130 }
131
132 /**
133 * Builder used to build {@link TextSelection} objects.
134 */
135 public static final class Builder {
136
137 private final int mStartIndex;
138 private final int mEndIndex;
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100139 private final Map<String, Float> mEntityConfidence = new ArrayMap<>();
140 @Nullable private String mId;
Tony Mak916bb9e2018-11-08 20:45:20 +0000141 @Nullable
142 private Bundle mExtras;
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000143
144 /**
145 * Creates a builder used to build {@link TextSelection} objects.
146 *
147 * @param startIndex the start index of the text selection.
148 * @param endIndex the end index of the text selection. Must be greater than startIndex
149 */
150 public Builder(@IntRange(from = 0) int startIndex, @IntRange(from = 0) int endIndex) {
151 Preconditions.checkArgument(startIndex >= 0);
152 Preconditions.checkArgument(endIndex > startIndex);
153 mStartIndex = startIndex;
154 mEndIndex = endIndex;
155 }
156
157 /**
158 * Sets an entity type for the classified text and assigns a confidence score.
159 *
160 * @param confidenceScore a value from 0 (low confidence) to 1 (high confidence).
161 * 0 implies the entity does not exist for the classified text.
162 * Values greater than 1 are clamped to 1.
163 */
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100164 @NonNull
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000165 public Builder setEntityType(
166 @NonNull @EntityType String type,
167 @FloatRange(from = 0.0, to = 1.0) float confidenceScore) {
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100168 Preconditions.checkNotNull(type);
Jan Althausbbe43df2017-11-30 15:01:40 +0100169 mEntityConfidence.put(type, confidenceScore);
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000170 return this;
171 }
172
173 /**
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100174 * Sets an id for the TextSelection object.
Abodunrinwa Toki1d775572017-05-08 16:03:01 +0100175 */
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100176 @NonNull
Tony Mak84368782018-05-15 16:43:44 +0800177 public Builder setId(@Nullable String id) {
178 mId = id;
Abodunrinwa Toki692b1962017-08-15 15:05:11 +0100179 return this;
180 }
181
182 /**
Tony Mak916bb9e2018-11-08 20:45:20 +0000183 * Sets the extended data.
184 *
185 * @return this builder
186 */
Tony Mak42ab9842019-03-05 15:38:51 +0000187 @NonNull
Tony Mak916bb9e2018-11-08 20:45:20 +0000188 public Builder setExtras(@Nullable Bundle extras) {
189 mExtras = extras;
190 return this;
191 }
192
193 /**
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000194 * Builds and returns {@link TextSelection} object.
195 */
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100196 @NonNull
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000197 public TextSelection build() {
Abodunrinwa Toki692b1962017-08-15 15:05:11 +0100198 return new TextSelection(
Tony Mak916bb9e2018-11-08 20:45:20 +0000199 mStartIndex, mEndIndex, mEntityConfidence, mId,
200 mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000201 }
202 }
Abodunrinwa Toki2b6020f2017-10-28 02:28:45 +0100203
204 /**
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100205 * A request object for generating TextSelection.
Abodunrinwa Toki2b6020f2017-10-28 02:28:45 +0100206 */
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100207 public static final class Request implements Parcelable {
Abodunrinwa Toki2b6020f2017-10-28 02:28:45 +0100208
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100209 private final CharSequence mText;
210 private final int mStartIndex;
211 private final int mEndIndex;
212 @Nullable private final LocaleList mDefaultLocales;
213 private final boolean mDarkLaunchAllowed;
Tony Mak916bb9e2018-11-08 20:45:20 +0000214 private final Bundle mExtras;
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000215 @Nullable private String mCallingPackageName;
Abodunrinwa Toki2b6020f2017-10-28 02:28:45 +0100216
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100217 private Request(
218 CharSequence text,
219 int startIndex,
220 int endIndex,
221 LocaleList defaultLocales,
Tony Mak916bb9e2018-11-08 20:45:20 +0000222 boolean darkLaunchAllowed,
223 Bundle extras) {
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100224 mText = text;
225 mStartIndex = startIndex;
226 mEndIndex = endIndex;
Abodunrinwa Toki2b6020f2017-10-28 02:28:45 +0100227 mDefaultLocales = defaultLocales;
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100228 mDarkLaunchAllowed = darkLaunchAllowed;
Tony Mak916bb9e2018-11-08 20:45:20 +0000229 mExtras = extras;
Abodunrinwa Toki2b6020f2017-10-28 02:28:45 +0100230 }
231
232 /**
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100233 * Returns the text providing context for the selected text (which is specified by the
234 * sub sequence starting at startIndex and ending at endIndex).
235 */
236 @NonNull
237 public CharSequence getText() {
238 return mText;
239 }
240
241 /**
242 * Returns start index of the selected part of text.
243 */
244 @IntRange(from = 0)
245 public int getStartIndex() {
246 return mStartIndex;
247 }
248
249 /**
250 * Returns end index of the selected part of text.
251 */
252 @IntRange(from = 0)
253 public int getEndIndex() {
254 return mEndIndex;
255 }
256
257 /**
258 * Returns true if the TextClassifier should return selection suggestions when "dark
259 * launched". Otherwise, returns false.
260 *
261 * @hide
262 */
263 public boolean isDarkLaunchAllowed() {
264 return mDarkLaunchAllowed;
265 }
266
267 /**
268 * @return ordered list of locale preferences that can be used to disambiguate the
269 * provided text.
Abodunrinwa Toki2b6020f2017-10-28 02:28:45 +0100270 */
271 @Nullable
272 public LocaleList getDefaultLocales() {
273 return mDefaultLocales;
274 }
275
276 /**
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000277 * Sets the name of the package that is sending this request.
278 * <p>
279 * Package-private for SystemTextClassifier's use.
280 * @hide
281 */
282 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
283 public void setCallingPackageName(@Nullable String callingPackageName) {
284 mCallingPackageName = callingPackageName;
285 }
286
287 /**
288 * Returns the name of the package that sent this request.
289 * This returns {@code null} if no calling package name is set.
290 */
291 @Nullable
292 public String getCallingPackageName() {
293 return mCallingPackageName;
294 }
295
296 /**
Tony Mak916bb9e2018-11-08 20:45:20 +0000297 * Returns the extended data.
298 *
299 * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
300 * prefer to hold a reference to the returned bundle rather than frequently calling this
301 * method.
302 */
303 @NonNull
304 public Bundle getExtras() {
305 return mExtras.deepCopy();
306 }
307
308 /**
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100309 * A builder for building TextSelection requests.
Abodunrinwa Toki2b6020f2017-10-28 02:28:45 +0100310 */
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100311 public static final class Builder {
Abodunrinwa Toki2b6020f2017-10-28 02:28:45 +0100312
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100313 private final CharSequence mText;
314 private final int mStartIndex;
315 private final int mEndIndex;
316
317 @Nullable private LocaleList mDefaultLocales;
318 private boolean mDarkLaunchAllowed;
Tony Mak916bb9e2018-11-08 20:45:20 +0000319 private Bundle mExtras;
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100320
321 /**
322 * @param text text providing context for the selected text (which is specified by the
323 * sub sequence starting at selectionStartIndex and ending at selectionEndIndex)
324 * @param startIndex start index of the selected part of text
325 * @param endIndex end index of the selected part of text
326 */
327 public Builder(
328 @NonNull CharSequence text,
329 @IntRange(from = 0) int startIndex,
330 @IntRange(from = 0) int endIndex) {
331 Utils.checkArgument(text, startIndex, endIndex);
332 mText = text;
333 mStartIndex = startIndex;
334 mEndIndex = endIndex;
335 }
336
337 /**
338 * @param defaultLocales ordered list of locale preferences that may be used to
339 * disambiguate the provided text. If no locale preferences exist, set this to null
340 * or an empty locale list.
341 *
342 * @return this builder.
343 */
344 @NonNull
345 public Builder setDefaultLocales(@Nullable LocaleList defaultLocales) {
346 mDefaultLocales = defaultLocales;
347 return this;
348 }
349
350 /**
351 * @param allowed whether or not the TextClassifier should return selection suggestions
352 * when "dark launched". When a TextClassifier is dark launched, it can suggest
353 * selection changes that should not be used to actually change the user's
354 * selection. Instead, the suggested selection is logged, compared with the user's
355 * selection interaction, and used to generate quality metrics for the
356 * TextClassifier. Not parceled.
357 *
358 * @return this builder.
359 * @hide
360 */
361 @NonNull
362 public Builder setDarkLaunchAllowed(boolean allowed) {
363 mDarkLaunchAllowed = allowed;
364 return this;
365 }
366
367 /**
Tony Mak916bb9e2018-11-08 20:45:20 +0000368 * Sets the extended data.
369 *
370 * @return this builder
371 */
Tony Mak42ab9842019-03-05 15:38:51 +0000372 @NonNull
Tony Mak916bb9e2018-11-08 20:45:20 +0000373 public Builder setExtras(@Nullable Bundle extras) {
374 mExtras = extras;
375 return this;
376 }
377
378 /**
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100379 * Builds and returns the request object.
380 */
381 @NonNull
382 public Request build() {
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000383 return new Request(new SpannedString(mText), mStartIndex, mEndIndex,
Tony Mak916bb9e2018-11-08 20:45:20 +0000384 mDefaultLocales, mDarkLaunchAllowed,
385 mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100386 }
Abodunrinwa Toki2b6020f2017-10-28 02:28:45 +0100387 }
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100388
389 @Override
390 public int describeContents() {
391 return 0;
392 }
393
394 @Override
395 public void writeToParcel(Parcel dest, int flags) {
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000396 dest.writeCharSequence(mText);
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100397 dest.writeInt(mStartIndex);
398 dest.writeInt(mEndIndex);
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000399 dest.writeParcelable(mDefaultLocales, flags);
400 dest.writeString(mCallingPackageName);
Tony Mak916bb9e2018-11-08 20:45:20 +0000401 dest.writeBundle(mExtras);
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100402 }
403
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000404 private static Request readFromParcel(Parcel in) {
405 final CharSequence text = in.readCharSequence();
406 final int startIndex = in.readInt();
407 final int endIndex = in.readInt();
408 final LocaleList defaultLocales = in.readParcelable(null);
409 final String callingPackageName = in.readString();
410 final Bundle extras = in.readBundle();
411
412 final Request request = new Request(text, startIndex, endIndex, defaultLocales,
413 /* darkLaunchAllowed= */ false, extras);
414 request.setCallingPackageName(callingPackageName);
415 return request;
416 }
417
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700418 public static final @android.annotation.NonNull Parcelable.Creator<Request> CREATOR =
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100419 new Parcelable.Creator<Request>() {
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100420 @Override
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100421 public Request createFromParcel(Parcel in) {
Abodunrinwa Toki0f138962018-12-03 22:35:10 +0000422 return readFromParcel(in);
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100423 }
424
425 @Override
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100426 public Request[] newArray(int size) {
427 return new Request[size];
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100428 }
429 };
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100430 }
431
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800432 @Override
433 public int describeContents() {
434 return 0;
435 }
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100436
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800437 @Override
438 public void writeToParcel(Parcel dest, int flags) {
439 dest.writeInt(mStartIndex);
440 dest.writeInt(mEndIndex);
441 mEntityConfidence.writeToParcel(dest, flags);
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100442 dest.writeString(mId);
Tony Mak916bb9e2018-11-08 20:45:20 +0000443 dest.writeBundle(mExtras);
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800444 }
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100445
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700446 public static final @android.annotation.NonNull Parcelable.Creator<TextSelection> CREATOR =
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800447 new Parcelable.Creator<TextSelection>() {
448 @Override
449 public TextSelection createFromParcel(Parcel in) {
450 return new TextSelection(in);
451 }
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100452
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800453 @Override
454 public TextSelection[] newArray(int size) {
455 return new TextSelection[size];
456 }
457 };
Jan Althaus0d9fbb92017-11-28 12:19:33 +0100458
Abodunrinwa Tokid32906c2018-01-18 04:34:44 -0800459 private TextSelection(Parcel in) {
460 mStartIndex = in.readInt();
461 mEndIndex = in.readInt();
462 mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
Abodunrinwa Toki080c8542018-03-27 00:04:06 +0100463 mId = in.readString();
Tony Mak916bb9e2018-11-08 20:45:20 +0000464 mExtras = in.readBundle();
Abodunrinwa Toki2b6020f2017-10-28 02:28:45 +0100465 }
Abodunrinwa Tokif001fef2017-01-04 23:51:42 +0000466}