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