Commit 0bc1ba79 authored by Yu Su's avatar Yu Su Committed by Commit Bot

Lens Code Refactoring Part II

- move intent generation to the private repository.
- remove extra Lens Prime class dependencies from Context Menu.
- simplify the displaying and onClick handling logic for Lens chip.

Part I can be found at:
https://chromium-review.googlesource.com/c/chromium/src/+/2527724

Change-Id: I26516b1bc39d488922e963269da3114aaa5e998a
Bug: 1145748, b/171252338
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2503636
Commit-Queue: Yu Su <yusuyoutube@google.com>
Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Reviewed-by: default avatarBen Goldberger <benwgold@google.com>
Reviewed-by: default avatarSinan Sahin <sinansahin@google.com>
Cr-Commit-Position: refs/heads/master@{#827037}
parent 5ca5d0f8
...@@ -350,6 +350,7 @@ chrome_java_sources = [ ...@@ -350,6 +350,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/contacts_picker/ChromePickerAdapter.java", "java/src/org/chromium/chrome/browser/contacts_picker/ChromePickerAdapter.java",
"java/src/org/chromium/chrome/browser/content/ContentUtils.java", "java/src/org/chromium/chrome/browser/content/ContentUtils.java",
"java/src/org/chromium/chrome/browser/content_capture/ContentCaptureHistoryDeletionObserver.java", "java/src/org/chromium/chrome/browser/content_capture/ContentCaptureHistoryDeletionObserver.java",
"java/src/org/chromium/chrome/browser/contextmenu/ChipDelegate.java",
"java/src/org/chromium/chrome/browser/contextmenu/ChipRenderParams.java", "java/src/org/chromium/chrome/browser/contextmenu/ChipRenderParams.java",
"java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuItem.java", "java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuItem.java",
"java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java", "java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java",
...@@ -357,7 +358,7 @@ chrome_java_sources = [ ...@@ -357,7 +358,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java", "java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java",
"java/src/org/chromium/chrome/browser/contextmenu/ContextMenuUi.java", "java/src/org/chromium/chrome/browser/contextmenu/ContextMenuUi.java",
"java/src/org/chromium/chrome/browser/contextmenu/ContextMenuUtils.java", "java/src/org/chromium/chrome/browser/contextmenu/ContextMenuUtils.java",
"java/src/org/chromium/chrome/browser/contextmenu/LensAsyncManager.java", "java/src/org/chromium/chrome/browser/contextmenu/LensChipDelegate.java",
"java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuChipController.java", "java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuChipController.java",
"java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuCoordinator.java", "java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuCoordinator.java",
"java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuHeaderCoordinator.java", "java/src/org/chromium/chrome/browser/contextmenu/RevampedContextMenuHeaderCoordinator.java",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.contextmenu;
import org.chromium.base.Callback;
/**
* Interface to handle chip data and actions.
*/
public interface ChipDelegate {
/**
* Retrieves the data for displaying a chip below the context menu.
* @param callback The callback will always be called with the retrieved ChipRenderParams.
* The ChipRenderParams will be null in the event there is no chip to show.
*/
void getChipRenderParams(Callback<ChipRenderParams> callback);
/**
* Called when the context menu is closed.
*/
void onMenuClosed();
/**
* Check whether the ChipRenderParams object is valid. Called before displaying the chip.
* @param chipRenderParams A wrapper object contains the params used when rendering the chip.
* @return True if the params are valid.
*/
boolean isValidChipRenderParams(ChipRenderParams chipRenderParams);
}
\ No newline at end of file
...@@ -18,5 +18,6 @@ public class ChipRenderParams { ...@@ -18,5 +18,6 @@ public class ChipRenderParams {
public @DrawableRes int iconResourceId; public @DrawableRes int iconResourceId;
// The callback to be called when the chip clicked. // The callback to be called when the chip clicked.
// A non-null ChipRenderParams will always have a non-null callback.
public Runnable onClickCallback; public Runnable onClickCallback;
} }
\ No newline at end of file
...@@ -13,7 +13,6 @@ import org.chromium.base.annotations.CalledByNative; ...@@ -13,7 +13,6 @@ import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.NativeMethods; 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.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 +47,7 @@ public class ContextMenuHelper { ...@@ -48,6 +47,7 @@ public class ContextMenuHelper {
private boolean mSelectedItemBeforeDismiss; private boolean mSelectedItemBeforeDismiss;
private boolean mIsIncognito; private boolean mIsIncognito;
private String mPageTitle; private String mPageTitle;
private ChipDelegate mChipDelegate;
private ContextMenuHelper(long nativeContextMenuHelper, WebContents webContents) { private ContextMenuHelper(long nativeContextMenuHelper, WebContents webContents) {
mNativeContextMenuHelper = nativeContextMenuHelper; mNativeContextMenuHelper = nativeContextMenuHelper;
...@@ -134,11 +134,10 @@ public class ContextMenuHelper { ...@@ -134,11 +134,10 @@ public class ContextMenuHelper {
mCurrentPopulator.onMenuClosed(); mCurrentPopulator.onMenuClosed();
mCurrentPopulator = null; mCurrentPopulator = null;
} }
if (LensUtils.enableImageChip(mIsIncognito) if (LensUtils.enableImageChip(mIsIncognito)) {
&& LensController.getInstance().isQueryEnabled()) { // If the image was being classified terminate the classification
// If the image was being classified terminate the classification.
// Has no effect if the classification already succeeded. // Has no effect if the classification already succeeded.
LensController.getInstance().terminateClassification(); mChipDelegate.onMenuClosed();
} }
if (mNativeContextMenuHelper == 0) return; if (mNativeContextMenuHelper == 0) return;
ContextMenuHelperJni.get().onContextMenuClosed( ContextMenuHelperJni.get().onContextMenuClosed(
...@@ -158,14 +157,12 @@ public class ContextMenuHelper { ...@@ -158,14 +157,12 @@ public class ContextMenuHelper {
final RevampedContextMenuCoordinator menuCoordinator = final RevampedContextMenuCoordinator menuCoordinator =
new RevampedContextMenuCoordinator(topContentOffsetPx, mCurrentNativeDelegate); new RevampedContextMenuCoordinator(topContentOffsetPx, mCurrentNativeDelegate);
mCurrentContextMenu = menuCoordinator; mCurrentContextMenu = menuCoordinator;
if (LensUtils.enableImageChip(mIsIncognito)) {
if (LensUtils.enableImageChip(mIsIncognito) mChipDelegate = new LensChipDelegate(mCurrentContextMenuParams.getPageUrl(),
&& LensController.getInstance().isQueryEnabled()) { mCurrentContextMenuParams.getTitleText(), mCurrentContextMenuParams.getSrcUrl(),
LensAsyncManager lensAsyncManager = new LensAsyncManager(mCurrentContextMenuParams, mPageTitle, mIsIncognito, mWebContents, mCurrentNativeDelegate);
mCurrentNativeDelegate, mWindow, mIsIncognito, mPageTitle, mWebContents); menuCoordinator.displayMenuWithChip(mWindow, mWebContents, mCurrentContextMenuParams,
menuCoordinator.displayMenuWithLensChip(mWindow, mWebContents, items, mCallback, mOnMenuShown, mOnMenuClosed, mChipDelegate);
mCurrentContextMenuParams, items, mCallback, mOnMenuShown, mOnMenuClosed,
lensAsyncManager);
} else { } else {
menuCoordinator.displayMenu(mWindow, mWebContents, mCurrentContextMenuParams, items, menuCoordinator.displayMenu(mWindow, mWebContents, mCurrentContextMenuParams, items,
mCallback, mOnMenuShown, mOnMenuClosed); mCallback, mOnMenuShown, mOnMenuClosed);
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.contextmenu;
import android.net.Uri;
import org.chromium.base.Callback;
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.content_public.browser.WebContents;
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.
*/
class LensAsyncManager {
private static final String TAG = "LensAsyncManager";
private ContextMenuParams mParams;
private ContextMenuNativeDelegate mNativeDelegate;
private LensQueryResult mLastCompletedQueryResult;
private WindowAndroid mWindow;
private boolean mIsIncognito;
private String mPageTitle;
private WebContents mWebContents;
/**
* Construct a lens async manager.
* @param params Context menu params used to retrieve additional metadata.
* @param nativeDelegate {@link ContextMenuNativeDelegate} used to retrieve image bytes.
* @param window The current window.
* @param isIncognito Whether the current tab is in incognito mode.
* @param pageTitle The title of the current tab's document.
* @param webContents The web contents representing the selected element.
*/
public LensAsyncManager(ContextMenuParams params, ContextMenuNativeDelegate nativeDelegate,
WindowAndroid window, boolean isIncognito, String pageTitle, WebContents webContents) {
mParams = params;
mNativeDelegate = nativeDelegate;
mWindow = window;
mIsIncognito = isIncognito;
mPageTitle = pageTitle;
mWebContents = webContents;
}
/**
* Make a Lens image query for the current render frame.
* @param replyCallback The function to callback with the query result.
*/
public void queryImageAsync(Callback<LensQueryResult> replyCallback) {
Callback<Uri> callback = (uri) -> {
LensQueryParams lensQueryParams =
(new LensQueryParams.Builder())
.withImageUri(uri)
.withPageUrl(mParams.getPageUrl())
.withImageTitleOrAltText(mParams.getTitleText())
.withPageTitle(mPageTitle)
.withWebContents(mWebContents)
.build();
LensController.getInstance().queryImage(lensQueryParams, (lensQueryResult) -> {
mLastCompletedQueryResult = lensQueryResult;
replyCallback.onResult(lensQueryResult);
});
};
// Must occur on UI thread.
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(), mParams.getPageUrl(), mLastCompletedQueryResult,
/* requiresConfirmation*/ false);
};
mNativeDelegate.retrieveImageForShare(ContextMenuImageFormat.PNG, callback);
}
}
\ No newline at end of file
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.contextmenu;
import android.net.Uri;
import org.chromium.base.Callback;
import org.chromium.chrome.browser.lens.LensController;
import org.chromium.chrome.browser.lens.LensQueryParams;
import org.chromium.content_public.browser.WebContents;
/**
* The class to handle Lens chip data and actions.
*/
public class LensChipDelegate implements ChipDelegate {
private LensQueryParams mLensQueryParams;
private LensController mLensController;
private ContextMenuNativeDelegate mNativeDelegate;
public LensChipDelegate(String pageUrl, String titleOrAltText, String srcUrl, String pageTitle,
boolean isIncognito, WebContents webContents,
ContextMenuNativeDelegate nativeDelegate) {
mLensController = LensController.getInstance();
if (!mLensController.isQueryEnabled()) {
return;
}
mLensQueryParams = (new LensQueryParams.Builder())
.withPageUrl(pageUrl)
.withImageTitleOrAltText(titleOrAltText)
.withSrcUrl(srcUrl)
.withPageTitle(pageTitle)
.withIsIncognito(isIncognito)
.withWebContents(webContents)
.build();
mNativeDelegate = nativeDelegate;
}
@Override
public void getChipRenderParams(Callback<ChipRenderParams> chipParamsCallback) {
if (mLensQueryParams == null) {
chipParamsCallback.onResult(null);
return;
}
Callback<Uri> callback = (uri) -> {
mLensQueryParams.setImageUri(uri);
mLensController.getChipRenderParams(
mLensQueryParams, (chipParams) -> { chipParamsCallback.onResult(chipParams); });
};
// Must occur on UI thread.
mNativeDelegate.retrieveImageForShare(ContextMenuImageFormat.ORIGINAL, callback);
}
@Override
public void onMenuClosed() {
if (mLensController.isQueryEnabled()) {
mLensController.terminateClassification();
}
}
@Override
public boolean isValidChipRenderParams(ChipRenderParams chipRenderParams) {
return chipRenderParams != null && chipRenderParams.titleResourceId != 0
&& chipRenderParams.onClickCallback != null && chipRenderParams.iconResourceId != 0;
}
}
...@@ -11,13 +11,11 @@ import android.view.LayoutInflater; ...@@ -11,13 +11,11 @@ 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.NonNull;
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;
...@@ -32,10 +30,11 @@ import java.lang.annotation.RetentionPolicy; ...@@ -32,10 +30,11 @@ import java.lang.annotation.RetentionPolicy;
class RevampedContextMenuChipController implements View.OnClickListener { class RevampedContextMenuChipController implements View.OnClickListener {
private boolean mFakeLensQueryResultForTesting; private boolean mFakeLensQueryResultForTesting;
private View mAnchorView; private View mAnchorView;
private LensAsyncManager mLensAsyncManager;
private ChipView mChipView; private ChipView mChipView;
private AnchoredPopupWindow mPopupWindow; private AnchoredPopupWindow mPopupWindow;
private Context mContext; private Context mContext;
private ChipRenderParams mChipRenderParams;
@VisibleForTesting @VisibleForTesting
@IntDef({ChipEvent.SHOWN, ChipEvent.CLICKED, ChipEvent.DISMISSED}) @IntDef({ChipEvent.SHOWN, ChipEvent.CLICKED, ChipEvent.DISMISSED})
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
...@@ -51,20 +50,9 @@ class RevampedContextMenuChipController implements View.OnClickListener { ...@@ -51,20 +50,9 @@ class RevampedContextMenuChipController implements View.OnClickListener {
"ContextMenu.LensChip.Event", chipEvent, ChipEvent.NUM_ENTRIES); "ContextMenu.LensChip.Event", chipEvent, ChipEvent.NUM_ENTRIES);
} }
/** RevampedContextMenuChipController(Context context, View anchorView) {
* Construct the chip controller.
* @param context The current activity context
* @param anchorView The view to use as a placement anchor for the chip popup window.
* @param lensAsyncManager The object responsible for making Lens requests.
* @param chipClickedCallback The callback to fire after a user clicks a lens chip.
*/
RevampedContextMenuChipController(
Context context, View anchorView, LensAsyncManager lensAsyncManager) {
mContext = context; mContext = context;
mLensAsyncManager = lensAsyncManager;
mAnchorView = anchorView; mAnchorView = anchorView;
mLensAsyncManager.queryImageAsync(
(lensQueryResult) -> { handleImageClassification(lensQueryResult); });
} }
/** /**
...@@ -110,28 +98,11 @@ class RevampedContextMenuChipController implements View.OnClickListener { ...@@ -110,28 +98,11 @@ class RevampedContextMenuChipController implements View.OnClickListener {
mFakeLensQueryResultForTesting = true; mFakeLensQueryResultForTesting = true;
} }
@VisibleForTesting
void handleImageClassification(@Nullable LensQueryResult lensQueryResult) {
if (mFakeLensQueryResultForTesting) {
lensQueryResult = (new LensQueryResult.Builder())
.withIsShoppyIntent(true)
.withLensIntentType(LensUtils.getLensShoppingIntentType())
.build();
}
if (lensQueryResult != null
&& (lensQueryResult.getIsShoppyIntent()
|| LensUtils.isLensShoppingIntentType(
lensQueryResult.getLensIntentType()))) {
showChip(mAnchorView);
};
}
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (v == mChipView) { if (v == mChipView) {
recordChipEvent(ChipEvent.CLICKED); recordChipEvent(ChipEvent.CLICKED);
mLensAsyncManager.searchWithGoogleLens(); mChipRenderParams.onClickCallback.run();
dismissLensChipIfShowing(); dismissLensChipIfShowing();
} }
} }
...@@ -148,20 +119,22 @@ class RevampedContextMenuChipController implements View.OnClickListener { ...@@ -148,20 +119,22 @@ class RevampedContextMenuChipController implements View.OnClickListener {
/** /**
* Inflate an anchored chip view, set it up, and show it to the user. * Inflate an anchored chip view, set it up, and show it to the user.
* @param isEnabled Whether is will be enabled. * @param chipRenderParams The data to construct the chip.
*/ */
private void showChip(View anchorView) { protected void showChip(@NonNull ChipRenderParams chipRenderParams) {
if (mPopupWindow != null) { if (mPopupWindow != null) {
// Chip has already been shown for this context menu. // Chip has already been shown for this context menu.
return; return;
} }
mChipRenderParams = chipRenderParams;
mChipView = (ChipView) LayoutInflater.from(mContext).inflate( mChipView = (ChipView) LayoutInflater.from(mContext).inflate(
R.layout.revamped_context_menu_chip, null); R.layout.revamped_context_menu_chip, null);
ViewRectProvider rectProvider = new ViewRectProvider(anchorView); ViewRectProvider rectProvider = new ViewRectProvider(mAnchorView);
// Draw a clear background to avoid blocking context menu items. // Draw a clear background to avoid blocking context menu items.
mPopupWindow = new AnchoredPopupWindow(mContext, anchorView, mPopupWindow = new AnchoredPopupWindow(mContext, mAnchorView,
new ColorDrawable(Color.TRANSPARENT), mChipView, rectProvider); new ColorDrawable(Color.TRANSPARENT), mChipView, rectProvider);
mPopupWindow.setAnimationStyle(R.style.ChipAnimation); mPopupWindow.setAnimationStyle(R.style.ChipAnimation);
mPopupWindow.setPreferredHorizontalOrientation( mPopupWindow.setPreferredHorizontalOrientation(
...@@ -175,14 +148,17 @@ class RevampedContextMenuChipController implements View.OnClickListener { ...@@ -175,14 +148,17 @@ class RevampedContextMenuChipController implements View.OnClickListener {
mPopupWindow.setMaxWidth( mPopupWindow.setMaxWidth(
mContext.getResources().getDimensionPixelSize(R.dimen.context_menu_chip_max_width)); mContext.getResources().getDimensionPixelSize(R.dimen.context_menu_chip_max_width));
mChipView.getPrimaryTextView().setText(SpanApplier.removeSpanText( mChipView.getPrimaryTextView().setText(
mContext.getString(R.string.contextmenu_shop_image_with_google_lens), SpanApplier.removeSpanText(mContext.getString(chipRenderParams.titleResourceId),
new SpanInfo("<new>", "</new>"))); new SpanInfo("<new>", "</new>")));
// TODO(benwgold): Consult with Chrome UX owners to see if Chip UI hierarchy should be // TODO(benwgold): Consult with Chrome UX owners to see if Chip UI hierarchy should be
// refactored. // refactored.
mChipView.getPrimaryTextView().setMaxWidth(getChipTextMaxWidthPx()); mChipView.getPrimaryTextView().setMaxWidth(getChipTextMaxWidthPx());
mChipView.setIcon(R.drawable.lens_icon, false); if (chipRenderParams.iconResourceId != 0) {
mChipView.setIcon(chipRenderParams.iconResourceId, false);
}
mChipView.addRemoveIcon(); mChipView.addRemoveIcon();
mChipView.setOnClickListener(this); mChipView.setOnClickListener(this);
......
...@@ -85,8 +85,8 @@ public class RevampedContextMenuCoordinator implements ContextMenuUi { ...@@ -85,8 +85,8 @@ public class RevampedContextMenuCoordinator implements ContextMenuUi {
ContextMenuParams params, List<Pair<Integer, ModelList>> items, ContextMenuParams params, List<Pair<Integer, ModelList>> items,
Callback<Integer> onItemClicked, final Runnable onMenuShown, Callback<Integer> onItemClicked, final Runnable onMenuShown,
final Runnable onMenuClosed) { final Runnable onMenuClosed) {
displayMenuWithLensChip(window, webContents, params, items, onItemClicked, onMenuShown, displayMenuWithChip(window, webContents, params, items, onItemClicked, onMenuShown,
onMenuClosed, /* lensAsyncManager=*/null); onMenuClosed, /* chipDelegate=*/null);
} }
@Override @Override
...@@ -94,13 +94,12 @@ public class RevampedContextMenuCoordinator implements ContextMenuUi { ...@@ -94,13 +94,12 @@ public class RevampedContextMenuCoordinator implements ContextMenuUi {
dismissDialog(); dismissDialog();
} }
// Shows the Context Menu in Chrome with the lens chip (if supported). // Shows the menu with chip.
void displayMenuWithLensChip(final WindowAndroid window, WebContents webContents, void displayMenuWithChip(final WindowAndroid window, WebContents webContents,
ContextMenuParams params, List<Pair<Integer, ModelList>> items, ContextMenuParams params, List<Pair<Integer, ModelList>> items,
Callback<Integer> onItemClicked, final Runnable onMenuShown, Callback<Integer> onItemClicked, final Runnable onMenuShown,
final Runnable onMenuClosed, @Nullable LensAsyncManager lensAsyncManager) { final Runnable onMenuClosed, @Nullable ChipDelegate chipDelegate) {
mOnMenuClosed = onMenuClosed; mOnMenuClosed = onMenuClosed;
final boolean lensShoppingFeatureEnabled = lensAsyncManager != null;
final boolean isPopup = params.getSourceType() == MenuSourceType.MENU_SOURCE_MOUSE; final boolean isPopup = params.getSourceType() == MenuSourceType.MENU_SOURCE_MOUSE;
Activity activity = window.getActivity().get(); Activity activity = window.getActivity().get();
final float density = activity.getResources().getDisplayMetrics().density; final float density = activity.getResources().getDisplayMetrics().density;
...@@ -113,10 +112,14 @@ public class RevampedContextMenuCoordinator implements ContextMenuUi { ...@@ -113,10 +112,14 @@ public class RevampedContextMenuCoordinator implements ContextMenuUi {
R.layout.context_menu_fullscreen_container, null); R.layout.context_menu_fullscreen_container, null);
// Only display a chip if an image was selected and the menu isn't a popup. // Only display a chip if an image was selected and the menu isn't a popup.
if (params.isImage() && lensShoppingFeatureEnabled && !isPopup) { if (params.isImage() && chipDelegate != null && !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);
activity, chipAnchorView, lensAsyncManager); chipDelegate.getChipRenderParams((chipRenderParams) -> {
if (chipDelegate.isValidChipRenderParams(chipRenderParams)) {
mChipController.showChip(chipRenderParams);
}
});
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;
...@@ -283,7 +286,11 @@ public class RevampedContextMenuCoordinator implements ContextMenuUi { ...@@ -283,7 +286,11 @@ public class RevampedContextMenuCoordinator implements ContextMenuUi {
// 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.setFakeLensQueryResultForTesting(); // IN-TEST mChipController.setFakeLensQueryResultForTesting(); // IN-TEST
mChipController.handleImageClassification(null); ChipRenderParams chipRenderParamsForTesting = new ChipRenderParams();
chipRenderParamsForTesting.titleResourceId =
R.string.contextmenu_shop_image_with_google_lens;
chipRenderParamsForTesting.onClickCallback = () -> {};
mChipController.showChip(chipRenderParamsForTesting);
} }
// Public only to allow references from RevampedContextMenuUtils.java // Public only to allow references from RevampedContextMenuUtils.java
......
// Copyright 2020 The Chromium Authors. All rights reserved. // Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
package org.chromium.chrome.browser.lens; package org.chromium.chrome.browser.lens;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
...@@ -16,7 +14,6 @@ import org.chromium.chrome.R; ...@@ -16,7 +14,6 @@ import org.chromium.chrome.R;
import org.chromium.chrome.browser.AppHooks; import org.chromium.chrome.browser.AppHooks;
import org.chromium.chrome.browser.contextmenu.ChipRenderParams; import org.chromium.chrome.browser.contextmenu.ChipRenderParams;
import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.base.WindowAndroid;
/** /**
* A class which manages communication with the Lens SDK. * A class which manages communication with the Lens SDK.
*/ */
...@@ -87,36 +84,14 @@ public class LensController { ...@@ -87,36 +84,14 @@ public class LensController {
* the activity. * the activity.
* @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 requiresConfirmation Whether the request requires an confirmation dialog.
* @param titleOrAltText The 'title' or, if empty, the 'alt' attribute of the image.
* @param lensIntentType The intent type of the request.
* @return The intent to Google Lens.
*/
public Intent getShareWithGoogleLensIntent(Uri imageUri, boolean isIncognito, String srcUrl,
boolean requiresConfirmation, String titleOrAltText, @Nullable String lensIntentType) {
return null;
}
// TODO(yusuyoutube): deprecate this function and switch to use the Direct Intent API.
/**
* Get a deeplink intent to Google Lens with an optional content provider image
* URI. The intent should be constructed immediately before the intent is fired
* to ensure that the launch timestamp is accurate.
*
* @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.
* @param srcUrl The 'src' attribute of the image.
* @param titleOrAltText The 'title' or, if empty, the 'alt' attribute of the image.
* @param pageUrl The url of the top level frame of the page.
* @param requiresConfirmation Whether the request requires an confirmation dialog. * @param requiresConfirmation Whether the request requires an confirmation dialog.
* @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 pageUrl The url of the current page.
* @param lensIntentType The intent type of the request. * @param lensIntentType The intent type of the request.
* @return The intent to Google Lens. * @return The intent to Google Lens.
*/ */
public Intent getShareWithGoogleLensIntent(Uri imageUri, boolean isIncognito, String srcUrl, public Intent getShareWithGoogleLensIntent(Uri imageUri, boolean isIncognito, String srcUrl,
boolean requiresConfirmation, String pageUrl, String titleOrAltText, boolean requiresConfirmation, String titleOrAltText, String pageUrl,
@Nullable String lensIntentType) { @Nullable String lensIntentType) {
return null; return null;
} }
...@@ -127,19 +102,18 @@ public class LensController { ...@@ -127,19 +102,18 @@ public class LensController {
* @param intent The intent to Google Lens. * @param intent The intent to Google Lens.
*/ */
public void startLens(WindowAndroid window, Intent intent) {} public void startLens(WindowAndroid window, Intent intent) {}
/** /**
* Retrieve the Text resource id for "Shop with Google Lens". * Retrieve the Text resource id for "Shop with Google Lens".
* @return The resource id for "Shop with Google Lens" string. * @return The resource id for "Shop with Google Lens" string.
*/ */
protected @StringRes int getShopWithGoogleLensTextResourceId() { protected @StringRes int getShopWithGoogleLensTextResourceId() {
return R.string.contextmenu_shop_image_with_google_lens; return R.string.contextmenu_shop_image_with_google_lens;
} }
/** /**
* Retrieve the Lens icon resource id. * Retrieve the Lens icon resource id.
* Need to put the resource id on the base class to suppress the UnusedResources warning. * Need to put the resource id on the base class to suppress the UnusedResources warning.
* @return The resource id for Lens icon * @return The resource id for Lens icon.
*/ */
protected @DrawableRes int getLensIconResourceId() { protected @DrawableRes int getLensIconResourceId() {
return R.drawable.lens_icon; return R.drawable.lens_icon;
......
...@@ -17,12 +17,9 @@ import androidx.test.filters.SmallTest; ...@@ -17,12 +17,9 @@ import androidx.test.filters.SmallTest;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
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;
...@@ -44,12 +41,13 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa ...@@ -44,12 +41,13 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa
// 16 (close button end padding) // 16 (close button end padding)
private static final int EXPECTEED_CHIP_WIDTH_DP = 234; private static final int EXPECTEED_CHIP_WIDTH_DP = 234;
private final Runnable mEmptyChipClickCallbackForTesting = () -> {
return;
};
private float mMeasuredDeviceDensity; private float mMeasuredDeviceDensity;
private View mAnchorView; private View mAnchorView;
@Mock
LensAsyncManager mLensAsyncManager;
@BeforeClass @BeforeClass
public static void setUpBeforeActivityLaunched() { public static void setUpBeforeActivityLaunched() {
DummyUiActivity.setTestLayout(R.layout.context_menu_fullscreen_container); DummyUiActivity.setTestLayout(R.layout.context_menu_fullscreen_container);
...@@ -68,30 +66,15 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa ...@@ -68,30 +66,15 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa
@Test @Test
@SmallTest @SmallTest
public void testChipNotShownWhenCallbackReturnsFalse() { public void testChipShownWhenCallbackReturnsChipRenderParams() {
RevampedContextMenuChipController chipController = new RevampedContextMenuChipController( RevampedContextMenuChipController chipController =
getActivity(), mAnchorView, mLensAsyncManager); new RevampedContextMenuChipController(getActivity(), mAnchorView);
TestThreadUtils.runOnUiThreadBlocking(() -> {
chipController.handleImageClassification((new LensQueryResult.Builder()).build());
});
assertNotNull("Anchor view was not initialized.", mAnchorView);
assertNull("Popup window was initialized unexpectedly.",
chipController.getCurrentPopupWindowForTesting());
}
@Test
@SmallTest
public void testChipShownWhenCallbackReturnsTrue() {
RevampedContextMenuChipController chipController = new RevampedContextMenuChipController(
getActivity(), mAnchorView, mLensAsyncManager);
TestThreadUtils.runOnUiThreadBlocking(() -> { TestThreadUtils.runOnUiThreadBlocking(() -> {
chipController.handleImageClassification( ChipRenderParams chipRenderParams = new ChipRenderParams();
(new LensQueryResult.Builder()) chipRenderParams.titleResourceId = R.string.contextmenu_shop_image_with_google_lens;
.withIsShoppyIntent(true) chipRenderParams.iconResourceId = R.drawable.lens_icon;
.withLensIntentType(LensUtils.getLensShoppingIntentType()) chipRenderParams.onClickCallback = mEmptyChipClickCallbackForTesting;
.build()); chipController.showChip(chipRenderParams);
}); });
assertNotNull("Anchor view was not initialized.", mAnchorView); assertNotNull("Anchor view was not initialized.", mAnchorView);
...@@ -104,8 +87,8 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa ...@@ -104,8 +87,8 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa
@Test @Test
@SmallTest @SmallTest
public void testDismissChipWhenNotShownBeforeClassificationReturned() { public void testDismissChipWhenNotShownBeforeClassificationReturned() {
RevampedContextMenuChipController chipController = new RevampedContextMenuChipController( RevampedContextMenuChipController chipController =
getActivity(), mAnchorView, mLensAsyncManager); new RevampedContextMenuChipController(getActivity(), mAnchorView);
TestThreadUtils.runOnUiThreadBlocking(() -> { chipController.dismissLensChipIfShowing(); }); TestThreadUtils.runOnUiThreadBlocking(() -> { chipController.dismissLensChipIfShowing(); });
assertNotNull("Anchor view was not initialized.", mAnchorView); assertNotNull("Anchor view was not initialized.", mAnchorView);
...@@ -116,14 +99,14 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa ...@@ -116,14 +99,14 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa
@Test @Test
@SmallTest @SmallTest
public void testDismissChipWhenShown() { public void testDismissChipWhenShown() {
RevampedContextMenuChipController chipController = new RevampedContextMenuChipController( RevampedContextMenuChipController chipController =
getActivity(), mAnchorView, mLensAsyncManager); new RevampedContextMenuChipController(getActivity(), mAnchorView);
TestThreadUtils.runOnUiThreadBlocking(() -> { TestThreadUtils.runOnUiThreadBlocking(() -> {
chipController.handleImageClassification( ChipRenderParams chipRenderParams = new ChipRenderParams();
(new LensQueryResult.Builder()) chipRenderParams.titleResourceId = R.string.contextmenu_shop_image_with_google_lens;
.withIsShoppyIntent(true) chipRenderParams.iconResourceId = R.drawable.lens_icon;
.withLensIntentType(LensUtils.getLensShoppingIntentType()) chipRenderParams.onClickCallback = mEmptyChipClickCallbackForTesting;
.build()); chipController.showChip(chipRenderParams);
chipController.dismissLensChipIfShowing(); chipController.dismissLensChipIfShowing();
}); });
...@@ -137,8 +120,8 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa ...@@ -137,8 +120,8 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa
@Test @Test
@SmallTest @SmallTest
public void testExpectedVerticalPxNeededForChip() { public void testExpectedVerticalPxNeededForChip() {
RevampedContextMenuChipController chipController = new RevampedContextMenuChipController( RevampedContextMenuChipController chipController =
getActivity(), mAnchorView, mLensAsyncManager); new RevampedContextMenuChipController(getActivity(), mAnchorView);
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());
...@@ -147,8 +130,8 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa ...@@ -147,8 +130,8 @@ public class RevampedContextMenuChipControllerTest extends DummyUiActivityTestCa
@Test @Test
@SmallTest @SmallTest
public void testExpectedChipTextMaxWidthPx() { public void testExpectedChipTextMaxWidthPx() {
RevampedContextMenuChipController chipController = new RevampedContextMenuChipController( RevampedContextMenuChipController chipController =
getActivity(), mAnchorView, mLensAsyncManager); new RevampedContextMenuChipController(getActivity(), mAnchorView);
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());
......
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