Commit 034cc628 authored by Xi Han's avatar Xi Han Committed by Commit Bot

[Start] Open Feeds in Tabs in StartSurface.

In this CL, Feeds articles are opened as a Tab instead of CCT from the
StartSurface. When back button is pressed from the Tab, it will return
to the original Feed articles on the StartSurface. Also support to open
the Feeds articles in incognito tab.
Demo: https://drive.google.com/file/d/1r3H3_S8EZms33vspI_IarE4NH3Eh9aI3/view?usp=sharing

Bug: 1108451
Change-Id: I651b2378970f0ed53d2b663cdd97fd2872057ad7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2424639
Commit-Queue: Xi Han <hanxi@chromium.org>
Reviewed-by: default avatarGanggui Tang <gogerald@chromium.org>
Reviewed-by: default avatarFilip Gorski <fgorski@chromium.org>
Reviewed-by: default avatarWei-Yin Chen (陳威尹) <wychen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#812823}
parent 9af3ef99
......@@ -86,7 +86,7 @@ class ExploreSurfaceCoordinator implements FeedSurfaceDelegate {
boolean isInNightMode, boolean isPlaceholderShown,
BottomSheetController bottomSheetController) {
if (mExploreSurfaceNavigationDelegate == null) {
mExploreSurfaceNavigationDelegate = new ExploreSurfaceNavigationDelegate(mActivity);
mExploreSurfaceNavigationDelegate = new ExploreSurfaceNavigationDelegate();
}
Profile profile = Profile.getLastUsedRegularProfile();
......
......@@ -4,17 +4,11 @@
package org.chromium.chrome.features.start_surface;
import android.content.Context;
import android.net.Uri;
import android.provider.Browser;
import androidx.annotation.Nullable;
import androidx.browser.customtabs.CustomTabsIntent;
import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.native_page.NativePageNavigationDelegate;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.start_surface.R;
import org.chromium.chrome.browser.tasks.ReturnToChromeExperimentsUtil;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.ui.base.PageTransition;
import org.chromium.ui.mojom.WindowOpenDisposition;
......@@ -22,34 +16,21 @@ import org.chromium.ui.mojom.WindowOpenDisposition;
/** Implementation of the {@link NativePageNavigationDelegate} for the explore surface. */
class ExploreSurfaceNavigationDelegate implements NativePageNavigationDelegate {
private static final String NEW_TAB_URL_HELP = "https://support.google.com/chrome/?p=new_tab";
private final Context mContext;
ExploreSurfaceNavigationDelegate(Context context) {
mContext = context;
}
ExploreSurfaceNavigationDelegate() {}
@Override
public boolean isOpenInNewWindowEnabled() {
return false;
}
// TODO(crbug.com/982018): Experiment opening feeds in normal Tabs.
@Override
@Nullable
public Tab openUrl(int windowOpenDisposition, LoadUrlParams loadUrlParams) {
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
builder.setShowTitle(true);
builder.setStartAnimations(mContext, R.anim.abc_grow_fade_in_from_bottom, 0);
builder.setExitAnimations(mContext, 0, R.anim.abc_shrink_fade_out_from_bottom);
CustomTabsIntent customTabsIntent = builder.build();
customTabsIntent.intent.setPackage(mContext.getPackageName());
customTabsIntent.intent.putExtra(IntentHandler.EXTRA_OPEN_NEW_INCOGNITO_TAB,
(windowOpenDisposition == WindowOpenDisposition.OFF_THE_RECORD) ? true : false);
customTabsIntent.intent.putExtra(Browser.EXTRA_APPLICATION_ID, mContext.getPackageName());
customTabsIntent.launchUrl(mContext, Uri.parse(loadUrlParams.getUrl()));
// TODO(crbug.com/982018): Return the opened tab and make sure it is opened in incoginito
// mode accordingly (note that payment window supports incognito mode).
boolean result = ReturnToChromeExperimentsUtil.willHandleLoadUrlFromStartSurface(
loadUrlParams.getUrl(), PageTransition.AUTO_BOOKMARK,
windowOpenDisposition == WindowOpenDisposition.OFF_THE_RECORD);
assert result;
return null;
}
......
......@@ -54,6 +54,7 @@ import org.chromium.chrome.browser.omnibox.UrlFocusChangeListener;
import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
import org.chromium.chrome.browser.preferences.Pref;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabLaunchType;
import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.tabmodel.TabModelObserver;
......@@ -641,7 +642,13 @@ class StartSurfaceMediator
}
mPropertyModel.set(IS_SHOWING_OVERVIEW, false);
destroyFeedSurfaceCoordinator();
if (mTabModelSelector.getCurrentTab() == null
|| mTabModelSelector.getCurrentTab().getLaunchType()
!= TabLaunchType.FROM_START_SURFACE) {
// TODO(https://crbug.com/1132852): Destroy FeedSurfaceCoordinator if users don't
// navigate back to Start after a while.
destroyFeedSurfaceCoordinator();
}
if (mNormalTabModelObserver != null) {
mNormalTabModel.removeObserver(mNormalTabModelObserver);
}
......
......@@ -7,7 +7,10 @@ package org.chromium.chrome.features.start_surface;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
......@@ -59,11 +62,13 @@ import org.chromium.base.ContextUtils;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
import org.chromium.chrome.browser.compositor.layouts.OverviewModeState;
import org.chromium.chrome.browser.feed.FeedSurfaceCoordinator;
import org.chromium.chrome.browser.night_mode.NightModeStateProvider;
import org.chromium.chrome.browser.ntp.FakeboxDelegate;
import org.chromium.chrome.browser.omnibox.UrlFocusChangeListener;
import org.chromium.chrome.browser.omnibox.voice.VoiceRecognitionHandler;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabLaunchType;
import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.tabmodel.TabModelObserver;
......@@ -93,6 +98,8 @@ public class StartSurfaceMediatorUnitTest {
@Mock
private TabModelSelector mTabModelSelector;
@Mock
private Tab mTab;
@Mock
private TabModel mNormalTabModel;
@Mock
private TabModel mIncognitoTabModel;
......@@ -101,6 +108,8 @@ public class StartSurfaceMediatorUnitTest {
@Mock
private ExploreSurfaceCoordinator.FeedSurfaceCreator mFeedSurfaceCreator;
@Mock
private FeedSurfaceCoordinator mFeedSurfaceCoordinator;
@Mock
private NightModeStateProvider mNightModeStateProvider;
@Mock
private BrowserControlsStateProvider mBrowserControlsStateProvider;
......@@ -144,6 +153,7 @@ public class StartSurfaceMediatorUnitTest {
.when(mSecondaryTasksSurfaceInitializer)
.initialize();
doReturn(false).when(mActivityStateChecker).isFinishingOrDestroyed();
doReturn(mTab).when(mTabModelSelector).getCurrentTab();
}
@After
......@@ -1505,6 +1515,50 @@ public class StartSurfaceMediatorUnitTest {
assertThat(mediator.getOverviewState(), equalTo(OverviewModeState.SHOWN_TABSWITCHER));
}
@Test
public void singleShowingPreviousFromATabOfFeeds() {
doReturn(false).when(mTabModelSelector).isIncognitoSelected();
doReturn(mVoiceRecognitionHandler).when(mFakeBoxDelegate).getVoiceRecognitionHandler();
doReturn(true).when(mVoiceRecognitionHandler).isVoiceSearchEnabled();
StartSurfaceMediator mediator = createStartSurfaceMediator(SurfaceMode.SINGLE_PANE, false);
InOrder mainTabGridController = inOrder(mMainTabGridController);
mainTabGridController.verify(mMainTabGridController)
.addOverviewModeObserver(mOverviewModeObserverCaptor.capture());
assertThat(mediator.getOverviewState(), equalTo(OverviewModeState.NOT_SHOWN));
mediator.setOverviewState(OverviewModeState.SHOWING_HOMEPAGE);
mPropertyModel.set(IS_EXPLORE_SURFACE_VISIBLE, true);
when(mFeedSurfaceCreator.createFeedSurfaceCoordinator(anyBoolean(), anyBoolean()))
.thenReturn(mFeedSurfaceCoordinator);
mediator.showOverview(false);
mainTabGridController.verify(mMainTabGridController).showOverview(eq(false));
assertThat(mediator.getOverviewState(), equalTo(OverviewModeState.SHOWN_HOMEPAGE));
assertThat(mPropertyModel.get(FEED_SURFACE_COORDINATOR), equalTo(mFeedSurfaceCoordinator));
doReturn(TabLaunchType.FROM_START_SURFACE).when(mTab).getLaunchType();
mediator.hideOverview(true);
mOverviewModeObserverCaptor.getValue().startedHiding();
mOverviewModeObserverCaptor.getValue().finishedHiding();
assertThat(mPropertyModel.get(FEED_SURFACE_COORDINATOR), equalTo(mFeedSurfaceCoordinator));
FeedSurfaceCoordinator feedSurfaceCoordinator = mock(FeedSurfaceCoordinator.class);
assertNotEquals(mFeedSurfaceCoordinator, feedSurfaceCoordinator);
when(mFeedSurfaceCreator.createFeedSurfaceCoordinator(anyBoolean(), anyBoolean()))
.thenReturn(feedSurfaceCoordinator);
mediator.setOverviewState(OverviewModeState.SHOWING_PREVIOUS);
mediator.showOverview(false);
mainTabGridController.verify(mMainTabGridController).showOverview(eq(false));
assertThat(mediator.getOverviewState(), equalTo(OverviewModeState.SHOWN_HOMEPAGE));
assertThat(mPropertyModel.get(FEED_SURFACE_COORDINATOR), equalTo(mFeedSurfaceCoordinator));
doReturn(TabLaunchType.FROM_LINK).when(mTab).getLaunchType();
mediator.hideOverview(true);
mOverviewModeObserverCaptor.getValue().startedHiding();
mOverviewModeObserverCaptor.getValue().finishedHiding();
assertNull(mPropertyModel.get(FEED_SURFACE_COORDINATOR));
}
@Test
public void changeTopContentOffset() {
doReturn(false).when(mTabModelSelector).isIncognitoSelected();
......
......@@ -149,7 +149,7 @@ class MostVisitedListCoordinator implements TileGroup.Observer, TileGroup.TileSe
@Override
public void onClick(View v) {
ReturnToChromeExperimentsUtil.willHandleLoadUrlFromStartSurface(
mTile.getUrl().getSpec(), PageTransition.AUTO_BOOKMARK);
mTile.getUrl().getSpec(), PageTransition.AUTO_BOOKMARK, null /*incognito*/);
}
@Override
......
......@@ -82,7 +82,7 @@ public class TrendyTermsCoordinator {
RecordUserAction.record("StartSurface.TrendyTerms.TapTerm");
String url = TemplateUrlServiceFactory.get().getUrlForSearchQuery(trendyTerm);
ReturnToChromeExperimentsUtil.willHandleLoadUrlFromStartSurface(
url, PageTransition.AUTO_BOOKMARK);
url, PageTransition.AUTO_BOOKMARK, null /*incognito*/);
};
PropertyModel trendInfo =
new PropertyModel.Builder(TrendyTermsProperties.ALL_KEYS)
......
......@@ -991,7 +991,7 @@ public class LocationBarLayout extends FrameLayout
// TODO(crbug.com/1085812): Should be taking a fulll loaded LoadUrlParams.
if (ReturnToChromeExperimentsUtil.willHandleLoadUrlWithPostDataFromStartSurface(
url, transition, postDataType, postData)) {
url, transition, postDataType, postData, mToolbarDataProvider.isIncognito())) {
return;
}
......
......@@ -23,7 +23,6 @@ import org.chromium.chrome.browser.flags.IntCachedFieldTrialParameter;
import org.chromium.chrome.browser.homepage.HomepageManager;
import org.chromium.chrome.browser.locale.LocaleManager;
import org.chromium.chrome.browser.tab.TabLaunchType;
import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.tasks.pseudotab.PseudoTab;
import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
......@@ -139,11 +138,13 @@ public final class ReturnToChromeExperimentsUtil {
*
* @param url The URL to load.
* @param transition The page transition type.
* @param incognito Whether to load URL in an incognito Tab.
* @return true if we have handled the navigation, false otherwise.
*/
public static boolean willHandleLoadUrlFromStartSurface(
String url, @PageTransition int transition) {
return willHandleLoadUrlWithPostDataFromStartSurface(url, transition, null, null);
String url, @PageTransition int transition, @Nullable Boolean incognito) {
return willHandleLoadUrlWithPostDataFromStartSurface(
url, transition, null, null, incognito);
}
/**
......@@ -155,24 +156,33 @@ public final class ReturnToChromeExperimentsUtil {
* @param postDataType postData type.
* @param postData POST data to include in the tab URL's request body, ex. bitmap when
* image search.
* @param incognito Whether to load URL in an incognito Tab. If null, the current tab model will
* be used.
* @return true if we have handled the navigation, false otherwise.
*/
public static boolean willHandleLoadUrlWithPostDataFromStartSurface(String url,
@PageTransition int transition, @Nullable String postDataType,
@Nullable byte[] postData) {
@Nullable byte[] postData, @Nullable Boolean incognito) {
ChromeActivity chromeActivity = getActivityPresentingOverviewWithOmnibox();
if (chromeActivity == null) return false;
// Create a new unparented tab.
TabModel model = chromeActivity.getCurrentTabModel();
boolean incognitoParam;
if (incognito == null) {
incognitoParam = chromeActivity.getCurrentTabModel().isIncognito();
} else {
incognitoParam = incognito;
}
LoadUrlParams params = new LoadUrlParams(url);
// TODO(https://crbug.com/1134187): This may no longer accurate.
params.setTransitionType(transition | PageTransition.FROM_ADDRESS_BAR);
if (!TextUtils.isEmpty(postDataType) && postData != null && postData.length != 0) {
params.setVerbatimHeaders("Content-Type: " + postDataType);
params.setPostData(ResourceRequestBody.createFromBytes(postData));
}
chromeActivity.getTabCreator(model.isIncognito())
chromeActivity.getTabCreator(incognitoParam)
.createNewTab(params, TabLaunchType.FROM_START_SURFACE, null);
if (transition == PageTransition.AUTO_BOOKMARK) {
......
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