Commit e86e1ae7 authored by Yu Su's avatar Yu Su Committed by Commit Bot

Refactor Lens chip code

- Decouple Lens Chip onClick from Lens Shopping Menu item selection.
- Switch to use #queryImage in LensAsyncManager. Will deprecate #classifyImage in a follow up cl.
- Support using the Lens intent type in Lens Prime API callback when creating Lens deeplink intent.

Change-Id: I541d080e4ec269fee4577153183037f15e78f576
Bug: 1099982, b/170126006
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2463455
Commit-Queue: Yu Su <yusuyoutube@google.com>
Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Reviewed-by: default avatarSinan Sahin <sinansahin@google.com>
Cr-Commit-Position: refs/heads/master@{#819876}
parent 11aaf149
...@@ -38,6 +38,7 @@ import org.chromium.chrome.browser.feature_engagement.TrackerFactory; ...@@ -38,6 +38,7 @@ import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
import org.chromium.chrome.browser.firstrun.FirstRunStatus; import org.chromium.chrome.browser.firstrun.FirstRunStatus;
import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.gsa.GSAState; import org.chromium.chrome.browser.gsa.GSAState;
import org.chromium.chrome.browser.lens.LensQueryResult;
import org.chromium.chrome.browser.locale.LocaleManager; import org.chromium.chrome.browser.locale.LocaleManager;
import org.chromium.chrome.browser.metrics.UkmRecorder; import org.chromium.chrome.browser.metrics.UkmRecorder;
import org.chromium.chrome.browser.performance_hints.PerformanceHintsObserver; import org.chromium.chrome.browser.performance_hints.PerformanceHintsObserver;
...@@ -85,6 +86,9 @@ public class ChromeContextMenuPopulator implements ContextMenuPopulator { ...@@ -85,6 +86,9 @@ public class ChromeContextMenuPopulator implements ContextMenuPopulator {
private final Supplier<ShareDelegate> mShareDelegateSupplier; private final Supplier<ShareDelegate> mShareDelegateSupplier;
private final ExternalAuthUtils mExternalAuthUtils; private final ExternalAuthUtils mExternalAuthUtils;
private final ContextMenuParams mParams; private final ContextMenuParams mParams;
// A predefined LensQueryResult used for Lens Shopping context menu item selection.
private final LensQueryResult mLensQueryResultWithShoppingItent =
(new LensQueryResult.Builder()).withIsShoppyIntent(true).build();
private boolean mEnableLensWithSearchByImageText; private boolean mEnableLensWithSearchByImageText;
private @Nullable UkmRecorder.Bridge mUkmRecorderBridge; private @Nullable UkmRecorder.Bridge mUkmRecorderBridge;
private ContextMenuNativeDelegate mNativeDelegate; private ContextMenuNativeDelegate mNativeDelegate;
...@@ -584,6 +588,11 @@ public class ChromeContextMenuPopulator implements ContextMenuPopulator { ...@@ -584,6 +588,11 @@ public class ChromeContextMenuPopulator implements ContextMenuPopulator {
return mItemDelegate.isIncognito(); return mItemDelegate.isIncognito();
} }
@Override
public String getPageTitle() {
return mItemDelegate.getPageTitle();
}
@Override @Override
public boolean onItemSelected(int itemId) { public boolean onItemSelected(int itemId) {
if (itemId == R.id.contextmenu_open_in_new_tab) { if (itemId == R.id.contextmenu_open_in_new_tab) {
...@@ -680,35 +689,33 @@ public class ChromeContextMenuPopulator implements ContextMenuPopulator { ...@@ -680,35 +689,33 @@ public class ChromeContextMenuPopulator implements ContextMenuPopulator {
ShareHelper.shareWithLastUsedComponent(shareParams); ShareHelper.shareWithLastUsedComponent(shareParams);
} else if (itemId == R.id.contextmenu_search_with_google_lens) { } else if (itemId == R.id.contextmenu_search_with_google_lens) {
recordContextMenuSelection(ContextMenuUma.Action.SEARCH_WITH_GOOGLE_LENS); recordContextMenuSelection(ContextMenuUma.Action.SEARCH_WITH_GOOGLE_LENS);
searchWithGoogleLens(mItemDelegate.isIncognito()); searchWithGoogleLens(/*requiresConfirmation=*/false, /*lensQueryResult=*/null);
SharedPreferencesManager prefManager = SharedPreferencesManager.getInstance(); SharedPreferencesManager prefManager = SharedPreferencesManager.getInstance();
prefManager.writeBoolean( prefManager.writeBoolean(
ChromePreferenceKeys.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS_CLICKED, true); ChromePreferenceKeys.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS_CLICKED, true);
} else if (itemId == R.id.contextmenu_search_by_image) { } else if (itemId == R.id.contextmenu_search_by_image) {
if (mEnableLensWithSearchByImageText) { if (mEnableLensWithSearchByImageText) {
recordContextMenuSelection(ContextMenuUma.Action.SEARCH_WITH_GOOGLE_LENS); recordContextMenuSelection(ContextMenuUma.Action.SEARCH_WITH_GOOGLE_LENS);
searchWithGoogleLens(mItemDelegate.isIncognito()); searchWithGoogleLens(/*requiresConfirmation=*/false, /*lensQueryResult=*/null);
} else { } else {
recordContextMenuSelection(ContextMenuUma.Action.SEARCH_BY_IMAGE); recordContextMenuSelection(ContextMenuUma.Action.SEARCH_BY_IMAGE);
mNativeDelegate.searchForImage(); mNativeDelegate.searchForImage();
} }
} else if (itemId == R.id.contextmenu_shop_similar_products) { } else if (itemId == R.id.contextmenu_shop_similar_products) {
recordContextMenuSelection(ContextMenuUma.Action.SHOP_SIMILAR_PRODUCTS); recordContextMenuSelection(ContextMenuUma.Action.SHOP_SIMILAR_PRODUCTS);
shopWithGoogleLens(mItemDelegate.isIncognito(), searchWithGoogleLens(/*requiresConfirmation=*/true, mLensQueryResultWithShoppingItent);
/*requiresConfirmation=*/true);
SharedPreferencesManager prefManager = SharedPreferencesManager.getInstance(); SharedPreferencesManager prefManager = SharedPreferencesManager.getInstance();
prefManager.writeBoolean( prefManager.writeBoolean(
ChromePreferenceKeys.CONTEXT_MENU_SHOP_SIMILAR_PRODUCTS_CLICKED, true); ChromePreferenceKeys.CONTEXT_MENU_SHOP_SIMILAR_PRODUCTS_CLICKED, true);
} else if (itemId == R.id.contextmenu_shop_image_with_google_lens) { } else if (itemId == R.id.contextmenu_shop_image_with_google_lens) {
recordContextMenuSelection(ContextMenuUma.Action.SHOP_IMAGE_WITH_GOOGLE_LENS); recordContextMenuSelection(ContextMenuUma.Action.SHOP_IMAGE_WITH_GOOGLE_LENS);
shopWithGoogleLens(mItemDelegate.isIncognito(), /*requiresConfirmation=*/false); searchWithGoogleLens(/*requiresConfirmation=*/false, mLensQueryResultWithShoppingItent);
SharedPreferencesManager prefManager = SharedPreferencesManager.getInstance(); SharedPreferencesManager prefManager = SharedPreferencesManager.getInstance();
prefManager.writeBoolean( prefManager.writeBoolean(
ChromePreferenceKeys.CONTEXT_MENU_SHOP_IMAGE_WITH_GOOGLE_LENS_CLICKED, true); ChromePreferenceKeys.CONTEXT_MENU_SHOP_IMAGE_WITH_GOOGLE_LENS_CLICKED, true);
} else if (itemId == R.id.contextmenu_search_similar_products) { } else if (itemId == R.id.contextmenu_search_similar_products) {
recordContextMenuSelection(ContextMenuUma.Action.SEARCH_SIMILAR_PRODUCTS); recordContextMenuSelection(ContextMenuUma.Action.SEARCH_SIMILAR_PRODUCTS);
shopWithGoogleLens(mItemDelegate.isIncognito(), searchWithGoogleLens(/*requiresConfirmation=*/true, mLensQueryResultWithShoppingItent);
/*requiresConfirmation=*/true);
SharedPreferencesManager prefManager = SharedPreferencesManager.getInstance(); SharedPreferencesManager prefManager = SharedPreferencesManager.getInstance();
prefManager.writeBoolean( prefManager.writeBoolean(
ChromePreferenceKeys.CONTEXT_MENU_SEARCH_SIMILAR_PRODUCTS_CLICKED, true); ChromePreferenceKeys.CONTEXT_MENU_SEARCH_SIMILAR_PRODUCTS_CLICKED, true);
...@@ -767,31 +774,6 @@ public class ChromeContextMenuPopulator implements ContextMenuPopulator { ...@@ -767,31 +774,6 @@ public class ChromeContextMenuPopulator implements ContextMenuPopulator {
ContextMenuImageFormat.ORIGINAL, mItemDelegate::onSaveImageToClipboard); ContextMenuImageFormat.ORIGINAL, mItemDelegate::onSaveImageToClipboard);
} }
/**
* 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.
*/
private void searchWithGoogleLens(boolean isIncognito) {
mNativeDelegate.retrieveImageForShare(ContextMenuImageFormat.PNG, (Uri imageUri) -> {
ShareHelper.shareImageWithGoogleLens(getWindow(), imageUri, isIncognito,
mParams.getSrcUrl(), mParams.getTitleText(),
/* isShoppyImage*/ false, /* requiresConfirmation*/ false);
});
}
/**
* 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.
* @param requiresConfirmation Whether the request requires an account dialog.
*/
private void shopWithGoogleLens(boolean isIncognito, boolean requiresConfirmation) {
mNativeDelegate.retrieveImageForShare(ContextMenuImageFormat.PNG, (Uri imageUri) -> {
ShareHelper.shareImageWithGoogleLens(getWindow(), imageUri, isIncognito,
mParams.getSrcUrl(), mParams.getTitleText(), /* isShoppyImage*/ true,
requiresConfirmation);
});
}
/** /**
* Share the image that triggered the current context menu. * Share the image that triggered the current context menu.
* Package-private, allowing access only from the context menu item to ensure that * Package-private, allowing access only from the context menu item to ensure that
...@@ -826,6 +808,20 @@ public class ChromeContextMenuPopulator implements ContextMenuPopulator { ...@@ -826,6 +808,20 @@ public class ChromeContextMenuPopulator implements ContextMenuPopulator {
return TemplateUrlServiceFactory.get(); return TemplateUrlServiceFactory.get();
} }
/**
* Search for the image by intenting to the lens app with the image data attached.
* @param requiresConfirmation Whether the request requires an account dialog.
* @param lensQueryResult A wrapper object which contains the results for the Lens image query.
*/
protected void searchWithGoogleLens(
boolean requiresConfirmation, @Nullable LensQueryResult lensQueryResult) {
mNativeDelegate.retrieveImageForShare(ContextMenuImageFormat.PNG, (Uri imageUri) -> {
ShareHelper.shareImageWithGoogleLens(getWindow(), imageUri, mItemDelegate.isIncognito(),
mParams.getSrcUrl(), mParams.getTitleText(), lensQueryResult,
requiresConfirmation);
});
}
/** /**
* Checks whether a url is empty or blank. * Checks whether a url is empty or blank.
* @param url The url need to be checked. * @param url The url need to be checked.
......
...@@ -15,6 +15,8 @@ import org.chromium.base.annotations.NativeMethods; ...@@ -15,6 +15,8 @@ import org.chromium.base.annotations.NativeMethods;
import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.task.PostTask; import org.chromium.base.task.PostTask;
import org.chromium.chrome.browser.lens.LensController; import org.chromium.chrome.browser.lens.LensController;
import org.chromium.chrome.browser.lens.LensQueryParams;
import org.chromium.chrome.browser.lens.LensQueryResult;
import org.chromium.chrome.browser.performance_hints.PerformanceHintsObserver; import org.chromium.chrome.browser.performance_hints.PerformanceHintsObserver;
import org.chromium.chrome.browser.share.LensUtils; import org.chromium.chrome.browser.share.LensUtils;
import org.chromium.components.embedder_support.contextmenu.ContextMenuParams; import org.chromium.components.embedder_support.contextmenu.ContextMenuParams;
...@@ -48,6 +50,7 @@ public class ContextMenuHelper { ...@@ -48,6 +50,7 @@ public class ContextMenuHelper {
private long mMenuShownTimeMs; private long mMenuShownTimeMs;
private boolean mSelectedItemBeforeDismiss; private boolean mSelectedItemBeforeDismiss;
private boolean mIsIncognito; private boolean mIsIncognito;
private String mPageTitle;
private ContextMenuHelper(long nativeContextMenuHelper, WebContents webContents) { private ContextMenuHelper(long nativeContextMenuHelper, WebContents webContents) {
mNativeContextMenuHelper = nativeContextMenuHelper; mNativeContextMenuHelper = nativeContextMenuHelper;
...@@ -106,6 +109,7 @@ public class ContextMenuHelper { ...@@ -106,6 +109,7 @@ public class ContextMenuHelper {
mCurrentPopulator = mPopulatorFactory.createContextMenuPopulator( mCurrentPopulator = mPopulatorFactory.createContextMenuPopulator(
windowAndroid.getActivity().get(), params, mCurrentNativeDelegate); windowAndroid.getActivity().get(), params, mCurrentNativeDelegate);
mIsIncognito = mCurrentPopulator.isIncognito(); mIsIncognito = mCurrentPopulator.isIncognito();
mPageTitle = mCurrentPopulator.getPageTitle();
mCurrentContextMenuParams = params; mCurrentContextMenuParams = params;
mWindow = windowAndroid; mWindow = windowAndroid;
mCallback = (result) -> { mCallback = (result) -> {
...@@ -148,9 +152,18 @@ public class ContextMenuHelper { ...@@ -148,9 +152,18 @@ public class ContextMenuHelper {
// latency impact. // latency impact.
if (LensUtils.enableShoppyImageMenuItem()) { if (LensUtils.enableShoppyImageMenuItem()) {
Callback<Uri> callback = (Uri uri) -> { Callback<Uri> callback = (Uri uri) -> {
LensController.getInstance().classifyImage(uri, LensQueryParams lensQueryParams =
(Boolean isShoppyImage) (new LensQueryParams.Builder())
-> displayRevampedContextMenu(topContentOffsetPx, isShoppyImage)); .withImageUri(uri)
.withPageUrl(mCurrentContextMenuParams.getPageUrl())
.withImageTitleOrAltText(mCurrentContextMenuParams.getTitleText())
.build();
LensController.getInstance().queryImage(lensQueryParams,
(LensQueryResult lensQueryResult)
-> displayRevampedContextMenu(topContentOffsetPx,
(lensQueryResult.getIsShoppyIntent()
|| LensUtils.isLensShoppingIntentType(
lensQueryResult.getLensIntentType()))));
}; };
mCurrentNativeDelegate.retrieveImageForShare(ContextMenuImageFormat.ORIGINAL, callback); mCurrentNativeDelegate.retrieveImageForShare(ContextMenuImageFormat.ORIGINAL, callback);
} else { } else {
...@@ -171,8 +184,8 @@ public class ContextMenuHelper { ...@@ -171,8 +184,8 @@ public class ContextMenuHelper {
mCurrentContextMenu = menuCoordinator; mCurrentContextMenu = menuCoordinator;
if (LensUtils.enableImageChip(mIsIncognito)) { if (LensUtils.enableImageChip(mIsIncognito)) {
LensAsyncManager lensAsyncManager = LensAsyncManager lensAsyncManager = new LensAsyncManager(mCurrentContextMenuParams,
new LensAsyncManager(mCurrentContextMenuParams, mCurrentNativeDelegate); mCurrentNativeDelegate, mWindow, mIsIncognito, mPageTitle);
menuCoordinator.displayMenuWithLensChip(mWindow, mWebContents, menuCoordinator.displayMenuWithLensChip(mWindow, mWebContents,
mCurrentContextMenuParams, items, mCallback, mOnMenuShown, mOnMenuClosed, mCurrentContextMenuParams, items, mCallback, mOnMenuShown, mOnMenuClosed,
lensAsyncManager); lensAsyncManager);
......
...@@ -8,8 +8,13 @@ import android.net.Uri; ...@@ -8,8 +8,13 @@ import android.net.Uri;
import org.chromium.base.Callback; import org.chromium.base.Callback;
import org.chromium.chrome.browser.lens.LensController; import org.chromium.chrome.browser.lens.LensController;
import org.chromium.chrome.browser.lens.LensQueryParams;
import org.chromium.chrome.browser.lens.LensQueryResult;
import org.chromium.chrome.browser.share.ShareHelper;
import org.chromium.components.embedder_support.contextmenu.ContextMenuParams; import org.chromium.components.embedder_support.contextmenu.ContextMenuParams;
import org.chromium.ui.base.WindowAndroid;
// TODO(b/170970926): Move LensAsyncManager to the private code repository.
/** /**
* Manage requests to the Lens SDK which may be asynchronous. * Manage requests to the Lens SDK which may be asynchronous.
*/ */
...@@ -18,28 +23,58 @@ class LensAsyncManager { ...@@ -18,28 +23,58 @@ class LensAsyncManager {
private ContextMenuParams mParams; private ContextMenuParams mParams;
private ContextMenuNativeDelegate mNativeDelegate; private ContextMenuNativeDelegate mNativeDelegate;
private LensQueryResult mLastCompletedQueryResult;
private WindowAndroid mWindow;
private boolean mIsIncognito;
private String mPageTitle;
/** /**
* Construct a lens async manager. * Construct a lens async manager.
* @param params Context menu params used to retrieve additional metadata. * @param params Context menu params used to retrieve additional metadata.
* @param nativeDelegate {@link ContextMenuNativeDelegate} used to retrieve image bytes. * @param nativeDelegate {@link ContextMenuNativeDelegate} used to retrieve image bytes.
* @param window The current window.
* @param isIncognito Whether the current tab is in incognito mode.
*/ */
public LensAsyncManager(ContextMenuParams params, ContextMenuNativeDelegate nativeDelegate) { public LensAsyncManager(ContextMenuParams params, ContextMenuNativeDelegate nativeDelegate,
WindowAndroid window, boolean isIncognito, String pageTitle) {
mParams = params; mParams = params;
mNativeDelegate = nativeDelegate; mNativeDelegate = nativeDelegate;
mWindow = window;
mIsIncognito = isIncognito;
mPageTitle = pageTitle;
} }
/** /**
* Make a lens classification call for the current render frame. * Make a Lens image query for the current render frame.
* @param replyCallback The function to callback with the classification. * @param replyCallback The function to callback with the query result.
*/ */
public void classifyImageAsync(Callback<Boolean> replyCallback) { public void queryImageAsync(Callback<LensQueryResult> replyCallback) {
Callback<Uri> callback = (uri) Callback<Uri> callback = (uri) -> {
-> LensController.getInstance().classifyImage(uri, mParams.getPageUrl(), LensQueryParams lensQueryParams =
mParams.getTitleText(), (isClassificationSuccessful) -> { (new LensQueryParams.Builder())
replyCallback.onResult(isClassificationSuccessful); .withImageUri(uri)
}); .withPageUrl(mParams.getPageUrl())
.withImageTitleOrAltText(mParams.getTitleText())
.withPageTitle(mPageTitle)
.build();
LensController.getInstance().queryImage(lensQueryParams, (lensQueryResult) -> {
mLastCompletedQueryResult = lensQueryResult;
replyCallback.onResult(lensQueryResult);
});
};
// Must occur on UI thread. // Must occur on UI thread.
mNativeDelegate.retrieveImageForShare(ContextMenuImageFormat.ORIGINAL, callback); mNativeDelegate.retrieveImageForShare(ContextMenuImageFormat.ORIGINAL, callback);
} }
/**
* Search with Google Lens with the last completed image query result.
*/
public void searchWithGoogleLens() {
Callback<Uri> callback = (uri) -> {
ShareHelper.shareImageWithGoogleLens(mWindow, uri, mIsIncognito, mParams.getSrcUrl(),
mParams.getTitleText(), mLastCompletedQueryResult,
/* requiresConfirmation*/ false);
};
mNativeDelegate.retrieveImageForShare(ContextMenuImageFormat.PNG, callback);
}
} }
\ No newline at end of file
...@@ -11,10 +11,13 @@ import android.view.LayoutInflater; ...@@ -11,10 +11,13 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.lens.LensQueryResult;
import org.chromium.chrome.browser.share.LensUtils;
import org.chromium.ui.text.SpanApplier; import org.chromium.ui.text.SpanApplier;
import org.chromium.ui.text.SpanApplier.SpanInfo; import org.chromium.ui.text.SpanApplier.SpanInfo;
import org.chromium.ui.widget.AnchoredPopupWindow; import org.chromium.ui.widget.AnchoredPopupWindow;
...@@ -27,9 +30,9 @@ import java.lang.annotation.RetentionPolicy; ...@@ -27,9 +30,9 @@ import java.lang.annotation.RetentionPolicy;
* A controller to handle chip construction and cross-app communication. * A controller to handle chip construction and cross-app communication.
*/ */
class RevampedContextMenuChipController implements View.OnClickListener { class RevampedContextMenuChipController implements View.OnClickListener {
private boolean mFakeLensQueryResultForTesting;
private View mAnchorView; private View mAnchorView;
private LensAsyncManager mLensAsyncManager; private LensAsyncManager mLensAsyncManager;
private Runnable mChipClickedCallback;
private ChipView mChipView; private ChipView mChipView;
private AnchoredPopupWindow mPopupWindow; private AnchoredPopupWindow mPopupWindow;
private Context mContext; private Context mContext;
...@@ -55,14 +58,13 @@ class RevampedContextMenuChipController implements View.OnClickListener { ...@@ -55,14 +58,13 @@ class RevampedContextMenuChipController implements View.OnClickListener {
* @param lensAsyncManager The object responsible for making Lens requests. * @param lensAsyncManager The object responsible for making Lens requests.
* @param chipClickedCallback The callback to fire after a user clicks a lens chip. * @param chipClickedCallback The callback to fire after a user clicks a lens chip.
*/ */
RevampedContextMenuChipController(Context context, View anchorView, RevampedContextMenuChipController(
LensAsyncManager lensAsyncManager, Runnable chipClickedCallback) { Context context, View anchorView, LensAsyncManager lensAsyncManager) {
mContext = context; mContext = context;
mLensAsyncManager = lensAsyncManager; mLensAsyncManager = lensAsyncManager;
mChipClickedCallback = chipClickedCallback;
mAnchorView = anchorView; mAnchorView = anchorView;
mLensAsyncManager.classifyImageAsync( mLensAsyncManager.queryImageAsync(
(isShoppingIntent) -> { handleImageClassification(isShoppingIntent); }); (lensQueryResult) -> { handleImageClassification(lensQueryResult); });
} }
/** /**
...@@ -101,9 +103,24 @@ class RevampedContextMenuChipController implements View.OnClickListener { ...@@ -101,9 +103,24 @@ class RevampedContextMenuChipController implements View.OnClickListener {
R.dimen.context_menu_chip_icon_size)); R.dimen.context_menu_chip_icon_size));
} }
// This method should only be used in test files. It is not marked
// @VisibleForTesting to allow the Coordinator to reference it in its
// own testing methods.
void setFakeLensQueryResultForTesting() {
mFakeLensQueryResultForTesting = true;
}
@VisibleForTesting @VisibleForTesting
void handleImageClassification(boolean isShoppingIntent) { void handleImageClassification(@Nullable LensQueryResult lensQueryResult) {
if (isShoppingIntent) { if (mFakeLensQueryResultForTesting) {
lensQueryResult = (new LensQueryResult.Builder())
.withIsShoppyIntent(true)
.withLensIntentType(LensUtils.getLensShoppingIntentType())
.build();
}
if (lensQueryResult != null && lensQueryResult.getIsShoppyIntent()
|| LensUtils.isLensShoppingIntentType(lensQueryResult.getLensIntentType())) {
showChip(mAnchorView); showChip(mAnchorView);
}; };
} }
...@@ -112,7 +129,7 @@ class RevampedContextMenuChipController implements View.OnClickListener { ...@@ -112,7 +129,7 @@ class RevampedContextMenuChipController implements View.OnClickListener {
public void onClick(View v) { public void onClick(View v) {
if (v == mChipView) { if (v == mChipView) {
recordChipEvent(ChipEvent.CLICKED); recordChipEvent(ChipEvent.CLICKED);
mChipClickedCallback.run(); mLensAsyncManager.searchWithGoogleLens();
dismissLensChipIfShowing(); dismissLensChipIfShowing();
} }
} }
......
...@@ -116,11 +116,7 @@ public class RevampedContextMenuCoordinator implements ContextMenuUi { ...@@ -116,11 +116,7 @@ public class RevampedContextMenuCoordinator implements ContextMenuUi {
if (params.isImage() && lensShoppingFeatureEnabled && !isPopup) { if (params.isImage() && lensShoppingFeatureEnabled && !isPopup) {
View chipAnchorView = layout.findViewById(R.id.context_menu_chip_anchor_point); View chipAnchorView = layout.findViewById(R.id.context_menu_chip_anchor_point);
mChipController = new RevampedContextMenuChipController( mChipController = new RevampedContextMenuChipController(
activity, chipAnchorView, lensAsyncManager, () -> { activity, chipAnchorView, lensAsyncManager);
// A chip selection should trigger the lens shopping action.
clickItem((int) R.id.contextmenu_shop_image_with_google_lens, activity,
onItemClicked);
});
dialogBottomMarginPx = mChipController.getVerticalPxNeededForChip(); dialogBottomMarginPx = mChipController.getVerticalPxNeededForChip();
// Allow dialog to get close to the top of the screen. // Allow dialog to get close to the top of the screen.
dialogTopMarginPx = dialogBottomMarginPx / 2; dialogTopMarginPx = dialogBottomMarginPx / 2;
...@@ -286,7 +282,8 @@ public class RevampedContextMenuCoordinator implements ContextMenuUi { ...@@ -286,7 +282,8 @@ public class RevampedContextMenuCoordinator implements ContextMenuUi {
void simulateShoppyImageClassificationForTesting() { void simulateShoppyImageClassificationForTesting() {
// Don't need to initialize controller because that should be triggered by // Don't need to initialize controller because that should be triggered by
// forcing feature flags. // forcing feature flags.
mChipController.handleImageClassification(true); mChipController.setFakeLensQueryResultForTesting(); // IN-TEST
mChipController.handleImageClassification(null);
} }
// Public only to allow references from RevampedContextMenuUtils.java // Public only to allow references from RevampedContextMenuUtils.java
......
...@@ -13,7 +13,6 @@ import android.net.Uri; ...@@ -13,7 +13,6 @@ import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.text.TextUtils; import android.text.TextUtils;
import androidx.annotation.IntDef;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import org.chromium.base.ContextUtils; import org.chromium.base.ContextUtils;
...@@ -21,6 +20,7 @@ import org.chromium.chrome.browser.IntentHandler; ...@@ -21,6 +20,7 @@ import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.externalauth.ExternalAuthUtils; import org.chromium.chrome.browser.externalauth.ExternalAuthUtils;
import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.lens.LensController; import org.chromium.chrome.browser.lens.LensController;
import org.chromium.chrome.browser.lens.LensQueryResult;
import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.signin.IdentityServicesProvider; import org.chromium.chrome.browser.signin.IdentityServicesProvider;
import org.chromium.components.signin.base.CoreAccountInfo; import org.chromium.components.signin.base.CoreAccountInfo;
...@@ -61,7 +61,7 @@ public class LensUtils { ...@@ -61,7 +61,7 @@ public class LensUtils {
"orderShareImageBeforeLens"; "orderShareImageBeforeLens";
private static final String MIN_AGSA_VERSION_NAME_FOR_LENS_POSTCAPTURE = "10.65"; private static final String MIN_AGSA_VERSION_NAME_FOR_LENS_POSTCAPTURE = "10.65";
private static final String MIN_AGSA_VERSION_NAME_FOR_LENS_CHROME_SHOPPING_INTENT = "11.16"; private static final String MIN_AGSA_VERSION_NAME_FOR_LENS_CHROME_SHOPPING_INTENT = "11.16";
private static final String LENS_INTENT_TYPE_LENS_CHROME_SHOPPING = "18"; private static final int LENS_INTENT_TYPE_LENS_CHROME_SHOPPING = 18;
private static final String LENS_SHOPPING_FEATURE_FLAG_VARIANT_NAME = "lensShopVariation"; private static final String LENS_SHOPPING_FEATURE_FLAG_VARIANT_NAME = "lensShopVariation";
private static final String LENS_DEFAULT_SHOPPING_URL_PATTERNS = private static final String LENS_DEFAULT_SHOPPING_URL_PATTERNS =
"^https://www.google.com/shopping/.*|^https://www.google.com/.*tbm=shop.*"; "^https://www.google.com/shopping/.*|^https://www.google.com/.*tbm=shop.*";
...@@ -72,15 +72,6 @@ public class LensUtils { ...@@ -72,15 +72,6 @@ public class LensUtils {
private static boolean sFakePassableLensEnvironmentForTesting; private static boolean sFakePassableLensEnvironmentForTesting;
private static boolean sFakeImageUrlInShoppingAllowlistForTesting; private static boolean sFakeImageUrlInShoppingAllowlistForTesting;
private static String sFakeVariationsForTesting; private static String sFakeVariationsForTesting;
/** Supported Lens intent types. */
@IntDef({
IntentType.DEFAULT,
IntentType.SHOPPING,
})
public @interface IntentType {
int DEFAULT = 0;
int SHOPPING = 1;
}
/* /*
* If true, short-circuit the version name intent check to always return a high enough version. * If true, short-circuit the version name intent check to always return a high enough version.
...@@ -133,7 +124,7 @@ public class LensUtils { ...@@ -133,7 +124,7 @@ public class LensUtils {
final Intent lensIntent = final Intent lensIntent =
getShareWithGoogleLensIntent(Uri.EMPTY, /* isIncognito= */ false, getShareWithGoogleLensIntent(Uri.EMPTY, /* isIncognito= */ false,
/* currentTimeNanos= */ 0L, /* srcUrl */ "", /* currentTimeNanos= */ 0L, /* srcUrl */ "",
/* titleOrAltText */ "", /* intentType */ IntentType.DEFAULT, /* titleOrAltText */ "", /* lensQueryResult */ null,
/* requiresConfirmation */ false); /* requiresConfirmation */ false);
final ComponentName lensActivity = lensIntent.resolveActivity(pm); final ComponentName lensActivity = lensIntent.resolveActivity(pm);
if (lensActivity == null) return ""; if (lensActivity == null) return "";
...@@ -242,14 +233,14 @@ public class LensUtils { ...@@ -242,14 +233,14 @@ public class LensUtils {
* @param srcUrl The 'src' attribute of the image. * @param srcUrl The 'src' attribute of the image.
* @param titleOrAltText The 'title' or, if empty, the 'alt' attribute of the * @param titleOrAltText The 'title' or, if empty, the 'alt' attribute of the
* image. * image.
* @param intentType The type of the intent. * @param LensQueryResult The image query result returned from Lens Prime API.
* @param requiresConfirmation Whether the request requires an confirmation dialog. * @param requiresConfirmation Whether the request requires an confirmation dialog.
* @return The intent to Google Lens. * @return The intent to Google Lens.
*/ */
public static Intent getShareWithGoogleLensIntent(final Uri imageUri, final boolean isIncognito, public static Intent getShareWithGoogleLensIntent(final Uri imageUri, final boolean isIncognito,
final long currentTimeNanos, final String srcUrl, final String titleOrAltText, final long currentTimeNanos, final String srcUrl, final String titleOrAltText,
@IntentType final int intentType, final boolean requiresConfirmation) { LensQueryResult lensQueryResult, final boolean requiresConfirmation) {
final CoreAccountInfo coreAccountInfo = final CoreAccountInfo coreAccountInfo =
IdentityServicesProvider.get() IdentityServicesProvider.get()
.getIdentityManager(Profile.getLastUsedRegularProfile()) .getIdentityManager(Profile.getLastUsedRegularProfile())
...@@ -270,9 +261,11 @@ public class LensUtils { ...@@ -270,9 +261,11 @@ public class LensUtils {
.appendQueryParameter( .appendQueryParameter(
LAUNCH_TIMESTAMP_URI_KEY, Long.toString(currentTimeNanos)); LAUNCH_TIMESTAMP_URI_KEY, Long.toString(currentTimeNanos));
if (intentType == IntentType.SHOPPING) { if (lensQueryResult != null
&& (lensQueryResult.getIsShoppyIntent()
|| isLensShoppingIntentType(lensQueryResult.getLensIntentType()))) {
lensUriBuilder.appendQueryParameter( lensUriBuilder.appendQueryParameter(
LENS_INTENT_TYPE_KEY, LENS_INTENT_TYPE_LENS_CHROME_SHOPPING); LENS_INTENT_TYPE_KEY, Integer.toString(getLensShoppingIntentType()));
} }
if (requiresConfirmation) { if (requiresConfirmation) {
...@@ -498,6 +491,21 @@ public class LensUtils { ...@@ -498,6 +491,21 @@ public class LensUtils {
return false; return false;
} }
/**
* @return the Lens shopping intent type integer.
*/
public static int getLensShoppingIntentType() {
return LENS_INTENT_TYPE_LENS_CHROME_SHOPPING;
}
/**
* Check if the the intent type is Lens shopping intent type.
* @return true if the intent type is shopping.
*/
public static boolean isLensShoppingIntentType(int intentType) {
return intentType == getLensShoppingIntentType();
}
/** /**
* Check if the uri matches any shopping url patterns. * Check if the uri matches any shopping url patterns.
*/ */
......
...@@ -27,6 +27,7 @@ import org.chromium.base.PackageManagerUtils; ...@@ -27,6 +27,7 @@ import org.chromium.base.PackageManagerUtils;
import org.chromium.base.StrictModeContext; import org.chromium.base.StrictModeContext;
import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.lens.LensQueryResult;
import org.chromium.chrome.browser.preferences.ChromePreferenceKeys; import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
import org.chromium.chrome.browser.preferences.SharedPreferencesManager; import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
import org.chromium.components.browser_ui.share.ShareParams; import org.chromium.components.browser_ui.share.ShareParams;
...@@ -124,15 +125,15 @@ public class ShareHelper extends org.chromium.components.browser_ui.share.ShareH ...@@ -124,15 +125,15 @@ public class ShareHelper extends org.chromium.components.browser_ui.share.ShareH
* @param isIncognito Whether the current tab is in incognito mode. * @param isIncognito Whether the current tab is in incognito mode.
* @param srcUrl The 'src' attribute of the image. * @param srcUrl The 'src' attribute of the image.
* @param titleOrAltText The 'title' or, if empty, the 'alt' attribute of the image. * @param titleOrAltText The 'title' or, if empty, the 'alt' attribute of the image.
* @param isShoppingIntent Whether the Lens intent is a shopping intent. * @param lensQueryResult The wrapper object which contains the classify result of Lens image
* query.
* @param requiresConfirmation Whether the request requires an confirmation dialog. * @param requiresConfirmation Whether the request requires an confirmation dialog.
*/ */
public static void shareImageWithGoogleLens(final WindowAndroid window, Uri imageUri, public static void shareImageWithGoogleLens(final WindowAndroid window, Uri imageUri,
boolean isIncognito, String srcUrl, String titleOrAltText, boolean isShoppingIntent, boolean isIncognito, String srcUrl, String titleOrAltText,
boolean requiresConfirmation) { LensQueryResult lensQueryResult, boolean requiresConfirmation) {
Intent shareIntent = LensUtils.getShareWithGoogleLensIntent(imageUri, isIncognito, Intent shareIntent = LensUtils.getShareWithGoogleLensIntent(imageUri, isIncognito,
SystemClock.elapsedRealtimeNanos(), srcUrl, titleOrAltText, SystemClock.elapsedRealtimeNanos(), srcUrl, titleOrAltText, lensQueryResult,
isShoppingIntent ? LensUtils.IntentType.SHOPPING : LensUtils.IntentType.DEFAULT,
requiresConfirmation); requiresConfirmation);
try { try {
// Pass an empty callback to ensure the triggered activity can identify the source // Pass an empty callback to ensure the triggered activity can identify the source
......
...@@ -74,6 +74,11 @@ public class TabContextMenuItemDelegate implements ContextMenuItemDelegate { ...@@ -74,6 +74,11 @@ public class TabContextMenuItemDelegate implements ContextMenuItemDelegate {
mTab.removeObserver(mDataReductionProxyContextMenuTabObserver); mTab.removeObserver(mDataReductionProxyContextMenuTabObserver);
} }
@Override
public String getPageTitle() {
return mTab.getTitle();
}
@Override @Override
public WebContents getWebContents() { public WebContents getWebContents() {
return mTab.getWebContents(); return mTab.getWebContents();
......
...@@ -57,4 +57,9 @@ public class TabContextMenuPopulator implements ContextMenuPopulator { ...@@ -57,4 +57,9 @@ public class TabContextMenuPopulator implements ContextMenuPopulator {
public boolean isIncognito() { public boolean isIncognito() {
return mPopulator.isIncognito(); return mPopulator.isIncognito();
} }
@Override
public String getPageTitle() {
return mPopulator.getPageTitle();
}
} }
...@@ -21,6 +21,8 @@ import org.mockito.Mock; ...@@ -21,6 +21,8 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.lens.LensQueryResult;
import org.chromium.chrome.browser.share.LensUtils;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.content_public.browser.test.util.TestThreadUtils; import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.ui.test.util.DummyUiActivity; import org.chromium.ui.test.util.DummyUiActivity;
...@@ -68,10 +70,11 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa ...@@ -68,10 +70,11 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa
@SmallTest @SmallTest
public void testChipNotShownWhenCallbackReturnsFalse() { public void testChipNotShownWhenCallbackReturnsFalse() {
RevampedContextMenuChipController chipController = new RevampedContextMenuChipController( RevampedContextMenuChipController chipController = new RevampedContextMenuChipController(
getActivity(), mAnchorView, mLensAsyncManager, () -> {}); getActivity(), mAnchorView, mLensAsyncManager);
TestThreadUtils.runOnUiThreadBlocking( TestThreadUtils.runOnUiThreadBlocking(() -> {
() -> { chipController.handleImageClassification(false); }); chipController.handleImageClassification((new LensQueryResult.Builder()).build());
});
assertNotNull("Anchor view was not initialized.", mAnchorView); assertNotNull("Anchor view was not initialized.", mAnchorView);
assertNull("Popup window was initialized unexpectedly.", assertNull("Popup window was initialized unexpectedly.",
...@@ -82,9 +85,14 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa ...@@ -82,9 +85,14 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa
@SmallTest @SmallTest
public void testChipShownWhenCallbackReturnsTrue() { public void testChipShownWhenCallbackReturnsTrue() {
RevampedContextMenuChipController chipController = new RevampedContextMenuChipController( RevampedContextMenuChipController chipController = new RevampedContextMenuChipController(
getActivity(), mAnchorView, mLensAsyncManager, () -> {}); getActivity(), mAnchorView, mLensAsyncManager);
TestThreadUtils.runOnUiThreadBlocking( TestThreadUtils.runOnUiThreadBlocking(() -> {
() -> { chipController.handleImageClassification(true); }); chipController.handleImageClassification(
(new LensQueryResult.Builder())
.withIsShoppyIntent(true)
.withLensIntentType(LensUtils.getLensShoppingIntentType())
.build());
});
assertNotNull("Anchor view was not initialized.", mAnchorView); assertNotNull("Anchor view was not initialized.", mAnchorView);
assertNotNull("Popup window was not initialized.", assertNotNull("Popup window was not initialized.",
...@@ -97,7 +105,7 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa ...@@ -97,7 +105,7 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa
@SmallTest @SmallTest
public void testDismissChipWhenNotShownBeforeClassificationReturned() { public void testDismissChipWhenNotShownBeforeClassificationReturned() {
RevampedContextMenuChipController chipController = new RevampedContextMenuChipController( RevampedContextMenuChipController chipController = new RevampedContextMenuChipController(
getActivity(), mAnchorView, mLensAsyncManager, () -> {}); getActivity(), mAnchorView, mLensAsyncManager);
TestThreadUtils.runOnUiThreadBlocking(() -> { chipController.dismissLensChipIfShowing(); }); TestThreadUtils.runOnUiThreadBlocking(() -> { chipController.dismissLensChipIfShowing(); });
assertNotNull("Anchor view was not initialized.", mAnchorView); assertNotNull("Anchor view was not initialized.", mAnchorView);
...@@ -109,9 +117,13 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa ...@@ -109,9 +117,13 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa
@SmallTest @SmallTest
public void testDismissChipWhenShown() { public void testDismissChipWhenShown() {
RevampedContextMenuChipController chipController = new RevampedContextMenuChipController( RevampedContextMenuChipController chipController = new RevampedContextMenuChipController(
getActivity(), mAnchorView, mLensAsyncManager, () -> {}); getActivity(), mAnchorView, mLensAsyncManager);
TestThreadUtils.runOnUiThreadBlocking(() -> { TestThreadUtils.runOnUiThreadBlocking(() -> {
chipController.handleImageClassification(true); chipController.handleImageClassification(
(new LensQueryResult.Builder())
.withIsShoppyIntent(true)
.withLensIntentType(LensUtils.getLensShoppingIntentType())
.build());
chipController.dismissLensChipIfShowing(); chipController.dismissLensChipIfShowing();
}); });
...@@ -126,7 +138,7 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa ...@@ -126,7 +138,7 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa
@SmallTest @SmallTest
public void testExpectedVerticalPxNeededForChip() { public void testExpectedVerticalPxNeededForChip() {
RevampedContextMenuChipController chipController = new RevampedContextMenuChipController( RevampedContextMenuChipController chipController = new RevampedContextMenuChipController(
getActivity(), mAnchorView, mLensAsyncManager, () -> {}); getActivity(), mAnchorView, mLensAsyncManager);
assertEquals("Vertical px is not matching the expectation", assertEquals("Vertical px is not matching the expectation",
(int) (EXPECTED_VERTICAL_DP * mMeasuredDeviceDensity), (int) (EXPECTED_VERTICAL_DP * mMeasuredDeviceDensity),
chipController.getVerticalPxNeededForChip()); chipController.getVerticalPxNeededForChip());
...@@ -136,7 +148,7 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa ...@@ -136,7 +148,7 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa
@SmallTest @SmallTest
public void testExpectedChipTextMaxWidthPx() { public void testExpectedChipTextMaxWidthPx() {
RevampedContextMenuChipController chipController = new RevampedContextMenuChipController( RevampedContextMenuChipController chipController = new RevampedContextMenuChipController(
getActivity(), mAnchorView, mLensAsyncManager, () -> {}); getActivity(), mAnchorView, mLensAsyncManager);
assertEquals("Vertical px is not matching the expectation", assertEquals("Vertical px is not matching the expectation",
(int) (EXPECTEED_CHIP_WIDTH_DP * mMeasuredDeviceDensity), (int) (EXPECTEED_CHIP_WIDTH_DP * mMeasuredDeviceDensity),
chipController.getChipTextMaxWidthPx()); chipController.getChipTextMaxWidthPx());
......
...@@ -422,19 +422,16 @@ public class RevampedContextMenuTest implements DownloadTestRule.CustomMainActiv ...@@ -422,19 +422,16 @@ public class RevampedContextMenuTest implements DownloadTestRule.CustomMainActiv
RevampedContextMenuCoordinator menuCoordinator = RevampedContextMenuCoordinator menuCoordinator =
RevampedContextMenuUtils.openContextMenu(tab, "testImage"); RevampedContextMenuUtils.openContextMenu(tab, "testImage");
// Needs to run on UI thread so creation happens on same thread as dismissal. // Needs to run on UI thread so creation happens on same thread as dismissal.
TestThreadUtils.runOnUiThreadBlocking( TestThreadUtils.runOnUiThreadBlocking(() -> {
() -> menuCoordinator.simulateShoppyImageClassificationForTesting()); menuCoordinator.simulateShoppyImageClassificationForTesting();
Assert.assertTrue("Chip popoup not showing.",
Assert.assertTrue("Chip popoup not showing.", menuCoordinator.getCurrentPopupWindowForTesting().isShowing());
menuCoordinator.getCurrentPopupWindowForTesting().isShowing()); menuCoordinator.clickChipForTesting();
});
RevampedContextMenuUtils.selectAlreadyOpenedContextMenuChipWithExpectedIntent(
InstrumentationRegistry.getInstrumentation(), mDownloadTestRule.getActivity(),
menuCoordinator, "testImage", R.id.contextmenu_shop_image_with_google_lens,
"com.google.android.googlequicksearchbox");
Assert.assertEquals("Selection histogram pings not equal to one", 1, Assert.assertEquals("Selection histogram pings not equal to one", 1,
RecordHistogram.getHistogramTotalCountForTesting( RecordHistogram.getHistogramValueCountForTesting("ContextMenu.LensChip.Event",
"ContextMenu.SelectedOptionAndroid.Image")); RevampedContextMenuChipController.ChipEvent.CLICKED));
Assert.assertFalse("Chip popoup still showing.", Assert.assertFalse("Chip popoup still showing.",
menuCoordinator.getCurrentPopupWindowForTesting().isShowing()); menuCoordinator.getCurrentPopupWindowForTesting().isShowing());
} }
......
...@@ -19,7 +19,7 @@ import org.junit.runner.RunWith; ...@@ -19,7 +19,7 @@ import org.junit.runner.RunWith;
import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.flags.ChromeSwitches; import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.share.LensUtils.IntentType; import org.chromium.chrome.browser.lens.LensQueryResult;
import org.chromium.chrome.test.ChromeBrowserTestRule; import org.chromium.chrome.test.ChromeBrowserTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.util.browser.Features.EnableFeatures; import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
...@@ -46,7 +46,7 @@ public class LensUtilsTest { ...@@ -46,7 +46,7 @@ public class LensUtilsTest {
Intent intentNoUri = getShareWithGoogleLensIntentOnUiThread(Uri.EMPTY, Intent intentNoUri = getShareWithGoogleLensIntentOnUiThread(Uri.EMPTY,
/* isIncognito= */ false, 1234L, /* srcUrl */ "", /* titleOrAltText */ "", /* isIncognito= */ false, 1234L, /* srcUrl */ "", /* titleOrAltText */ "",
/* intentType= */ IntentType.DEFAULT, /* lensQueryResult */ null,
/* requiresConfirmation= */ false); /* requiresConfirmation= */ false);
Assert.assertEquals("Intent without image has incorrect URI", "googleapp://lens", Assert.assertEquals("Intent without image has incorrect URI", "googleapp://lens",
intentNoUri.getData().toString()); intentNoUri.getData().toString());
...@@ -56,7 +56,7 @@ public class LensUtilsTest { ...@@ -56,7 +56,7 @@ public class LensUtilsTest {
final String contentUrl = "content://image-url"; final String contentUrl = "content://image-url";
Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl), Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl),
/* isIncognito= */ false, 1234L, /* srcUrl */ "", /* titleOrAltText */ "", /* isIncognito= */ false, 1234L, /* srcUrl */ "", /* titleOrAltText */ "",
/* intentType= */ IntentType.DEFAULT, /* lensQueryResult */ null,
/* requiresConfirmation= */ false); /* requiresConfirmation= */ false);
Assert.assertEquals("Intent with image has incorrect URI", Assert.assertEquals("Intent with image has incorrect URI",
"googleapp://lens?LensBitmapUriKey=content%3A%2F%2Fimage-url&AccountNameUriKey=" "googleapp://lens?LensBitmapUriKey=content%3A%2F%2Fimage-url&AccountNameUriKey="
...@@ -288,7 +288,7 @@ public class LensUtilsTest { ...@@ -288,7 +288,7 @@ public class LensUtilsTest {
Intent intentNoUri = getShareWithGoogleLensIntentOnUiThread(Uri.EMPTY, Intent intentNoUri = getShareWithGoogleLensIntentOnUiThread(Uri.EMPTY,
/* isIncognito= */ false, 1234L, /* srcUrl */ "", /* titleOrAltText */ "", /* isIncognito= */ false, 1234L, /* srcUrl */ "", /* titleOrAltText */ "",
/* intentType= */ IntentType.DEFAULT, /* lensQueryResult */ null,
/* requiresConfirmation= */ false); /* requiresConfirmation= */ false);
Assert.assertEquals("Intent without image has incorrect URI", "google://lens", Assert.assertEquals("Intent without image has incorrect URI", "google://lens",
intentNoUri.getData().toString()); intentNoUri.getData().toString());
...@@ -298,7 +298,7 @@ public class LensUtilsTest { ...@@ -298,7 +298,7 @@ public class LensUtilsTest {
final String contentUrl = "content://image-url"; final String contentUrl = "content://image-url";
Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl), Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl),
/* isIncognito= */ false, 1234L, /* srcUrl */ "", /* titleOrAltText */ "", /* isIncognito= */ false, 1234L, /* srcUrl */ "", /* titleOrAltText */ "",
/* intentType= */ IntentType.DEFAULT, /* lensQueryResult */ null,
/* requiresConfirmation= */ false); /* requiresConfirmation= */ false);
Assert.assertEquals("Intent with image has incorrect URI", Assert.assertEquals("Intent with image has incorrect URI",
"google://lens?LensBitmapUriKey=content%3A%2F%2Fimage-url" "google://lens?LensBitmapUriKey=content%3A%2F%2Fimage-url"
...@@ -319,7 +319,7 @@ public class LensUtilsTest { ...@@ -319,7 +319,7 @@ public class LensUtilsTest {
mBrowserTestRule.addTestAccountThenSigninAndEnableSync(); mBrowserTestRule.addTestAccountThenSigninAndEnableSync();
Intent intentNoUri = getShareWithGoogleLensIntentOnUiThread(Uri.EMPTY, Intent intentNoUri = getShareWithGoogleLensIntentOnUiThread(Uri.EMPTY,
/* isIncognito= */ true, 1234L, /* srcUrl */ "", /* titleOrAltText */ "", /* isIncognito= */ true, 1234L, /* srcUrl */ "", /* titleOrAltText */ "",
/* intentType= */ IntentType.DEFAULT, /* lensQueryResult */ null,
/* requiresConfirmation= */ false); /* requiresConfirmation= */ false);
Assert.assertEquals("Intent without image has incorrect URI", "googleapp://lens", Assert.assertEquals("Intent without image has incorrect URI", "googleapp://lens",
intentNoUri.getData().toString()); intentNoUri.getData().toString());
...@@ -329,7 +329,7 @@ public class LensUtilsTest { ...@@ -329,7 +329,7 @@ public class LensUtilsTest {
final String contentUrl = "content://image-url"; final String contentUrl = "content://image-url";
Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl), Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl),
/* isIncognito= */ true, 1234L, /* srcUrl */ "", /* titleOrAltText */ "", /* isIncognito= */ true, 1234L, /* srcUrl */ "", /* titleOrAltText */ "",
/* intentType= */ IntentType.DEFAULT, /* lensQueryResult */ null,
/* requiresConfirmation= */ false); /* requiresConfirmation= */ false);
// The account name should not be included in the intent because the uesr is incognito. // The account name should not be included in the intent because the uesr is incognito.
Assert.assertEquals("Intent with image has incorrect URI", Assert.assertEquals("Intent with image has incorrect URI",
...@@ -353,7 +353,7 @@ public class LensUtilsTest { ...@@ -353,7 +353,7 @@ public class LensUtilsTest {
final String contentUrl = "content://image-url"; final String contentUrl = "content://image-url";
Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl), Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl),
/* isIncognito= */ false, 1234L, /* srcUrl */ "", /* isIncognito= */ false, 1234L, /* srcUrl */ "",
/* titleOrAltText */ "", /* intentType= */ IntentType.DEFAULT, /* titleOrAltText */ "", /* lensQueryResult */ null,
/* requiresConfirmation= */ false); /* requiresConfirmation= */ false);
// The account name should not be included in the intent because the uesr is incognito. // The account name should not be included in the intent because the uesr is incognito.
Assert.assertEquals("Intent with image has incorrect URI", Assert.assertEquals("Intent with image has incorrect URI",
...@@ -378,7 +378,7 @@ public class LensUtilsTest { ...@@ -378,7 +378,7 @@ public class LensUtilsTest {
final String contentUrl = "content://image-url"; final String contentUrl = "content://image-url";
Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl), Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl),
/* isIncognito= */ true, 1234L, /* srcUrl */ "", /* isIncognito= */ true, 1234L, /* srcUrl */ "",
/* titleOrAltText */ "", /* intentType= */ IntentType.DEFAULT, /* titleOrAltText */ "", /* lensQueryResult */ null,
/* requiresConfirmation= */ false); /* requiresConfirmation= */ false);
// The account name should not be included in the intent because the uesr is incognito. // The account name should not be included in the intent because the uesr is incognito.
Assert.assertEquals("Intent with image has incorrect URI", Assert.assertEquals("Intent with image has incorrect URI",
...@@ -398,7 +398,7 @@ public class LensUtilsTest { ...@@ -398,7 +398,7 @@ public class LensUtilsTest {
public void getShareWithGoogleLensIntentNotSignedInTest() { public void getShareWithGoogleLensIntentNotSignedInTest() {
Intent intentNoUri = getShareWithGoogleLensIntentOnUiThread(Uri.EMPTY, Intent intentNoUri = getShareWithGoogleLensIntentOnUiThread(Uri.EMPTY,
/* isIncognito= */ false, 1234L, /* srcUrl */ "", /* titleOrAltText */ "", /* isIncognito= */ false, 1234L, /* srcUrl */ "", /* titleOrAltText */ "",
/* intentType= */ IntentType.DEFAULT, /* lensQueryResult */ null,
/* requiresConfirmation= */ false); /* requiresConfirmation= */ false);
Assert.assertEquals("Intent without image has incorrect URI", "googleapp://lens", Assert.assertEquals("Intent without image has incorrect URI", "googleapp://lens",
intentNoUri.getData().toString()); intentNoUri.getData().toString());
...@@ -408,7 +408,7 @@ public class LensUtilsTest { ...@@ -408,7 +408,7 @@ public class LensUtilsTest {
final String contentUrl = "content://image-url"; final String contentUrl = "content://image-url";
Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl), Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl),
/* isIncognito= */ false, 1234L, /* srcUrl */ "", /* titleOrAltText */ "", /* isIncognito= */ false, 1234L, /* srcUrl */ "", /* titleOrAltText */ "",
/* intentType= */ IntentType.DEFAULT, /* lensQueryResult */ null,
/* requiresConfirmation= */ false); /* requiresConfirmation= */ false);
Assert.assertEquals("Intent with image has incorrect URI", Assert.assertEquals("Intent with image has incorrect URI",
"googleapp://lens?LensBitmapUriKey=content%3A%2F%2Fimage-url&AccountNameUriKey=" "googleapp://lens?LensBitmapUriKey=content%3A%2F%2Fimage-url&AccountNameUriKey="
...@@ -429,7 +429,7 @@ public class LensUtilsTest { ...@@ -429,7 +429,7 @@ public class LensUtilsTest {
Intent intentWithContentUriZeroTimestamp = Intent intentWithContentUriZeroTimestamp =
getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl), getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl),
/* isIncognito= */ false, 0L, /* srcUrl */ "", /* titleOrAltText */ "", /* isIncognito= */ false, 0L, /* srcUrl */ "", /* titleOrAltText */ "",
/* intentType= */ IntentType.DEFAULT, /* lensQueryResult */ null,
/* requiresConfirmation= */ false); /* requiresConfirmation= */ false);
Assert.assertEquals("Intent with image has incorrect URI", Assert.assertEquals("Intent with image has incorrect URI",
"googleapp://lens?LensBitmapUriKey=content%3A%2F%2Fimage-url&AccountNameUriKey=" "googleapp://lens?LensBitmapUriKey=content%3A%2F%2Fimage-url&AccountNameUriKey="
...@@ -439,11 +439,11 @@ public class LensUtilsTest { ...@@ -439,11 +439,11 @@ public class LensUtilsTest {
private Intent getShareWithGoogleLensIntentOnUiThread(Uri imageUri, boolean isIncognito, private Intent getShareWithGoogleLensIntentOnUiThread(Uri imageUri, boolean isIncognito,
long currentTimeNanos, String srcUrl, String titleOrAltText, long currentTimeNanos, String srcUrl, String titleOrAltText,
@IntentType final int intentType, boolean requiresConfirmation) { LensQueryResult lensQueryResult, boolean requiresConfirmation) {
return TestThreadUtils.runOnUiThreadBlockingNoException( return TestThreadUtils.runOnUiThreadBlockingNoException(
() ()
-> LensUtils.getShareWithGoogleLensIntent(imageUri, isIncognito, -> LensUtils.getShareWithGoogleLensIntent(imageUri, isIncognito,
currentTimeNanos, srcUrl, titleOrAltText, intentType, currentTimeNanos, srcUrl, titleOrAltText, lensQueryResult,
requiresConfirmation)); requiresConfirmation));
} }
...@@ -458,7 +458,7 @@ public class LensUtilsTest { ...@@ -458,7 +458,7 @@ public class LensUtilsTest {
Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl), Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl),
/* isIncognito= */ false, 1234L, /* srcUrl */ "", /* isIncognito= */ false, 1234L, /* srcUrl */ "",
/* titleOrAltText */ "An image description.", /* titleOrAltText */ "An image description.",
/* intentType= */ IntentType.DEFAULT, /* lensQueryResult */ null,
/* requiresConfirmation= */ false); /* requiresConfirmation= */ false);
// The account name should not be included in the intent because the uesr is incognito. // The account name should not be included in the intent because the uesr is incognito.
Assert.assertEquals("Intent with image has incorrect URI", Assert.assertEquals("Intent with image has incorrect URI",
...@@ -485,7 +485,7 @@ public class LensUtilsTest { ...@@ -485,7 +485,7 @@ public class LensUtilsTest {
Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl), Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl),
/* isIncognito= */ true, 1234L, /* srcUrl */ "", /* isIncognito= */ true, 1234L, /* srcUrl */ "",
/* titleOrAltText */ "An image description.", /* titleOrAltText */ "An image description.",
/* intentType= */ IntentType.DEFAULT, /* lensQueryResult */ null,
/* requiresConfirmation= */ false); /* requiresConfirmation= */ false);
// The account name should not be included in the intent because the uesr is incognito. // The account name should not be included in the intent because the uesr is incognito.
Assert.assertEquals("Intent with image has incorrect URI", Assert.assertEquals("Intent with image has incorrect URI",
...@@ -504,9 +504,13 @@ public class LensUtilsTest { ...@@ -504,9 +504,13 @@ public class LensUtilsTest {
@SmallTest @SmallTest
public void getShareWithGoogleLensIntentWithShoppingIntentTest() { public void getShareWithGoogleLensIntentWithShoppingIntentTest() {
final String contentUrl = "content://image-url"; final String contentUrl = "content://image-url";
LensQueryResult lensQueryResult = (new LensQueryResult.Builder())
.withIsShoppyIntent(true)
.withLensIntentType(18)
.build();
Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl), Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl),
/* isIncognito= */ false, 1234L, /* srcUrl */ "", /* isIncognito= */ false, 1234L, /* srcUrl */ "",
/* titleOrAltText */ "", /* intentType= */ IntentType.SHOPPING, /* titleOrAltText */ "", /* lensQueryResult= */ lensQueryResult,
/* requiresConfirmation= */ false); /* requiresConfirmation= */ false);
// The account name should not be included in the intent because the uesr is incognito. // The account name should not be included in the intent because the uesr is incognito.
Assert.assertEquals("Intent with image has incorrect URI", Assert.assertEquals("Intent with image has incorrect URI",
...@@ -527,7 +531,7 @@ public class LensUtilsTest { ...@@ -527,7 +531,7 @@ public class LensUtilsTest {
final String contentUrl = "content://image-url"; final String contentUrl = "content://image-url";
Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl), Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl),
/* isIncognito= */ false, 1234L, /* srcUrl */ "", /* isIncognito= */ false, 1234L, /* srcUrl */ "",
/* titleOrAltText */ "", /* intentType= */ IntentType.DEFAULT, /* titleOrAltText */ "", /* lensQueryResult */ null,
/* requiresConfirmation= */ true); /* requiresConfirmation= */ true);
// The account name should not be included in the intent because the uesr is incognito. // The account name should not be included in the intent because the uesr is incognito.
Assert.assertEquals("Intent with image has incorrect URI", Assert.assertEquals("Intent with image has incorrect URI",
...@@ -555,7 +559,7 @@ public class LensUtilsTest { ...@@ -555,7 +559,7 @@ public class LensUtilsTest {
Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl), Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl),
/* isIncognito= */ false, 1234L, /* srcUrl */ "", /* isIncognito= */ false, 1234L, /* srcUrl */ "",
/* titleOrAltText */ "An image description.", /* titleOrAltText */ "An image description.",
/* intentType= */ IntentType.DEFAULT, /* lensQueryResult */ null,
/* requiresConfirmation= */ false); /* requiresConfirmation= */ false);
// The account name should not be included in the intent because the uesr is incognito. // The account name should not be included in the intent because the uesr is incognito.
Assert.assertEquals("Intent with image has incorrect URI", Assert.assertEquals("Intent with image has incorrect URI",
...@@ -577,7 +581,7 @@ public class LensUtilsTest { ...@@ -577,7 +581,7 @@ public class LensUtilsTest {
final String contentUrl = "content://image-url"; final String contentUrl = "content://image-url";
Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl), Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl),
/* isIncognito= */ false, 1234L, /* srcUrl */ "http://www.google.com?key=val", /* isIncognito= */ false, 1234L, /* srcUrl */ "http://www.google.com?key=val",
/* titleOrAltText */ "", /* intentType= */ IntentType.DEFAULT, /* titleOrAltText */ "", /* lensQueryResult */ null,
/* requiresConfirmation= */ false); /* requiresConfirmation= */ false);
// The account name should not be included in the intent because the uesr is incognito. // The account name should not be included in the intent because the uesr is incognito.
Assert.assertEquals("Intent with image has incorrect URI", Assert.assertEquals("Intent with image has incorrect URI",
...@@ -603,7 +607,7 @@ public class LensUtilsTest { ...@@ -603,7 +607,7 @@ public class LensUtilsTest {
final String contentUrl = "content://image-url"; final String contentUrl = "content://image-url";
Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl), Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl),
/* isIncognito= */ false, 1234L, /* srcUrl */ "http://www.google.com?key=val", /* isIncognito= */ false, 1234L, /* srcUrl */ "http://www.google.com?key=val",
/* titleOrAltText */ "", /* intentType= */ IntentType.DEFAULT, /* titleOrAltText */ "", /* lensQueryResult */ null,
/* requiresConfirmation= */ false); /* requiresConfirmation= */ false);
// The account name should not be included in the intent because the uesr is incognito. // The account name should not be included in the intent because the uesr is incognito.
Assert.assertEquals("Intent with image has incorrect URI", Assert.assertEquals("Intent with image has incorrect URI",
...@@ -630,7 +634,7 @@ public class LensUtilsTest { ...@@ -630,7 +634,7 @@ public class LensUtilsTest {
final String contentUrl = "content://image-url"; final String contentUrl = "content://image-url";
Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl), Intent intentWithContentUri = getShareWithGoogleLensIntentOnUiThread(Uri.parse(contentUrl),
/* isIncognito= */ true, 1234L, /* srcUrl */ "http://www.google.com?key=val", /* isIncognito= */ true, 1234L, /* srcUrl */ "http://www.google.com?key=val",
/* titleOrAltText */ "", /* intentType= */ IntentType.DEFAULT, /* titleOrAltText */ "", /* lensQueryResult */ null,
/* requiresConfirmation= */ false); /* requiresConfirmation= */ false);
// The account name should not be included in the intent because the uesr is incognito. // The account name should not be included in the intent because the uesr is incognito.
Assert.assertEquals("Intent with image has incorrect URI", Assert.assertEquals("Intent with image has incorrect URI",
......
...@@ -32,6 +32,11 @@ public interface ContextMenuItemDelegate { ...@@ -32,6 +32,11 @@ public interface ContextMenuItemDelegate {
*/ */
void onDestroy(); void onDestroy();
/**
* @return The title of the current tab associated with this delegate..
*/
String getPageTitle();
/** /**
* @return The web contents of the current tab owned by this delegate. * @return The web contents of the current tab owned by this delegate.
*/ */
......
...@@ -42,4 +42,9 @@ public interface ContextMenuPopulator { ...@@ -42,4 +42,9 @@ public interface ContextMenuPopulator {
* Determines whether the the containing browser is switched to incognito mode. * Determines whether the the containing browser is switched to incognito mode.
*/ */
boolean isIncognito(); boolean isIncognito();
/**
* @return The title of current web page.
*/
String getPageTitle();
} }
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