1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859
|
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.view.textclassifier;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.app.RemoteAction;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.BitmapFactory;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
import android.text.SpannedString;
import android.util.ArrayMap;
import android.view.View.OnClickListener;
import android.view.textclassifier.TextClassifier.EntityType;
import android.view.textclassifier.TextClassifier.Utils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.google.android.textclassifier.AnnotatorModel;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
/**
* Information for generating a widget to handle classified text.
*
* <p>A TextClassification object contains icons, labels, onClickListeners and intents that may
* be used to build a widget that can be used to act on classified text. There is the concept of a
* <i>primary action</i> and other <i>secondary actions</i>.
*
* <p>e.g. building a view that, when clicked, shares the classified text with the preferred app:
*
* <pre>{@code
* // Called preferably outside the UiThread.
* TextClassification classification = textClassifier.classifyText(allText, 10, 25);
*
* // Called on the UiThread.
* Button button = new Button(context);
* button.setCompoundDrawablesWithIntrinsicBounds(classification.getIcon(), null, null, null);
* button.setText(classification.getLabel());
* button.setOnClickListener(v -> classification.getActions().get(0).getActionIntent().send());
* }</pre>
*
* <p>e.g. starting an action mode with menu items that can handle the classified text:
*
* <pre>{@code
* // Called preferably outside the UiThread.
* final TextClassification classification = textClassifier.classifyText(allText, 10, 25);
*
* // Called on the UiThread.
* view.startActionMode(new ActionMode.Callback() {
*
* public boolean onCreateActionMode(ActionMode mode, Menu menu) {
* for (int i = 0; i < classification.getActions().size(); ++i) {
* RemoteAction action = classification.getActions().get(i);
* menu.add(Menu.NONE, i, 20, action.getTitle())
* .setIcon(action.getIcon());
* }
* return true;
* }
*
* public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
* classification.getActions().get(item.getItemId()).getActionIntent().send();
* return true;
* }
*
* ...
* });
* }</pre>
*/
public final class TextClassification implements Parcelable {
/**
* @hide
*/
public static final TextClassification EMPTY = new TextClassification.Builder().build();
private static final String LOG_TAG = "TextClassification";
// TODO(toki): investigate a way to derive this based on device properties.
private static final int MAX_LEGACY_ICON_SIZE = 192;
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {IntentType.UNSUPPORTED, IntentType.ACTIVITY, IntentType.SERVICE})
private @interface IntentType {
int UNSUPPORTED = -1;
int ACTIVITY = 0;
int SERVICE = 1;
}
@NonNull private final String mText;
@Nullable private final Drawable mLegacyIcon;
@Nullable private final String mLegacyLabel;
@Nullable private final Intent mLegacyIntent;
@Nullable private final OnClickListener mLegacyOnClickListener;
@NonNull private final List<RemoteAction> mActions;
@NonNull private final EntityConfidence mEntityConfidence;
@Nullable private final String mId;
@NonNull private final Bundle mExtras;
private TextClassification(
@Nullable String text,
@Nullable Drawable legacyIcon,
@Nullable String legacyLabel,
@Nullable Intent legacyIntent,
@Nullable OnClickListener legacyOnClickListener,
@NonNull List<RemoteAction> actions,
@NonNull EntityConfidence entityConfidence,
@Nullable String id,
@NonNull Bundle extras) {
mText = text;
mLegacyIcon = legacyIcon;
mLegacyLabel = legacyLabel;
mLegacyIntent = legacyIntent;
mLegacyOnClickListener = legacyOnClickListener;
mActions = Collections.unmodifiableList(actions);
mEntityConfidence = Preconditions.checkNotNull(entityConfidence);
mId = id;
mExtras = extras;
}
/**
* Gets the classified text.
*/
@Nullable
public String getText() {
return mText;
}
/**
* Returns the number of entities found in the classified text.
*/
@IntRange(from = 0)
public int getEntityCount() {
return mEntityConfidence.getEntities().size();
}
/**
* Returns the entity at the specified index. Entities are ordered from high confidence
* to low confidence.
*
* @throws IndexOutOfBoundsException if the specified index is out of range.
* @see #getEntityCount() for the number of entities available.
*/
@NonNull
public @EntityType String getEntity(int index) {
return mEntityConfidence.getEntities().get(index);
}
/**
* Returns the confidence score for the specified entity. The value ranges from
* 0 (low confidence) to 1 (high confidence). 0 indicates that the entity was not found for the
* classified text.
*/
@FloatRange(from = 0.0, to = 1.0)
public float getConfidenceScore(@EntityType String entity) {
return mEntityConfidence.getConfidenceScore(entity);
}
/**
* Returns a list of actions that may be performed on the text. The list is ordered based on
* the likelihood that a user will use the action, with the most likely action appearing first.
*/
public List<RemoteAction> getActions() {
return mActions;
}
/**
* Returns an icon that may be rendered on a widget used to act on the classified text.
*
* <p><strong>NOTE: </strong>This field is not parcelable and only represents the icon of the
* first {@link RemoteAction} (if one exists) when this object is read from a parcel.
*
* @deprecated Use {@link #getActions()} instead.
*/
@Deprecated
@Nullable
public Drawable getIcon() {
return mLegacyIcon;
}
/**
* Returns a label that may be rendered on a widget used to act on the classified text.
*
* <p><strong>NOTE: </strong>This field is not parcelable and only represents the label of the
* first {@link RemoteAction} (if one exists) when this object is read from a parcel.
*
* @deprecated Use {@link #getActions()} instead.
*/
@Deprecated
@Nullable
public CharSequence getLabel() {
return mLegacyLabel;
}
/**
* Returns an intent that may be fired to act on the classified text.
*
* <p><strong>NOTE: </strong>This field is not parcelled and will always return null when this
* object is read from a parcel.
*
* @deprecated Use {@link #getActions()} instead.
*/
@Deprecated
@Nullable
public Intent getIntent() {
return mLegacyIntent;
}
/**
* Returns the OnClickListener that may be triggered to act on the classified text.
*
* <p><strong>NOTE: </strong>This field is not parcelable and only represents the first
* {@link RemoteAction} (if one exists) when this object is read from a parcel.
*
* @deprecated Use {@link #getActions()} instead.
*/
@Nullable
public OnClickListener getOnClickListener() {
return mLegacyOnClickListener;
}
/**
* Returns the id, if one exists, for this object.
*/
@Nullable
public String getId() {
return mId;
}
/**
* Returns the extended data.
*
* <p><b>NOTE: </b>Do not modify this bundle.
*/
@NonNull
public Bundle getExtras() {
return mExtras;
}
@Override
public String toString() {
return String.format(Locale.US,
"TextClassification {text=%s, entities=%s, actions=%s, id=%s, extras=%s}",
mText, mEntityConfidence, mActions, mId, mExtras);
}
/**
* Creates an OnClickListener that triggers the specified PendingIntent.
*
* @hide
*/
public static OnClickListener createIntentOnClickListener(@NonNull final PendingIntent intent) {
Preconditions.checkNotNull(intent);
return v -> {
try {
intent.send();
} catch (PendingIntent.CanceledException e) {
Log.e(LOG_TAG, "Error sending PendingIntent", e);
}
};
}
/**
* Creates a PendingIntent for the specified intent.
* Returns null if the intent is not supported for the specified context.
*
* @throws IllegalArgumentException if context or intent is null
* @hide
*/
public static PendingIntent createPendingIntent(
@NonNull final Context context, @NonNull final Intent intent, int requestCode) {
return PendingIntent.getActivity(
context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
/**
* Builder for building {@link TextClassification} objects.
*
* <p>e.g.
*
* <pre>{@code
* TextClassification classification = new TextClassification.Builder()
* .setText(classifiedText)
* .setEntityType(TextClassifier.TYPE_EMAIL, 0.9)
* .setEntityType(TextClassifier.TYPE_OTHER, 0.1)
* .addAction(remoteAction1)
* .addAction(remoteAction2)
* .build();
* }</pre>
*/
public static final class Builder {
@NonNull private List<RemoteAction> mActions = new ArrayList<>();
@NonNull private final Map<String, Float> mTypeScoreMap = new ArrayMap<>();
@NonNull
private final Map<String, AnnotatorModel.ClassificationResult> mClassificationResults =
new ArrayMap<>();
@Nullable private String mText;
@Nullable private Drawable mLegacyIcon;
@Nullable private String mLegacyLabel;
@Nullable private Intent mLegacyIntent;
@Nullable private OnClickListener mLegacyOnClickListener;
@Nullable private String mId;
@Nullable private Bundle mExtras;
@NonNull private final ArrayList<Intent> mActionIntents = new ArrayList<>();
@Nullable private Bundle mForeignLanguageExtra;
/**
* Sets the classified text.
*/
@NonNull
public Builder setText(@Nullable String text) {
mText = text;
return this;
}
/**
* Sets an entity type for the classification result and assigns a confidence score.
* If a confidence score had already been set for the specified entity type, this will
* override that score.
*
* @param confidenceScore a value from 0 (low confidence) to 1 (high confidence).
* 0 implies the entity does not exist for the classified text.
* Values greater than 1 are clamped to 1.
*/
@NonNull
public Builder setEntityType(
@NonNull @EntityType String type,
@FloatRange(from = 0.0, to = 1.0) float confidenceScore) {
setEntityType(type, confidenceScore, null);
return this;
}
/**
* @see #setEntityType(String, float)
*
* @hide
*/
@NonNull
public Builder setEntityType(AnnotatorModel.ClassificationResult classificationResult) {
setEntityType(
classificationResult.getCollection(),
classificationResult.getScore(),
classificationResult);
return this;
}
/**
* @see #setEntityType(String, float)
*
* @hide
*/
@NonNull
private Builder setEntityType(
@NonNull @EntityType String type,
@FloatRange(from = 0.0, to = 1.0) float confidenceScore,
@Nullable AnnotatorModel.ClassificationResult classificationResult) {
mTypeScoreMap.put(type, confidenceScore);
mClassificationResults.put(type, classificationResult);
return this;
}
/**
* Adds an action that may be performed on the classified text. Actions should be added in
* order of likelihood that the user will use them, with the most likely action being added
* first.
*/
@NonNull
public Builder addAction(@NonNull RemoteAction action) {
return addAction(action, null);
}
/**
* @param intent the intent in the remote action.
* @see #addAction(RemoteAction)
* @hide
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public Builder addAction(RemoteAction action, @Nullable Intent intent) {
Preconditions.checkArgument(action != null);
mActions.add(action);
mActionIntents.add(intent);
return this;
}
/**
* Sets the icon for the <i>primary</i> action that may be rendered on a widget used to act
* on the classified text.
*
* <p><strong>NOTE: </strong>This field is not parcelled. If read from a parcel, the
* returned icon represents the icon of the first {@link RemoteAction} (if one exists).
*
* @deprecated Use {@link #addAction(RemoteAction)} instead.
*/
@Deprecated
@NonNull
public Builder setIcon(@Nullable Drawable icon) {
mLegacyIcon = icon;
return this;
}
/**
* Sets the label for the <i>primary</i> action that may be rendered on a widget used to
* act on the classified text.
*
* <p><strong>NOTE: </strong>This field is not parcelled. If read from a parcel, the
* returned label represents the label of the first {@link RemoteAction} (if one exists).
*
* @deprecated Use {@link #addAction(RemoteAction)} instead.
*/
@Deprecated
@NonNull
public Builder setLabel(@Nullable String label) {
mLegacyLabel = label;
return this;
}
/**
* Sets the intent for the <i>primary</i> action that may be fired to act on the classified
* text.
*
* <p><strong>NOTE: </strong>This field is not parcelled.
*
* @deprecated Use {@link #addAction(RemoteAction)} instead.
*/
@Deprecated
@NonNull
public Builder setIntent(@Nullable Intent intent) {
mLegacyIntent = intent;
return this;
}
/**
* Sets the OnClickListener for the <i>primary</i> action that may be triggered to act on
* the classified text.
*
* <p><strong>NOTE: </strong>This field is not parcelable. If read from a parcel, the
* returned OnClickListener represents the first {@link RemoteAction} (if one exists).
*
* @deprecated Use {@link #addAction(RemoteAction)} instead.
*/
@Deprecated
@NonNull
public Builder setOnClickListener(@Nullable OnClickListener onClickListener) {
mLegacyOnClickListener = onClickListener;
return this;
}
/**
* Sets an id for the TextClassification object.
*/
@NonNull
public Builder setId(@Nullable String id) {
mId = id;
return this;
}
/**
* Sets the extended data.
*/
@NonNull
public Builder setExtras(@Nullable Bundle extras) {
mExtras = extras;
return this;
}
/**
* @see #setExtras(Bundle)
* @hide
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public Builder setForeignLanguageExtra(@Nullable Bundle extra) {
mForeignLanguageExtra = extra;
return this;
}
/**
* Builds and returns a {@link TextClassification} object.
*/
@NonNull
public TextClassification build() {
EntityConfidence entityConfidence = new EntityConfidence(mTypeScoreMap);
return new TextClassification(mText, mLegacyIcon, mLegacyLabel, mLegacyIntent,
mLegacyOnClickListener, mActions, entityConfidence, mId,
buildExtras(entityConfidence));
}
private Bundle buildExtras(EntityConfidence entityConfidence) {
final Bundle extras = mExtras == null ? new Bundle() : mExtras;
if (mActionIntents.stream().anyMatch(Objects::nonNull)) {
ExtrasUtils.putActionsIntents(extras, mActionIntents);
}
if (mForeignLanguageExtra != null) {
ExtrasUtils.putForeignLanguageExtra(extras, mForeignLanguageExtra);
}
List<String> sortedTypes = entityConfidence.getEntities();
ArrayList<AnnotatorModel.ClassificationResult> sortedEntities = new ArrayList<>();
for (String type : sortedTypes) {
sortedEntities.add(mClassificationResults.get(type));
}
ExtrasUtils.putEntities(
extras, sortedEntities.toArray(new AnnotatorModel.ClassificationResult[0]));
return extras.isEmpty() ? Bundle.EMPTY : extras;
}
}
/**
* A request object for generating TextClassification.
*/
public static final class Request implements Parcelable {
private final CharSequence mText;
private final int mStartIndex;
private final int mEndIndex;
@Nullable private final LocaleList mDefaultLocales;
@Nullable private final ZonedDateTime mReferenceTime;
@NonNull private final Bundle mExtras;
@Nullable private String mCallingPackageName;
@UserIdInt
private int mUserId = UserHandle.USER_NULL;
private Request(
CharSequence text,
int startIndex,
int endIndex,
LocaleList defaultLocales,
ZonedDateTime referenceTime,
Bundle extras) {
mText = text;
mStartIndex = startIndex;
mEndIndex = endIndex;
mDefaultLocales = defaultLocales;
mReferenceTime = referenceTime;
mExtras = extras;
}
/**
* Returns the text providing context for the text to classify (which is specified
* by the sub sequence starting at startIndex and ending at endIndex)
*/
@NonNull
public CharSequence getText() {
return mText;
}
/**
* Returns start index of the text to classify.
*/
@IntRange(from = 0)
public int getStartIndex() {
return mStartIndex;
}
/**
* Returns end index of the text to classify.
*/
@IntRange(from = 0)
public int getEndIndex() {
return mEndIndex;
}
/**
* @return ordered list of locale preferences that can be used to disambiguate
* the provided text.
*/
@Nullable
public LocaleList getDefaultLocales() {
return mDefaultLocales;
}
/**
* @return reference time based on which relative dates (e.g. "tomorrow") should be
* interpreted.
*/
@Nullable
public ZonedDateTime getReferenceTime() {
return mReferenceTime;
}
/**
* Sets the name of the package that is sending this request.
* <p>
* For SystemTextClassifier's use.
* @hide
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public void setCallingPackageName(@Nullable String callingPackageName) {
mCallingPackageName = callingPackageName;
}
/**
* Returns the name of the package that sent this request.
* This returns {@code null} if no calling package name is set.
*/
@Nullable
public String getCallingPackageName() {
return mCallingPackageName;
}
/**
* Sets the id of the user that sent this request.
* <p>
* Package-private for SystemTextClassifier's use.
*/
void setUserId(@UserIdInt int userId) {
mUserId = userId;
}
/**
* Returns the id of the user that sent this request.
* @hide
*/
@UserIdInt
public int getUserId() {
return mUserId;
}
/**
* Returns the extended data.
*
* <p><b>NOTE: </b>Do not modify this bundle.
*/
@NonNull
public Bundle getExtras() {
return mExtras;
}
/**
* A builder for building TextClassification requests.
*/
public static final class Builder {
private final CharSequence mText;
private final int mStartIndex;
private final int mEndIndex;
private Bundle mExtras;
@Nullable private LocaleList mDefaultLocales;
@Nullable private ZonedDateTime mReferenceTime;
/**
* @param text text providing context for the text to classify (which is specified
* by the sub sequence starting at startIndex and ending at endIndex)
* @param startIndex start index of the text to classify
* @param endIndex end index of the text to classify
*/
public Builder(
@NonNull CharSequence text,
@IntRange(from = 0) int startIndex,
@IntRange(from = 0) int endIndex) {
Utils.checkArgument(text, startIndex, endIndex);
mText = text;
mStartIndex = startIndex;
mEndIndex = endIndex;
}
/**
* @param defaultLocales ordered list of locale preferences that may be used to
* disambiguate the provided text. If no locale preferences exist, set this to null
* or an empty locale list.
*
* @return this builder
*/
@NonNull
public Builder setDefaultLocales(@Nullable LocaleList defaultLocales) {
mDefaultLocales = defaultLocales;
return this;
}
/**
* @param referenceTime reference time based on which relative dates (e.g. "tomorrow"
* should be interpreted. This should usually be the time when the text was
* originally composed. If no reference time is set, now is used.
*
* @return this builder
*/
@NonNull
public Builder setReferenceTime(@Nullable ZonedDateTime referenceTime) {
mReferenceTime = referenceTime;
return this;
}
/**
* Sets the extended data.
*
* @return this builder
*/
@NonNull
public Builder setExtras(@Nullable Bundle extras) {
mExtras = extras;
return this;
}
/**
* Builds and returns the request object.
*/
@NonNull
public Request build() {
return new Request(new SpannedString(mText), mStartIndex, mEndIndex,
mDefaultLocales, mReferenceTime,
mExtras == null ? Bundle.EMPTY : mExtras);
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeCharSequence(mText);
dest.writeInt(mStartIndex);
dest.writeInt(mEndIndex);
dest.writeParcelable(mDefaultLocales, flags);
dest.writeString(mReferenceTime == null ? null : mReferenceTime.toString());
dest.writeString(mCallingPackageName);
dest.writeInt(mUserId);
dest.writeBundle(mExtras);
}
private static Request readFromParcel(Parcel in) {
final CharSequence text = in.readCharSequence();
final int startIndex = in.readInt();
final int endIndex = in.readInt();
final LocaleList defaultLocales = in.readParcelable(null);
final String referenceTimeString = in.readString();
final ZonedDateTime referenceTime = referenceTimeString == null
? null : ZonedDateTime.parse(referenceTimeString);
final String callingPackageName = in.readString();
final int userId = in.readInt();
final Bundle extras = in.readBundle();
final Request request = new Request(text, startIndex, endIndex,
defaultLocales, referenceTime, extras);
request.setCallingPackageName(callingPackageName);
request.setUserId(userId);
return request;
}
public static final @android.annotation.NonNull Parcelable.Creator<Request> CREATOR =
new Parcelable.Creator<Request>() {
@Override
public Request createFromParcel(Parcel in) {
return readFromParcel(in);
}
@Override
public Request[] newArray(int size) {
return new Request[size];
}
};
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mText);
// NOTE: legacy fields are not parcelled.
dest.writeTypedList(mActions);
mEntityConfidence.writeToParcel(dest, flags);
dest.writeString(mId);
dest.writeBundle(mExtras);
}
public static final @android.annotation.NonNull Parcelable.Creator<TextClassification> CREATOR =
new Parcelable.Creator<TextClassification>() {
@Override
public TextClassification createFromParcel(Parcel in) {
return new TextClassification(in);
}
@Override
public TextClassification[] newArray(int size) {
return new TextClassification[size];
}
};
private TextClassification(Parcel in) {
mText = in.readString();
mActions = in.createTypedArrayList(RemoteAction.CREATOR);
if (!mActions.isEmpty()) {
final RemoteAction action = mActions.get(0);
mLegacyIcon = maybeLoadDrawable(action.getIcon());
mLegacyLabel = action.getTitle().toString();
mLegacyOnClickListener = createIntentOnClickListener(mActions.get(0).getActionIntent());
} else {
mLegacyIcon = null;
mLegacyLabel = null;
mLegacyOnClickListener = null;
}
mLegacyIntent = null; // mLegacyIntent is not parcelled.
mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
mId = in.readString();
mExtras = in.readBundle();
}
// Best effort attempt to try to load a drawable from the provided icon.
@Nullable
private static Drawable maybeLoadDrawable(Icon icon) {
if (icon == null) {
return null;
}
switch (icon.getType()) {
case Icon.TYPE_BITMAP:
return new BitmapDrawable(Resources.getSystem(), icon.getBitmap());
case Icon.TYPE_ADAPTIVE_BITMAP:
return new AdaptiveIconDrawable(null,
new BitmapDrawable(Resources.getSystem(), icon.getBitmap()));
case Icon.TYPE_DATA:
return new BitmapDrawable(
Resources.getSystem(),
BitmapFactory.decodeByteArray(
icon.getDataBytes(), icon.getDataOffset(), icon.getDataLength()));
}
return null;
}
}
|