Commit 8246b316 authored by benwgold's avatar benwgold Committed by Commit Bot

Resubmit change 1792239 making change to test setup to resolve flaky test.

Original Description:
Pass signed in account name when available along with the deeplink intent.

Also pass an indicator about whether incognito mode is available and do
not send account name if enabled.

Finally, refactor image sharing to separate out image retrieval from sharing functionality and use callbacks to get cleaner separation between lens and other sharing use cases.


Remove unnecessary sign in setup


Format


Add error strings to lens asserts


Add test coverage for both signed-in and signed-out cases


Rebase and resolve conflicts


Format


Remove unused import


Remove unused imports. Fix comments.


Add comments and update tests


Initial prototype of code to pass account information with intent.


Testing fetching account + incognito status

Bug: 1002247
Change-Id: I2f32270d3b1afaaa044db224c8b29d92f4951700
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1807607Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Commit-Queue: Ben Goldberger <benwgold@google.com>
Cr-Commit-Position: refs/heads/master@{#696981}
parent 7c4091c8
......@@ -545,7 +545,7 @@ public class ChromeContextMenuPopulator implements ContextMenuPopulator {
ShareHelper.share(linkShareParams);
} else if (itemId == R.id.contextmenu_search_with_google_lens) {
ContextMenuUma.record(params, ContextMenuUma.Action.SEARCH_WITH_GOOGLE_LENS);
helper.searchWithGoogleLens();
helper.searchWithGoogleLens(mDelegate.isIncognito());
} else if (itemId == R.id.contextmenu_search_by_image) {
ContextMenuUma.record(params, ContextMenuUma.Action.SEARCH_BY_IMAGE);
helper.searchForImage();
......
......@@ -5,11 +5,10 @@
package org.chromium.chrome.browser.contextmenu;
import android.app.Activity;
import android.content.ComponentName;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.Nullable;
import android.util.Pair;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
......@@ -188,9 +187,12 @@ public class ContextMenuHelper implements OnCreateContextMenuListener {
/**
* Search for the image by intenting to the lens app with the image data attached.
* @param isIncognito Whether the image to search came from an incognito context.
*/
public void searchWithGoogleLens() {
shareImageDirectly(null, /* shareWithGoogleLens = */ true);
public void searchWithGoogleLens(boolean isIncognito) {
retrieveImage((Uri imageUri) -> {
ShareHelper.shareImageWithGoogleLens(mActivity, imageUri, isIncognito);
});
}
/**
......@@ -207,36 +209,37 @@ public class ContextMenuHelper implements OnCreateContextMenuListener {
* it will use the right activity set when the menu was displayed.
*/
void shareImage() {
shareImageDirectly(null, /* shareWithGoogleLens = */ false);
retrieveImage((Uri imageUri) -> { ShareHelper.shareImage(mActivity, null, imageUri); });
}
/**
* Share the image that triggered the current context menu with the last app used to share.
*/
private void shareImageWithLastShareComponent() {
shareImageDirectly(
ShareHelper.getLastShareComponentName(null), /* shareWithGoogleLens = */ false);
retrieveImage((Uri imageUri) -> {
ShareHelper.shareImage(
mActivity, ShareHelper.getLastShareComponentName(null), imageUri);
});
}
/**
* Share image triggered with the current context menu directly with a specific app.
* @param name The {@link ComponentName} of the app to share the image directly with.
* @param shareWithGoogleLens Whether to share with the Google Lens (overrides the
* value specified in the ComponentName field).
* Retrieves a URI for the selected image for sharing with external apps. If the function fails
* to retrieve the image bytes or generate a URI the callback will *not* be called.
* @param callback Called once the the image is generated and ready to be shared.
*/
private void shareImageDirectly(
@Nullable final ComponentName name, boolean shareWithGoogleLens) {
private void retrieveImage(Callback<Uri> callback) {
if (mNativeContextMenuHelper == 0) return;
Callback<byte[]> callback = new Callback<byte[]>() {
Callback<byte[]> imageRetrievalCallback = new Callback<byte[]>() {
@Override
public void onResult(byte[] result) {
if (mActivity == null) return;
ShareHelper.shareImage(mActivity, result, name, shareWithGoogleLens);
ShareHelper.generateUriFromData(mActivity, result, callback);
}
};
ContextMenuHelperJni.get().retrieveImageForShare(mNativeContextMenuHelper,
ContextMenuHelper.this, callback, MAX_SHARE_DIMEN_PX, MAX_SHARE_DIMEN_PX);
ContextMenuHelper.this, imageRetrievalCallback, MAX_SHARE_DIMEN_PX,
MAX_SHARE_DIMEN_PX);
}
/**
......
......@@ -15,6 +15,7 @@ import android.text.TextUtils;
import org.chromium.base.ContextUtils;
import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.IntentHandler;
import org.chromium.components.signin.ChromeSigninController;
/**
* This class provides utilities for intenting into Google Lens.
......@@ -22,6 +23,8 @@ import org.chromium.chrome.browser.IntentHandler;
public class LensUtils {
private static final String LENS_CONTRACT_URI = "googleapp://lens";
private static final String LENS_BITMAP_URI_KEY = "LensBitmapUriKey";
private static final String ACCOUNT_NAME_URI_KEY = "AccountNameUriKey";
private static final String INCOGNITO_URI_KEY = "IncognitoUriKey";
private static final String MIN_AGSA_VERSION_FEATURE_PARAM_NAME = "minAgsaVersionName";
private static final String MIN_AGSA_VERSION_NAME_FOR_LENS_POSTCAPTURE = "8.19";
......@@ -35,7 +38,8 @@ public class LensUtils {
public static String getLensActivityVersionNameIfAvailable(Context context) {
try {
PackageManager pm = context.getPackageManager();
Intent lensIntent = getShareWithGoogleLensIntent(Uri.EMPTY);
// No data transmission occurring so safe to assume incognito is false.
Intent lensIntent = getShareWithGoogleLensIntent(Uri.EMPTY, /* isIncognito= */ false);
ComponentName lensActivity = lensIntent.resolveActivity(pm);
if (lensActivity == null) return "";
PackageInfo packageInfo = pm.getPackageInfo(lensActivity.getPackageName(), 0);
......@@ -111,14 +115,22 @@ public class LensUtils {
* Get a deeplink intent to Google Lens with an optional content provider image URI.
* @param imageUri The content provider URI generated by chrome (or empty URI)
* if only resolving the activity.
* @param isIncognito Whether the current tab is in incognito mode.
* @return The intent to Google Lens.
*/
public static Intent getShareWithGoogleLensIntent(Uri imageUri) {
public static Intent getShareWithGoogleLensIntent(Uri imageUri, boolean isIncognito) {
String signedInAccountName = ChromeSigninController.get().getSignedInAccountName();
// If incognito do not send the account name to avoid leaking session information to Lens.
if (signedInAccountName == null || isIncognito) signedInAccountName = "";
Uri lensUri = Uri.parse(LENS_CONTRACT_URI);
if (!Uri.EMPTY.equals(imageUri)) {
lensUri = lensUri.buildUpon()
.appendQueryParameter(LENS_BITMAP_URI_KEY, imageUri.toString())
.build();
lensUri =
lensUri.buildUpon()
.appendQueryParameter(LENS_BITMAP_URI_KEY, imageUri.toString())
.appendQueryParameter(ACCOUNT_NAME_URI_KEY, signedInAccountName)
.appendQueryParameter(INCOGNITO_URI_KEY, Boolean.toString(isIncognito))
.build();
ContextUtils.getApplicationContext().grantUriPermission(
IntentHandler.PACKAGE_GSA, imageUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
......
......@@ -316,16 +316,16 @@ public class ShareHelper {
}
/**
* Trigger the share action for the given image data.
* Generate a temporary URI for a set of JPEG bytes and provide that URI to a callback for
* sharing.
* @param activity The activity used to trigger the share action.
* @param jpegImageData The image data to be shared in jpeg format.
* @param name When this is not null, it will share the image directly with the
* {@link ComponentName}
* @param shareWithGoogleLens When this is true activate a special intent
* to Google Lens and ignore the value set in 'name'.
* @param callback A provided callback function which will act on the generated URI.
*/
public static void shareImage(final Activity activity, final byte[] jpegImageData,
final ComponentName name, final boolean shareWithGoogleLens) {
public static void generateUriFromData(
final Activity activity, final byte[] jpegImageData, Callback<Uri> callback) {
if (jpegImageData.length == 0) {
Log.w(TAG, "Share failed -- Received image contains no data.");
return;
......@@ -360,35 +360,53 @@ public class ShareHelper {
@Override
protected void onPostExecute(Uri imageUri) {
if (imageUri == null) return;
if (imageUri == null) {
return;
}
if (ApplicationStatus.getStateForApplication()
!= ApplicationState.HAS_DESTROYED_ACTIVITIES) {
Intent shareIntent;
if (shareWithGoogleLens) {
shareIntent = LensUtils.getShareWithGoogleLensIntent(imageUri);
fireIntent(activity, shareIntent, true);
} else {
shareIntent = getShareImageIntent(imageUri);
if (name == null) {
if (TargetChosenReceiver.isSupported()) {
TargetChosenReceiver.sendChooserIntent(
true, activity, shareIntent, null, null);
} else {
Intent chooserIntent = Intent.createChooser(shareIntent,
activity.getString(R.string.share_link_chooser_title));
fireIntent(activity, chooserIntent, false);
}
} else {
shareIntent.setComponent(name);
fireIntent(activity, shareIntent, false);
}
}
== ApplicationState.HAS_DESTROYED_ACTIVITIES) {
return;
}
callback.onResult(imageUri);
}
}.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
}
/**
* Share an image URI with an activity identified by the provided Component Name.
* @param activity The current activity
* @param name The component name of the activity to share the image with.
* @param imageUri The url to share with the external activity.
*/
public static void shareImage(final Activity activity, final ComponentName name, Uri imageUri) {
Intent shareIntent = getShareImageIntent(imageUri);
if (name == null) {
if (TargetChosenReceiver.isSupported()) {
TargetChosenReceiver.sendChooserIntent(true, activity, shareIntent, null, null);
} else {
Intent chooserIntent = Intent.createChooser(
shareIntent, activity.getString(R.string.share_link_chooser_title));
fireIntent(activity, chooserIntent, false);
}
} else {
shareIntent.setComponent(name);
fireIntent(activity, shareIntent, false);
}
}
/**
* Share an image URI with Google Lens.
* @param activity The current activity
* @param imageUri The url to share with the app.
* @param isIncognito Whether the current tab is in incognito mode.
*/
public static void shareImageWithGoogleLens(
final Activity activity, Uri imageUri, boolean isIncognito) {
Intent shareIntent = LensUtils.getShareWithGoogleLensIntent(imageUri, isIncognito);
fireIntent(activity, shareIntent, /* allowIdentification= */ true);
}
private static class ExternallyVisibleUriCallback implements Callback<String> {
private Callback<Uri> mComposedCallback;
ExternallyVisibleUriCallback(Callback<Uri> cb) {
......
......@@ -19,6 +19,8 @@ import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.test.ChromeBrowserTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.util.browser.Features;
import org.chromium.chrome.test.util.browser.signin.SigninTestUtil;
import org.chromium.components.signin.ChromeSigninController;
/**
* Tests of {@link LensUtils}.
......@@ -95,19 +97,86 @@ public class LensUtilsTest {
}
/**
* Test {@link LensUtils#getShareWithGoogleLensIntent()} method.
* Test {@link LensUtils#getShareWithGoogleLensIntent()} method when user is signed in.
*/
@Test
@SmallTest
public void getShareWithGoogleLensIntentTest() {
Intent intentNoUri = LensUtils.getShareWithGoogleLensIntent(Uri.EMPTY);
Assert.assertEquals("googleapp://lens", intentNoUri.getData().toString());
Assert.assertEquals(Intent.ACTION_VIEW, intentNoUri.getAction());
public void getShareWithGoogleLensIntentSignedInTest() {
SigninTestUtil.addAndSignInTestAccount();
Assert.assertEquals("Chrome should be signed into the test account", "test@gmail.com",
ChromeSigninController.get().getSignedInAccountName());
Intent intentNoUri =
LensUtils.getShareWithGoogleLensIntent(Uri.EMPTY, /* isIncognito= */ false);
Assert.assertEquals("Intent without image has incorrect URI", "googleapp://lens",
intentNoUri.getData().toString());
Assert.assertEquals("Intent without image has incorrect action", Intent.ACTION_VIEW,
intentNoUri.getAction());
final String contentUrl = "content://image-url";
Intent intentWithContentUri = LensUtils.getShareWithGoogleLensIntent(
Uri.parse(contentUrl), /* isIncognito= */ false);
Assert.assertEquals("Intent with image has incorrect URI",
"googleapp://lens?LensBitmapUriKey=content%3A%2F%2Fimage-url&AccountNameUriKey="
+ "test%40gmail.com&IncognitoUriKey=false",
intentWithContentUri.getData().toString());
Assert.assertEquals("Intent with image has incorrect action", Intent.ACTION_VIEW,
intentWithContentUri.getAction());
}
/**
* Test {@link LensUtils#getShareWithGoogleLensIntent()} method when user is incognito.
*/
@Test
@SmallTest
public void getShareWithGoogleLensIntentIncognitoTest() {
SigninTestUtil.addAndSignInTestAccount();
Assert.assertEquals("Chrome should be signed into the test account", "test@gmail.com",
ChromeSigninController.get().getSignedInAccountName());
Intent intentNoUri =
LensUtils.getShareWithGoogleLensIntent(Uri.EMPTY, /* isIncognito= */ true);
Assert.assertEquals("Intent without image has incorrect URI", "googleapp://lens",
intentNoUri.getData().toString());
Assert.assertEquals("Intent without image has incorrect action", Intent.ACTION_VIEW,
intentNoUri.getAction());
final String contentUrl = "content://image-url";
Intent intentWithContentUri = LensUtils.getShareWithGoogleLensIntent(
Uri.parse(contentUrl), /* isIncognito= */ true);
// The account name should not be included in the intent because the uesr is incognito.
Assert.assertEquals("Intent with image has incorrect URI",
"googleapp://lens?LensBitmapUriKey=content%3A%2F%2Fimage-url&AccountNameUriKey="
+ "&IncognitoUriKey=true",
intentWithContentUri.getData().toString());
Assert.assertEquals("Intent with image has incorrect action", Intent.ACTION_VIEW,
intentWithContentUri.getAction());
}
/**
* Test {@link LensUtils#getShareWithGoogleLensIntent()} method when user is not signed in.
*/
@Test
@SmallTest
public void getShareWithGoogleLensIntentNotSignedInTest() {
Assert.assertNull("Chrome should not be signed in",
ChromeSigninController.get().getSignedInAccountName());
Intent intentNoUri =
LensUtils.getShareWithGoogleLensIntent(Uri.EMPTY, /* isIncognito= */ false);
Assert.assertEquals("Intent without image has incorrect URI", "googleapp://lens",
intentNoUri.getData().toString());
Assert.assertEquals("Intent without image has incorrect action", Intent.ACTION_VIEW,
intentNoUri.getAction());
final String contentUrl = "content://image-url";
Intent intentWithContentUri = LensUtils.getShareWithGoogleLensIntent(Uri.parse(contentUrl));
Assert.assertEquals("googleapp://lens?LensBitmapUriKey=content%3A%2F%2Fimage-url",
Intent intentWithContentUri = LensUtils.getShareWithGoogleLensIntent(
Uri.parse(contentUrl), /* isIncognito= */ false);
Assert.assertEquals("Intent with image has incorrect URI",
"googleapp://lens?LensBitmapUriKey=content%3A%2F%2Fimage-url&AccountNameUriKey="
+ "&IncognitoUriKey=false",
intentWithContentUri.getData().toString());
Assert.assertEquals(Intent.ACTION_VIEW, intentWithContentUri.getAction());
Assert.assertEquals("Intent with image has incorrect action", Intent.ACTION_VIEW,
intentWithContentUri.getAction());
}
}
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment