summaryrefslogtreecommitdiff
path: root/java/src
diff options
context:
space:
mode:
author Andrey Epin <ayepin@google.com> 2023-02-03 08:56:17 -0800
committer Andrey Epin <ayepin@google.com> 2023-02-03 19:12:49 -0800
commit89697b484befb174482c92f5072f01836678d46c (patch)
tree793d96affef382ec314b0d044d628f8567068c76 /java/src
parent651ea96e4e069309cf3d1c5f518388e1410d5172 (diff)
Add optional text exclusion from media + text share
Add an option to the user to exclude text from being shared from a media + text sharing case. The text is ecluded only if the primary send inten is used i.e. it is possible for senders to specify alterntive intents that can also be used. Bug: 262277421 Test: manual testing of: (1) regualr image sharing, (2) image and text/web-link sharing, (3) using of an alternative intent, (4) sharing to a shortcut. All of those for one and two (+work) profiles. Test: integration tests Change-Id: Iebb33b951853c6588157fa92d3826b6a6e096591
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/intentresolver/ChooserActivity.java30
-rw-r--r--java/src/com/android/intentresolver/ChooserContentPreviewUi.java50
-rw-r--r--java/src/com/android/intentresolver/chooser/DisplayResolveInfo.java5
-rw-r--r--java/src/com/android/intentresolver/chooser/MultiDisplayResolveInfo.java5
-rw-r--r--java/src/com/android/intentresolver/chooser/NotSelectableTargetInfo.java7
-rw-r--r--java/src/com/android/intentresolver/chooser/SelectableTargetInfo.java6
-rw-r--r--java/src/com/android/intentresolver/chooser/TargetInfo.java6
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