1 /* 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 17 package android.view.textclassifier; 18 19 import android.annotation.FloatRange; 20 import android.annotation.IntRange; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.UserIdInt; 24 import android.os.Bundle; 25 import android.os.LocaleList; 26 import android.os.Parcel; 27 import android.os.Parcelable; 28 import android.os.UserHandle; 29 import android.text.SpannedString; 30 import android.util.ArrayMap; 31 import android.view.textclassifier.TextClassifier.EntityType; 32 import android.view.textclassifier.TextClassifier.Utils; 33 34 import com.android.internal.annotations.VisibleForTesting; 35 import com.android.internal.util.Preconditions; 36 37 import java.util.Locale; 38 import java.util.Map; 39 40 /** 41 * Information about where text selection should be. 42 */ 43 public final class TextSelection implements Parcelable { 44 45 private final int mStartIndex; 46 private final int mEndIndex; 47 private final EntityConfidence mEntityConfidence; 48 @Nullable private final String mId; 49 private final Bundle mExtras; 50 TextSelection( int startIndex, int endIndex, Map<String, Float> entityConfidence, String id, Bundle extras)51 private TextSelection( 52 int startIndex, int endIndex, Map<String, Float> entityConfidence, String id, 53 Bundle extras) { 54 mStartIndex = startIndex; 55 mEndIndex = endIndex; 56 mEntityConfidence = new EntityConfidence(entityConfidence); 57 mId = id; 58 mExtras = extras; 59 } 60 61 /** 62 * Returns the start index of the text selection. 63 */ getSelectionStartIndex()64 public int getSelectionStartIndex() { 65 return mStartIndex; 66 } 67 68 /** 69 * Returns the end index of the text selection. 70 */ getSelectionEndIndex()71 public int getSelectionEndIndex() { 72 return mEndIndex; 73 } 74 75 /** 76 * Returns the number of entities found in the classified text. 77 */ 78 @IntRange(from = 0) getEntityCount()79 public int getEntityCount() { 80 return mEntityConfidence.getEntities().size(); 81 } 82 83 /** 84 * Returns the entity at the specified index. Entities are ordered from high confidence 85 * to low confidence. 86 * 87 * @throws IndexOutOfBoundsException if the specified index is out of range. 88 * @see #getEntityCount() for the number of entities available. 89 */ 90 @NonNull 91 @EntityType getEntity(int index)92 public String getEntity(int index) { 93 return mEntityConfidence.getEntities().get(index); 94 } 95 96 /** 97 * Returns the confidence score for the specified entity. The value ranges from 98 * 0 (low confidence) to 1 (high confidence). 0 indicates that the entity was not found for the 99 * classified text. 100 */ 101 @FloatRange(from = 0.0, to = 1.0) getConfidenceScore(@ntityType String entity)102 public float getConfidenceScore(@EntityType String entity) { 103 return mEntityConfidence.getConfidenceScore(entity); 104 } 105 106 /** 107 * Returns the id, if one exists, for this object. 108 */ 109 @Nullable getId()110 public String getId() { 111 return mId; 112 } 113 114 /** 115 * Returns the extended data. 116 * 117 * <p><b>NOTE: </b>Do not modify this bundle. 118 */ 119 @NonNull getExtras()120 public Bundle getExtras() { 121 return mExtras; 122 } 123 124 @Override toString()125 public String toString() { 126 return String.format( 127 Locale.US, 128 "TextSelection {id=%s, startIndex=%d, endIndex=%d, entities=%s}", 129 mId, mStartIndex, mEndIndex, mEntityConfidence); 130 } 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; 139 private final Map<String, Float> mEntityConfidence = new ArrayMap<>(); 140 @Nullable private String mId; 141 @Nullable 142 private Bundle mExtras; 143 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 */ Builder(@ntRangefrom = 0) int startIndex, @IntRange(from = 0) int endIndex)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 */ 164 @NonNull setEntityType( @onNull @ntityType String type, @FloatRange(from = 0.0, to = 1.0) float confidenceScore)165 public Builder setEntityType( 166 @NonNull @EntityType String type, 167 @FloatRange(from = 0.0, to = 1.0) float confidenceScore) { 168 Preconditions.checkNotNull(type); 169 mEntityConfidence.put(type, confidenceScore); 170 return this; 171 } 172 173 /** 174 * Sets an id for the TextSelection object. 175 */ 176 @NonNull setId(@ullable String id)177 public Builder setId(@Nullable String id) { 178 mId = id; 179 return this; 180 } 181 182 /** 183 * Sets the extended data. 184 * 185 * @return this builder 186 */ 187 @NonNull setExtras(@ullable Bundle extras)188 public Builder setExtras(@Nullable Bundle extras) { 189 mExtras = extras; 190 return this; 191 } 192 193 /** 194 * Builds and returns {@link TextSelection} object. 195 */ 196 @NonNull build()197 public TextSelection build() { 198 return new TextSelection( 199 mStartIndex, mEndIndex, mEntityConfidence, mId, 200 mExtras == null ? Bundle.EMPTY : mExtras); 201 } 202 } 203 204 /** 205 * A request object for generating TextSelection. 206 */ 207 public static final class Request implements Parcelable { 208 209 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; 214 private final Bundle mExtras; 215 @Nullable private String mCallingPackageName; 216 @UserIdInt 217 private int mUserId = UserHandle.USER_NULL; 218 Request( CharSequence text, int startIndex, int endIndex, LocaleList defaultLocales, boolean darkLaunchAllowed, Bundle extras)219 private Request( 220 CharSequence text, 221 int startIndex, 222 int endIndex, 223 LocaleList defaultLocales, 224 boolean darkLaunchAllowed, 225 Bundle extras) { 226 mText = text; 227 mStartIndex = startIndex; 228 mEndIndex = endIndex; 229 mDefaultLocales = defaultLocales; 230 mDarkLaunchAllowed = darkLaunchAllowed; 231 mExtras = extras; 232 } 233 234 /** 235 * Returns the text providing context for the selected text (which is specified by the 236 * sub sequence starting at startIndex and ending at endIndex). 237 */ 238 @NonNull getText()239 public CharSequence getText() { 240 return mText; 241 } 242 243 /** 244 * Returns start index of the selected part of text. 245 */ 246 @IntRange(from = 0) getStartIndex()247 public int getStartIndex() { 248 return mStartIndex; 249 } 250 251 /** 252 * Returns end index of the selected part of text. 253 */ 254 @IntRange(from = 0) getEndIndex()255 public int getEndIndex() { 256 return mEndIndex; 257 } 258 259 /** 260 * Returns true if the TextClassifier should return selection suggestions when "dark 261 * launched". Otherwise, returns false. 262 * 263 * @hide 264 */ isDarkLaunchAllowed()265 public boolean isDarkLaunchAllowed() { 266 return mDarkLaunchAllowed; 267 } 268 269 /** 270 * @return ordered list of locale preferences that can be used to disambiguate the 271 * provided text. 272 */ 273 @Nullable getDefaultLocales()274 public LocaleList getDefaultLocales() { 275 return mDefaultLocales; 276 } 277 278 /** 279 * Sets the name of the package that is sending this request. 280 * <p> 281 * Package-private for SystemTextClassifier's use. 282 * @hide 283 */ 284 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) setCallingPackageName(@ullable String callingPackageName)285 public void setCallingPackageName(@Nullable String callingPackageName) { 286 mCallingPackageName = callingPackageName; 287 } 288 289 /** 290 * Returns the name of the package that sent this request. 291 * This returns {@code null} if no calling package name is set. 292 */ 293 @Nullable getCallingPackageName()294 public String getCallingPackageName() { 295 return mCallingPackageName; 296 } 297 298 /** 299 * Sets the id of the user that sent this request. 300 * <p> 301 * Package-private for SystemTextClassifier's use. 302 */ setUserId(@serIdInt int userId)303 void setUserId(@UserIdInt int userId) { 304 mUserId = userId; 305 } 306 307 /** 308 * Returns the id of the user that sent this request. 309 * @hide 310 */ 311 @UserIdInt getUserId()312 public int getUserId() { 313 return mUserId; 314 } 315 316 /** 317 * Returns the extended data. 318 * 319 * <p><b>NOTE: </b>Do not modify this bundle. 320 */ 321 @NonNull getExtras()322 public Bundle getExtras() { 323 return mExtras; 324 } 325 326 /** 327 * A builder for building TextSelection requests. 328 */ 329 public static final class Builder { 330 331 private final CharSequence mText; 332 private final int mStartIndex; 333 private final int mEndIndex; 334 335 @Nullable private LocaleList mDefaultLocales; 336 private boolean mDarkLaunchAllowed; 337 private Bundle mExtras; 338 339 /** 340 * @param text text providing context for the selected text (which is specified by the 341 * sub sequence starting at selectionStartIndex and ending at selectionEndIndex) 342 * @param startIndex start index of the selected part of text 343 * @param endIndex end index of the selected part of text 344 */ Builder( @onNull CharSequence text, @IntRange(from = 0) int startIndex, @IntRange(from = 0) int endIndex)345 public Builder( 346 @NonNull CharSequence text, 347 @IntRange(from = 0) int startIndex, 348 @IntRange(from = 0) int endIndex) { 349 Utils.checkArgument(text, startIndex, endIndex); 350 mText = text; 351 mStartIndex = startIndex; 352 mEndIndex = endIndex; 353 } 354 355 /** 356 * @param defaultLocales ordered list of locale preferences that may be used to 357 * disambiguate the provided text. If no locale preferences exist, set this to null 358 * or an empty locale list. 359 * 360 * @return this builder. 361 */ 362 @NonNull setDefaultLocales(@ullable LocaleList defaultLocales)363 public Builder setDefaultLocales(@Nullable LocaleList defaultLocales) { 364 mDefaultLocales = defaultLocales; 365 return this; 366 } 367 368 /** 369 * @param allowed whether or not the TextClassifier should return selection suggestions 370 * when "dark launched". When a TextClassifier is dark launched, it can suggest 371 * selection changes that should not be used to actually change the user's 372 * selection. Instead, the suggested selection is logged, compared with the user's 373 * selection interaction, and used to generate quality metrics for the 374 * TextClassifier. Not parceled. 375 * 376 * @return this builder. 377 * @hide 378 */ 379 @NonNull setDarkLaunchAllowed(boolean allowed)380 public Builder setDarkLaunchAllowed(boolean allowed) { 381 mDarkLaunchAllowed = allowed; 382 return this; 383 } 384 385 /** 386 * Sets the extended data. 387 * 388 * @return this builder 389 */ 390 @NonNull setExtras(@ullable Bundle extras)391 public Builder setExtras(@Nullable Bundle extras) { 392 mExtras = extras; 393 return this; 394 } 395 396 /** 397 * Builds and returns the request object. 398 */ 399 @NonNull build()400 public Request build() { 401 return new Request(new SpannedString(mText), mStartIndex, mEndIndex, 402 mDefaultLocales, mDarkLaunchAllowed, 403 mExtras == null ? Bundle.EMPTY : mExtras); 404 } 405 } 406 407 @Override describeContents()408 public int describeContents() { 409 return 0; 410 } 411 412 @Override writeToParcel(Parcel dest, int flags)413 public void writeToParcel(Parcel dest, int flags) { 414 dest.writeCharSequence(mText); 415 dest.writeInt(mStartIndex); 416 dest.writeInt(mEndIndex); 417 dest.writeParcelable(mDefaultLocales, flags); 418 dest.writeString(mCallingPackageName); 419 dest.writeInt(mUserId); 420 dest.writeBundle(mExtras); 421 } 422 readFromParcel(Parcel in)423 private static Request readFromParcel(Parcel in) { 424 final CharSequence text = in.readCharSequence(); 425 final int startIndex = in.readInt(); 426 final int endIndex = in.readInt(); 427 final LocaleList defaultLocales = in.readParcelable(null); 428 final String callingPackageName = in.readString(); 429 final int userId = in.readInt(); 430 final Bundle extras = in.readBundle(); 431 432 final Request request = new Request(text, startIndex, endIndex, defaultLocales, 433 /* darkLaunchAllowed= */ false, extras); 434 request.setCallingPackageName(callingPackageName); 435 request.setUserId(userId); 436 return request; 437 } 438 439 public static final @android.annotation.NonNull Parcelable.Creator<Request> CREATOR = 440 new Parcelable.Creator<Request>() { 441 @Override 442 public Request createFromParcel(Parcel in) { 443 return readFromParcel(in); 444 } 445 446 @Override 447 public Request[] newArray(int size) { 448 return new Request[size]; 449 } 450 }; 451 } 452 453 @Override describeContents()454 public int describeContents() { 455 return 0; 456 } 457 458 @Override writeToParcel(Parcel dest, int flags)459 public void writeToParcel(Parcel dest, int flags) { 460 dest.writeInt(mStartIndex); 461 dest.writeInt(mEndIndex); 462 mEntityConfidence.writeToParcel(dest, flags); 463 dest.writeString(mId); 464 dest.writeBundle(mExtras); 465 } 466 467 public static final @android.annotation.NonNull Parcelable.Creator<TextSelection> CREATOR = 468 new Parcelable.Creator<TextSelection>() { 469 @Override 470 public TextSelection createFromParcel(Parcel in) { 471 return new TextSelection(in); 472 } 473 474 @Override 475 public TextSelection[] newArray(int size) { 476 return new TextSelection[size]; 477 } 478 }; 479 TextSelection(Parcel in)480 private TextSelection(Parcel in) { 481 mStartIndex = in.readInt(); 482 mEndIndex = in.readInt(); 483 mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in); 484 mId = in.readString(); 485 mExtras = in.readBundle(); 486 } 487 } 488