Commit c1381d9c authored by Sky Malice's avatar Sky Malice Committed by Commit Bot

[Feed] Open Feed articles offline if possible.

Bug: 866126
Change-Id: I4c300a57b7fbb9041419f9806eb0e656a22f1fbd
Reviewed-on: https://chromium-review.googlesource.com/1197791
Commit-Queue: Sky Malice <skym@chromium.org>
Reviewed-by: default avatarFilip Gorski <fgorski@chromium.org>
Reviewed-by: default avatarBecky Zhou <huayinz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#590107}
parent 6eca5f27
......@@ -38,6 +38,7 @@ import org.chromium.chrome.browser.ntp.NewTabPage;
import org.chromium.chrome.browser.ntp.NewTabPageLayout;
import org.chromium.chrome.browser.ntp.SnapScrollHelper;
import org.chromium.chrome.browser.ntp.snippets.SectionHeaderView;
import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.search_engines.TemplateUrlService;
import org.chromium.chrome.browser.snackbar.Snackbar;
......@@ -276,9 +277,12 @@ public class FeedNewTabPage extends NewTabPage {
Profile profile = mTab.getProfile();
mImageLoader = new FeedImageLoader(profile, activity);
ActionApi actionApi = new FeedActionHandler(mNewTabPageManager.getNavigationDelegate(),
() -> FeedProcessScopeFactory.getFeedScheduler().onSuggestionConsumed());
FeedOfflineIndicator offlineIndicator = FeedProcessScopeFactory.getFeedOfflineIndicator();
Runnable consumptionObserver =
() -> FeedProcessScopeFactory.getFeedScheduler().onSuggestionConsumed();
ActionApi actionApi = new FeedActionHandler(mNewTabPageManager.getNavigationDelegate(),
consumptionObserver, offlineIndicator, OfflinePageBridge.getForProfile(profile));
FeedStreamScope streamScope =
feedProcessScope
.createFeedStreamScopeBuilder(activity, mImageLoader, actionApi,
......
......@@ -9,7 +9,10 @@ import android.support.annotation.NonNull;
import com.google.android.libraries.feed.api.knowncontent.ContentMetadata;
import com.google.android.libraries.feed.host.action.ActionApi;
import org.chromium.chrome.browser.feed.FeedOfflineIndicator;
import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
import org.chromium.chrome.browser.suggestions.SuggestionsNavigationDelegate;
import org.chromium.components.offline_items_collection.LaunchLocation;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.ui.base.PageTransition;
import org.chromium.ui.mojom.WindowOpenDisposition;
......@@ -20,23 +23,30 @@ import org.chromium.ui.mojom.WindowOpenDisposition;
public class FeedActionHandler implements ActionApi {
private final SuggestionsNavigationDelegate mDelegate;
private final Runnable mSuggestionConsumedObserver;
private final FeedOfflineIndicator mOfflineIndicator;
private final OfflinePageBridge mOfflinePageBridge;
/**
* @param delegate The {@link SuggestionsNavigationDelegate} that this handler calls when
* handling some of the actions.
* handling some of the actions.
* @param suggestionConsumedObserver An observer that is interested in any time a suggestion is
* consumed by the user.
* consumed by the user.
* @param offlineIndicator Tracks offline pages and can supply this handler with offline ids.
* @param offlinePageBridge Capable of updating {@link LoadUrlParams} to include offline ids.
*/
public FeedActionHandler(@NonNull SuggestionsNavigationDelegate delegate,
@NonNull Runnable suggestionConsumedObserver) {
@NonNull Runnable suggestionConsumedObserver,
@NonNull FeedOfflineIndicator offlineIndicator,
@NonNull OfflinePageBridge offlinePageBridge) {
mDelegate = delegate;
mSuggestionConsumedObserver = suggestionConsumedObserver;
mOfflineIndicator = offlineIndicator;
mOfflinePageBridge = offlinePageBridge;
}
@Override
public void openUrl(String url) {
mDelegate.openUrl(WindowOpenDisposition.CURRENT_TAB, createLoadUrlParams(url));
mSuggestionConsumedObserver.run();
openOfflineIfPossible(WindowOpenDisposition.CURRENT_TAB, url);
}
@Override
......@@ -57,8 +67,7 @@ public class FeedActionHandler implements ActionApi {
@Override
public void openUrlInNewTab(String url) {
mDelegate.openUrl(WindowOpenDisposition.NEW_BACKGROUND_TAB, createLoadUrlParams(url));
mSuggestionConsumedObserver.run();
openOfflineIfPossible(WindowOpenDisposition.NEW_BACKGROUND_TAB, url);
}
@Override
......@@ -68,8 +77,7 @@ public class FeedActionHandler implements ActionApi {
@Override
public void openUrlInNewWindow(String url) {
mDelegate.openUrl(WindowOpenDisposition.NEW_WINDOW, createLoadUrlParams(url));
mSuggestionConsumedObserver.run();
openOfflineIfPossible(WindowOpenDisposition.NEW_WINDOW, url);
}
@Override
......@@ -103,4 +111,28 @@ public class FeedActionHandler implements ActionApi {
private LoadUrlParams createLoadUrlParams(String url) {
return new LoadUrlParams(url, PageTransition.AUTO_BOOKMARK);
}
/**
* Opens the given url in offline mode if possible by setting load params so the offline
* interceptor will handle the request. If there is no offline id, fall back to just opening the
* |url| through the |mDelegate|.
*
* @param disposition How to open the article. Should not be OFF_THE_RECORD, as offline pages
* does not support opening articles while incognito.
* @param url The url of the article. Should match what was previously requested by Feed to
* OfflineIndicatorApi implementation exactly.
*/
private void openOfflineIfPossible(int disposition, String url) {
Long maybeOfflineId = mOfflineIndicator.getOfflineIdIfPageIsOfflined(url);
if (maybeOfflineId == null) {
mDelegate.openUrl(disposition, createLoadUrlParams(url));
} else {
mOfflinePageBridge.getLoadUrlParamsByOfflineId(
maybeOfflineId, LaunchLocation.SUGGESTION, (loadUrlParams) -> {
loadUrlParams.setVerbatimHeaders(loadUrlParams.getExtraHeadersString());
mDelegate.openUrl(disposition, loadUrlParams);
});
}
mSuggestionConsumedObserver.run();
}
}
......@@ -41,6 +41,7 @@ if (enable_feed_in_chrome) {
"junit/src/org/chromium/chrome/browser/feed/FeedJournalStorageTest.java",
"junit/src/org/chromium/chrome/browser/feed/FeedOfflineBridgeTest.java",
"junit/src/org/chromium/chrome/browser/feed/StreamLifecycleManagerTest.java",
"junit/src/org/chromium/chrome/browser/feed/action/FeedActionHandlerTest.java",
]
feed_test_java_sources = [
......
// Copyright 2018 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.feed.action;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.support.test.filters.SmallTest;
import com.google.android.libraries.feed.api.knowncontent.ContentMetadata;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.chromium.base.Callback;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.feed.FeedOfflineIndicator;
import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
import org.chromium.chrome.browser.suggestions.SuggestionsNavigationDelegate;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.ui.mojom.WindowOpenDisposition;
import java.util.Collections;
/**
* Unit tests for {@link FeedActionHandler}.
*/
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class FeedActionHandlerTest {
private static final String TEST_URL = "http://www.one.com/";
private static final Long OFFLINE_ID = 12345L;
@Mock
private SuggestionsNavigationDelegate mDelegate;
@Mock
private Runnable mSuggestionConsumedObserver;
@Mock
private FeedOfflineIndicator mOfflineIndicator;
@Mock
private OfflinePageBridge mOfflinePageBridge;
@Captor
private ArgumentCaptor<Integer> mDispositionCapture;
@Captor
private ArgumentCaptor<LoadUrlParams> mLoadParamsCapture;
@Captor
private ArgumentCaptor<Long> mOfflineIdCapture;
@Captor
private ArgumentCaptor<Callback<LoadUrlParams>> mLoadUrlParamsCallbackCapture;
private FeedActionHandler mActionHandler;
private void verifyOpenedOffline(int expectedDisposition) {
assertEquals(OFFLINE_ID, mOfflineIdCapture.getValue());
verify(mDelegate, times(1))
.openUrl(mDispositionCapture.capture(), mLoadParamsCapture.capture());
assertEquals(expectedDisposition, (int) mDispositionCapture.getValue());
assertTrue(
mLoadParamsCapture.getValue().getVerbatimHeaders().contains(OFFLINE_ID.toString()));
verify(mSuggestionConsumedObserver, times(1)).run();
}
private void verifyOpenedOnline(int expectedDisposition) {
verify(mDelegate, times(1))
.openUrl(mDispositionCapture.capture(), mLoadParamsCapture.capture());
assertEquals((int) mDispositionCapture.getValue(), expectedDisposition);
assertEquals(null, mLoadParamsCapture.getValue().getVerbatimHeaders());
verify(mSuggestionConsumedObserver, times(1)).run();
}
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mActionHandler = new FeedActionHandler(
mDelegate, mSuggestionConsumedObserver, mOfflineIndicator, mOfflinePageBridge);
doAnswer(invocation -> {
LoadUrlParams params = new LoadUrlParams("");
params.setExtraHeaders(Collections.singletonMap("", OFFLINE_ID.toString()));
mLoadUrlParamsCallbackCapture.getValue().onResult(params);
return null;
})
.when(mOfflinePageBridge)
.getLoadUrlParamsByOfflineId(mOfflineIdCapture.capture(), anyInt(),
mLoadUrlParamsCallbackCapture.capture());
}
@Test
@SmallTest
public void testOpenUrlOnline() {
when(mOfflineIndicator.getOfflineIdIfPageIsOfflined(TEST_URL)).thenReturn(OFFLINE_ID);
mActionHandler.openUrl(TEST_URL);
verifyOpenedOffline(WindowOpenDisposition.CURRENT_TAB);
}
@Test
@SmallTest
public void testOpenUrlOffline() {
when(mOfflineIndicator.getOfflineIdIfPageIsOfflined(TEST_URL)).thenReturn(null);
mActionHandler.openUrl(TEST_URL);
verifyOpenedOnline(WindowOpenDisposition.CURRENT_TAB);
}
@Test
@SmallTest
public void testOpenUrlInIncognitoModeWithOffline() {
when(mOfflineIndicator.getOfflineIdIfPageIsOfflined(TEST_URL)).thenReturn(OFFLINE_ID);
mActionHandler.openUrlInIncognitoMode(TEST_URL);
// Even though this page has an offlined version, it should not be used because offline
// pages does not support incognito mode.
verifyOpenedOnline(WindowOpenDisposition.OFF_THE_RECORD);
}
@Test
@SmallTest
public void testOpenUrlInNewTabOnline() {
when(mOfflineIndicator.getOfflineIdIfPageIsOfflined(TEST_URL)).thenReturn(OFFLINE_ID);
mActionHandler.openUrlInNewTab(TEST_URL);
verifyOpenedOffline(WindowOpenDisposition.NEW_BACKGROUND_TAB);
}
@Test
@SmallTest
public void testOpenUrlInNewTabOffline() {
when(mOfflineIndicator.getOfflineIdIfPageIsOfflined(TEST_URL)).thenReturn(null);
mActionHandler.openUrlInNewTab(TEST_URL);
verifyOpenedOnline(WindowOpenDisposition.NEW_BACKGROUND_TAB);
}
@Test
@SmallTest
public void testOpenUrlInNewWindowOnline() {
when(mOfflineIndicator.getOfflineIdIfPageIsOfflined(TEST_URL)).thenReturn(OFFLINE_ID);
mActionHandler.openUrlInNewWindow(TEST_URL);
verifyOpenedOffline(WindowOpenDisposition.NEW_WINDOW);
}
@Test
@SmallTest
public void testOpenUrlInNewWindowOffline() {
when(mOfflineIndicator.getOfflineIdIfPageIsOfflined(TEST_URL)).thenReturn(null);
mActionHandler.openUrlInNewWindow(TEST_URL);
verifyOpenedOnline(WindowOpenDisposition.NEW_WINDOW);
}
@Test
@SmallTest
public void testDownloadUrlWithOffline() {
when(mOfflineIndicator.getOfflineIdIfPageIsOfflined(TEST_URL)).thenReturn(OFFLINE_ID);
ContentMetadata metadata = new ContentMetadata(TEST_URL, "", 0, null, null, null, null);
mActionHandler.downloadUrl(metadata);
// Even though this page has an offlined version, this is not a request to open the page,
// and as such the load params should not be updated with the offline id.
verifyOpenedOnline(WindowOpenDisposition.SAVE_TO_DISK);
}
}
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