• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 com.android.intentresolver.chooser;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.app.Activity;
22 import android.app.prediction.AppTarget;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.pm.ResolveInfo;
27 import android.content.pm.ShortcutInfo;
28 import android.os.Bundle;
29 import android.os.UserHandle;
30 import android.util.HashedStringCache;
31 
32 import androidx.annotation.VisibleForTesting;
33 
34 import com.google.common.collect.ImmutableList;
35 
36 import java.util.ArrayList;
37 import java.util.List;
38 
39 /**
40  * An implementation of {@link TargetInfo} with immutable data. Any modifications must be made by
41  * creating a new instance (e.g., via {@link ImmutableTargetInfo#toBuilder()}).
42  */
43 public final class ImmutableTargetInfo implements TargetInfo {
44     private static final String TAG = "TargetInfo";
45 
46     /** Delegate interface to implement {@link TargetInfo#getHashedTargetIdForMetrics()}. */
47     public interface TargetHashProvider {
48         /** Request a hash for the specified {@code target}. */
getHashedTargetIdForMetrics( TargetInfo target, Context context)49         HashedStringCache.HashResult getHashedTargetIdForMetrics(
50                 TargetInfo target, Context context);
51     }
52 
53     /** Delegate interface to request that the target be launched by a particular API. */
54     public interface TargetActivityStarter {
55         /**
56          * Request that the delegate use the {@link Activity#startAsCaller()} API to launch the
57          * specified {@code target}.
58          *
59          * @return true if the target was launched successfully.
60          */
startAsCaller(TargetInfo target, Activity activity, Bundle options, int userId)61         boolean startAsCaller(TargetInfo target, Activity activity, Bundle options, int userId);
62 
63         /**
64          * Request that the delegate use the {@link Activity#startAsUser()} API to launch the
65          * specified {@code target}.
66          *
67          * @return true if the target was launched successfully.
68          */
startAsUser(TargetInfo target, Activity activity, Bundle options, UserHandle user)69         boolean startAsUser(TargetInfo target, Activity activity, Bundle options, UserHandle user);
70     }
71 
72     enum LegacyTargetType {
73         NOT_LEGACY_TARGET,
74         EMPTY_TARGET_INFO,
75         PLACEHOLDER_TARGET_INFO,
76         SELECTABLE_TARGET_INFO,
77         DISPLAY_RESOLVE_INFO,
78         MULTI_DISPLAY_RESOLVE_INFO
79     };
80 
81     /** Builder API to construct {@code ImmutableTargetInfo} instances. */
82     public static class Builder {
83         @Nullable
84         private ComponentName mResolvedComponentName;
85 
86         @Nullable
87         private Intent mResolvedIntent;
88 
89         @Nullable
90         private Intent mBaseIntentToSend;
91 
92         @Nullable
93         private Intent mTargetIntent;
94 
95         @Nullable
96         private ComponentName mChooserTargetComponentName;
97 
98         @Nullable
99         private ShortcutInfo mDirectShareShortcutInfo;
100 
101         @Nullable
102         private AppTarget mDirectShareAppTarget;
103 
104         @Nullable
105         private DisplayResolveInfo mDisplayResolveInfo;
106 
107         @Nullable
108         private TargetHashProvider mHashProvider;
109 
110         @Nullable
111         private Intent mReferrerFillInIntent;
112 
113         @Nullable
114         private TargetActivityStarter mActivityStarter;
115 
116         @Nullable
117         private ResolveInfo mResolveInfo;
118 
119         @Nullable
120         private CharSequence mDisplayLabel;
121 
122         @Nullable
123         private CharSequence mExtendedInfo;
124 
125         @Nullable
126         private IconHolder mDisplayIconHolder;
127 
128         private boolean mIsSuspended;
129         private boolean mIsPinned;
130         private float mModifiedScore = -0.1f;
131         private LegacyTargetType mLegacyType = LegacyTargetType.NOT_LEGACY_TARGET;
132 
133         private ImmutableList<Intent> mAlternateSourceIntents = ImmutableList.of();
134         private ImmutableList<DisplayResolveInfo> mAllDisplayTargets = ImmutableList.of();
135 
136         /**
137          * Configure an {@link Intent} to be built in to the output target as the resolution for the
138          * requested target data.
139          */
setResolvedIntent(Intent resolvedIntent)140         public Builder setResolvedIntent(Intent resolvedIntent) {
141             mResolvedIntent = resolvedIntent;
142             return this;
143         }
144 
145         /**
146          * Configure an {@link Intent} to be built in to the output target as the "base intent to
147          * send," which may be a refinement of any of our source targets. This is private because
148          * it's only used internally by {@link #tryToCloneWithAppliedRefinement()}; if it's ever
149          * expanded, the builder should probably be responsible for enforcing the refinement check.
150          */
setBaseIntentToSend(Intent baseIntent)151         private Builder setBaseIntentToSend(Intent baseIntent) {
152             mBaseIntentToSend = baseIntent;
153             return this;
154         }
155 
156         /**
157          * Configure an {@link Intent} to be built in to the output as the "target intent."
158          */
setTargetIntent(Intent targetIntent)159         public Builder setTargetIntent(Intent targetIntent) {
160             mTargetIntent = targetIntent;
161             return this;
162         }
163 
164         /**
165          * Configure a fill-in intent provided by the referrer to be used in populating the launch
166          * intent if the output target is ever selected.
167          *
168          * @see android.content.Intent#fillIn(Intent, int)
169          */
setReferrerFillInIntent(@ullable Intent referrerFillInIntent)170         public Builder setReferrerFillInIntent(@Nullable Intent referrerFillInIntent) {
171             mReferrerFillInIntent = referrerFillInIntent;
172             return this;
173         }
174 
175         /**
176          * Configure a {@link ComponentName} to be built in to the output target, as the real
177          * component we were able to resolve on this device given the available target data.
178          */
setResolvedComponentName(@ullable ComponentName resolvedComponentName)179         public Builder setResolvedComponentName(@Nullable ComponentName resolvedComponentName) {
180             mResolvedComponentName = resolvedComponentName;
181             return this;
182         }
183 
184         /**
185          * Configure a {@link ComponentName} to be built in to the output target, as the component
186          * supposedly associated with a {@link ChooserTarget} from which the builder data is being
187          * derived.
188          */
setChooserTargetComponentName(@ullable ComponentName componentName)189         public Builder setChooserTargetComponentName(@Nullable ComponentName componentName) {
190             mChooserTargetComponentName = componentName;
191             return this;
192         }
193 
194         /** Configure the {@link TargetActivityStarter} to be built in to the output target. */
setActivityStarter(TargetActivityStarter activityStarter)195         public Builder setActivityStarter(TargetActivityStarter activityStarter) {
196             mActivityStarter = activityStarter;
197             return this;
198         }
199 
200         /** Configure the {@link ResolveInfo} to be built in to the output target. */
setResolveInfo(ResolveInfo resolveInfo)201         public Builder setResolveInfo(ResolveInfo resolveInfo) {
202             mResolveInfo = resolveInfo;
203             return this;
204         }
205 
206         /** Configure the display label to be built in to the output target. */
setDisplayLabel(CharSequence displayLabel)207         public Builder setDisplayLabel(CharSequence displayLabel) {
208             mDisplayLabel = displayLabel;
209             return this;
210         }
211 
212         /** Configure the extended info to be built in to the output target. */
setExtendedInfo(CharSequence extendedInfo)213         public Builder setExtendedInfo(CharSequence extendedInfo) {
214             mExtendedInfo = extendedInfo;
215             return this;
216         }
217 
218         /** Configure the {@link IconHolder} to be built in to the output target. */
setDisplayIconHolder(IconHolder displayIconHolder)219         public Builder setDisplayIconHolder(IconHolder displayIconHolder) {
220             mDisplayIconHolder = displayIconHolder;
221             return this;
222         }
223 
224         /** Configure the list of alternate source intents we could resolve for this target. */
setAlternateSourceIntents(List<Intent> sourceIntents)225         public Builder setAlternateSourceIntents(List<Intent> sourceIntents) {
226             mAlternateSourceIntents = immutableCopyOrEmpty(sourceIntents);
227             return this;
228         }
229 
230        /**
231         * Configure the full list of source intents we could resolve for this target. This is
232         * effectively the same as calling {@link #setResolvedIntent()} with the first element of
233         * the list, and {@link #setAlternateSourceIntents()} with the remainder (or clearing those
234         * fields on the builder if there are no corresponding elements in the list).
235         */
setAllSourceIntents(List<Intent> sourceIntents)236         public Builder setAllSourceIntents(List<Intent> sourceIntents) {
237             if ((sourceIntents == null) || sourceIntents.isEmpty()) {
238                 setResolvedIntent(null);
239                 setAlternateSourceIntents(null);
240                 return this;
241             }
242 
243             setResolvedIntent(sourceIntents.get(0));
244             setAlternateSourceIntents(sourceIntents.subList(1, sourceIntents.size()));
245             return this;
246         }
247 
248         /** Configure the list of display targets to be built in to the output target. */
setAllDisplayTargets(List<DisplayResolveInfo> targets)249         public Builder setAllDisplayTargets(List<DisplayResolveInfo> targets) {
250             mAllDisplayTargets = immutableCopyOrEmpty(targets);
251             return this;
252         }
253 
254         /** Configure the is-suspended status to be built in to the output target. */
setIsSuspended(boolean isSuspended)255         public Builder setIsSuspended(boolean isSuspended) {
256             mIsSuspended = isSuspended;
257             return this;
258         }
259 
260         /** Configure the is-pinned status to be built in to the output target. */
setIsPinned(boolean isPinned)261         public Builder setIsPinned(boolean isPinned) {
262             mIsPinned = isPinned;
263             return this;
264         }
265 
266         /** Configure the modified score to be built in to the output target. */
setModifiedScore(float modifiedScore)267         public Builder setModifiedScore(float modifiedScore) {
268             mModifiedScore = modifiedScore;
269             return this;
270         }
271 
272         /** Configure the {@link ShortcutInfo} to be built in to the output target. */
setDirectShareShortcutInfo(@ullable ShortcutInfo shortcutInfo)273         public Builder setDirectShareShortcutInfo(@Nullable ShortcutInfo shortcutInfo) {
274             mDirectShareShortcutInfo = shortcutInfo;
275             return this;
276         }
277 
278         /** Configure the {@link AppTarget} to be built in to the output target. */
setDirectShareAppTarget(@ullable AppTarget appTarget)279         public Builder setDirectShareAppTarget(@Nullable AppTarget appTarget) {
280             mDirectShareAppTarget = appTarget;
281             return this;
282         }
283 
284         /** Configure the {@link DisplayResolveInfo} to be built in to the output target. */
setDisplayResolveInfo(@ullable DisplayResolveInfo displayResolveInfo)285         public Builder setDisplayResolveInfo(@Nullable DisplayResolveInfo displayResolveInfo) {
286             mDisplayResolveInfo = displayResolveInfo;
287             return this;
288         }
289 
290         /** Configure the {@link TargetHashProvider} to be built in to the output target. */
setHashProvider(@ullable TargetHashProvider hashProvider)291         public Builder setHashProvider(@Nullable TargetHashProvider hashProvider) {
292             mHashProvider = hashProvider;
293             return this;
294         }
295 
setLegacyType(@onNull LegacyTargetType legacyType)296         Builder setLegacyType(@NonNull LegacyTargetType legacyType) {
297             mLegacyType = legacyType;
298             return this;
299         }
300 
301         /** Construct an {@code ImmutableTargetInfo} with the current builder data. */
build()302         public ImmutableTargetInfo build() {
303             List<Intent> sourceIntents = new ArrayList<>();
304             if (mResolvedIntent != null) {
305                 sourceIntents.add(mResolvedIntent);
306             }
307             if (mAlternateSourceIntents != null) {
308                 sourceIntents.addAll(mAlternateSourceIntents);
309             }
310 
311             Intent baseIntentToSend = mBaseIntentToSend;
312             if ((baseIntentToSend == null) && !sourceIntents.isEmpty()) {
313                 baseIntentToSend = sourceIntents.get(0);
314             }
315             if (baseIntentToSend != null) {
316                 baseIntentToSend = new Intent(baseIntentToSend);
317                 if (mReferrerFillInIntent != null) {
318                     baseIntentToSend.fillIn(mReferrerFillInIntent, 0);
319                 }
320             }
321 
322             return new ImmutableTargetInfo(
323                     baseIntentToSend,
324                     ImmutableList.copyOf(sourceIntents),
325                     mTargetIntent,
326                     mReferrerFillInIntent,
327                     mResolvedComponentName,
328                     mChooserTargetComponentName,
329                     mActivityStarter,
330                     mResolveInfo,
331                     mDisplayLabel,
332                     mExtendedInfo,
333                     mDisplayIconHolder,
334                     mAllDisplayTargets,
335                     mIsSuspended,
336                     mIsPinned,
337                     mModifiedScore,
338                     mDirectShareShortcutInfo,
339                     mDirectShareAppTarget,
340                     mDisplayResolveInfo,
341                     mHashProvider,
342                     mLegacyType);
343         }
344     }
345 
346     @Nullable
347     private final Intent mReferrerFillInIntent;
348 
349     @Nullable
350     private final ComponentName mResolvedComponentName;
351 
352     @Nullable
353     private final ComponentName mChooserTargetComponentName;
354 
355     @Nullable
356     private final ShortcutInfo mDirectShareShortcutInfo;
357 
358     @Nullable
359     private final AppTarget mDirectShareAppTarget;
360 
361     @Nullable
362     private final DisplayResolveInfo mDisplayResolveInfo;
363 
364     @Nullable
365     private final TargetHashProvider mHashProvider;
366 
367     private final Intent mBaseIntentToSend;
368     private final ImmutableList<Intent> mSourceIntents;
369     private final Intent mTargetIntent;
370     private final TargetActivityStarter mActivityStarter;
371     private final ResolveInfo mResolveInfo;
372     private final CharSequence mDisplayLabel;
373     private final CharSequence mExtendedInfo;
374     private final IconHolder mDisplayIconHolder;
375     private final ImmutableList<DisplayResolveInfo> mAllDisplayTargets;
376     private final boolean mIsSuspended;
377     private final boolean mIsPinned;
378     private final float mModifiedScore;
379     private final LegacyTargetType mLegacyType;
380 
381     /** Construct a {@link Builder}. */
newBuilder()382     public static Builder newBuilder() {
383         return new Builder();
384     }
385 
386     /** Construct a {@link Builder} pre-initialized to match this target. */
toBuilder()387     public Builder toBuilder() {
388         return newBuilder()
389                 .setBaseIntentToSend(getBaseIntentToSend())
390                 .setResolvedIntent(getResolvedIntent())
391                 .setTargetIntent(getTargetIntent())
392                 .setReferrerFillInIntent(getReferrerFillInIntent())
393                 .setResolvedComponentName(getResolvedComponentName())
394                 .setChooserTargetComponentName(getChooserTargetComponentName())
395                 .setActivityStarter(mActivityStarter)
396                 .setResolveInfo(getResolveInfo())
397                 .setDisplayLabel(getDisplayLabel())
398                 .setExtendedInfo(getExtendedInfo())
399                 .setDisplayIconHolder(getDisplayIconHolder())
400                 .setAllSourceIntents(getAllSourceIntents())
401                 .setAllDisplayTargets(getAllDisplayTargets())
402                 .setIsSuspended(isSuspended())
403                 .setIsPinned(isPinned())
404                 .setModifiedScore(getModifiedScore())
405                 .setDirectShareShortcutInfo(getDirectShareShortcutInfo())
406                 .setDirectShareAppTarget(getDirectShareAppTarget())
407                 .setDisplayResolveInfo(getDisplayResolveInfo())
408                 .setHashProvider(getHashProvider())
409                 .setLegacyType(mLegacyType);
410     }
411 
412     @VisibleForTesting
getBaseIntentToSend()413     Intent getBaseIntentToSend() {
414         return mBaseIntentToSend;
415     }
416 
417     @Override
418     @Nullable
tryToCloneWithAppliedRefinement(Intent proposedRefinement)419     public ImmutableTargetInfo tryToCloneWithAppliedRefinement(Intent proposedRefinement) {
420         Intent matchingBase =
421                 getAllSourceIntents()
422                         .stream()
423                         .filter(i -> i.filterEquals(proposedRefinement))
424                         .findFirst()
425                         .orElse(null);
426         if (matchingBase == null) {
427             return null;
428         }
429 
430         Intent merged = TargetInfo.mergeRefinementIntoMatchingBaseIntent(
431                 matchingBase, proposedRefinement);
432         return toBuilder().setBaseIntentToSend(merged).build();
433     }
434 
435     @Override
getResolvedIntent()436     public Intent getResolvedIntent() {
437         return (mSourceIntents.isEmpty() ? null : mSourceIntents.get(0));
438     }
439 
440     @Override
getTargetIntent()441     public Intent getTargetIntent() {
442         return mTargetIntent;
443     }
444 
445     @Nullable
getReferrerFillInIntent()446     public Intent getReferrerFillInIntent() {
447         return mReferrerFillInIntent;
448     }
449 
450     @Override
451     @Nullable
getResolvedComponentName()452     public ComponentName getResolvedComponentName() {
453         return mResolvedComponentName;
454     }
455 
456     @Override
457     @Nullable
getChooserTargetComponentName()458     public ComponentName getChooserTargetComponentName() {
459         return mChooserTargetComponentName;
460     }
461 
462     @Override
startAsCaller(Activity activity, Bundle options, int userId)463     public boolean startAsCaller(Activity activity, Bundle options, int userId) {
464         // TODO: make sure that the component name is set in all cases
465         return mActivityStarter.startAsCaller(this, activity, options, userId);
466     }
467 
468     @Override
startAsUser(Activity activity, Bundle options, UserHandle user)469     public boolean startAsUser(Activity activity, Bundle options, UserHandle user) {
470         // TODO: make sure that the component name is set in all cases
471         return mActivityStarter.startAsUser(this, activity, options, user);
472     }
473 
474     @Override
getResolveInfo()475     public ResolveInfo getResolveInfo() {
476         return mResolveInfo;
477     }
478 
479     @Override
getDisplayLabel()480     public CharSequence getDisplayLabel() {
481         return mDisplayLabel;
482     }
483 
484     @Override
getExtendedInfo()485     public CharSequence getExtendedInfo() {
486         return mExtendedInfo;
487     }
488 
489     @Override
getDisplayIconHolder()490     public IconHolder getDisplayIconHolder() {
491         return mDisplayIconHolder;
492     }
493 
494     @Override
getAllSourceIntents()495     public List<Intent> getAllSourceIntents() {
496         return mSourceIntents;
497     }
498 
499     @Override
getAllDisplayTargets()500     public ArrayList<DisplayResolveInfo> getAllDisplayTargets() {
501         ArrayList<DisplayResolveInfo> targets = new ArrayList<>();
502         targets.addAll(mAllDisplayTargets);
503         return targets;
504     }
505 
506     @Override
isSuspended()507     public boolean isSuspended() {
508         return mIsSuspended;
509     }
510 
511     @Override
isPinned()512     public boolean isPinned() {
513         return mIsPinned;
514     }
515 
516     @Override
getModifiedScore()517     public float getModifiedScore() {
518         return mModifiedScore;
519     }
520 
521     @Override
522     @Nullable
getDirectShareShortcutInfo()523     public ShortcutInfo getDirectShareShortcutInfo() {
524         return mDirectShareShortcutInfo;
525     }
526 
527     @Override
528     @Nullable
getDirectShareAppTarget()529     public AppTarget getDirectShareAppTarget() {
530         return mDirectShareAppTarget;
531     }
532 
533     @Override
534     @Nullable
getDisplayResolveInfo()535     public DisplayResolveInfo getDisplayResolveInfo() {
536         return mDisplayResolveInfo;
537     }
538 
539     @Override
getHashedTargetIdForMetrics(Context context)540     public HashedStringCache.HashResult getHashedTargetIdForMetrics(Context context) {
541         return (mHashProvider == null)
542                 ? null : mHashProvider.getHashedTargetIdForMetrics(this, context);
543     }
544 
545     @VisibleForTesting
546     @Nullable
getHashProvider()547     TargetHashProvider getHashProvider() {
548         return mHashProvider;
549     }
550 
551     @Override
isEmptyTargetInfo()552     public boolean isEmptyTargetInfo() {
553         return mLegacyType == LegacyTargetType.EMPTY_TARGET_INFO;
554     }
555 
556     @Override
isPlaceHolderTargetInfo()557     public boolean isPlaceHolderTargetInfo() {
558         return mLegacyType == LegacyTargetType.PLACEHOLDER_TARGET_INFO;
559     }
560 
561     @Override
isNotSelectableTargetInfo()562     public boolean isNotSelectableTargetInfo() {
563         return isEmptyTargetInfo() || isPlaceHolderTargetInfo();
564     }
565 
566     @Override
isSelectableTargetInfo()567     public boolean isSelectableTargetInfo() {
568         return mLegacyType == LegacyTargetType.SELECTABLE_TARGET_INFO;
569     }
570 
571     @Override
isChooserTargetInfo()572     public boolean isChooserTargetInfo() {
573         return isNotSelectableTargetInfo() || isSelectableTargetInfo();
574     }
575 
576     @Override
isMultiDisplayResolveInfo()577     public boolean isMultiDisplayResolveInfo() {
578         return mLegacyType == LegacyTargetType.MULTI_DISPLAY_RESOLVE_INFO;
579     }
580 
581     @Override
isDisplayResolveInfo()582     public boolean isDisplayResolveInfo() {
583         return (mLegacyType == LegacyTargetType.DISPLAY_RESOLVE_INFO)
584                 || isMultiDisplayResolveInfo();
585     }
586 
ImmutableTargetInfo( Intent baseIntentToSend, ImmutableList<Intent> sourceIntents, Intent targetIntent, @Nullable Intent referrerFillInIntent, @Nullable ComponentName resolvedComponentName, @Nullable ComponentName chooserTargetComponentName, TargetActivityStarter activityStarter, ResolveInfo resolveInfo, CharSequence displayLabel, CharSequence extendedInfo, IconHolder iconHolder, ImmutableList<DisplayResolveInfo> allDisplayTargets, boolean isSuspended, boolean isPinned, float modifiedScore, @Nullable ShortcutInfo directShareShortcutInfo, @Nullable AppTarget directShareAppTarget, @Nullable DisplayResolveInfo displayResolveInfo, @Nullable TargetHashProvider hashProvider, LegacyTargetType legacyType)587     private ImmutableTargetInfo(
588             Intent baseIntentToSend,
589             ImmutableList<Intent> sourceIntents,
590             Intent targetIntent,
591             @Nullable Intent referrerFillInIntent,
592             @Nullable ComponentName resolvedComponentName,
593             @Nullable ComponentName chooserTargetComponentName,
594             TargetActivityStarter activityStarter,
595             ResolveInfo resolveInfo,
596             CharSequence displayLabel,
597             CharSequence extendedInfo,
598             IconHolder iconHolder,
599             ImmutableList<DisplayResolveInfo> allDisplayTargets,
600             boolean isSuspended,
601             boolean isPinned,
602             float modifiedScore,
603             @Nullable ShortcutInfo directShareShortcutInfo,
604             @Nullable AppTarget directShareAppTarget,
605             @Nullable DisplayResolveInfo displayResolveInfo,
606             @Nullable TargetHashProvider hashProvider,
607             LegacyTargetType legacyType) {
608         mBaseIntentToSend = baseIntentToSend;
609         mSourceIntents = sourceIntents;
610         mTargetIntent = targetIntent;
611         mReferrerFillInIntent = referrerFillInIntent;
612         mResolvedComponentName = resolvedComponentName;
613         mChooserTargetComponentName = chooserTargetComponentName;
614         mActivityStarter = activityStarter;
615         mResolveInfo = resolveInfo;
616         mDisplayLabel = displayLabel;
617         mExtendedInfo = extendedInfo;
618         mDisplayIconHolder = iconHolder;
619         mAllDisplayTargets = allDisplayTargets;
620         mIsSuspended = isSuspended;
621         mIsPinned = isPinned;
622         mModifiedScore = modifiedScore;
623         mDirectShareShortcutInfo = directShareShortcutInfo;
624         mDirectShareAppTarget = directShareAppTarget;
625         mDisplayResolveInfo = displayResolveInfo;
626         mHashProvider = hashProvider;
627         mLegacyType = legacyType;
628     }
629 
immutableCopyOrEmpty(@ullable List<E> source)630     private static <E> ImmutableList<E> immutableCopyOrEmpty(@Nullable List<E> source) {
631         return (source == null) ? ImmutableList.of() : ImmutableList.copyOf(source);
632     }
633 }
634