blob: a7be9503f604273c8aab5a6bc5f481f9fadf7d87 [file] [log] [blame]
// Copyright 2019 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.touchless;
import android.support.v4.view.ViewCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.TraceEvent;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.GlobalDiscardableReferencePool;
import org.chromium.chrome.browser.native_page.BasicNativePage;
import org.chromium.chrome.browser.native_page.ContextMenuManager;
import org.chromium.chrome.browser.native_page.NativePageHost;
import org.chromium.chrome.browser.ntp.NewTabPage;
import org.chromium.chrome.browser.ntp.NewTabPageUma;
import org.chromium.chrome.browser.ntp.cards.NewTabPageAdapter;
import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.suggestions.SuggestionsDependencyFactory;
import org.chromium.chrome.browser.suggestions.SuggestionsEventReporter;
import org.chromium.chrome.browser.suggestions.SuggestionsNavigationDelegate;
import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegateImpl;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.ui.widget.displaystyle.UiConfig;
import org.chromium.chrome.browser.util.UrlConstants;
import org.chromium.chrome.touchless.R;
import org.chromium.ui.modaldialog.ModalDialogManager;
import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
/**
* Condensed new tab page for touchless devices.
*
* Acts as Coordinator for the new tab view structure, a recyclerview, that recycles content
* suggestions. Will own the coordinators for the other subsections:
* - Most Likley carousel
* - Recent tab button
*/
public class TouchlessNewTabPage extends BasicNativePage {
private static final String TAG = "TouchlessNewTabPage";
private ModalDialogManager mModalDialogManager;
private String mTitle;
private int mBackgroundColor;
private TouchlessNewTabPageMediator mMediator;
private OpenLastTabCoordinator mOpenLastTabCoordinator;
private FrameLayout mView;
private TouchlessNewTabPageTopLayout mRecyclerTopmostView;
private TouchlessRecyclerView mRecyclerView;
private Tab mTab;
private TouchlessContextMenuManager mContextMenuManager;
private SiteSuggestionsCoordinator mSiteSuggestionsCoordinator;
public TouchlessNewTabPage(ChromeActivity activity, NativePageHost host) {
super(activity, host); // Super calls initialize at the beginning of the constructor.
}
@Override
protected void initialize(ChromeActivity activity, NativePageHost nativePageHost) {
TraceEvent.begin(TAG);
mModalDialogManager = activity.getModalDialogManager();
mTab = nativePageHost.getActiveTab();
Profile profile = mTab.getProfile();
mTitle = activity.getResources().getString(R.string.button_new_tab);
mBackgroundColor = ApiCompatibilityUtils.getColor(
activity.getResources(), R.color.modern_primary_color);
mView = (FrameLayout) LayoutInflater.from(activity).inflate(
R.layout.new_tab_page_touchless_view, null);
mMediator = new TouchlessNewTabPageMediator(nativePageHost.getActiveTab());
mRecyclerView = mView.findViewById(R.id.suggestions_recycler_view);
mRecyclerTopmostView = (TouchlessNewTabPageTopLayout) LayoutInflater.from(activity).inflate(
R.layout.new_tab_page_touchless, mRecyclerView, false);
OpenLastTabView openLastTabButton =
mRecyclerTopmostView.findViewById(R.id.open_last_tab_button);
mOpenLastTabCoordinator = new OpenLastTabCoordinator(activity, profile, nativePageHost,
openLastTabButton, mRecyclerView.getTouchlessLayoutManager());
// TODO(dewittj): Initialize the tile suggestions coordinator here.
initializeContentSuggestions(
activity, nativePageHost, mRecyclerView.getTouchlessLayoutManager());
NewTabPageUma.recordIsUserOnline();
NewTabPageUma.recordLoadType(activity);
TraceEvent.end(TAG);
}
/*
* TODO(dewittj): This uses SuggestionsRecyclerView and NewTabPageAdapter in a legacy manner
* that does not properly support modern MVC code architecture.
*/
private void initializeContentSuggestions(ChromeActivity activity,
NativePageHost nativePageHost, TouchlessLayoutManager layoutManager) {
long constructedTimeNs = System.nanoTime();
NewTabPageUma.trackTimeToFirstDraw(mRecyclerView, constructedTimeNs);
Profile profile = mTab.getProfile();
SuggestionsDependencyFactory depsFactory = SuggestionsDependencyFactory.getInstance();
SuggestionsSource suggestionsSource = depsFactory.createSuggestionSource(profile);
SuggestionsEventReporter eventReporter = depsFactory.createEventReporter();
SuggestionsNavigationDelegate navigationDelegate = new SuggestionsNavigationDelegate(
activity, profile, nativePageHost, TabModelSelector.from(mTab));
SuggestionsUiDelegate suggestionsUiDelegate = new SuggestionsUiDelegateImpl(
suggestionsSource, eventReporter, navigationDelegate, profile, nativePageHost,
GlobalDiscardableReferencePool.getReferencePool(), activity.getSnackbarManager());
suggestionsUiDelegate.addDestructionObserver(this::destroy);
assert suggestionsUiDelegate.getSuggestionsSource() != null;
// Don't store a direct reference to the activity, because it might change later if the tab
// is reparented.
Runnable closeContextMenuCallback = () -> mTab.getActivity().closeContextMenu();
mContextMenuManager = new TouchlessContextMenuManager(activity,
activity.getModalDialogManager(), suggestionsUiDelegate.getNavigationDelegate(),
mRecyclerView::setTouchEnabled, closeContextMenuCallback,
NewTabPage.CONTEXT_MENU_USER_ACTION_PREFIX);
mTab.getWindowAndroid().addContextMenuCloseListener(mContextMenuManager);
UiConfig uiConfig = new UiConfig(mRecyclerView);
mRecyclerView.init(uiConfig, closeContextMenuCallback);
// Infinite scrolling view for site suggestions.
mSiteSuggestionsCoordinator = new SiteSuggestionsCoordinator(mRecyclerTopmostView, profile,
navigationDelegate, mContextMenuManager, suggestionsUiDelegate.getImageFetcher(),
layoutManager);
NewTabPageAdapter newTabPageAdapter = new TouchlessNewTabPageAdapter(suggestionsUiDelegate,
mRecyclerTopmostView, uiConfig,
SuggestionsDependencyFactory.getInstance().getOfflinePageBridge(profile),
mContextMenuManager, mMediator.getModel(),
mOpenLastTabCoordinator.getFocusableComponent(),
mSiteSuggestionsCoordinator.getFocusableComponent(),
layoutManager.createCallbackToSetViewToFocus());
PropertyModelChangeProcessor.create(
mMediator.getModel(), mRecyclerView, ContentSuggestionsViewBinder::bind);
newTabPageAdapter.refreshSuggestions();
eventReporter.onSurfaceOpened();
// Set after the Mediator is constructed so that it has time to refresh the suggestions
// before requesting a layout.
mRecyclerView.setAdapter(newTabPageAdapter);
}
@Override
public View getView() {
return mView;
}
@Override
public String getHost() {
return UrlConstants.NTP_HOST;
}
@Override
public String getTitle() {
return mTitle;
}
@Override
public int getBackgroundColor() {
return mBackgroundColor;
}
@Override
public void destroy() {
assert !ViewCompat.isAttachedToWindow(getView())
: "Destroy called before removed from window";
mMediator.destroy();
mTab.getWindowAndroid().removeContextMenuCloseListener(mContextMenuManager);
mSiteSuggestionsCoordinator.destroy();
mOpenLastTabCoordinator.destroy();
super.destroy();
}
public void showContextMenu() {
View focusedView = getView().findFocus();
if (focusedView == null) return;
ContextMenuManager.Delegate delegate =
ContextMenuManager.getDelegateFromFocusedView(focusedView);
if (delegate == null) return;
mContextMenuManager.showTouchlessContextMenu(mModalDialogManager, delegate);
}
}