• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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;
18 
19 import static com.android.intentresolver.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE;
20 import static com.android.intentresolver.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER;
21 
22 import android.annotation.Nullable;
23 import android.app.ActivityManager;
24 import android.app.prediction.AppTarget;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.pm.ActivityInfo;
29 import android.content.pm.LabeledIntent;
30 import android.content.pm.PackageManager;
31 import android.content.pm.ResolveInfo;
32 import android.content.pm.ShortcutInfo;
33 import android.graphics.drawable.Drawable;
34 import android.os.AsyncTask;
35 import android.os.Trace;
36 import android.os.UserHandle;
37 import android.os.UserManager;
38 import android.provider.DeviceConfig;
39 import android.service.chooser.ChooserTarget;
40 import android.text.Layout;
41 import android.util.Log;
42 import android.view.View;
43 import android.view.ViewGroup;
44 import android.widget.TextView;
45 
46 import com.android.intentresolver.chooser.DisplayResolveInfo;
47 import com.android.intentresolver.chooser.MultiDisplayResolveInfo;
48 import com.android.intentresolver.chooser.NotSelectableTargetInfo;
49 import com.android.intentresolver.chooser.SelectableTargetInfo;
50 import com.android.intentresolver.chooser.TargetInfo;
51 import com.android.intentresolver.icons.TargetDataLoader;
52 import com.android.intentresolver.logging.EventLog;
53 import com.android.internal.annotations.VisibleForTesting;
54 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
55 
56 import java.util.ArrayList;
57 import java.util.HashSet;
58 import java.util.List;
59 import java.util.Map;
60 import java.util.Set;
61 import java.util.stream.Collectors;
62 
63 public class ChooserListAdapter extends ResolverListAdapter {
64     private static final String TAG = "ChooserListAdapter";
65     private static final boolean DEBUG = false;
66 
67     public static final int NO_POSITION = -1;
68     public static final int TARGET_BAD = -1;
69     public static final int TARGET_CALLER = 0;
70     public static final int TARGET_SERVICE = 1;
71     public static final int TARGET_STANDARD = 2;
72     public static final int TARGET_STANDARD_AZ = 3;
73 
74     private static final int MAX_SUGGESTED_APP_TARGETS = 4;
75 
76     /** {@link #getBaseScore} */
77     public static final float CALLER_TARGET_SCORE_BOOST = 900.f;
78     /** {@link #getBaseScore} */
79     public static final float SHORTCUT_TARGET_SCORE_BOOST = 90.f;
80 
81     private final ChooserRequestParameters mChooserRequest;
82     private final int mMaxRankedTargets;
83 
84     private final EventLog mEventLog;
85 
86     private final Set<TargetInfo> mRequestedIcons = new HashSet<>();
87 
88     // Reserve spots for incoming direct share targets by adding placeholders
89     private final TargetInfo mPlaceHolderTargetInfo;
90     private final TargetDataLoader mTargetDataLoader;
91     private final List<TargetInfo> mServiceTargets = new ArrayList<>();
92     private final List<DisplayResolveInfo> mCallerTargets = new ArrayList<>();
93 
94     private final ShortcutSelectionLogic mShortcutSelectionLogic;
95 
96     // Sorted list of DisplayResolveInfos for the alphabetical app section.
97     private List<DisplayResolveInfo> mSortedList = new ArrayList<>();
98 
99     private final ItemRevealAnimationTracker mAnimationTracker = new ItemRevealAnimationTracker();
100 
101     // For pinned direct share labels, if the text spans multiple lines, the TextView will consume
102     // the full width, even if the characters actually take up less than that. Measure the actual
103     // line widths and constrain the View's width based upon that so that the pin doesn't end up
104     // very far from the text.
105     private final View.OnLayoutChangeListener mPinTextSpacingListener =
106             new View.OnLayoutChangeListener() {
107                 @Override
108                 public void onLayoutChange(View v, int left, int top, int right, int bottom,
109                         int oldLeft, int oldTop, int oldRight, int oldBottom) {
110                     TextView textView = (TextView) v;
111                     Layout layout = textView.getLayout();
112                     if (layout != null) {
113                         int textWidth = 0;
114                         for (int line = 0; line < layout.getLineCount(); line++) {
115                             textWidth = Math.max((int) Math.ceil(layout.getLineMax(line)),
116                                     textWidth);
117                         }
118                         int desiredWidth = textWidth + textView.getPaddingLeft()
119                                 + textView.getPaddingRight();
120                         if (textView.getWidth() > desiredWidth) {
121                             ViewGroup.LayoutParams params = textView.getLayoutParams();
122                             params.width = desiredWidth;
123                             textView.setLayoutParams(params);
124                             // Need to wait until layout pass is over before requesting layout.
125                             textView.post(() -> textView.requestLayout());
126                         }
127                         textView.removeOnLayoutChangeListener(this);
128                     }
129                 }
130             };
131 
ChooserListAdapter( Context context, List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList, boolean filterLastUsed, ResolverListController resolverListController, UserHandle userHandle, Intent targetIntent, ResolverListCommunicator resolverListCommunicator, PackageManager packageManager, EventLog eventLog, ChooserRequestParameters chooserRequest, int maxRankedTargets, UserHandle initialIntentsUserSpace, TargetDataLoader targetDataLoader)132     public ChooserListAdapter(
133             Context context,
134             List<Intent> payloadIntents,
135             Intent[] initialIntents,
136             List<ResolveInfo> rList,
137             boolean filterLastUsed,
138             ResolverListController resolverListController,
139             UserHandle userHandle,
140             Intent targetIntent,
141             ResolverListCommunicator resolverListCommunicator,
142             PackageManager packageManager,
143             EventLog eventLog,
144             ChooserRequestParameters chooserRequest,
145             int maxRankedTargets,
146             UserHandle initialIntentsUserSpace,
147             TargetDataLoader targetDataLoader) {
148         // Don't send the initial intents through the shared ResolverActivity path,
149         // we want to separate them into a different section.
150         super(
151                 context,
152                 payloadIntents,
153                 null,
154                 rList,
155                 filterLastUsed,
156                 resolverListController,
157                 userHandle,
158                 targetIntent,
159                 resolverListCommunicator,
160                 initialIntentsUserSpace,
161                 targetDataLoader);
162 
163         mChooserRequest = chooserRequest;
164         mMaxRankedTargets = maxRankedTargets;
165 
166         mPlaceHolderTargetInfo = NotSelectableTargetInfo.newPlaceHolderTargetInfo(context);
167         mTargetDataLoader = targetDataLoader;
168         createPlaceHolders();
169         mEventLog = eventLog;
170         mShortcutSelectionLogic = new ShortcutSelectionLogic(
171                 context.getResources().getInteger(R.integer.config_maxShortcutTargetsPerApp),
172                 DeviceConfig.getBoolean(
173                         DeviceConfig.NAMESPACE_SYSTEMUI,
174                         SystemUiDeviceConfigFlags.APPLY_SHARING_APP_LIMITS_IN_SYSUI,
175                         true)
176         );
177 
178         if (initialIntents != null) {
179             for (int i = 0; i < initialIntents.length; i++) {
180                 final Intent ii = initialIntents[i];
181                 if (ii == null) {
182                     continue;
183                 }
184 
185                 // We reimplement Intent#resolveActivityInfo here because if we have an
186                 // implicit intent, we want the ResolveInfo returned by PackageManager
187                 // instead of one we reconstruct ourselves. The ResolveInfo returned might
188                 // have extra metadata and resolvePackageName set and we want to respect that.
189                 ResolveInfo ri = null;
190                 ActivityInfo ai = null;
191                 final ComponentName cn = ii.getComponent();
192                 if (cn != null) {
193                     try {
194                         ai = packageManager.getActivityInfo(
195                                 ii.getComponent(),
196                                 PackageManager.ComponentInfoFlags.of(PackageManager.GET_META_DATA));
197                         ri = new ResolveInfo();
198                         ri.activityInfo = ai;
199                     } catch (PackageManager.NameNotFoundException ignored) {
200                         // ai will == null below
201                     }
202                 }
203                 if (ai == null) {
204                     // Because of AIDL bug, resolveActivity can't accept subclasses of Intent.
205                     final Intent rii = (ii.getClass() == Intent.class) ? ii : new Intent(ii);
206                     ri = packageManager.resolveActivity(
207                             rii,
208                             PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY));
209                     ai = ri != null ? ri.activityInfo : null;
210                 }
211                 if (ai == null) {
212                     Log.w(TAG, "No activity found for " + ii);
213                     continue;
214                 }
215                 UserManager userManager =
216                         (UserManager) context.getSystemService(Context.USER_SERVICE);
217                 if (ii instanceof LabeledIntent) {
218                     LabeledIntent li = (LabeledIntent) ii;
219                     ri.resolvePackageName = li.getSourcePackage();
220                     ri.labelRes = li.getLabelResource();
221                     ri.nonLocalizedLabel = li.getNonLocalizedLabel();
222                     ri.icon = li.getIconResource();
223                     ri.iconResourceId = ri.icon;
224                 }
225                 if (userManager.isManagedProfile()) {
226                     ri.noResourceId = true;
227                     ri.icon = 0;
228                 }
229                 ri.userHandle = initialIntentsUserSpace;
230                 // TODO: remove DisplayResolveInfo dependency on presentation getter
231                 DisplayResolveInfo displayResolveInfo = DisplayResolveInfo.newDisplayResolveInfo(
232                         ii, ri, ii, mTargetDataLoader.createPresentationGetter(ri));
233                 mCallerTargets.add(displayResolveInfo);
234                 if (mCallerTargets.size() == MAX_SUGGESTED_APP_TARGETS) break;
235             }
236         }
237     }
238 
239     @Override
handlePackagesChanged()240     public void handlePackagesChanged() {
241         if (DEBUG) {
242             Log.d(TAG, "clearing queryTargets on package change");
243         }
244         createPlaceHolders();
245         mResolverListCommunicator.onHandlePackagesChanged(this);
246 
247     }
248 
249     @Override
rebuildList(boolean doPostProcessing)250     protected boolean rebuildList(boolean doPostProcessing) {
251         mAnimationTracker.reset();
252         mSortedList.clear();
253         boolean result = super.rebuildList(doPostProcessing);
254         notifyDataSetChanged();
255         return result;
256     }
257 
createPlaceHolders()258     private void createPlaceHolders() {
259         mServiceTargets.clear();
260         for (int i = 0; i < mMaxRankedTargets; ++i) {
261             mServiceTargets.add(mPlaceHolderTargetInfo);
262         }
263     }
264 
265     @Override
onCreateView(ViewGroup parent)266     View onCreateView(ViewGroup parent) {
267         return mInflater.inflate(R.layout.resolve_grid_item, parent, false);
268     }
269 
270     @VisibleForTesting
271     @Override
onBindView(View view, TargetInfo info, int position)272     public void onBindView(View view, TargetInfo info, int position) {
273         final ViewHolder holder = (ViewHolder) view.getTag();
274 
275         if (info == null) {
276             holder.icon.setImageDrawable(loadIconPlaceholder());
277             return;
278         }
279 
280         holder.bindLabel(info.getDisplayLabel(), info.getExtendedInfo());
281         mAnimationTracker.animateLabel(holder.text, info);
282         if (holder.text2.getVisibility() == View.VISIBLE) {
283             mAnimationTracker.animateLabel(holder.text2, info);
284         }
285         holder.bindIcon(info);
286         if (info.getDisplayIconHolder().getDisplayIcon() != null) {
287             mAnimationTracker.animateIcon(holder.icon, info);
288         } else {
289             holder.icon.clearAnimation();
290         }
291 
292         if (info.isSelectableTargetInfo()) {
293             // direct share targets should append the application name for a better readout
294             DisplayResolveInfo rInfo = info.getDisplayResolveInfo();
295             CharSequence appName = rInfo != null ? rInfo.getDisplayLabel() : "";
296             CharSequence extendedInfo = info.getExtendedInfo();
297             String contentDescription = String.join(" ", info.getDisplayLabel(),
298                     extendedInfo != null ? extendedInfo : "", appName);
299             holder.updateContentDescription(contentDescription);
300             if (!info.hasDisplayIcon()) {
301                 loadDirectShareIcon((SelectableTargetInfo) info);
302             }
303         } else if (info.isDisplayResolveInfo()) {
304             DisplayResolveInfo dri = (DisplayResolveInfo) info;
305             if (!dri.hasDisplayIcon()) {
306                 loadIcon(dri);
307             }
308         }
309 
310         // If target is loading, show a special placeholder shape in the label, make unclickable
311         if (info.isPlaceHolderTargetInfo()) {
312             final int maxWidth = mContext.getResources().getDimensionPixelSize(
313                     R.dimen.chooser_direct_share_label_placeholder_max_width);
314             holder.text.setMaxWidth(maxWidth);
315             holder.text.setBackground(mContext.getResources().getDrawable(
316                     R.drawable.chooser_direct_share_label_placeholder, mContext.getTheme()));
317             // Prevent rippling by removing background containing ripple
318             holder.itemView.setBackground(null);
319         } else {
320             holder.text.setMaxWidth(Integer.MAX_VALUE);
321             holder.text.setBackground(null);
322             holder.itemView.setBackground(holder.defaultItemViewBackground);
323         }
324 
325         // Always remove the spacing listener, attach as needed to direct share targets below.
326         holder.text.removeOnLayoutChangeListener(mPinTextSpacingListener);
327 
328         if (info.isMultiDisplayResolveInfo()) {
329             // If the target is grouped show an indicator
330             Drawable bkg = mContext.getDrawable(R.drawable.chooser_group_background);
331             holder.text.setPaddingRelative(0, 0, bkg.getIntrinsicWidth() /* end */, 0);
332             holder.text.setBackground(bkg);
333         } else if (info.isPinned() && (getPositionTargetType(position) == TARGET_STANDARD
334                 || getPositionTargetType(position) == TARGET_SERVICE)) {
335             // If the appShare or directShare target is pinned and in the suggested row show a
336             // pinned indicator
337             Drawable bkg = mContext.getDrawable(R.drawable.chooser_pinned_background);
338             holder.text.setPaddingRelative(bkg.getIntrinsicWidth() /* start */, 0, 0, 0);
339             holder.text.setBackground(bkg);
340             holder.text.addOnLayoutChangeListener(mPinTextSpacingListener);
341         } else {
342             holder.text.setBackground(null);
343             holder.text.setPaddingRelative(0, 0, 0, 0);
344         }
345     }
346 
loadDirectShareIcon(SelectableTargetInfo info)347     private void loadDirectShareIcon(SelectableTargetInfo info) {
348         if (mRequestedIcons.add(info)) {
349             mTargetDataLoader.loadDirectShareIcon(
350                     info,
351                     getUserHandle(),
352                     (drawable) -> onDirectShareIconLoaded(info, drawable));
353         }
354     }
355 
onDirectShareIconLoaded(SelectableTargetInfo mTargetInfo, Drawable icon)356     private void onDirectShareIconLoaded(SelectableTargetInfo mTargetInfo, Drawable icon) {
357         if (icon != null && !mTargetInfo.hasDisplayIcon()) {
358             mTargetInfo.getDisplayIconHolder().setDisplayIcon(icon);
359             notifyDataSetChanged();
360         }
361     }
362 
updateAlphabeticalList()363     void updateAlphabeticalList() {
364         // TODO: this procedure seems like it should be relatively lightweight. Why does it need to
365         // run in an `AsyncTask`?
366         new AsyncTask<Void, Void, List<DisplayResolveInfo>>() {
367             @Override
368             protected List<DisplayResolveInfo> doInBackground(Void... voids) {
369                 try {
370                     Trace.beginSection("update-alphabetical-list");
371                     return updateList();
372                 } finally {
373                     Trace.endSection();
374                 }
375             }
376 
377             private List<DisplayResolveInfo> updateList() {
378                 List<DisplayResolveInfo> allTargets = new ArrayList<>();
379                 allTargets.addAll(getTargetsInCurrentDisplayList());
380                 allTargets.addAll(mCallerTargets);
381 
382                 // Consolidate multiple targets from same app.
383                 return allTargets
384                         .stream()
385                         .collect(Collectors.groupingBy(target ->
386                                 target.getResolvedComponentName().getPackageName()
387                                 + "#" + target.getDisplayLabel()
388                                 + '#' + target.getResolveInfo().userHandle.getIdentifier()
389                         ))
390                         .values()
391                         .stream()
392                         .map(appTargets ->
393                                 (appTargets.size() == 1)
394                                 ? appTargets.get(0)
395                                 : MultiDisplayResolveInfo.newMultiDisplayResolveInfo(appTargets))
396                         .sorted(new ChooserActivity.AzInfoComparator(mContext))
397                         .collect(Collectors.toList());
398             }
399             @Override
400             protected void onPostExecute(List<DisplayResolveInfo> newList) {
401                 mSortedList = newList;
402                 notifyDataSetChanged();
403             }
404         }.execute();
405     }
406 
407     @Override
getCount()408     public int getCount() {
409         return getRankedTargetCount() + getAlphaTargetCount()
410                 + getSelectableServiceTargetCount() + getCallerTargetCount();
411     }
412 
413     @Override
getUnfilteredCount()414     public int getUnfilteredCount() {
415         int appTargets = super.getUnfilteredCount();
416         if (appTargets > mMaxRankedTargets) {
417             // TODO: what does this condition mean?
418             appTargets = appTargets + mMaxRankedTargets;
419         }
420         return appTargets + getSelectableServiceTargetCount() + getCallerTargetCount();
421     }
422 
423 
getCallerTargetCount()424     public int getCallerTargetCount() {
425         return mCallerTargets.size();
426     }
427 
428     /**
429      * Filter out placeholders and non-selectable service targets
430      */
getSelectableServiceTargetCount()431     public int getSelectableServiceTargetCount() {
432         int count = 0;
433         for (TargetInfo info : mServiceTargets) {
434             if (info.isSelectableTargetInfo()) {
435                 count++;
436             }
437         }
438         return count;
439     }
440 
getServiceTargetCount()441     public int getServiceTargetCount() {
442         if (mChooserRequest.isSendActionTarget() && !ActivityManager.isLowRamDeviceStatic()) {
443             return Math.min(mServiceTargets.size(), mMaxRankedTargets);
444         }
445 
446         return 0;
447     }
448 
getAlphaTargetCount()449     public int getAlphaTargetCount() {
450         int groupedCount = mSortedList.size();
451         int ungroupedCount = mCallerTargets.size() + getDisplayResolveInfoCount();
452         return (ungroupedCount > mMaxRankedTargets) ? groupedCount : 0;
453     }
454 
455     /**
456      * Fetch ranked app target count
457      */
getRankedTargetCount()458     public int getRankedTargetCount() {
459         int spacesAvailable = mMaxRankedTargets - getCallerTargetCount();
460         return Math.min(spacesAvailable, super.getCount());
461     }
462 
463     /** Get all the {@link DisplayResolveInfo} data for our targets. */
getDisplayResolveInfos()464     public DisplayResolveInfo[] getDisplayResolveInfos() {
465         int size = getDisplayResolveInfoCount();
466         DisplayResolveInfo[] resolvedTargets = new DisplayResolveInfo[size];
467         for (int i = 0; i < size; i++) {
468             resolvedTargets[i] = getDisplayResolveInfo(i);
469         }
470         return resolvedTargets;
471     }
472 
getPositionTargetType(int position)473     public int getPositionTargetType(int position) {
474         int offset = 0;
475 
476         final int serviceTargetCount = getServiceTargetCount();
477         if (position < serviceTargetCount) {
478             return TARGET_SERVICE;
479         }
480         offset += serviceTargetCount;
481 
482         final int callerTargetCount = getCallerTargetCount();
483         if (position - offset < callerTargetCount) {
484             return TARGET_CALLER;
485         }
486         offset += callerTargetCount;
487 
488         final int rankedTargetCount = getRankedTargetCount();
489         if (position - offset < rankedTargetCount) {
490             return TARGET_STANDARD;
491         }
492         offset += rankedTargetCount;
493 
494         final int standardTargetCount = getAlphaTargetCount();
495         if (position - offset < standardTargetCount) {
496             return TARGET_STANDARD_AZ;
497         }
498 
499         return TARGET_BAD;
500     }
501 
502     @Override
getItem(int position)503     public TargetInfo getItem(int position) {
504         return targetInfoForPosition(position, true);
505     }
506 
507     /**
508      * Find target info for a given position.
509      * Since ChooserActivity displays several sections of content, determine which
510      * section provides this item.
511      */
512     @Override
targetInfoForPosition(int position, boolean filtered)513     public TargetInfo targetInfoForPosition(int position, boolean filtered) {
514         if (position == NO_POSITION) {
515             return null;
516         }
517 
518         int offset = 0;
519 
520         // Direct share targets
521         final int serviceTargetCount = filtered ? getServiceTargetCount() :
522                 getSelectableServiceTargetCount();
523         if (position < serviceTargetCount) {
524             return mServiceTargets.get(position);
525         }
526         offset += serviceTargetCount;
527 
528         // Targets provided by calling app
529         final int callerTargetCount = getCallerTargetCount();
530         if (position - offset < callerTargetCount) {
531             return mCallerTargets.get(position - offset);
532         }
533         offset += callerTargetCount;
534 
535         // Ranked standard app targets
536         final int rankedTargetCount = getRankedTargetCount();
537         if (position - offset < rankedTargetCount) {
538             return filtered ? super.getItem(position - offset)
539                     : getDisplayResolveInfo(position - offset);
540         }
541         offset += rankedTargetCount;
542 
543         // Alphabetical complete app target list.
544         if (position - offset < getAlphaTargetCount() && !mSortedList.isEmpty()) {
545             return mSortedList.get(position - offset);
546         }
547 
548         return null;
549     }
550 
551     // Check whether {@code dri} should be added into mDisplayList.
552     @Override
shouldAddResolveInfo(DisplayResolveInfo dri)553     protected boolean shouldAddResolveInfo(DisplayResolveInfo dri) {
554         // Checks if this info is already listed in callerTargets.
555         for (TargetInfo existingInfo : mCallerTargets) {
556             if (mResolverListCommunicator.resolveInfoMatch(
557                     dri.getResolveInfo(), existingInfo.getResolveInfo())) {
558                 return false;
559             }
560         }
561         return super.shouldAddResolveInfo(dri);
562     }
563 
564     /**
565      * Fetch surfaced direct share target info
566      */
getSurfacedTargetInfo()567     public List<TargetInfo> getSurfacedTargetInfo() {
568         return mServiceTargets.subList(0,
569                 Math.min(mMaxRankedTargets, getSelectableServiceTargetCount()));
570     }
571 
572 
573     /**
574      * Evaluate targets for inclusion in the direct share area. May not be included
575      * if score is too low.
576      */
addServiceResults( @ullable DisplayResolveInfo origTarget, List<ChooserTarget> targets, @ChooserActivity.ShareTargetType int targetType, Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos, Map<ChooserTarget, AppTarget> directShareToAppTargets)577     public void addServiceResults(
578             @Nullable DisplayResolveInfo origTarget,
579             List<ChooserTarget> targets,
580             @ChooserActivity.ShareTargetType int targetType,
581             Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos,
582             Map<ChooserTarget, AppTarget> directShareToAppTargets) {
583         // Avoid inserting any potentially late results.
584         if ((mServiceTargets.size() == 1) && mServiceTargets.get(0).isEmptyTargetInfo()) {
585             return;
586         }
587         boolean isShortcutResult = targetType == TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER
588                 || targetType == TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE;
589         boolean isUpdated = mShortcutSelectionLogic.addServiceResults(
590                 origTarget,
591                 getBaseScore(origTarget, targetType),
592                 targets,
593                 isShortcutResult,
594                 directShareToShortcutInfos,
595                 directShareToAppTargets,
596                 mContext.createContextAsUser(getUserHandle(), 0),
597                 mChooserRequest.getTargetIntent(),
598                 mChooserRequest.getReferrerFillInIntent(),
599                 mMaxRankedTargets,
600                 mServiceTargets);
601         if (isUpdated) {
602             notifyDataSetChanged();
603         }
604     }
605 
606     /**
607      * Use the scoring system along with artificial boosts to create up to 4 distinct buckets:
608      * <ol>
609      *   <li>App-supplied targets
610      *   <li>Shortcuts ranked via App Prediction Manager
611      *   <li>Shortcuts ranked via legacy heuristics
612      *   <li>Legacy direct share targets
613      * </ol>
614      */
getBaseScore( DisplayResolveInfo target, @ChooserActivity.ShareTargetType int targetType)615     public float getBaseScore(
616             DisplayResolveInfo target,
617             @ChooserActivity.ShareTargetType int targetType) {
618         if (target == null) {
619             return CALLER_TARGET_SCORE_BOOST;
620         }
621         float score = super.getScore(target);
622         if (targetType == TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER
623                 || targetType == TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE) {
624             return score * SHORTCUT_TARGET_SCORE_BOOST;
625         }
626         return score;
627     }
628 
629     /**
630      * Calling this marks service target loading complete, and will attempt to no longer
631      * update the direct share area.
632      */
completeServiceTargetLoading()633     public void completeServiceTargetLoading() {
634         mServiceTargets.removeIf(o -> o.isPlaceHolderTargetInfo());
635         if (mServiceTargets.isEmpty()) {
636             mServiceTargets.add(NotSelectableTargetInfo.newEmptyTargetInfo());
637             mEventLog.logSharesheetEmptyDirectShareRow();
638         }
639         notifyDataSetChanged();
640     }
641 
642     /**
643      * Rather than fully sorting the input list, this sorting task will put the top k elements
644      * in the head of input list and fill the tail with other elements in undetermined order.
645      */
646     @Override
647     AsyncTask<List<ResolvedComponentInfo>,
648                 Void,
createSortingTask(boolean doPostProcessing)649                 List<ResolvedComponentInfo>> createSortingTask(boolean doPostProcessing) {
650         return new AsyncTask<List<ResolvedComponentInfo>,
651                 Void,
652                 List<ResolvedComponentInfo>>() {
653             @Override
654             protected List<ResolvedComponentInfo> doInBackground(
655                     List<ResolvedComponentInfo>... params) {
656                 Trace.beginSection("ChooserListAdapter#SortingTask");
657                 mResolverListController.topK(params[0], mMaxRankedTargets);
658                 Trace.endSection();
659                 return params[0];
660             }
661             @Override
662             protected void onPostExecute(List<ResolvedComponentInfo> sortedComponents) {
663                 processSortedList(sortedComponents, doPostProcessing);
664                 if (doPostProcessing) {
665                     mResolverListCommunicator.updateProfileViewButton();
666                     notifyDataSetChanged();
667                 }
668             }
669         };
670     }
671 
672 }
673