• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 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.IntDef;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.UserIdInt;
24 import android.content.Context;
25 import android.os.Bundle;
26 import android.os.LocaleList;
27 import android.os.Parcel;
28 import android.os.Parcelable;
29 import android.os.UserHandle;
30 import android.text.Spannable;
31 import android.text.method.MovementMethod;
32 import android.text.style.ClickableSpan;
33 import android.text.style.URLSpan;
34 import android.view.View;
35 import android.view.textclassifier.TextClassifier.EntityConfig;
36 import android.view.textclassifier.TextClassifier.EntityType;
37 import android.widget.TextView;
38 
39 import com.android.internal.annotations.VisibleForTesting;
40 import com.android.internal.annotations.VisibleForTesting.Visibility;
41 import com.android.internal.util.Preconditions;
42 
43 import java.lang.annotation.Retention;
44 import java.lang.annotation.RetentionPolicy;
45 import java.util.ArrayList;
46 import java.util.Collection;
47 import java.util.Collections;
48 import java.util.List;
49 import java.util.Locale;
50 import java.util.Map;
51 import java.util.function.Function;
52 
53 /**
54  * A collection of links, representing subsequences of text and the entity types (phone number,
55  * address, url, etc) they may be.
56  */
57 public final class TextLinks implements Parcelable {
58 
59     /**
60      * Return status of an attempt to apply TextLinks to text.
61      * @hide
62      */
63     @Retention(RetentionPolicy.SOURCE)
64     @IntDef({STATUS_LINKS_APPLIED, STATUS_NO_LINKS_FOUND, STATUS_NO_LINKS_APPLIED,
65             STATUS_DIFFERENT_TEXT, STATUS_UNSUPPORTED_CHARACTER})
66     public @interface Status {}
67 
68     /** Links were successfully applied to the text. */
69     public static final int STATUS_LINKS_APPLIED = 0;
70 
71     /** No links exist to apply to text. Links count is zero. */
72     public static final int STATUS_NO_LINKS_FOUND = 1;
73 
74     /** No links applied to text. The links were filtered out. */
75     public static final int STATUS_NO_LINKS_APPLIED = 2;
76 
77     /** The specified text does not match the text used to generate the links. */
78     public static final int STATUS_DIFFERENT_TEXT = 3;
79 
80     /** The specified text contains unsupported characters. */
81     public static final int STATUS_UNSUPPORTED_CHARACTER = 4;
82 
83     /** @hide */
84     @Retention(RetentionPolicy.SOURCE)
85     @IntDef({APPLY_STRATEGY_IGNORE, APPLY_STRATEGY_REPLACE})
86     public @interface ApplyStrategy {}
87 
88     /**
89      * Do not replace {@link ClickableSpan}s that exist where the {@link TextLinkSpan} needs to
90      * be applied to. Do not apply the TextLinkSpan.
91      */
92     public static final int APPLY_STRATEGY_IGNORE = 0;
93 
94     /**
95      * Replace any {@link ClickableSpan}s that exist where the {@link TextLinkSpan} needs to be
96      * applied to.
97      */
98     public static final int APPLY_STRATEGY_REPLACE = 1;
99 
100     private final String mFullText;
101     private final List<TextLink> mLinks;
102     private final Bundle mExtras;
103 
TextLinks(String fullText, ArrayList<TextLink> links, Bundle extras)104     private TextLinks(String fullText, ArrayList<TextLink> links, Bundle extras) {
105         mFullText = fullText;
106         mLinks = Collections.unmodifiableList(links);
107         mExtras = extras;
108     }
109 
110     /**
111      * Returns the text that was used to generate these links.
112      * @hide
113      */
114     @NonNull
getText()115     public String getText() {
116         return mFullText;
117     }
118 
119     /**
120      * Returns an unmodifiable Collection of the links.
121      */
122     @NonNull
getLinks()123     public Collection<TextLink> getLinks() {
124         return mLinks;
125     }
126 
127     /**
128      * Returns the extended data.
129      *
130      * <p><b>NOTE: </b>Do not modify this bundle.
131      */
132     @NonNull
getExtras()133     public Bundle getExtras() {
134         return mExtras;
135     }
136 
137     /**
138      * Annotates the given text with the generated links. It will fail if the provided text doesn't
139      * match the original text used to create the TextLinks.
140      *
141      * <p><strong>NOTE: </strong>It may be necessary to set a LinkMovementMethod on the TextView
142      * widget to properly handle links. See {@link TextView#setMovementMethod(MovementMethod)}
143      *
144      * @param text the text to apply the links to. Must match the original text
145      * @param applyStrategy the apply strategy used to determine how to apply links to text.
146      *      e.g {@link TextLinks#APPLY_STRATEGY_IGNORE}
147      * @param spanFactory a custom span factory for converting TextLinks to TextLinkSpans.
148      *      Set to {@code null} to use the default span factory.
149      *
150      * @return a status code indicating whether or not the links were successfully applied
151      *      e.g. {@link #STATUS_LINKS_APPLIED}
152      */
153     @Status
apply( @onNull Spannable text, @ApplyStrategy int applyStrategy, @Nullable Function<TextLink, TextLinkSpan> spanFactory)154     public int apply(
155             @NonNull Spannable text,
156             @ApplyStrategy int applyStrategy,
157             @Nullable Function<TextLink, TextLinkSpan> spanFactory) {
158         Preconditions.checkNotNull(text);
159         return new TextLinksParams.Builder()
160                 .setApplyStrategy(applyStrategy)
161                 .setSpanFactory(spanFactory)
162                 .build()
163                 .apply(text, this);
164     }
165 
166     @Override
toString()167     public String toString() {
168         return String.format(Locale.US, "TextLinks{fullText=%s, links=%s}", mFullText, mLinks);
169     }
170 
171     @Override
describeContents()172     public int describeContents() {
173         return 0;
174     }
175 
176     @Override
writeToParcel(Parcel dest, int flags)177     public void writeToParcel(Parcel dest, int flags) {
178         dest.writeString(mFullText);
179         dest.writeTypedList(mLinks);
180         dest.writeBundle(mExtras);
181     }
182 
183     public static final @android.annotation.NonNull Parcelable.Creator<TextLinks> CREATOR =
184             new Parcelable.Creator<TextLinks>() {
185                 @Override
186                 public TextLinks createFromParcel(Parcel in) {
187                     return new TextLinks(in);
188                 }
189 
190                 @Override
191                 public TextLinks[] newArray(int size) {
192                     return new TextLinks[size];
193                 }
194             };
195 
TextLinks(Parcel in)196     private TextLinks(Parcel in) {
197         mFullText = in.readString();
198         mLinks = in.createTypedArrayList(TextLink.CREATOR);
199         mExtras = in.readBundle();
200     }
201 
202     /**
203      * A link, identifying a substring of text and possible entity types for it.
204      */
205     public static final class TextLink implements Parcelable {
206         private final EntityConfidence mEntityScores;
207         private final int mStart;
208         private final int mEnd;
209         private final Bundle mExtras;
210         @Nullable private final URLSpan mUrlSpan;
211 
212         /**
213          * Create a new TextLink.
214          *
215          * @param start The start index of the identified subsequence
216          * @param end The end index of the identified subsequence
217          * @param entityConfidence A mapping of entity type to confidence score
218          * @param extras A bundle containing custom data related to this TextLink
219          * @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled
220          *
221          * @throws IllegalArgumentException if {@code entityConfidence} is null or empty
222          * @throws IllegalArgumentException if {@code start} is greater than {@code end}
223          */
TextLink(int start, int end, @NonNull EntityConfidence entityConfidence, @NonNull Bundle extras, @Nullable URLSpan urlSpan)224         private TextLink(int start, int end, @NonNull EntityConfidence entityConfidence,
225                 @NonNull Bundle extras, @Nullable URLSpan urlSpan) {
226             Preconditions.checkNotNull(entityConfidence);
227             Preconditions.checkArgument(!entityConfidence.getEntities().isEmpty());
228             Preconditions.checkArgument(start <= end);
229             Preconditions.checkNotNull(extras);
230             mStart = start;
231             mEnd = end;
232             mEntityScores = entityConfidence;
233             mUrlSpan = urlSpan;
234             mExtras = extras;
235         }
236 
237         /**
238          * Returns the start index of this link in the original text.
239          *
240          * @return the start index
241          */
getStart()242         public int getStart() {
243             return mStart;
244         }
245 
246         /**
247          * Returns the end index of this link in the original text.
248          *
249          * @return the end index
250          */
getEnd()251         public int getEnd() {
252             return mEnd;
253         }
254 
255         /**
256          * Returns the number of entity types that have confidence scores.
257          *
258          * @return the entity count
259          */
getEntityCount()260         public int getEntityCount() {
261             return mEntityScores.getEntities().size();
262         }
263 
264         /**
265          * Returns the entity type at a given index. Entity types are sorted by confidence.
266          *
267          * @return the entity type at the provided index
268          */
getEntity(int index)269         @NonNull public @EntityType String getEntity(int index) {
270             return mEntityScores.getEntities().get(index);
271         }
272 
273         /**
274          * Returns the confidence score for a particular entity type.
275          *
276          * @param entityType the entity type
277          */
getConfidenceScore( @ntityType String entityType)278         public @FloatRange(from = 0.0, to = 1.0) float getConfidenceScore(
279                 @EntityType String entityType) {
280             return mEntityScores.getConfidenceScore(entityType);
281         }
282 
283         /**
284          * Returns a bundle containing custom data related to this TextLink.
285          */
286         @NonNull
getExtras()287         public Bundle getExtras() {
288             return mExtras;
289         }
290 
291         @Override
toString()292         public String toString() {
293             return String.format(Locale.US,
294                     "TextLink{start=%s, end=%s, entityScores=%s, urlSpan=%s}",
295                     mStart, mEnd, mEntityScores, mUrlSpan);
296         }
297 
298         @Override
describeContents()299         public int describeContents() {
300             return 0;
301         }
302 
303         @Override
writeToParcel(Parcel dest, int flags)304         public void writeToParcel(Parcel dest, int flags) {
305             mEntityScores.writeToParcel(dest, flags);
306             dest.writeInt(mStart);
307             dest.writeInt(mEnd);
308             dest.writeBundle(mExtras);
309         }
310 
readFromParcel(Parcel in)311         private static TextLink readFromParcel(Parcel in) {
312             final EntityConfidence entityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
313             final int start = in.readInt();
314             final int end = in.readInt();
315             final Bundle extras = in.readBundle();
316             return new TextLink(start, end, entityConfidence, extras, null /* urlSpan */);
317         }
318 
319         public static final @android.annotation.NonNull Parcelable.Creator<TextLink> CREATOR =
320                 new Parcelable.Creator<TextLink>() {
321                     @Override
322                     public TextLink createFromParcel(Parcel in) {
323                         return readFromParcel(in);
324                     }
325 
326                     @Override
327                     public TextLink[] newArray(int size) {
328                         return new TextLink[size];
329                     }
330                 };
331     }
332 
333     /**
334      * A request object for generating TextLinks.
335      */
336     public static final class Request implements Parcelable {
337 
338         private final CharSequence mText;
339         @Nullable private final LocaleList mDefaultLocales;
340         @Nullable private final EntityConfig mEntityConfig;
341         private final boolean mLegacyFallback;
342         @Nullable private String mCallingPackageName;
343         private final Bundle mExtras;
344         @UserIdInt
345         private int mUserId = UserHandle.USER_NULL;
346 
Request( CharSequence text, LocaleList defaultLocales, EntityConfig entityConfig, boolean legacyFallback, Bundle extras)347         private Request(
348                 CharSequence text,
349                 LocaleList defaultLocales,
350                 EntityConfig entityConfig,
351                 boolean legacyFallback,
352                 Bundle extras) {
353             mText = text;
354             mDefaultLocales = defaultLocales;
355             mEntityConfig = entityConfig;
356             mLegacyFallback = legacyFallback;
357             mExtras = extras;
358         }
359 
360         /**
361          * Returns the text to generate links for.
362          */
363         @NonNull
getText()364         public CharSequence getText() {
365             return mText;
366         }
367 
368         /**
369          * @return ordered list of locale preferences that can be used to disambiguate
370          *      the provided text
371          */
372         @Nullable
getDefaultLocales()373         public LocaleList getDefaultLocales() {
374             return mDefaultLocales;
375         }
376 
377         /**
378          * @return The config representing the set of entities to look for
379          * @see Builder#setEntityConfig(EntityConfig)
380          */
381         @Nullable
getEntityConfig()382         public EntityConfig getEntityConfig() {
383             return mEntityConfig;
384         }
385 
386         /**
387          * Returns whether the TextClassifier can fallback to legacy links if smart linkify is
388          * disabled.
389          * <strong>Note: </strong>This is not parcelled.
390          * @hide
391          */
isLegacyFallback()392         public boolean isLegacyFallback() {
393             return mLegacyFallback;
394         }
395 
396         /**
397          * Sets the name of the package that is sending this request.
398          * <p>
399          * Package-private for SystemTextClassifier's use.
400          * @hide
401          */
402         @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
setCallingPackageName(@ullable String callingPackageName)403         public void setCallingPackageName(@Nullable String callingPackageName) {
404             mCallingPackageName = callingPackageName;
405         }
406 
407         /**
408          * Returns the name of the package that sent this request.
409          * This returns {@code null} if no calling package name is set.
410          */
411         @Nullable
getCallingPackageName()412         public String getCallingPackageName() {
413             return mCallingPackageName;
414         }
415 
416         /**
417          * Sets the id of the user that sent this request.
418          * <p>
419          * Package-private for SystemTextClassifier's use.
420          */
setUserId(@serIdInt int userId)421         void setUserId(@UserIdInt int userId) {
422             mUserId = userId;
423         }
424 
425         /**
426          * Returns the id of the user that sent this request.
427          * @hide
428          */
429         @UserIdInt
getUserId()430         public int getUserId() {
431             return mUserId;
432         }
433 
434         /**
435          * Returns the extended data.
436          *
437          * <p><b>NOTE: </b>Do not modify this bundle.
438          */
439         @NonNull
getExtras()440         public Bundle getExtras() {
441             return mExtras;
442         }
443 
444         /**
445          * A builder for building TextLinks requests.
446          */
447         public static final class Builder {
448 
449             private final CharSequence mText;
450 
451             @Nullable private LocaleList mDefaultLocales;
452             @Nullable private EntityConfig mEntityConfig;
453             private boolean mLegacyFallback = true; // Use legacy fall back by default.
454             @Nullable private Bundle mExtras;
455 
Builder(@onNull CharSequence text)456             public Builder(@NonNull CharSequence text) {
457                 mText = Preconditions.checkNotNull(text);
458             }
459 
460             /**
461              * @param defaultLocales ordered list of locale preferences that may be used to
462              *                       disambiguate the provided text. If no locale preferences exist,
463              *                       set this to null or an empty locale list.
464              * @return this builder
465              */
466             @NonNull
setDefaultLocales(@ullable LocaleList defaultLocales)467             public Builder setDefaultLocales(@Nullable LocaleList defaultLocales) {
468                 mDefaultLocales = defaultLocales;
469                 return this;
470             }
471 
472             /**
473              * Sets the entity configuration to use. This determines what types of entities the
474              * TextClassifier will look for.
475              * Set to {@code null} for the default entity config and teh TextClassifier will
476              * automatically determine what links to generate.
477              *
478              * @return this builder
479              */
480             @NonNull
setEntityConfig(@ullable EntityConfig entityConfig)481             public Builder setEntityConfig(@Nullable EntityConfig entityConfig) {
482                 mEntityConfig = entityConfig;
483                 return this;
484             }
485 
486             /**
487              * Sets whether the TextClassifier can fallback to legacy links if smart linkify is
488              * disabled.
489              *
490              * <p><strong>Note: </strong>This is not parcelled.
491              *
492              * @return this builder
493              * @hide
494              */
495             @NonNull
setLegacyFallback(boolean legacyFallback)496             public Builder setLegacyFallback(boolean legacyFallback) {
497                 mLegacyFallback = legacyFallback;
498                 return this;
499             }
500 
501             /**
502              * Sets the extended data.
503              *
504              * @return this builder
505              */
setExtras(@ullable Bundle extras)506             public Builder setExtras(@Nullable Bundle extras) {
507                 mExtras = extras;
508                 return this;
509             }
510 
511             /**
512              * Builds and returns the request object.
513              */
514             @NonNull
build()515             public Request build() {
516                 return new Request(
517                         mText, mDefaultLocales, mEntityConfig,
518                         mLegacyFallback,
519                         mExtras == null ? Bundle.EMPTY : mExtras);
520             }
521         }
522 
523         @Override
describeContents()524         public int describeContents() {
525             return 0;
526         }
527 
528         @Override
writeToParcel(Parcel dest, int flags)529         public void writeToParcel(Parcel dest, int flags) {
530             dest.writeString(mText.toString());
531             dest.writeParcelable(mDefaultLocales, flags);
532             dest.writeParcelable(mEntityConfig, flags);
533             dest.writeString(mCallingPackageName);
534             dest.writeInt(mUserId);
535             dest.writeBundle(mExtras);
536         }
537 
readFromParcel(Parcel in)538         private static Request readFromParcel(Parcel in) {
539             final String text = in.readString();
540             final LocaleList defaultLocales = in.readParcelable(null);
541             final EntityConfig entityConfig = in.readParcelable(null);
542             final String callingPackageName = in.readString();
543             final int userId = in.readInt();
544             final Bundle extras = in.readBundle();
545 
546             final Request request = new Request(text, defaultLocales, entityConfig,
547                     /* legacyFallback= */ true, extras);
548             request.setCallingPackageName(callingPackageName);
549             request.setUserId(userId);
550             return request;
551         }
552 
553         public static final @android.annotation.NonNull Parcelable.Creator<Request> CREATOR =
554                 new Parcelable.Creator<Request>() {
555                     @Override
556                     public Request createFromParcel(Parcel in) {
557                         return readFromParcel(in);
558                     }
559 
560                     @Override
561                     public Request[] newArray(int size) {
562                         return new Request[size];
563                     }
564                 };
565     }
566 
567     /**
568      * A ClickableSpan for a TextLink.
569      *
570      * <p>Applies only to TextViews.
571      */
572     public static class TextLinkSpan extends ClickableSpan {
573 
574         /**
575          * How the clickspan is triggered.
576          * @hide
577          */
578         @Retention(RetentionPolicy.SOURCE)
579         @IntDef({INVOCATION_METHOD_UNSPECIFIED, INVOCATION_METHOD_TOUCH,
580                 INVOCATION_METHOD_KEYBOARD})
581         public @interface InvocationMethod {}
582 
583         /** @hide */
584         public static final int INVOCATION_METHOD_UNSPECIFIED = -1;
585         /** @hide */
586         public static final int INVOCATION_METHOD_TOUCH = 0;
587         /** @hide */
588         public static final int INVOCATION_METHOD_KEYBOARD = 1;
589 
590         private final TextLink mTextLink;
591 
TextLinkSpan(@onNull TextLink textLink)592         public TextLinkSpan(@NonNull TextLink textLink) {
593             mTextLink = textLink;
594         }
595 
596         @Override
onClick(View widget)597         public void onClick(View widget) {
598             onClick(widget, INVOCATION_METHOD_UNSPECIFIED);
599         }
600 
601         /** @hide */
onClick(View widget, @InvocationMethod int invocationMethod)602         public final void onClick(View widget, @InvocationMethod int invocationMethod) {
603             if (widget instanceof TextView) {
604                 final TextView textView = (TextView) widget;
605                 final Context context = textView.getContext();
606                 if (TextClassificationManager.getSettings(context).isSmartLinkifyEnabled()) {
607                     switch (invocationMethod) {
608                         case INVOCATION_METHOD_TOUCH:
609                             textView.requestActionMode(this);
610                             break;
611                         case INVOCATION_METHOD_KEYBOARD:// fall though
612                         case INVOCATION_METHOD_UNSPECIFIED:  // fall through
613                         default:
614                             textView.handleClick(this);
615                             break;
616                     }
617                 } else {
618                     if (mTextLink.mUrlSpan != null) {
619                         mTextLink.mUrlSpan.onClick(textView);
620                     } else {
621                         textView.handleClick(this);
622                     }
623                 }
624             }
625         }
626 
getTextLink()627         public final TextLink getTextLink() {
628             return mTextLink;
629         }
630 
631         /** @hide */
632         @VisibleForTesting(visibility = Visibility.PRIVATE)
633         @Nullable
getUrl()634         public final String getUrl() {
635             if (mTextLink.mUrlSpan != null) {
636                 return mTextLink.mUrlSpan.getURL();
637             }
638             return null;
639         }
640     }
641 
642     /**
643      * A builder to construct a TextLinks instance.
644      */
645     public static final class Builder {
646         private final String mFullText;
647         private final ArrayList<TextLink> mLinks;
648         private Bundle mExtras;
649 
650         /**
651          * Create a new TextLinks.Builder.
652          *
653          * @param fullText The full text to annotate with links
654          */
Builder(@onNull String fullText)655         public Builder(@NonNull String fullText) {
656             mFullText = Preconditions.checkNotNull(fullText);
657             mLinks = new ArrayList<>();
658         }
659 
660         /**
661          * Adds a TextLink.
662          *
663          * @param start The start index of the identified subsequence
664          * @param end The end index of the identified subsequence
665          * @param entityScores A mapping of entity type to confidence score
666          *
667          * @throws IllegalArgumentException if entityScores is null or empty.
668          */
669         @NonNull
addLink(int start, int end, @NonNull Map<String, Float> entityScores)670         public Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores) {
671             return addLink(start, end, entityScores, Bundle.EMPTY, null);
672         }
673 
674         /**
675          * Adds a TextLink.
676          *
677          * @see #addLink(int, int, Map)
678          * @param extras An optional bundle containing custom data related to this TextLink
679          */
680         @NonNull
addLink(int start, int end, @NonNull Map<String, Float> entityScores, @NonNull Bundle extras)681         public Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores,
682                 @NonNull Bundle extras) {
683             return addLink(start, end, entityScores, extras, null);
684         }
685 
686         /**
687          * @see #addLink(int, int, Map)
688          * @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled.
689          */
690         @NonNull
addLink(int start, int end, @NonNull Map<String, Float> entityScores, @Nullable URLSpan urlSpan)691         Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores,
692                 @Nullable URLSpan urlSpan) {
693             return addLink(start, end, entityScores, Bundle.EMPTY, urlSpan);
694         }
695 
addLink(int start, int end, @NonNull Map<String, Float> entityScores, @NonNull Bundle extras, @Nullable URLSpan urlSpan)696         private Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores,
697                 @NonNull Bundle extras, @Nullable URLSpan urlSpan) {
698             mLinks.add(new TextLink(
699                     start, end, new EntityConfidence(entityScores), extras, urlSpan));
700             return this;
701         }
702 
703         /**
704          * Removes all {@link TextLink}s.
705          */
706         @NonNull
clearTextLinks()707         public Builder clearTextLinks() {
708             mLinks.clear();
709             return this;
710         }
711 
712         /**
713          * Sets the extended data.
714          *
715          * @return this builder
716          */
717         @NonNull
setExtras(@ullable Bundle extras)718         public Builder setExtras(@Nullable Bundle extras) {
719             mExtras = extras;
720             return this;
721         }
722 
723         /**
724          * Constructs a TextLinks instance.
725          *
726          * @return the constructed TextLinks
727          */
728         @NonNull
build()729         public TextLinks build() {
730             return new TextLinks(mFullText, mLinks,
731                     mExtras == null ? Bundle.EMPTY : mExtras);
732         }
733     }
734 }
735