diff options
8 files changed, 151 insertions, 123 deletions
diff --git a/java/src/com/android/intentresolver/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java index 5d169d24..d0e47562 100644 --- a/java/src/com/android/intentresolver/ChooserActivity.java +++ b/java/src/com/android/intentresolver/ChooserActivity.java @@ -26,6 +26,7 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityManager; @@ -140,6 +141,7 @@ import java.lang.annotation.RetentionPolicy; import java.net.URISyntaxException; import java.text.Collator; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -149,7 +151,7 @@ import java.util.function.Supplier; /** * The Chooser Activity handles intent resolution specifically for sharing intents - - * for example, those generated by @see android.content.Intent#createChooser(Intent, CharSequence). + * for example, as generated by {@see android.content.Intent#createChooser(Intent, CharSequence)}. * */ public class ChooserActivity extends ResolverActivity implements @@ -1068,7 +1070,12 @@ public class ChooserActivity extends ResolverActivity implements } } - private ViewGroup createContentPreviewView(ViewGroup parent) { + /** + * Create a view that will be shown in the content preview area + * @param parent reference to the parent container where the view should be attached to + * @return content preview view + */ + protected ViewGroup createContentPreviewView(ViewGroup parent) { Intent targetIntent = getTargetIntent(); int previewType = findPreferredContentPreview(targetIntent, getContentResolver()); return displayContentPreview(previewType, targetIntent, getLayoutInflater(), parent); @@ -2067,6 +2074,7 @@ public class ChooserActivity extends ResolverActivity implements if (matchingShortcuts.isEmpty()) { continue; } + List<ChooserTarget> chooserTargets = mShortcutToChooserTargetConverter .convertToChooserTarget( matchingShortcuts, @@ -2588,7 +2596,7 @@ public class ChooserActivity extends ResolverActivity implements boolean isExpandable = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT && !isInMultiWindowMode(); - if (directShareHeight != 0 && isSendAction(getTargetIntent()) + if (directShareHeight != 0 && shouldShowContentPreview() && isExpandable) { // make sure to leave room for direct share 4->8 expansion int requiredExpansionHeight = @@ -2828,7 +2836,14 @@ public class ChooserActivity extends ResolverActivity implements return shouldShowTabs() && mMultiProfilePagerAdapter.getListAdapterForUserHandle( UserHandle.of(UserHandle.myUserId())).getCount() > 0 - && isSendAction(getTargetIntent()); + && shouldShowContentPreview(); + } + + /** + * @return true if we want to show the content preview area + */ + protected boolean shouldShowContentPreview() { + return isSendAction(getTargetIntent()); } private void updateStickyContentPreview() { @@ -2880,12 +2895,14 @@ public class ChooserActivity extends ResolverActivity implements private void startFinishAnimation() { View rootView = findRootView(); - rootView.startAnimation(new FinishAnimation(this, rootView)); + if (rootView != null) { + rootView.startAnimation(new FinishAnimation(this, rootView)); + } } private boolean maybeCancelFinishAnimation() { View rootView = findRootView(); - Animation animation = rootView.getAnimation(); + Animation animation = (rootView == null) ? null : rootView.getAnimation(); if (animation instanceof FinishAnimation) { boolean hasEnded = animation.hasEnded(); animation.cancel(); @@ -3159,7 +3176,7 @@ public class ChooserActivity extends ResolverActivity implements return 0; } - if (!isSendAction(getTargetIntent())) { + if (!shouldShowContentPreview()) { return 0; } @@ -3190,7 +3207,7 @@ public class ChooserActivity extends ResolverActivity implements // There can be at most one row in the listview, that is internally // a ViewGroup with 2 rows public int getServiceTargetRowCount() { - if (isSendAction(getTargetIntent()) + if (shouldShowContentPreview() && !ActivityManager.isLowRamDeviceStatic()) { return 1; } @@ -3649,6 +3666,7 @@ public class ChooserActivity extends ResolverActivity implements this.mRows = rows; this.mCellCountPerRow = cellCountPerRow; this.mCellVisibility = new boolean[rows.size() * cellCountPerRow]; + Arrays.fill(mCellVisibility, true); this.mListAdapterSupplier = listAdapterSupplier; } @@ -4012,11 +4030,13 @@ public class ChooserActivity extends ResolverActivity implements */ private static class FinishAnimation extends AlphaAnimation implements Animation.AnimationListener { + @Nullable private Activity mActivity; + @Nullable private View mRootView; private final float mFromAlpha; - FinishAnimation(Activity activity, View rootView) { + FinishAnimation(@NonNull Activity activity, @NonNull View rootView) { super(rootView.getAlpha(), 0.0f); mActivity = activity; mRootView = rootView; @@ -4040,7 +4060,9 @@ public class ChooserActivity extends ResolverActivity implements @Override public void cancel() { - mRootView.setAlpha(mFromAlpha); + if (mRootView != null) { + mRootView.setAlpha(mFromAlpha); + } cleanup(); super.cancel(); } @@ -4051,9 +4073,10 @@ public class ChooserActivity extends ResolverActivity implements @Override public void onAnimationEnd(Animation animation) { - if (mActivity != null) { - mActivity.finish(); - cleanup(); + Activity activity = mActivity; + cleanup(); + if (activity != null) { + activity.finish(); } } diff --git a/java/src/com/android/intentresolver/ChooserListAdapter.java b/java/src/com/android/intentresolver/ChooserListAdapter.java index 3d760129..5e508ed8 100644 --- a/java/src/com/android/intentresolver/ChooserListAdapter.java +++ b/java/src/com/android/intentresolver/ChooserListAdapter.java @@ -243,6 +243,7 @@ public class ChooserListAdapter extends ResolverListAdapter { mListViewDataChanged = false; } + private void createPlaceHolders() { mServiceTargets.clear(); for (int i = 0; i < mChooserListCommunicator.getMaxRankedTargets(); i++) { @@ -252,8 +253,7 @@ public class ChooserListAdapter extends ResolverListAdapter { @Override View onCreateView(ViewGroup parent) { - return mInflater.inflate( - R.layout.resolve_grid_item, parent, false); + return mInflater.inflate(R.layout.resolve_grid_item, parent, false); } @Override @@ -266,25 +266,20 @@ 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()); - 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 { - holder.bindIcon(info); + if (!dri.hasDisplayIcon()) { + loadIcon(dri); } } @@ -325,32 +320,6 @@ 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 @@ -560,12 +529,11 @@ public class ChooserListAdapter extends ResolverListAdapter { List<ChooserTarget> targets, @ChooserActivity.ShareTargetType int targetType, Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos) { - // Avoid inserting any potentially late results - if (mServiceTargets.size() == 1 - && mServiceTargets.get(0) instanceof ChooserActivity.EmptyTargetInfo) { + // Avoid inserting any potentially late results. + if ((mServiceTargets.size() == 1) + && (mServiceTargets.get(0) instanceof ChooserActivity.EmptyTargetInfo)) { return; } - boolean isShortcutResult = targetType == TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER || targetType == TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE; boolean isUpdated = mShortcutSelectionLogic.addServiceResults( @@ -619,7 +587,6 @@ public class ChooserListAdapter extends ResolverListAdapter { notifyDataSetChanged(); } - public ChooserTarget getChooserTargetForValue(int value) { return mServiceTargets.get(value).getChooserTarget(); } @@ -678,24 +645,10 @@ 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}. */ - @VisibleForTesting - public interface ChooserListCommunicator extends ResolverListCommunicator { + interface ChooserListCommunicator extends ResolverListCommunicator { int getMaxRankedTargets(); diff --git a/java/src/com/android/intentresolver/ResolverActivity.java b/java/src/com/android/intentresolver/ResolverActivity.java index 453a6e84..6c013221 100644 --- a/java/src/com/android/intentresolver/ResolverActivity.java +++ b/java/src/com/android/intentresolver/ResolverActivity.java @@ -55,6 +55,7 @@ import android.content.pm.UserInfo; import android.content.res.Configuration; import android.content.res.TypedArray; import android.graphics.Insets; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -94,7 +95,6 @@ import com.android.intentresolver.AbstractMultiProfilePagerAdapter.Profile; import com.android.intentresolver.chooser.ChooserTargetInfo; import com.android.intentresolver.chooser.DisplayResolveInfo; import com.android.intentresolver.chooser.TargetInfo; - import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.PackageMonitor; import com.android.internal.logging.MetricsLogger; @@ -1475,14 +1475,21 @@ public class ResolverActivity extends Activity implements mMultiProfilePagerAdapter.getActiveListAdapter().mDisplayList.get(0); boolean inWorkProfile = getCurrentProfile() == PROFILE_WORK; - ResolverListAdapter inactiveAdapter = mMultiProfilePagerAdapter.getInactiveListAdapter(); - DisplayResolveInfo otherProfileResolveInfo = inactiveAdapter.mDisplayList.get(0); + final ResolverListAdapter inactiveAdapter = + mMultiProfilePagerAdapter.getInactiveListAdapter(); + final DisplayResolveInfo otherProfileResolveInfo = inactiveAdapter.mDisplayList.get(0); // Load the icon asynchronously ImageView icon = findViewById(com.android.internal.R.id.icon); - ResolverListAdapter.LoadIconTask iconTask = inactiveAdapter.new LoadIconTask( - otherProfileResolveInfo, new ResolverListAdapter.ViewHolder(icon)); - iconTask.execute(); + inactiveAdapter.new LoadIconTask(otherProfileResolveInfo) { + @Override + protected void onPostExecute(Drawable drawable) { + if (!isDestroyed()) { + otherProfileResolveInfo.setDisplayIcon(drawable); + new ResolverListAdapter.ViewHolder(icon).bindIcon(otherProfileResolveInfo); + } + } + }.execute(); ((TextView) findViewById(com.android.internal.R.id.open_cross_profile)).setText( getResources().getString( diff --git a/java/src/com/android/intentresolver/ResolverListAdapter.java b/java/src/com/android/intentresolver/ResolverListAdapter.java index 898d8c8e..251b157b 100644 --- a/java/src/com/android/intentresolver/ResolverListAdapter.java +++ b/java/src/com/android/intentresolver/ResolverListAdapter.java @@ -54,11 +54,13 @@ import android.widget.TextView; import com.android.intentresolver.ResolverActivity.ResolvedComponentInfo; import com.android.intentresolver.chooser.DisplayResolveInfo; import com.android.intentresolver.chooser.TargetInfo; - import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class ResolverListAdapter extends BaseAdapter { private static final String TAG = "ResolverListAdapter"; @@ -87,6 +89,8 @@ public class ResolverListAdapter extends BaseAdapter { private Runnable mPostListReadyRunnable; private final boolean mIsAudioCaptureDevice; private boolean mIsTabLoaded; + private final Map<DisplayResolveInfo, LoadIconTask> mIconLoaders = new HashMap<>(); + private final Map<DisplayResolveInfo, LoadLabelTask> mLabelLoaders = new HashMap<>(); public ResolverListAdapter(Context context, List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList, @@ -636,26 +640,47 @@ public class ResolverListAdapter extends BaseAdapter { if (info == null) { holder.icon.setImageDrawable( mContext.getDrawable(R.drawable.resolver_icon_placeholder)); + holder.bindLabel("", "", false); return; } - if (info instanceof DisplayResolveInfo - && !((DisplayResolveInfo) info).hasDisplayLabel()) { - getLoadLabelTask((DisplayResolveInfo) info, holder).execute(); - } else { - holder.bindLabel(info.getDisplayLabel(), info.getExtendedInfo(), alwaysShowSubLabel()); + if (info instanceof DisplayResolveInfo) { + DisplayResolveInfo dri = (DisplayResolveInfo) info; + boolean hasLabel = dri.hasDisplayLabel(); + holder.bindLabel( + dri.getDisplayLabel(), + dri.getExtendedInfo(), + hasLabel && alwaysShowSubLabel()); + holder.bindIcon(info); + if (!hasLabel) { + loadLabel(dri); + } + if (!dri.hasDisplayIcon()) { + loadIcon(dri); + } } + } - if (info instanceof DisplayResolveInfo - && !((DisplayResolveInfo) info).hasDisplayIcon()) { - new LoadIconTask((DisplayResolveInfo) info, holder).execute(); - } else { - holder.bindIcon(info); + protected final void loadIcon(DisplayResolveInfo info) { + LoadIconTask task = mIconLoaders.get(info); + if (task == null) { + task = new LoadIconTask((DisplayResolveInfo) info); + mIconLoaders.put(info, task); + task.execute(); } } - protected LoadLabelTask getLoadLabelTask(DisplayResolveInfo info, ViewHolder holder) { - return new LoadLabelTask(info, holder); + private void loadLabel(DisplayResolveInfo info) { + LoadLabelTask task = mLabelLoaders.get(info); + if (task == null) { + task = createLoadLabelTask(info); + mLabelLoaders.put(info, task); + task.execute(); + } + } + + protected LoadLabelTask createLoadLabelTask(DisplayResolveInfo info) { + return new LoadLabelTask(info); } public void onDestroy() { @@ -666,6 +691,16 @@ public class ResolverListAdapter extends BaseAdapter { if (mResolverListController != null) { mResolverListController.destroy(); } + cancelTasks(mIconLoaders.values()); + cancelTasks(mLabelLoaders.values()); + mIconLoaders.clear(); + mLabelLoaders.clear(); + } + + private <T extends AsyncTask> void cancelTasks(Collection<T> tasks) { + for (T task: tasks) { + task.cancel(false); + } } private static ColorMatrixColorFilter getSuspendedColorMatrix() { @@ -888,11 +923,9 @@ public class ResolverListAdapter extends BaseAdapter { protected class LoadLabelTask extends AsyncTask<Void, Void, CharSequence[]> { private final DisplayResolveInfo mDisplayResolveInfo; - private final ViewHolder mHolder; - protected LoadLabelTask(DisplayResolveInfo dri, ViewHolder holder) { + protected LoadLabelTask(DisplayResolveInfo dri) { mDisplayResolveInfo = dri; - mHolder = holder; } @Override @@ -930,21 +963,22 @@ public class ResolverListAdapter extends BaseAdapter { @Override protected void onPostExecute(CharSequence[] result) { + if (mDisplayResolveInfo.hasDisplayLabel()) { + return; + } mDisplayResolveInfo.setDisplayLabel(result[0]); mDisplayResolveInfo.setExtendedInfo(result[1]); - mHolder.bindLabel(result[0], result[1], alwaysShowSubLabel()); + notifyDataSetChanged(); } } class LoadIconTask extends AsyncTask<Void, Void, Drawable> { protected final DisplayResolveInfo mDisplayResolveInfo; private final ResolveInfo mResolveInfo; - private ViewHolder mHolder; - LoadIconTask(DisplayResolveInfo dri, ViewHolder holder) { + LoadIconTask(DisplayResolveInfo dri) { mDisplayResolveInfo = dri; mResolveInfo = dri.getResolveInfo(); - mHolder = holder; } @Override @@ -958,17 +992,9 @@ public class ResolverListAdapter extends BaseAdapter { mResolverListCommunicator.updateProfileViewButton(); } else if (!mDisplayResolveInfo.hasDisplayIcon()) { mDisplayResolveInfo.setDisplayIcon(d); - mHolder.bindIcon(mDisplayResolveInfo); - // Notify in case view is already bound to resolve the race conditions on - // low end devices notifyDataSetChanged(); } } - - public void setViewHolder(ViewHolder holder) { - mHolder = holder; - mHolder.bindIcon(mDisplayResolveInfo); - } } /** diff --git a/java/src/com/android/intentresolver/SimpleIconFactory.java b/java/src/com/android/intentresolver/SimpleIconFactory.java index b05b4f68..ec5179ac 100644 --- a/java/src/com/android/intentresolver/SimpleIconFactory.java +++ b/java/src/com/android/intentresolver/SimpleIconFactory.java @@ -50,6 +50,8 @@ import android.util.AttributeSet; import android.util.Pools.SynchronizedPool; import android.util.TypedValue; +import com.android.internal.annotations.VisibleForTesting; + import org.xmlpull.v1.XmlPullParser; import java.nio.ByteBuffer; @@ -67,6 +69,7 @@ public class SimpleIconFactory { private static final SynchronizedPool<SimpleIconFactory> sPool = new SynchronizedPool<>(Runtime.getRuntime().availableProcessors()); + private static boolean sPoolEnabled = true; private static final int DEFAULT_WRAPPER_BACKGROUND = Color.WHITE; private static final float BLUR_FACTOR = 1.5f / 48; @@ -90,7 +93,7 @@ public class SimpleIconFactory { */ @Deprecated public static SimpleIconFactory obtain(Context ctx) { - SimpleIconFactory instance = sPool.acquire(); + SimpleIconFactory instance = sPoolEnabled ? sPool.acquire() : null; if (instance == null) { final ActivityManager am = (ActivityManager) ctx.getSystemService(ACTIVITY_SERVICE); final int iconDpi = (am == null) ? 0 : am.getLauncherLargeIconDensity(); @@ -104,6 +107,17 @@ public class SimpleIconFactory { return instance; } + /** + * Enables or disables SimpleIconFactory objects pooling. It is enabled in production, you + * could use this method in tests and disable the pooling to make the icon rendering more + * deterministic because some sizing parameters will not be cached. Please ensure that you + * reset this value back after finishing the test. + */ + @VisibleForTesting + public static void setPoolEnabled(boolean poolEnabled) { + sPoolEnabled = poolEnabled; + } + private static int getAttrDimFromContext(Context ctx, @AttrRes int attrId, String errorMsg) { final Resources res = ctx.getResources(); TypedValue outVal = new TypedValue(); diff --git a/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java b/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java index e7ffe3c6..c4bca266 100644 --- a/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java +++ b/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java @@ -172,14 +172,14 @@ public class DisplayResolveInfo implements TargetInfo, Parcelable { @Override public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) { - prepareIntentForCrossProfileLaunch(mResolvedIntent, userId); + TargetInfo.prepareIntentForCrossProfileLaunch(mResolvedIntent, userId); activity.startActivityAsCaller(mResolvedIntent, options, false, userId); return true; } @Override public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { - prepareIntentForCrossProfileLaunch(mResolvedIntent, user.getIdentifier()); + TargetInfo.prepareIntentForCrossProfileLaunch(mResolvedIntent, user.getIdentifier()); activity.startActivityAsUser(mResolvedIntent, options, user); return false; } @@ -224,13 +224,6 @@ public class DisplayResolveInfo implements TargetInfo, Parcelable { } }; - private static void prepareIntentForCrossProfileLaunch(Intent intent, int targetUserId) { - final int currentUserId = UserHandle.myUserId(); - if (targetUserId != currentUserId) { - intent.fixUris(currentUserId); - } - } - private DisplayResolveInfo(Parcel in) { mDisplayLabel = in.readCharSequence(); mExtendedInfo = in.readCharSequence(); diff --git a/java/src/com/android/intentresolver/chooser/SelectableTargetInfo.java b/java/src/com/android/intentresolver/chooser/SelectableTargetInfo.java index dffe2f6c..179966ad 100644 --- a/java/src/com/android/intentresolver/chooser/SelectableTargetInfo.java +++ b/java/src/com/android/intentresolver/chooser/SelectableTargetInfo.java @@ -259,6 +259,7 @@ public final class SelectableTargetInfo implements ChooserTargetInfo { } intent.setComponent(mChooserTarget.getComponentName()); intent.putExtras(mChooserTarget.getIntentExtras()); + TargetInfo.prepareIntentForCrossProfileLaunch(intent, userId); // Important: we will ignore the target security checks in ActivityManager // if and only if the ChooserTarget's target package is the same package diff --git a/java/src/com/android/intentresolver/chooser/TargetInfo.java b/java/src/com/android/intentresolver/chooser/TargetInfo.java index fabb26c2..e1970354 100644 --- a/java/src/com/android/intentresolver/chooser/TargetInfo.java +++ b/java/src/com/android/intentresolver/chooser/TargetInfo.java @@ -130,4 +130,15 @@ public interface TargetInfo { * @return true if this target should be pinned to the front by the request of the user */ boolean isPinned(); + + /** + * Fix the URIs in {@code intent} if cross-profile sharing is required. This should be called + * before launching the intent as another user. + */ + static void prepareIntentForCrossProfileLaunch(Intent intent, int targetUserId) { + final int currentUserId = UserHandle.myUserId(); + if (targetUserId != currentUserId) { + intent.fixUris(currentUserId); + } + } } |