diff options
Diffstat (limited to 'java/src/com')
7 files changed, 100 insertions, 9 deletions
diff --git a/java/src/com/android/intentresolver/ChooserActivity.java b/java/src/com/android/intentresolver/ChooserActivity.java index e741b06c..3a7d4e68 100644 --- a/java/src/com/android/intentresolver/ChooserActivity.java +++ b/java/src/com/android/intentresolver/ChooserActivity.java @@ -262,6 +262,8 @@ public class ChooserActivity extends ResolverActivity implements private final SparseArray<ProfileRecord> mProfileRecords = new SparseArray<>(); + private boolean mExcludeSharedText = false; + public ChooserActivity() {} @Override @@ -771,6 +773,11 @@ public class ChooserActivity extends ResolverActivity implements ? null : createReselectionRunnable(reselectionAction); } + + @Override + public Consumer<Boolean> getExcludeSharedTextAction() { + return (isExcluded) -> mExcludeSharedText = isExcluded; + } }; ViewGroup layout = mChooserContentPreviewUi.displayContentPreview( @@ -1196,6 +1203,7 @@ public class ChooserActivity extends ResolverActivity implements } } updateModelAndChooserCounts(target); + maybeRemoveSharedText(target); return super.onTargetSelected(target, alwaysCheck); } @@ -1384,6 +1392,27 @@ public class ChooserActivity extends ResolverActivity implements mIsSuccessfullySelected = true; } + private void maybeRemoveSharedText(@androidx.annotation.NonNull TargetInfo targetInfo) { + Intent targetIntent = targetInfo.getTargetIntent(); + if (targetIntent == null) { + return; + } + Intent originalTargetIntent = new Intent(mChooserRequest.getTargetIntent()); + // Our TargetInfo implementations add associated component to the intent, let's do the same + // for the sake of the comparison below. + if (targetIntent.getComponent() != null) { + originalTargetIntent.setComponent(targetIntent.getComponent()); + } + // Use filterEquals as a way to check that the primary intent is in use (and not an + // alternative one). For example, an app is sharing an image and a link with mime type + // "image/png" and provides an alternative intent to share only the link with mime type + // "text/uri". Should there be a target that accepts only the latter, the alternative intent + // will be used and we don't want to exclude the link from it. + if (mExcludeSharedText && originalTargetIntent.filterEquals(targetIntent)) { + targetIntent.removeExtra(Intent.EXTRA_TEXT); + } + } + private void sendImpressionToAppPredictor(TargetInfo targetInfo, ChooserListAdapter adapter) { // Send DS target impression info to AppPredictor, only when user chooses app share. if (targetInfo.isChooserTargetInfo()) { @@ -1451,6 +1480,7 @@ public class ChooserActivity extends ResolverActivity implements + " cannot match refined source intent " + matchingIntent); } else { TargetInfo clonedTarget = selectedTarget.cloneFilledIn(matchingIntent, 0); + maybeRemoveSharedText(clonedTarget); if (super.onTargetSelected(clonedTarget, false)) { updateModelAndChooserCounts(clonedTarget); finish(); diff --git a/java/src/com/android/intentresolver/ChooserContentPreviewUi.java b/java/src/com/android/intentresolver/ChooserContentPreviewUi.java index 9bef3553..91abd9d0 100644 --- a/java/src/com/android/intentresolver/ChooserContentPreviewUi.java +++ b/java/src/com/android/intentresolver/ChooserContentPreviewUi.java @@ -40,6 +40,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewStub; import android.view.animation.DecelerateInterpolator; +import android.widget.CheckBox; import android.widget.ImageView; import android.widget.TextView; @@ -59,6 +60,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Consumer; import java.util.stream.Collectors; /** @@ -100,6 +102,17 @@ public final class ChooserContentPreviewUi { */ @Nullable Runnable getReselectionAction(); + + /** + * <p> + * Creates an exclude-text action that can be called when the user changes shared text + * status in the Media + Text preview. + * </p> + * <p> + * <code>true</code> argument value indicates that the text should be excluded. + * </p> + */ + Consumer<Boolean> getExcludeSharedTextAction(); } /** @@ -222,7 +235,8 @@ public final class ChooserContentPreviewUi { transitionElementStatusCallback, contentResolver, imageClassifier, - actionRowLayout); + actionRowLayout, + actionFactory); break; case CONTENT_PREVIEW_FILE: layout = displayFileContentPreview( @@ -360,7 +374,8 @@ public final class ChooserContentPreviewUi { TransitionElementStatusCallback transitionElementStatusCallback, ContentResolver contentResolver, ImageMimeTypeClassifier imageClassifier, - @LayoutRes int actionRowLayout) { + @LayoutRes int actionRowLayout, + ActionFactory actionFactory) { ViewGroup contentPreviewLayout = (ViewGroup) layoutInflater.inflate( R.layout.chooser_grid_preview_image, parent, false); ImagePreviewView imagePreview = inflateImagePreviewView(contentPreviewLayout); @@ -391,7 +406,8 @@ public final class ChooserContentPreviewUi { setTextInImagePreviewVisibility( contentPreviewLayout, - targetIntent.getCharSequenceExtra(Intent.EXTRA_TEXT)); + targetIntent.getCharSequenceExtra(Intent.EXTRA_TEXT), + actionFactory); imagePreview.setTransitionElementStatusCallback(transitionElementStatusCallback); imagePreview.setImages(imageUris, imageLoader); @@ -399,20 +415,36 @@ public final class ChooserContentPreviewUi { } private void setTextInImagePreviewVisibility( - ViewGroup contentPreview, CharSequence text) { + ViewGroup contentPreview, CharSequence text, ActionFactory actionFactory) { int visibility = mFeatureFlagRepository.isEnabled(Flags.SHARESHEET_IMAGE_AND_TEXT_PREVIEW) && !TextUtils.isEmpty(text) ? View.VISIBLE : View.GONE; - TextView textView = contentPreview + final TextView textView = contentPreview .requireViewById(com.android.internal.R.id.content_preview_text); + CheckBox actionView = contentPreview + .requireViewById(R.id.include_text_action); textView.setVisibility(visibility); - int linkMask = visibility == View.VISIBLE && HttpUriMatcher.isHttpUri(text.toString()) - ? Linkify.WEB_URLS - : 0; - textView.setAutoLinkMask(linkMask); + boolean isLink = visibility == View.VISIBLE && HttpUriMatcher.isHttpUri(text.toString()); + textView.setAutoLinkMask(isLink ? Linkify.WEB_URLS : 0); textView.setText(text); + + if (visibility == View.VISIBLE) { + final int[] actionLabels = isLink + ? new int[] { R.string.include_link, R.string.exclude_link } + : new int[] { R.string.include_text, R.string.exclude_text }; + final Consumer<Boolean> shareTextAction = actionFactory.getExcludeSharedTextAction(); + actionView.setChecked(true); + actionView.setText(actionLabels[1]); + shareTextAction.accept(false); + actionView.setOnCheckedChangeListener((view, isChecked) -> { + view.setText(actionLabels[isChecked ? 1 : 0]); + textView.setVisibility(isChecked ? View.VISIBLE : View.GONE); + shareTextAction.accept(!isChecked); + }); + } + actionView.setVisibility(visibility); } private static List<ActionRow.Action> createImagePreviewActions( diff --git a/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java b/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java index 1b729c0e..4bbf59d8 100644 --- a/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java +++ b/java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java @@ -224,6 +224,11 @@ public class DisplayResolveInfo implements TargetInfo { return false; } + @Override + public Intent getTargetIntent() { + return mResolvedIntent; + } + public boolean isSuspended() { return mIsSuspended; } diff --git a/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java b/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java index e4cec887..0d79e5d5 100644 --- a/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java +++ b/java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java @@ -119,4 +119,9 @@ public class MultiDisplayResolveInfo extends DisplayResolveInfo { public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { return mTargetInfos.get(mSelected).startAsUser(activity, options, user); } + + @Override + public Intent getTargetIntent() { + return mTargetInfos.get(mSelected).getTargetIntent(); + } } diff --git a/java/src/com/android/intentresolver/chooser/NotSelectableTargetInfo.java b/java/src/com/android/intentresolver/chooser/NotSelectableTargetInfo.java index d6333374..9a2c971f 100644 --- a/java/src/com/android/intentresolver/chooser/NotSelectableTargetInfo.java +++ b/java/src/com/android/intentresolver/chooser/NotSelectableTargetInfo.java @@ -16,6 +16,7 @@ package com.android.intentresolver.chooser; +import android.annotation.Nullable; import android.app.Activity; import android.content.ComponentName; import android.content.Context; @@ -101,6 +102,12 @@ public abstract class NotSelectableTargetInfo extends ChooserTargetInfo { return false; } + @Nullable + @Override + public Intent getTargetIntent() { + return null; + } + public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { return false; } diff --git a/java/src/com/android/intentresolver/chooser/SelectableTargetInfo.java b/java/src/com/android/intentresolver/chooser/SelectableTargetInfo.java index 3ab50175..ca778233 100644 --- a/java/src/com/android/intentresolver/chooser/SelectableTargetInfo.java +++ b/java/src/com/android/intentresolver/chooser/SelectableTargetInfo.java @@ -346,6 +346,12 @@ public final class SelectableTargetInfo extends ChooserTargetInfo { return mActivityStarter.startAsUser(activity, options, user); } + @Nullable + @Override + public Intent getTargetIntent() { + return mBaseIntentToSend; + } + @Override public ResolveInfo getResolveInfo() { return mResolveInfo; diff --git a/java/src/com/android/intentresolver/chooser/TargetInfo.java b/java/src/com/android/intentresolver/chooser/TargetInfo.java index 72dd1b0b..7dcf66b2 100644 --- a/java/src/com/android/intentresolver/chooser/TargetInfo.java +++ b/java/src/com/android/intentresolver/chooser/TargetInfo.java @@ -88,6 +88,12 @@ public interface TargetInfo { Intent getResolvedIntent(); /** + * Get the target intent, the one that will be used with one of the <code>start</code> methods. + * @return the intent with target will be launced with. + */ + @Nullable Intent getTargetIntent(); + + /** * Get the resolved component name that represents this target. Note that this may not * be the component that will be directly launched by calling one of the <code>start</code> * methods provided; this is the component that will be credited with the launch. This may be |