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