Merge "Load direct-share icons asynchronously" into tm-qpr-dev am: 1809ffcf0e am: bb1a6ee1c8
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18973259 Change-Id: I97c84130642975bc7913b36b27332eedbf484137 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -43,6 +43,7 @@ import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
|
||||
import com.android.internal.app.chooser.ChooserTargetInfo;
|
||||
import com.android.internal.app.chooser.DisplayResolveInfo;
|
||||
@@ -86,7 +87,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
|
||||
private final ChooserActivityLogger mChooserActivityLogger;
|
||||
|
||||
private int mNumShortcutResults = 0;
|
||||
private Map<DisplayResolveInfo, LoadIconTask> mIconLoaders = new HashMap<>();
|
||||
private final Map<TargetInfo, AsyncTask> mIconLoaders = new HashMap<>();
|
||||
private boolean mApplySharingAppLimits;
|
||||
|
||||
// Reserve spots for incoming direct share targets by adding placeholders
|
||||
@@ -104,6 +105,8 @@ public class ChooserListAdapter extends ResolverListAdapter {
|
||||
private AppPredictor mAppPredictor;
|
||||
private AppPredictor.Callback mAppPredictorCallback;
|
||||
|
||||
private LoadDirectShareIconTaskProvider mTestLoadDirectShareTaskProvider;
|
||||
|
||||
// For pinned direct share labels, if the text spans multiple lines, the TextView will consume
|
||||
// the full width, even if the characters actually take up less than that. Measure the actual
|
||||
// line widths and constrain the View's width based upon that so that the pin doesn't end up
|
||||
@@ -240,7 +243,6 @@ public class ChooserListAdapter extends ResolverListAdapter {
|
||||
mListViewDataChanged = false;
|
||||
}
|
||||
|
||||
|
||||
private void createPlaceHolders() {
|
||||
mNumShortcutResults = 0;
|
||||
mServiceTargets.clear();
|
||||
@@ -265,31 +267,25 @@ public class ChooserListAdapter extends ResolverListAdapter {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(info instanceof DisplayResolveInfo)) {
|
||||
holder.bindLabel(info.getDisplayLabel(), info.getExtendedInfo(), alwaysShowSubLabel());
|
||||
holder.bindIcon(info);
|
||||
|
||||
if (info instanceof SelectableTargetInfo) {
|
||||
// direct share targets should append the application name for a better readout
|
||||
DisplayResolveInfo rInfo = ((SelectableTargetInfo) info).getDisplayResolveInfo();
|
||||
CharSequence appName = rInfo != null ? rInfo.getDisplayLabel() : "";
|
||||
CharSequence extendedInfo = info.getExtendedInfo();
|
||||
String contentDescription = String.join(" ", info.getDisplayLabel(),
|
||||
extendedInfo != null ? extendedInfo : "", appName);
|
||||
holder.updateContentDescription(contentDescription);
|
||||
}
|
||||
} else {
|
||||
if (info instanceof DisplayResolveInfo) {
|
||||
DisplayResolveInfo dri = (DisplayResolveInfo) info;
|
||||
holder.bindLabel(dri.getDisplayLabel(), dri.getExtendedInfo(), alwaysShowSubLabel());
|
||||
LoadIconTask task = mIconLoaders.get(dri);
|
||||
if (task == null) {
|
||||
task = new LoadIconTask(dri, holder);
|
||||
mIconLoaders.put(dri, task);
|
||||
task.execute();
|
||||
startDisplayResolveInfoIconLoading(holder, dri);
|
||||
} else {
|
||||
holder.bindLabel(info.getDisplayLabel(), info.getExtendedInfo(), alwaysShowSubLabel());
|
||||
|
||||
if (info instanceof SelectableTargetInfo) {
|
||||
SelectableTargetInfo selectableInfo = (SelectableTargetInfo) info;
|
||||
// direct share targets should append the application name for a better readout
|
||||
DisplayResolveInfo rInfo = selectableInfo.getDisplayResolveInfo();
|
||||
CharSequence appName = rInfo != null ? rInfo.getDisplayLabel() : "";
|
||||
CharSequence extendedInfo = selectableInfo.getExtendedInfo();
|
||||
String contentDescription = String.join(" ", selectableInfo.getDisplayLabel(),
|
||||
extendedInfo != null ? extendedInfo : "", appName);
|
||||
holder.updateContentDescription(contentDescription);
|
||||
startSelectableTargetInfoIconLoading(holder, selectableInfo);
|
||||
} else {
|
||||
// The holder was potentially changed as the underlying items were
|
||||
// reshuffled, so reset the target holder
|
||||
task.setViewHolder(holder);
|
||||
holder.bindIcon(info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,6 +326,32 @@ public class ChooserListAdapter extends ResolverListAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
private void startDisplayResolveInfoIconLoading(ViewHolder holder, DisplayResolveInfo info) {
|
||||
LoadIconTask task = (LoadIconTask) mIconLoaders.get(info);
|
||||
if (task == null) {
|
||||
task = new LoadIconTask(info, holder);
|
||||
mIconLoaders.put(info, task);
|
||||
task.execute();
|
||||
} else {
|
||||
// The holder was potentially changed as the underlying items were
|
||||
// reshuffled, so reset the target holder
|
||||
task.setViewHolder(holder);
|
||||
}
|
||||
}
|
||||
|
||||
private void startSelectableTargetInfoIconLoading(
|
||||
ViewHolder holder, SelectableTargetInfo info) {
|
||||
LoadDirectShareIconTask task = (LoadDirectShareIconTask) mIconLoaders.get(info);
|
||||
if (task == null) {
|
||||
task = mTestLoadDirectShareTaskProvider == null
|
||||
? new LoadDirectShareIconTask(info)
|
||||
: mTestLoadDirectShareTaskProvider.get();
|
||||
mIconLoaders.put(info, task);
|
||||
task.loadIcon();
|
||||
}
|
||||
task.setViewHolder(holder);
|
||||
}
|
||||
|
||||
void updateAlphabeticalList() {
|
||||
new AsyncTask<Void, Void, List<DisplayResolveInfo>>() {
|
||||
@Override
|
||||
@@ -344,7 +366,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
|
||||
Map<String, DisplayResolveInfo> consolidated = new HashMap<>();
|
||||
for (DisplayResolveInfo info : allTargets) {
|
||||
String resolvedTarget = info.getResolvedComponentName().getPackageName()
|
||||
+ '#' + info.getDisplayLabel();
|
||||
+ '#' + info.getDisplayLabel();
|
||||
DisplayResolveInfo multiDri = consolidated.get(resolvedTarget);
|
||||
if (multiDri == null) {
|
||||
consolidated.put(resolvedTarget, info);
|
||||
@@ -353,7 +375,7 @@ public class ChooserListAdapter extends ResolverListAdapter {
|
||||
} else {
|
||||
// create consolidated target from the single DisplayResolveInfo
|
||||
MultiDisplayResolveInfo multiDisplayResolveInfo =
|
||||
new MultiDisplayResolveInfo(resolvedTarget, multiDri);
|
||||
new MultiDisplayResolveInfo(resolvedTarget, multiDri);
|
||||
multiDisplayResolveInfo.addTarget(info);
|
||||
consolidated.put(resolvedTarget, multiDisplayResolveInfo);
|
||||
}
|
||||
@@ -739,11 +761,25 @@ public class ChooserListAdapter extends ResolverListAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An alias for onBindView to use with unit tests.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public void testViewBind(View view, TargetInfo info, int position) {
|
||||
onBindView(view, info, position);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setTestLoadDirectShareTaskProvider(LoadDirectShareIconTaskProvider provider) {
|
||||
mTestLoadDirectShareTaskProvider = provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Necessary methods to communicate between {@link ChooserListAdapter}
|
||||
* and {@link ChooserActivity}.
|
||||
*/
|
||||
interface ChooserListCommunicator extends ResolverListCommunicator {
|
||||
@VisibleForTesting
|
||||
public interface ChooserListCommunicator extends ResolverListCommunicator {
|
||||
|
||||
int getMaxRankedTargets();
|
||||
|
||||
@@ -751,4 +787,59 @@ public class ChooserListAdapter extends ResolverListAdapter {
|
||||
|
||||
boolean isSendAction(Intent targetIntent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads direct share targets icons.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public class LoadDirectShareIconTask extends AsyncTask<Void, Void, Void> {
|
||||
private final SelectableTargetInfo mTargetInfo;
|
||||
private ViewHolder mViewHolder;
|
||||
|
||||
private LoadDirectShareIconTask(SelectableTargetInfo targetInfo) {
|
||||
mTargetInfo = targetInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
mTargetInfo.loadIcon();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void arg) {
|
||||
if (mViewHolder != null) {
|
||||
mViewHolder.bindIcon(mTargetInfo);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies a view holder that will be updated when the task is completed.
|
||||
*/
|
||||
public void setViewHolder(ViewHolder viewHolder) {
|
||||
mViewHolder = viewHolder;
|
||||
mViewHolder.bindIcon(mTargetInfo);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* An alias for execute to use with unit tests.
|
||||
*/
|
||||
public void loadIcon() {
|
||||
execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface for the unit tests to override icon loading task creation
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public interface LoadDirectShareIconTaskProvider {
|
||||
/**
|
||||
* Provides an instance of the task.
|
||||
* @return
|
||||
*/
|
||||
LoadDirectShareIconTask get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -834,7 +834,11 @@ public class ResolverListAdapter extends BaseAdapter {
|
||||
void onHandlePackagesChanged(ResolverListAdapter listAdapter);
|
||||
}
|
||||
|
||||
static class ViewHolder {
|
||||
/**
|
||||
* A view holder.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static class ViewHolder {
|
||||
public View itemView;
|
||||
public Drawable defaultItemViewBackground;
|
||||
|
||||
@@ -842,7 +846,8 @@ public class ResolverListAdapter extends BaseAdapter {
|
||||
public TextView text2;
|
||||
public ImageView icon;
|
||||
|
||||
ViewHolder(View view) {
|
||||
@VisibleForTesting
|
||||
public ViewHolder(View view) {
|
||||
itemView = view;
|
||||
defaultItemViewBackground = view.getBackground();
|
||||
text = (TextView) view.findViewById(com.android.internal.R.id.text1);
|
||||
|
||||
@@ -37,6 +37,7 @@ import android.service.chooser.ChooserTarget;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.app.ChooserActivity;
|
||||
import com.android.internal.app.ResolverActivity;
|
||||
import com.android.internal.app.ResolverListAdapter.ActivityInfoPresentationGetter;
|
||||
@@ -59,8 +60,11 @@ public final class SelectableTargetInfo implements ChooserTargetInfo {
|
||||
private final String mDisplayLabel;
|
||||
private final PackageManager mPm;
|
||||
private final SelectableTargetInfoCommunicator mSelectableTargetInfoCommunicator;
|
||||
@GuardedBy("this")
|
||||
private ShortcutInfo mShortcutInfo;
|
||||
private Drawable mBadgeIcon = null;
|
||||
private CharSequence mBadgeContentDescription;
|
||||
@GuardedBy("this")
|
||||
private Drawable mDisplayIcon;
|
||||
private final Intent mFillInIntent;
|
||||
private final int mFillInFlags;
|
||||
@@ -78,6 +82,7 @@ public final class SelectableTargetInfo implements ChooserTargetInfo {
|
||||
mModifiedScore = modifiedScore;
|
||||
mPm = mContext.getPackageManager();
|
||||
mSelectableTargetInfoCommunicator = selectableTargetInfoComunicator;
|
||||
mShortcutInfo = shortcutInfo;
|
||||
mIsPinned = shortcutInfo != null && shortcutInfo.isPinned();
|
||||
if (sourceInfo != null) {
|
||||
final ResolveInfo ri = sourceInfo.getResolveInfo();
|
||||
@@ -92,8 +97,6 @@ public final class SelectableTargetInfo implements ChooserTargetInfo {
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO(b/121287224): do this in the background thread, and only for selected targets
|
||||
mDisplayIcon = getChooserTargetIconDrawable(chooserTarget, shortcutInfo);
|
||||
|
||||
if (sourceInfo != null) {
|
||||
mBackupResolveInfo = null;
|
||||
@@ -118,7 +121,10 @@ public final class SelectableTargetInfo implements ChooserTargetInfo {
|
||||
mChooserTarget = other.mChooserTarget;
|
||||
mBadgeIcon = other.mBadgeIcon;
|
||||
mBadgeContentDescription = other.mBadgeContentDescription;
|
||||
mDisplayIcon = other.mDisplayIcon;
|
||||
synchronized (other) {
|
||||
mShortcutInfo = other.mShortcutInfo;
|
||||
mDisplayIcon = other.mDisplayIcon;
|
||||
}
|
||||
mFillInIntent = fillInIntent;
|
||||
mFillInFlags = flags;
|
||||
mModifiedScore = other.mModifiedScore;
|
||||
@@ -141,6 +147,25 @@ public final class SelectableTargetInfo implements ChooserTargetInfo {
|
||||
return mSourceInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load display icon, if needed.
|
||||
*/
|
||||
public void loadIcon() {
|
||||
ShortcutInfo shortcutInfo;
|
||||
Drawable icon;
|
||||
synchronized (this) {
|
||||
shortcutInfo = mShortcutInfo;
|
||||
icon = mDisplayIcon;
|
||||
}
|
||||
if (icon == null && shortcutInfo != null) {
|
||||
icon = getChooserTargetIconDrawable(mChooserTarget, shortcutInfo);
|
||||
synchronized (this) {
|
||||
mDisplayIcon = icon;
|
||||
mShortcutInfo = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Drawable getChooserTargetIconDrawable(ChooserTarget target,
|
||||
@Nullable ShortcutInfo shortcutInfo) {
|
||||
Drawable directShareIcon = null;
|
||||
@@ -270,7 +295,7 @@ public final class SelectableTargetInfo implements ChooserTargetInfo {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getDisplayIcon(Context context) {
|
||||
public synchronized Drawable getDisplayIcon(Context context) {
|
||||
return mDisplayIcon;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user