1 /* 2 * Copyright (C) 2018 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.icu.util.ULocale; 25 import android.os.Bundle; 26 import android.os.Parcel; 27 import android.os.Parcelable; 28 import android.os.UserHandle; 29 import android.util.ArrayMap; 30 31 import com.android.internal.annotations.VisibleForTesting; 32 import com.android.internal.util.Preconditions; 33 34 import java.util.Locale; 35 import java.util.Map; 36 37 /** 38 * Represents the result of language detection of a piece of text. 39 * <p> 40 * This contains a list of locales, each paired with a confidence score, sorted in decreasing 41 * order of those scores. E.g., for a given input text, the model may return 42 * {@code [<"en", 0.85>, <"fr", 0.15>]}. This sample result means the model reports that it is 43 * 85% likely that the entire text is in English and 15% likely that the entire text is in French, 44 * etc. It does not mean that 85% of the input is in English and 15% is in French. 45 */ 46 public final class TextLanguage implements Parcelable { 47 48 public static final @android.annotation.NonNull Creator<TextLanguage> CREATOR = new Creator<TextLanguage>() { 49 @Override 50 public TextLanguage createFromParcel(Parcel in) { 51 return readFromParcel(in); 52 } 53 54 @Override 55 public TextLanguage[] newArray(int size) { 56 return new TextLanguage[size]; 57 } 58 }; 59 60 static final TextLanguage EMPTY = new Builder().build(); 61 62 @Nullable private final String mId; 63 private final EntityConfidence mEntityConfidence; 64 private final Bundle mBundle; 65 TextLanguage( @ullable String id, EntityConfidence entityConfidence, Bundle bundle)66 private TextLanguage( 67 @Nullable String id, 68 EntityConfidence entityConfidence, 69 Bundle bundle) { 70 mId = id; 71 mEntityConfidence = entityConfidence; 72 mBundle = bundle; 73 } 74 75 /** 76 * Returns the id, if one exists, for this object. 77 */ 78 @Nullable getId()79 public String getId() { 80 return mId; 81 } 82 83 /** 84 * Returns the number of possible locales for the processed text. 85 */ 86 @IntRange(from = 0) getLocaleHypothesisCount()87 public int getLocaleHypothesisCount() { 88 return mEntityConfidence.getEntities().size(); 89 } 90 91 /** 92 * Returns the language locale at the specified index. Locales are ordered from high 93 * confidence to low confidence. 94 * <p> 95 * See {@link #getLocaleHypothesisCount()} for the number of locales available. 96 * 97 * @throws IndexOutOfBoundsException if the specified index is out of range. 98 */ 99 @NonNull getLocale(int index)100 public ULocale getLocale(int index) { 101 return ULocale.forLanguageTag(mEntityConfidence.getEntities().get(index)); 102 } 103 104 /** 105 * Returns the confidence score for the specified language locale. The value ranges from 106 * 0 (low confidence) to 1 (high confidence). 0 indicates that the locale was not found for 107 * the processed text. 108 */ 109 @FloatRange(from = 0.0, to = 1.0) getConfidenceScore(@onNull ULocale locale)110 public float getConfidenceScore(@NonNull ULocale locale) { 111 return mEntityConfidence.getConfidenceScore(locale.toLanguageTag()); 112 } 113 114 /** 115 * Returns a bundle containing non-structured extra information about this result. What is 116 * returned in the extras is specific to the {@link TextClassifier} implementation. 117 * 118 * <p><b>NOTE: </b>Do not modify this bundle. 119 */ 120 @NonNull getExtras()121 public Bundle getExtras() { 122 return mBundle; 123 } 124 125 @Override toString()126 public String toString() { 127 return String.format( 128 Locale.US, 129 "TextLanguage {id=%s, locales=%s, bundle=%s}", 130 mId, mEntityConfidence, mBundle); 131 } 132 133 @Override describeContents()134 public int describeContents() { 135 return 0; 136 } 137 138 @Override writeToParcel(Parcel dest, int flags)139 public void writeToParcel(Parcel dest, int flags) { 140 dest.writeString(mId); 141 mEntityConfidence.writeToParcel(dest, flags); 142 dest.writeBundle(mBundle); 143 } 144 readFromParcel(Parcel in)145 private static TextLanguage readFromParcel(Parcel in) { 146 return new TextLanguage( 147 in.readString(), 148 EntityConfidence.CREATOR.createFromParcel(in), 149 in.readBundle()); 150 } 151 152 /** 153 * Builder used to build TextLanguage objects. 154 */ 155 public static final class Builder { 156 157 @Nullable private String mId; 158 private final Map<String, Float> mEntityConfidenceMap = new ArrayMap<>(); 159 @Nullable private Bundle mBundle; 160 161 /** 162 * Sets a language locale for the processed text and assigns a confidence score. If the 163 * locale has already been set, this updates it. 164 * 165 * @param confidenceScore a value from 0 (low confidence) to 1 (high confidence). 166 * 0 implies the locale does not exist for the processed text. 167 * Values greater than 1 are clamped to 1. 168 */ 169 @NonNull putLocale( @onNull ULocale locale, @FloatRange(from = 0.0, to = 1.0) float confidenceScore)170 public Builder putLocale( 171 @NonNull ULocale locale, 172 @FloatRange(from = 0.0, to = 1.0) float confidenceScore) { 173 Preconditions.checkNotNull(locale); 174 mEntityConfidenceMap.put(locale.toLanguageTag(), confidenceScore); 175 return this; 176 } 177 178 /** 179 * Sets an optional id for the TextLanguage object. 180 */ 181 @NonNull setId(@ullable String id)182 public Builder setId(@Nullable String id) { 183 mId = id; 184 return this; 185 } 186 187 /** 188 * Sets a bundle containing non-structured extra information about the TextLanguage object. 189 */ 190 @NonNull setExtras(@onNull Bundle bundle)191 public Builder setExtras(@NonNull Bundle bundle) { 192 mBundle = Preconditions.checkNotNull(bundle); 193 return this; 194 } 195 196 /** 197 * Builds and returns a new TextLanguage object. 198 * <p> 199 * If necessary, this method will verify fields, clamp them, and make them immutable. 200 */ 201 @NonNull build()202 public TextLanguage build() { 203 mBundle = mBundle == null ? Bundle.EMPTY : mBundle; 204 return new TextLanguage( 205 mId, 206 new EntityConfidence(mEntityConfidenceMap), 207 mBundle); 208 } 209 } 210 211 /** 212 * A request object for detecting the language of a piece of text. 213 */ 214 public static final class Request implements Parcelable { 215 216 public static final @android.annotation.NonNull Creator<Request> CREATOR = new Creator<Request>() { 217 @Override 218 public Request createFromParcel(Parcel in) { 219 return readFromParcel(in); 220 } 221 222 @Override 223 public Request[] newArray(int size) { 224 return new Request[size]; 225 } 226 }; 227 228 private final CharSequence mText; 229 private final Bundle mExtra; 230 @Nullable private String mCallingPackageName; 231 @UserIdInt 232 private int mUserId = UserHandle.USER_NULL; 233 Request(CharSequence text, Bundle bundle)234 private Request(CharSequence text, Bundle bundle) { 235 mText = text; 236 mExtra = bundle; 237 } 238 239 /** 240 * Returns the text to process. 241 */ 242 @NonNull getText()243 public CharSequence getText() { 244 return mText; 245 } 246 247 /** 248 * Sets the name of the package that is sending this request. 249 * Package-private for SystemTextClassifier's use. 250 * @hide 251 */ 252 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) setCallingPackageName(@ullable String callingPackageName)253 public void setCallingPackageName(@Nullable String callingPackageName) { 254 mCallingPackageName = callingPackageName; 255 } 256 257 /** 258 * Returns the name of the package that sent this request. 259 * This returns null if no calling package name is set. 260 */ 261 @Nullable getCallingPackageName()262 public String getCallingPackageName() { 263 return mCallingPackageName; 264 } 265 266 /** 267 * Sets the id of the user that sent this request. 268 * <p> 269 * Package-private for SystemTextClassifier's use. 270 */ setUserId(@serIdInt int userId)271 void setUserId(@UserIdInt int userId) { 272 mUserId = userId; 273 } 274 275 /** 276 * Returns the id of the user that sent this request. 277 * @hide 278 */ 279 @UserIdInt getUserId()280 public int getUserId() { 281 return mUserId; 282 } 283 284 /** 285 * Returns a bundle containing non-structured extra information about this request. 286 * 287 * <p><b>NOTE: </b>Do not modify this bundle. 288 */ 289 @NonNull getExtras()290 public Bundle getExtras() { 291 return mExtra; 292 } 293 294 @Override describeContents()295 public int describeContents() { 296 return 0; 297 } 298 299 @Override writeToParcel(Parcel dest, int flags)300 public void writeToParcel(Parcel dest, int flags) { 301 dest.writeCharSequence(mText); 302 dest.writeString(mCallingPackageName); 303 dest.writeInt(mUserId); 304 dest.writeBundle(mExtra); 305 } 306 readFromParcel(Parcel in)307 private static Request readFromParcel(Parcel in) { 308 final CharSequence text = in.readCharSequence(); 309 final String callingPackageName = in.readString(); 310 final int userId = in.readInt(); 311 final Bundle extra = in.readBundle(); 312 313 final Request request = new Request(text, extra); 314 request.setCallingPackageName(callingPackageName); 315 request.setUserId(userId); 316 return request; 317 } 318 319 /** 320 * A builder for building TextLanguage requests. 321 */ 322 public static final class Builder { 323 324 private final CharSequence mText; 325 @Nullable private Bundle mBundle; 326 327 /** 328 * Creates a builder to build TextLanguage requests. 329 * 330 * @param text the text to process. 331 */ Builder(@onNull CharSequence text)332 public Builder(@NonNull CharSequence text) { 333 mText = Preconditions.checkNotNull(text); 334 } 335 336 /** 337 * Sets a bundle containing non-structured extra information about the request. 338 */ 339 @NonNull setExtras(@onNull Bundle bundle)340 public Builder setExtras(@NonNull Bundle bundle) { 341 mBundle = Preconditions.checkNotNull(bundle); 342 return this; 343 } 344 345 /** 346 * Builds and returns a new TextLanguage request object. 347 * <p> 348 * If necessary, this method will verify fields, clamp them, and make them immutable. 349 */ 350 @NonNull build()351 public Request build() { 352 return new Request(mText.toString(), mBundle == null ? Bundle.EMPTY : mBundle); 353 } 354 } 355 } 356 } 357