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