Commit a08a277e authored by ckitagawa's avatar ckitagawa Committed by Commit Bot

[Paint Preview] Add the ability to resolve link clicks

This CL handles link clicking for Paint Previews. The C++ hit testing
was hooked up in a previous CL. This CL implements the Java side by
propagating the URL to an endpoint that understands URLs and knows how
to handle them e.g. loading the link in a Tab.

Bug: 1061434
Change-Id: If7b4e1bc6f5a8f6df290f99c5d8cf3acdf934191
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2110528
Commit-Queue: Calder Kitagawa <ckitagawa@chromium.org>
Reviewed-by: default avatarMehran Mahmoudi <mahmoudi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#752455}
parent d748b03c
...@@ -11,8 +11,10 @@ import org.chromium.chrome.browser.paint_preview.services.PaintPreviewDemoServic ...@@ -11,8 +11,10 @@ import org.chromium.chrome.browser.paint_preview.services.PaintPreviewDemoServic
import org.chromium.chrome.browser.paint_preview.services.PaintPreviewDemoServiceFactory; import org.chromium.chrome.browser.paint_preview.services.PaintPreviewDemoServiceFactory;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
import org.chromium.components.paintpreview.player.PlayerManager; import org.chromium.components.paintpreview.player.PlayerManager;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.UiThreadTaskTraits; import org.chromium.content_public.browser.UiThreadTaskTraits;
import org.chromium.ui.widget.Toast; import org.chromium.ui.widget.Toast;
import org.chromium.url.GURL;
/** /**
* Responsible for displaying the Paint Preview demo. When displaying, the Paint Preview will * Responsible for displaying the Paint Preview demo. When displaying, the Paint Preview will
...@@ -45,6 +47,7 @@ public class PaintPreviewDemoManager implements Destroyable { ...@@ -45,6 +47,7 @@ public class PaintPreviewDemoManager implements Destroyable {
if (success) { if (success) {
mPlayerManager = new PlayerManager(mTab.getUrl(), mTab.getContext(), mPlayerManager = new PlayerManager(mTab.getUrl(), mTab.getContext(),
mPaintPreviewDemoService, String.valueOf(mTab.getId()), mPaintPreviewDemoService, String.valueOf(mTab.getId()),
PaintPreviewDemoManager.this::onLinkClicked,
safeToShow -> { addPlayerView(safeToShow); }); safeToShow -> { addPlayerView(safeToShow); });
} }
int toastStringRes = success ? R.string.paint_preview_demo_capture_success int toastStringRes = success ? R.string.paint_preview_demo_capture_success
...@@ -83,6 +86,12 @@ public class PaintPreviewDemoManager implements Destroyable { ...@@ -83,6 +86,12 @@ public class PaintPreviewDemoManager implements Destroyable {
&& mPlayerManager.getView().getParent() == mTab.getContentView(); && mPlayerManager.getView().getParent() == mTab.getContentView();
} }
private void onLinkClicked(GURL url) {
if (mTab == null || !url.isValid() || url.isEmpty()) return;
mTab.loadUrl(new LoadUrlParams(url.getSpec()));
}
@Override @Override
public void destroy() { public void destroy() {
if (mPaintPreviewDemoService != null) { if (mPaintPreviewDemoService != null) {
......
...@@ -9,6 +9,8 @@ import org.chromium.chrome.browser.paint_preview.services.PaintPreviewTabService ...@@ -9,6 +9,8 @@ import org.chromium.chrome.browser.paint_preview.services.PaintPreviewTabService
import org.chromium.chrome.browser.paint_preview.services.PaintPreviewTabServiceFactory; import org.chromium.chrome.browser.paint_preview.services.PaintPreviewTabServiceFactory;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
import org.chromium.components.paintpreview.player.PlayerManager; import org.chromium.components.paintpreview.player.PlayerManager;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.url.GURL;
/** /**
* Responsible for checking for and displaying Paint Previews that are associated with a * Responsible for checking for and displaying Paint Previews that are associated with a
...@@ -38,6 +40,7 @@ public class TabbedPaintPreviewPlayer { ...@@ -38,6 +40,7 @@ public class TabbedPaintPreviewPlayer {
if (hasCapture) { if (hasCapture) {
mPlayerManager = new PlayerManager(mTab.getUrl(), mTab.getContext(), mPlayerManager = new PlayerManager(mTab.getUrl(), mTab.getContext(),
mPaintPreviewTabService, String.valueOf(mTab.getId()), mPaintPreviewTabService, String.valueOf(mTab.getId()),
TabbedPaintPreviewPlayer.this::onLinkClicked,
safeToShow -> { addPlayerView(safeToShow, shownCallback); }); safeToShow -> { addPlayerView(safeToShow, shownCallback); });
} }
...@@ -66,4 +69,11 @@ public class TabbedPaintPreviewPlayer { ...@@ -66,4 +69,11 @@ public class TabbedPaintPreviewPlayer {
shownCallback.onResult(safeToShow); shownCallback.onResult(safeToShow);
} }
private void onLinkClicked(GURL url) {
if (mTab == null || !url.isValid() || url.isEmpty()) return;
mTab.loadUrl(new LoadUrlParams(url.getSpec()));
removePaintPreview();
}
} }
...@@ -47,6 +47,7 @@ android_library("java") { ...@@ -47,6 +47,7 @@ android_library("java") {
annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
sources = [ sources = [
"java/src/org/chromium/components/paintpreview/player/LinkClickHandler.java",
"java/src/org/chromium/components/paintpreview/player/PaintPreviewFrame.java", "java/src/org/chromium/components/paintpreview/player/PaintPreviewFrame.java",
"java/src/org/chromium/components/paintpreview/player/PlayerCompositorDelegate.java", "java/src/org/chromium/components/paintpreview/player/PlayerCompositorDelegate.java",
"java/src/org/chromium/components/paintpreview/player/PlayerCompositorDelegateImpl.java", "java/src/org/chromium/components/paintpreview/player/PlayerCompositorDelegateImpl.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.components.paintpreview.player;
import org.chromium.url.GURL;
/**
* Interface for processing link click events from the player's hit tests.
*/
public interface LinkClickHandler { void onLinkClicked(GURL url); }
...@@ -31,11 +31,14 @@ class PlayerCompositorDelegateImpl implements PlayerCompositorDelegate { ...@@ -31,11 +31,14 @@ class PlayerCompositorDelegateImpl implements PlayerCompositorDelegate {
} }
private CompositorListener mCompositorListener; private CompositorListener mCompositorListener;
private LinkClickHandler mLinkClickHandler;
private long mNativePlayerCompositorDelegate; private long mNativePlayerCompositorDelegate;
PlayerCompositorDelegateImpl(NativePaintPreviewServiceProvider service, GURL url, PlayerCompositorDelegateImpl(NativePaintPreviewServiceProvider service, GURL url,
String directoryKey, @Nonnull CompositorListener compositorListener) { String directoryKey, @Nonnull CompositorListener compositorListener,
@Nonnull LinkClickHandler linkClickHandler) {
mCompositorListener = compositorListener; mCompositorListener = compositorListener;
mLinkClickHandler = linkClickHandler;
if (service != null && service.getNativeService() != 0) { if (service != null && service.getNativeService() != 0) {
mNativePlayerCompositorDelegate = PlayerCompositorDelegateImplJni.get().initialize( mNativePlayerCompositorDelegate = PlayerCompositorDelegateImplJni.get().initialize(
this, service.getNativeService(), url.getSpec(), directoryKey); this, service.getNativeService(), url.getSpec(), directoryKey);
...@@ -102,7 +105,7 @@ class PlayerCompositorDelegateImpl implements PlayerCompositorDelegate { ...@@ -102,7 +105,7 @@ class PlayerCompositorDelegateImpl implements PlayerCompositorDelegate {
@CalledByNative @CalledByNative
public void onLinkClicked(String url) { public void onLinkClicked(String url) {
// TODO(crbug/1061434): Handle navigation to provided URL. mLinkClickHandler.onLinkClicked(new GURL(url));
} }
void destroy() { void destroy() {
......
...@@ -21,6 +21,8 @@ import org.chromium.url.GURL; ...@@ -21,6 +21,8 @@ import org.chromium.url.GURL;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.annotation.Nonnull;
/** /**
* This is the only public class in this package and is hence the access point of this component for * This is the only public class in this package and is hence the access point of this component for
* the outer world. Users should call {@link #destroy()} to ensure the native part is destroyed. * the outer world. Users should call {@link #destroy()} to ensure the native part is destroyed.
...@@ -34,10 +36,11 @@ public class PlayerManager { ...@@ -34,10 +36,11 @@ public class PlayerManager {
public PlayerManager(GURL url, Context context, public PlayerManager(GURL url, Context context,
NativePaintPreviewServiceProvider nativePaintPreviewServiceProvider, NativePaintPreviewServiceProvider nativePaintPreviewServiceProvider,
String directoryKey, Callback<Boolean> viewReadyCallback) { String directoryKey, @Nonnull LinkClickHandler linkClickHandler,
Callback<Boolean> viewReadyCallback) {
mContext = context; mContext = context;
mDelegate = new PlayerCompositorDelegateImpl( mDelegate = new PlayerCompositorDelegateImpl(nativePaintPreviewServiceProvider, url,
nativePaintPreviewServiceProvider, url, directoryKey, this::onCompositorReady); directoryKey, this::onCompositorReady, linkClickHandler);
mHostView = new FrameLayout(mContext); mHostView = new FrameLayout(mContext);
mViewReadyCallback = viewReadyCallback; mViewReadyCallback = viewReadyCallback;
} }
......
...@@ -4,7 +4,9 @@ ...@@ -4,7 +4,9 @@
package org.chromium.components.paintpreview.player; package org.chromium.components.paintpreview.player;
import android.os.SystemClock;
import android.support.test.filters.MediumTest; import android.support.test.filters.MediumTest;
import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
...@@ -32,23 +34,43 @@ public class PaintPreviewPlayerTest extends DummyUiActivityTestCase { ...@@ -32,23 +34,43 @@ public class PaintPreviewPlayerTest extends DummyUiActivityTestCase {
private static final String TEST_DATA_DIR = "components/test/data/"; private static final String TEST_DATA_DIR = "components/test/data/";
private static final String TEST_DIRECTORY_KEY = "wikipedia"; private static final String TEST_DIRECTORY_KEY = "wikipedia";
private static final String TEST_URL = "https://en.m.wikipedia.org/wiki/Main_Page"; private static final String TEST_URL = "https://en.m.wikipedia.org/wiki/Main_Page";
private static final String TEST_EXPECTED_LINK_URL =
"https://en.m.wikipedia.org/wiki/File:Grave_marker_of_J._R._Kealoha_(d._1877).jpg";
@Rule @Rule
public PaintPreviewTestRule mPaintPreviewTestRule = new PaintPreviewTestRule(); public PaintPreviewTestRule mPaintPreviewTestRule = new PaintPreviewTestRule();
private PlayerManager mPlayerManager; private PlayerManager mPlayerManager;
/**
* LinkClickHandler implementation for caching the last URL that was clicked.
*/
public class TestLinkClickHandler implements LinkClickHandler {
private GURL mUrl;
@Override
public void onLinkClicked(GURL url) {
mUrl = url;
}
public GURL getUrl() {
return mUrl;
}
}
/** /**
* Tests the the player correctly initializes and displays a sample paint preview with 1 frame. * Tests the the player correctly initializes and displays a sample paint preview with 1 frame.
*/ */
@Test @Test
@MediumTest @MediumTest
public void singleFrameDisplayTest() { public void singleFrameDisplayTest() {
TestLinkClickHandler linkClickHandler = new TestLinkClickHandler();
PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> { PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> {
PaintPreviewTestService service = PaintPreviewTestService service =
new PaintPreviewTestService(UrlUtils.getIsolatedTestFilePath(TEST_DATA_DIR)); new PaintPreviewTestService(UrlUtils.getIsolatedTestFilePath(TEST_DATA_DIR));
mPlayerManager = new PlayerManager(new GURL(TEST_URL), getActivity(), service, mPlayerManager = new PlayerManager(new GURL(TEST_URL), getActivity(), service,
TEST_DIRECTORY_KEY, success -> { Assert.assertTrue(success); }); TEST_DIRECTORY_KEY, linkClickHandler,
success -> { Assert.assertTrue(success); });
getActivity().setContentView(mPlayerManager.getView()); getActivity().setContentView(mPlayerManager.getView());
}); });
...@@ -76,5 +98,24 @@ public class PaintPreviewPlayerTest extends DummyUiActivityTestCase { ...@@ -76,5 +98,24 @@ public class PaintPreviewPlayerTest extends DummyUiActivityTestCase {
}, },
"Player size doesn't match R.id.content", TIMEOUT_MS, "Player size doesn't match R.id.content", TIMEOUT_MS,
CriteriaHelper.DEFAULT_POLLING_INTERVAL); CriteriaHelper.DEFAULT_POLLING_INTERVAL);
long downTime = SystemClock.uptimeMillis();
long eventTime = downTime + 100;
MotionEvent downEvent = MotionEvent.obtain(
downTime, downTime + 100, MotionEvent.ACTION_DOWN, 67.0f, 527.0f, 0);
MotionEvent upEvent = MotionEvent.obtain(
downTime + 150, downTime + 200, MotionEvent.ACTION_UP, 67.0f, 527.0f, 0);
PostTask.postTask(UiThreadTaskTraits.DEFAULT, () -> {
playerHostView.dispatchTouchEvent(downEvent);
playerHostView.dispatchTouchEvent(upEvent);
});
CriteriaHelper.pollUiThread(() -> {
GURL url = linkClickHandler.getUrl();
if (url == null) return false;
return url.getSpec().equals(TEST_EXPECTED_LINK_URL);
}, "Link press failed", TIMEOUT_MS, CriteriaHelper.DEFAULT_POLLING_INTERVAL);
} }
} }
...@@ -200,13 +200,11 @@ void PlayerCompositorDelegateAndroid::OnClick( ...@@ -200,13 +200,11 @@ void PlayerCompositorDelegateAndroid::OnClick(
gfx::Rect(static_cast<int>(j_x), static_cast<int>(j_y), 1U, 1U)); gfx::Rect(static_cast<int>(j_x), static_cast<int>(j_y), 1U, 1U));
if (res.empty()) if (res.empty())
return; return;
if (res.size() == 1U) {
Java_PlayerCompositorDelegateImpl_onLinkClicked(
env, java_ref_,
base::android::ConvertUTF8ToJavaString(env, res[0]->spec()));
return;
}
// TODO(crbug/1061435): Resolve cases where there are multiple links. // TODO(crbug/1061435): Resolve cases where there are multiple links.
// For now just return the first in the list.
Java_PlayerCompositorDelegateImpl_onLinkClicked(
env, java_ref_,
base::android::ConvertUTF8ToJavaString(env, res[0]->spec()));
} }
void PlayerCompositorDelegateAndroid::Destroy(JNIEnv* env) { void PlayerCompositorDelegateAndroid::Destroy(JNIEnv* env) {
......
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