[go: nahoru, domu]

blob: 9a5890cb1bfa6bdd87390496d2ded5bacf19a0b7 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.features.start_surface;
import android.app.Activity;
import android.content.res.Resources;
import android.os.SystemClock;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.google.android.material.appbar.AppBarLayout;
import org.chromium.base.ActivityState;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.Log;
import org.chromium.base.MathUtils;
import org.chromium.base.ObserverList;
import org.chromium.base.jank_tracker.JankTracker;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.supplier.ObservableSupplier;
import org.chromium.base.supplier.ObservableSupplierImpl;
import org.chromium.base.supplier.OneshotSupplier;
import org.chromium.base.supplier.OneshotSupplierImpl;
import org.chromium.base.supplier.Supplier;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.back_press.BackPressManager;
import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider;
import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
import org.chromium.chrome.browser.feed.FeedSwipeRefreshLayout;
import org.chromium.chrome.browser.feed.ScrollListener;
import org.chromium.chrome.browser.feed.ScrollableContainerDelegate;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.fullscreen.BrowserControlsManager;
import org.chromium.chrome.browser.incognito.reauth.IncognitoReauthController;
import org.chromium.chrome.browser.init.ChromeActivityNativeDelegate;
import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
import org.chromium.chrome.browser.logo.LogoUtils;
import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher;
import org.chromium.chrome.browser.omnibox.OmniboxFeatures;
import org.chromium.chrome.browser.omnibox.OmniboxStub;
import org.chromium.chrome.browser.omnibox.styles.OmniboxResourceProvider;
import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
import org.chromium.chrome.browser.preferences.ChromeSharedPreferences;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.profiles.ProfileManager;
import org.chromium.chrome.browser.query_tiles.QueryTileSection;
import org.chromium.chrome.browser.query_tiles.QueryTileUtils;
import org.chromium.chrome.browser.share.ShareDelegate;
import org.chromium.chrome.browser.suggestions.tile.MostVisitedTilesCoordinator;
import org.chromium.chrome.browser.suggestions.tile.TileGroupDelegateImpl;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.tasks.ReturnToChromeUtil;
import org.chromium.chrome.browser.tasks.tab_management.RecyclerViewPosition;
import org.chromium.chrome.browser.tasks.tab_management.TabManagementDelegate.TabSwitcherType;
import org.chromium.chrome.browser.tasks.tab_management.TabManagementDelegateProvider;
import org.chromium.chrome.browser.tasks.tab_management.TabSwitcher;
import org.chromium.chrome.browser.tasks.tab_management.TabSwitcherCustomViewManager;
import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities;
import org.chromium.chrome.browser.toolbar.top.Toolbar;
import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
import org.chromium.chrome.browser.util.BrowserUiUtils;
import org.chromium.chrome.features.tasks.MostVisitedTileNavigationDelegate;
import org.chromium.chrome.features.tasks.SingleTabSwitcherCoordinator;
import org.chromium.chrome.features.tasks.TasksSurface;
import org.chromium.chrome.features.tasks.TasksSurfaceCoordinator;
import org.chromium.chrome.features.tasks.TasksSurfaceProperties;
import org.chromium.chrome.features.tasks.TasksView;
import org.chromium.chrome.features.tasks.TasksViewBinder;
import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
import org.chromium.components.browser_ui.widget.MenuOrKeyboardActionController;
import org.chromium.components.browser_ui.widget.scrim.ScrimCoordinator;
import org.chromium.components.user_prefs.UserPrefs;
import org.chromium.ui.base.DeviceFormFactor;
import org.chromium.ui.base.ViewUtils;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.modaldialog.ModalDialogManager;
import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
import org.chromium.ui.resources.dynamics.DynamicResourceLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Root coordinator that is responsible for showing start surfaces, like a grid of Tabs, explore
* surface and the bottom bar to switch between them.
*/
public class StartSurfaceCoordinator implements StartSurface {
private final Activity mActivity;
private final ScrimCoordinator mScrimCoordinator;
private final StartSurfaceMediator mStartSurfaceMediator;
private final boolean mIsStartSurfaceEnabled;
private final BottomSheetController mBottomSheetController;
private final Supplier<Tab> mParentTabSupplier;
private final WindowAndroid mWindowAndroid;
private final JankTracker mJankTracker;
private ViewGroup mContainerView;
private final Supplier<DynamicResourceLoader> mDynamicResourceLoaderSupplier;
private final TabModelSelector mTabModelSelector;
private final BrowserControlsManager mBrowserControlsManager;
private final SnackbarManager mSnackbarManager;
private final Supplier<ShareDelegate> mShareDelegateSupplier;
private final Supplier<OmniboxStub> mOmniboxStubSupplier;
private final TabContentManager mTabContentManager;
private final ModalDialogManager mModalDialogManager;
private final ChromeActivityNativeDelegate mChromeActivityNativeDelegate;
private final ActivityLifecycleDispatcher mActivityLifecycleDispatcher;
private final TabCreatorManager mTabCreatorManager;
private final MenuOrKeyboardActionController mMenuOrKeyboardActionController;
private final MultiWindowModeStateDispatcher mMultiWindowModeStateDispatcher;
private final Supplier<Toolbar> mToolbarSupplier;
// TODO(crbug.com/1315676): Directly return the supplier from {@link TabSwitcherCoordinator}.
private final ObservableSupplierImpl<TabSwitcherCustomViewManager>
mTabSwitcherCustomViewManagerSupplier;
private final OneshotSupplier<IncognitoReauthController> mIncognitoReauthControllerSupplier;
@VisibleForTesting
static final String START_SHOWN_AT_STARTUP_UMA = "Startup.Android.StartSurfaceShownAtStartup";
private static final String TAG = "StartSurface";
private static final int MAX_TILE_ROWS_FOR_GRID_MVT = 2;
private final boolean mUseMagicSpace;
// Non-null in SurfaceMode.SINGLE_PANE mode.
@Nullable private TasksSurface mTasksSurface;
// Non-null in SurfaceMode.SINGLE_PANE mode.
@Nullable private PropertyModelChangeProcessor mTasksSurfacePropertyModelChangeProcessor;
private PropertyModelChangeProcessor mStartSurfaceWithParentViewPropertyModelChangeProcessor;
private PropertyModelChangeProcessor mStartSurfacePropertyModelChangeProcessor;
// TODO(crbug.com/1315676): Remove this once the start surface refactoring is done, since the
// secondary tasks surface will go away.
// Non-null in SurfaceMode.SINGLE_PANE mode to show more tabs.
@Nullable private TasksSurface mSecondaryTasksSurface;
// Non-null in SurfaceMode.SINGLE_PANE mode to show more tabs.
@Nullable
private PropertyModelChangeProcessor mSecondaryTasksSurfacePropertyModelChangeProcessor;
// Non-null in SurfaceMode.NO_START_SURFACE to show the tabs.
@Nullable private TabSwitcher mGridTabSwitcher;
// Non-null in SurfaceMode.SINGLE_PANE modes.
@Nullable private ExploreSurfaceCoordinatorFactory mExploreSurfaceCoordinatorFactory;
// Non-null in SurfaceMode.SINGLE_PANE modes.
// TODO(crbug.com/982018): Get rid of this reference since the mediator keeps a reference to it.
@Nullable private PropertyModel mPropertyModel;
// Used to remember TabSwitcher.OnTabSelectingListener in SurfaceMode.SINGLE_PANE mode for more
// tabs surface if necessary.
@Nullable private TabSwitcher.OnTabSelectingListener mOnTabSelectingListener;
// Whether the {@link initWithNative()} is called.
private boolean mIsInitializedWithNative;
// A flag of whether there is a pending call to {@link initialize()} but waiting for native's
// initialization.
private boolean mIsInitPending;
private boolean mIsSecondaryTaskInitPending;
// Listeners used by the contained surfaces (e.g., Explore) to listen to the scroll changes on
// the main scrollable container of the start surface.
private final ObserverList<ScrollListener> mScrollListeners =
new ObserverList<ScrollListener>();
// Time at which constructor started to run. Used for feed reliability logging.
private final long mConstructedTimeNs;
private final boolean mIsStartSurfaceRefactorEnabled;
@Nullable
private AppBarLayout.OnOffsetChangedListener mOffsetChangedListenerToGenerateScrollEvents;
// For pull-to-refresh.
@Nullable private FeedSwipeRefreshLayout mSwipeRefreshLayout;
// The single or carousel Tab switcher module on the Start surface.
// None-null when the Start surface refactoring is enabled.
@Nullable private TabSwitcher mTabSwitcherModule;
// The view of Start surface layout.
// None-null when the Start surface refactoring is enabled.
@Nullable private TasksView mView;
private MostVisitedTilesCoordinator mMostVisitedCoordinator;
private MostVisitedSuggestionsUiDelegate mSuggestionsUiDelegate;
private TileGroupDelegateImpl mTileGroupDelegate;
private ObservableSupplier<Profile> mProfileSupplier;
private QueryTileSection mQueryTileSection;
private boolean mIsMVTilesInitialized;
private final boolean mIsSurfacePolishEnabled;
private class ScrollableContainerDelegateImpl implements ScrollableContainerDelegate {
@Override
public void addScrollListener(ScrollListener listener) {
mScrollListeners.addObserver(listener);
}
@Override
public void removeScrollListener(ScrollListener listener) {
mScrollListeners.removeObserver(listener);
}
@Override
public int getVerticalScrollOffset() {
// Always return 0 because the offset is directly provided by the observer.
return 0;
}
@Override
public int getRootViewHeight() {
return mContainerView.getHeight();
}
@Override
public int getTopPositionRelativeToContainerView(View childView) {
int[] pos = new int[2];
ViewUtils.getRelativeLayoutPosition(mContainerView, childView, pos);
return pos[1];
}
}
/**
* @param activity The current Android {@link Activity}.
* @param scrimCoordinator The coordinator for the scrim widget.
* @param sheetController Controls the bottom sheet.
* @param startSurfaceOneshotSupplier Supplies the start surface.
* @param parentTabSupplier Supplies the current parent {@link Tab}.
* @param hadWarmStart Whether the application had a warm start.
* @param windowAndroid The current {@link WindowAndroid}.a
* @param jankTracker asd
* @param containerView The container {@link ViewGroup} for this ui, also the root view for
* StartSurface.
* @param dynamicResourceLoaderSupplier Supplies the current {@link DynamicResourceLoader}.
* @param tabModelSelector The current {@link TabModelSelector}.
* @param browserControlsManager Manages the browser controls.
* @param snackbarManager Manages the snackbar.
* @param shareDelegateSupplier Supplies the {@link ShareDelegate}.
* @param omniboxStubSupplier Supplies the {@link OmniboxStub}.
* @param tabContentManager Manages the tab content.
* @param modalDialogManager Manages modal dialogs.
* @param chromeActivityNativeDelegate An activity delegate to handle native initialization.
* @param activityLifecycleDispatcher Allows observation of the activity lifecycle.
* @param tabCreatorManager Manages {@link Tab} creation.
* @param menuOrKeyboardActionController allows access to menu or keyboard actions.
* @param multiWindowModeStateDispatcher Gives access to the multi window mode state.
* @param toolbarSupplier Supplies the {@link Toolbar}.
* @param backPressManager {@link BackPressManager} to handle back press.
* @param incognitoReauthControllerSupplier {@link OneshotSupplier<IncognitoReauthController>}
* to detect pending re-auth when tab switcher is shown.
* @param profileSupplier Supplies the {@Profile}.
* @param tabSwitcherClickHandler The {@link OnClickListener} for the tab switcher button.
*/
public StartSurfaceCoordinator(
@NonNull Activity activity,
@NonNull ScrimCoordinator scrimCoordinator,
@NonNull BottomSheetController sheetController,
@NonNull OneshotSupplierImpl<StartSurface> startSurfaceOneshotSupplier,
@NonNull Supplier<Tab> parentTabSupplier,
boolean hadWarmStart,
@NonNull WindowAndroid windowAndroid,
@NonNull JankTracker jankTracker,
@NonNull ViewGroup containerView,
@NonNull Supplier<DynamicResourceLoader> dynamicResourceLoaderSupplier,
@NonNull TabModelSelector tabModelSelector,
@NonNull BrowserControlsManager browserControlsManager,
@NonNull SnackbarManager snackbarManager,
@NonNull Supplier<ShareDelegate> shareDelegateSupplier,
@NonNull Supplier<OmniboxStub> omniboxStubSupplier,
@NonNull TabContentManager tabContentManager,
@NonNull ModalDialogManager modalDialogManager,
@NonNull ChromeActivityNativeDelegate chromeActivityNativeDelegate,
@NonNull ActivityLifecycleDispatcher activityLifecycleDispatcher,
@NonNull TabCreatorManager tabCreatorManager,
@NonNull MenuOrKeyboardActionController menuOrKeyboardActionController,
@NonNull MultiWindowModeStateDispatcher multiWindowModeStateDispatcher,
@NonNull Supplier<Toolbar> toolbarSupplier,
BackPressManager backPressManager,
@NonNull OneshotSupplier<IncognitoReauthController> incognitoReauthControllerSupplier,
@NonNull OnClickListener tabSwitcherClickHandler,
@NonNull ObservableSupplier<Profile> profileSupplier) {
mConstructedTimeNs = SystemClock.elapsedRealtimeNanos();
mActivity = activity;
mScrimCoordinator = scrimCoordinator;
mIsStartSurfaceEnabled = ReturnToChromeUtil.isStartSurfaceEnabled(mActivity);
mBottomSheetController = sheetController;
mParentTabSupplier = parentTabSupplier;
mWindowAndroid = windowAndroid;
mJankTracker = jankTracker;
mContainerView = containerView;
mDynamicResourceLoaderSupplier = dynamicResourceLoaderSupplier;
mTabModelSelector = tabModelSelector;
mBrowserControlsManager = browserControlsManager;
mSnackbarManager = snackbarManager;
mShareDelegateSupplier = shareDelegateSupplier;
mOmniboxStubSupplier = omniboxStubSupplier;
mTabContentManager = tabContentManager;
mModalDialogManager = modalDialogManager;
mChromeActivityNativeDelegate = chromeActivityNativeDelegate;
mActivityLifecycleDispatcher = activityLifecycleDispatcher;
mTabCreatorManager = tabCreatorManager;
mMenuOrKeyboardActionController = menuOrKeyboardActionController;
mMultiWindowModeStateDispatcher = multiWindowModeStateDispatcher;
mToolbarSupplier = toolbarSupplier;
mIncognitoReauthControllerSupplier = incognitoReauthControllerSupplier;
mProfileSupplier = profileSupplier;
mUseMagicSpace = mIsStartSurfaceEnabled && StartSurfaceConfiguration.useMagicSpace();
mTabSwitcherCustomViewManagerSupplier = new ObservableSupplierImpl<>();
boolean excludeQueryTiles =
!mIsStartSurfaceEnabled || !ChromeFeatureList.sQueryTilesOnStart.isEnabled();
mIsStartSurfaceRefactorEnabled =
ReturnToChromeUtil.isStartSurfaceRefactorEnabled(mActivity);
mIsSurfacePolishEnabled = ChromeFeatureList.sSurfacePolish.isEnabled();
TabSwitcher.Controller controller = null;
Runnable initializeMVTilesRunnable = null;
View logoContainerView = null;
ViewGroup feedPlaceholderParentView = null;
if (!mIsStartSurfaceEnabled && !mIsStartSurfaceRefactorEnabled) {
// Create Tab switcher directly to save one layer in the view hierarchy.
mGridTabSwitcher =
TabManagementDelegateProvider.getDelegate()
.createGridTabSwitcher(
activity,
activityLifecycleDispatcher,
tabModelSelector,
tabContentManager,
browserControlsManager,
tabCreatorManager,
menuOrKeyboardActionController,
containerView,
multiWindowModeStateDispatcher,
scrimCoordinator,
/* rootView= */ containerView,
dynamicResourceLoaderSupplier,
snackbarManager,
modalDialogManager,
incognitoReauthControllerSupplier,
backPressManager,
/* layoutStateProviderSupplier= */ null);
mTabSwitcherCustomViewManagerSupplier.set(
mGridTabSwitcher.getTabSwitcherCustomViewManager());
controller = mGridTabSwitcher.getController();
} else if (!mIsStartSurfaceRefactorEnabled) {
assert mIsStartSurfaceEnabled;
// createSwipeRefreshLayout has to be called before creating any surface.
createSwipeRefreshLayout();
createAndSetStartSurface(excludeQueryTiles);
controller = mTasksSurface.getController();
initializeMVTilesRunnable = mTasksSurface::initializeMVTiles;
logoContainerView = mTasksSurface.getView().findViewById(R.id.logo_container);
feedPlaceholderParentView = mTasksSurface.getBodyViewContainer();
} else {
assert mIsStartSurfaceEnabled && mIsStartSurfaceRefactorEnabled;
// createSwipeRefreshLayout has to be called before creating any surface.
createSwipeRefreshLayout();
createStartSurfaceWithoutTasksSurface(excludeQueryTiles);
initializeMVTilesRunnable = this::initializeMVTiles;
logoContainerView = mView.findViewById(R.id.logo_container);
feedPlaceholderParentView = mView.findViewById(R.id.tasks_surface_body);
}
mStartSurfaceMediator =
new StartSurfaceMediator(
controller,
containerView,
mTabSwitcherModule,
mTabModelSelector,
mPropertyModel,
mTasksSurface != null ? this::initializeSecondaryTasksSurface : null,
mIsStartSurfaceEnabled,
mActivity,
mBrowserControlsManager,
this::isActivityFinishingOrDestroyed,
mTabCreatorManager,
excludeQueryTiles,
startSurfaceOneshotSupplier,
hadWarmStart,
initializeMVTilesRunnable,
mParentTabSupplier,
logoContainerView,
mGridTabSwitcher == null ? backPressManager : null,
feedPlaceholderParentView,
mActivityLifecycleDispatcher,
tabSwitcherClickHandler,
mProfileSupplier);
startSurfaceOneshotSupplier.set(this);
}
// Implements StartSurface.
@Override
public void initialize() {
// TODO (crbug.com/1041047): Move more stuff from the constructor to here for lazy
// initialization.
if (!mIsInitializedWithNative) {
mIsInitPending = true;
return;
}
mIsInitPending = false;
if (mTasksSurface != null) {
mTasksSurface.initialize();
}
}
@Override
public void destroy() {
onHide();
if (mOffsetChangedListenerToGenerateScrollEvents != null) {
removeHeaderOffsetChangeListener(mOffsetChangedListenerToGenerateScrollEvents);
mOffsetChangedListenerToGenerateScrollEvents = null;
}
mProfileSupplier = null;
if (mStartSurfaceMediator != null) {
mStartSurfaceMediator.destroy();
}
}
@Override
public void onHide() {
if (mIsInitializedWithNative) {
mStartSurfaceMediator.mayRecordHomepageSessionEnd();
if (mTasksSurface != null) {
mTasksSurface.onHide();
}
if (mSecondaryTasksSurface != null) {
assert !mIsStartSurfaceRefactorEnabled;
mSecondaryTasksSurface.onHide();
}
if (mSuggestionsUiDelegate != null) {
mSuggestionsUiDelegate.onDestroy();
mSuggestionsUiDelegate = null;
}
if (mTileGroupDelegate != null) {
mTileGroupDelegate.destroy();
mTileGroupDelegate = null;
}
if (mMostVisitedCoordinator != null) {
mMostVisitedCoordinator.destroyMvtiles();
mIsMVTilesInitialized = false;
}
}
mStartSurfaceMediator.onHide();
}
@Override
public void show(boolean animate) {
if (!mUseMagicSpace) {
getSingleTabListDelegate().prepareTabSwitcherView();
}
mStartSurfaceMediator.show(animate);
}
@Override
public void hide(boolean animate) {
hideTabSwitcherView(false);
onHide();
}
@Override
public void addHeaderOffsetChangeListener(
AppBarLayout.OnOffsetChangedListener onOffsetChangedListener) {
// TODO (crbug.com/1113852): Add a header offset change listener for incognito homepage.
if (mTasksSurface != null) {
mTasksSurface.addHeaderOffsetChangeListener(onOffsetChangedListener);
} else if (mView != null) {
mView.addHeaderOffsetChangeListener(onOffsetChangedListener);
}
}
@Override
public void removeHeaderOffsetChangeListener(
AppBarLayout.OnOffsetChangedListener onOffsetChangedListener) {
if (mTasksSurface != null) {
mTasksSurface.removeHeaderOffsetChangeListener(onOffsetChangedListener);
} else if (mView != null) {
mView.removeHeaderOffsetChangeListener(onOffsetChangedListener);
}
}
@Override
public void addStateChangeObserver(StateObserver observer) {
mStartSurfaceMediator.addStateChangeObserver(observer);
}
@Override
public void removeStateChangeObserver(StateObserver observer) {
mStartSurfaceMediator.removeStateChangeObserver(observer);
}
@Override
public void setOnTabSelectingListener(StartSurface.OnTabSelectingListener listener) {
mStartSurfaceMediator.setOnTabSelectingListener(listener);
if (mTasksSurface != null) {
mTasksSurface.setOnTabSelectingListener(mStartSurfaceMediator);
} else if (mGridTabSwitcher != null) {
mGridTabSwitcher.setOnTabSelectingListener(mStartSurfaceMediator);
} else if (mTabSwitcherModule != null) {
mTabSwitcherModule.setOnTabSelectingListener(mStartSurfaceMediator);
}
if (mIsStartSurfaceRefactorEnabled) return;
// Set OnTabSelectingListener to the more tabs tasks surface as well if it has been
// instantiated, otherwise remember it for the future instantiation.
if (mIsStartSurfaceEnabled) {
if (mSecondaryTasksSurface == null) {
mOnTabSelectingListener = mStartSurfaceMediator;
} else {
mSecondaryTasksSurface.setOnTabSelectingListener(mStartSurfaceMediator);
}
}
}
@Override
public void initWithNative() {
if (mIsInitializedWithNative) return;
mIsInitializedWithNative = true;
if (mIsStartSurfaceEnabled) {
ViewGroup parentView =
mView != null
? mView.getBodyViewContainer()
: mTasksSurface.getBodyViewContainer();
mExploreSurfaceCoordinatorFactory =
new ExploreSurfaceCoordinatorFactory(
mActivity,
parentView,
mPropertyModel,
mBottomSheetController,
mParentTabSupplier,
new ScrollableContainerDelegateImpl(),
mSnackbarManager,
mShareDelegateSupplier,
mWindowAndroid,
mJankTracker,
mTabModelSelector,
mToolbarSupplier,
mConstructedTimeNs,
mSwipeRefreshLayout);
}
mStartSurfaceMediator.initWithNative(
mIsStartSurfaceEnabled ? mOmniboxStubSupplier.get() : null,
mExploreSurfaceCoordinatorFactory,
UserPrefs.get(Profile.getLastUsedRegularProfile()),
mSnackbarManager);
if (mGridTabSwitcher != null) {
mGridTabSwitcher.initWithNative();
}
if (mTasksSurface != null) {
mTasksSurface.onFinishNativeInitialization(
mActivity,
mOmniboxStubSupplier.get(),
mStartSurfaceMediator.getFeedReliabilityLogger());
}
if (mIsInitPending) {
initialize();
}
if (mIsStartSurfaceRefactorEnabled) return;
if (mIsSecondaryTaskInitPending) {
mIsSecondaryTaskInitPending = false;
mSecondaryTasksSurface.onFinishNativeInitialization(
mActivity, mOmniboxStubSupplier.get(), /* feedReliabilityLogger= */ null);
mSecondaryTasksSurface.initialize();
}
}
public StartSurfaceMediator getMediatorForTesting() {
return mStartSurfaceMediator;
}
@Override
public void addTabSwitcherViewObserver(TabSwitcherViewObserver observer) {
mStartSurfaceMediator.addTabSwitcherViewObserver(observer);
}
@Override
public void removeTabSwitcherViewObserver(TabSwitcherViewObserver listener) {
mStartSurfaceMediator.removeTabSwitcherViewObserver(listener);
}
@Override
public void hideTabSwitcherView(boolean animate) {
mStartSurfaceMediator.hideTabSwitcherView(animate);
}
@Override
public void beforeShowTabSwitcherView() {
// TODO(crbug/1386265): This is a temporary workaround to ensure the layout can run
// offscreen while invisible for animation purposes. We should refactor this so that the
// visibility is controlled independently of IS_SHOWING_OVERVIEW. The final state is
// View.VISIBLE after the animation so we don't need to restore the state after the
// animation changes.
if (mSwipeRefreshLayout != null) {
mSwipeRefreshLayout.setVisibility(View.VISIBLE);
}
}
@Override
public void beforeHideTabSwitcherView() {
mStartSurfaceMediator.beforeHideTabSwitcherView();
}
@Override
public void showOverview(boolean animate) {
mStartSurfaceMediator.showOverview(animate);
}
@Override
public void setStartSurfaceState(int state, int launchOrigin) {
mStartSurfaceMediator.setStartSurfaceState(state, launchOrigin);
}
@Override
public void setStartSurfaceState(int state) {
mStartSurfaceMediator.setStartSurfaceState(state);
}
@Override
public void setLaunchOrigin(int launchOrigin) {
mStartSurfaceMediator.setLaunchOrigin(launchOrigin);
}
@Override
public void resetScrollPosition() {
mStartSurfaceMediator.resetScrollPosition();
}
@Override
public boolean onBackPressed() {
if (mGridTabSwitcher != null) {
return mGridTabSwitcher.getController().onBackPressed();
}
return mStartSurfaceMediator.onBackPressed();
}
@Override
public int getStartSurfaceState() {
return mStartSurfaceMediator.getStartSurfaceState();
}
@Override
public int getPreviousStartSurfaceState() {
return mStartSurfaceMediator.getPreviousStartSurfaceState();
}
@Override
@Nullable
public TabSwitcher.Controller getGridTabSwitcherController() {
return mStartSurfaceMediator.getTabSwitcherController();
}
@Override
public ViewGroup getTabSwitcherContainer() {
return mStartSurfaceMediator.getTabSwitcherContainer();
}
@Override
public void setSnackbarParentView(ViewGroup parentView) {
mStartSurfaceMediator.setSnackbarParentView(parentView);
}
@Override
public boolean isShowingStartSurfaceHomepage() {
return mStartSurfaceMediator.isShowingStartSurfaceHomepage();
}
@Override
public boolean isHomepageShown() {
return mStartSurfaceMediator.isHomepageShown();
}
@Override
public TabSwitcher.TabListDelegate getGridTabListDelegate() {
if (mIsStartSurfaceEnabled) {
if (mIsStartSurfaceRefactorEnabled) {
// Unreachable.
return null;
}
if (mSecondaryTasksSurface == null) {
mStartSurfaceMediator.setSecondaryTasksSurfaceController(
initializeSecondaryTasksSurface());
}
return mSecondaryTasksSurface.getTabListDelegate();
} else {
return mGridTabSwitcher.getTabListDelegate();
}
}
@Override
public int getTabSwitcherTabListModelSize() {
if (mGridTabSwitcher != null) {
return mGridTabSwitcher.getTabSwitcherTabListModelSize();
} else if (mSecondaryTasksSurface != null) {
return mSecondaryTasksSurface.getTabSwitcherTabListModelSize();
}
return 0;
}
@Override
public void setTabSwitcherRecyclerViewPosition(RecyclerViewPosition recyclerViewPosition) {
if (mGridTabSwitcher != null) {
mGridTabSwitcher.setTabSwitcherRecyclerViewPosition(recyclerViewPosition);
} else if (mSecondaryTasksSurface != null) {
mSecondaryTasksSurface.setTabSwitcherRecyclerViewPosition(recyclerViewPosition);
}
}
@Override
public TabSwitcher.TabListDelegate getSingleTabListDelegate() {
if (mIsStartSurfaceEnabled) {
if (mIsStartSurfaceRefactorEnabled) {
return mTabSwitcherModule.getTabListDelegate();
} else {
assert mTasksSurface != null;
return mTasksSurface.getTabListDelegate();
}
} else {
return null;
}
}
@Override
public Supplier<Boolean> getTabGridDialogVisibilitySupplier() {
// If TabSwitcher has been created directly, use the TabGridDialogVisibilitySupplier from
// TabSwitcher.
if (mGridTabSwitcher != null) {
return mGridTabSwitcher.getTabGridDialogVisibilitySupplier();
} else if (mTabSwitcherModule != null) {
return () -> mTabSwitcherModule.getTabGridDialogVisibilitySupplier() != null;
}
return () -> {
// Return true if either mTasksSurface or mSecondaryTasksSurface has a visible dialog.
assert mTasksSurface != null;
if (mTasksSurface.getTabGridDialogVisibilitySupplier() != null) {
if (mTasksSurface.getTabGridDialogVisibilitySupplier().get()) return true;
}
if (mSecondaryTasksSurface != null
&& mSecondaryTasksSurface.getTabGridDialogVisibilitySupplier() != null) {
assert !mIsStartSurfaceRefactorEnabled;
if (mSecondaryTasksSurface.getTabGridDialogVisibilitySupplier().get()) return true;
}
return false;
};
}
@Override
public void onOverviewShownAtLaunch(
boolean isOverviewShownOnStartup, long activityCreationTimeMs) {
if (isOverviewShownOnStartup) {
mStartSurfaceMediator.onOverviewShownAtLaunch(activityCreationTimeMs);
}
if (ReturnToChromeUtil.isStartSurfaceEnabled(mActivity)) {
if (isOverviewShownOnStartup) {
ReturnToChromeUtil.recordHistogramsWhenOverviewIsShownAtLaunch();
}
Log.i(TAG, "Recorded %s = %b", START_SHOWN_AT_STARTUP_UMA, isOverviewShownOnStartup);
RecordHistogram.recordBooleanHistogram(
START_SHOWN_AT_STARTUP_UMA, isOverviewShownOnStartup);
// The segmentation results should only be cached when Start is enabled, for example,
// not on tablet.
if (StartSurfaceConfiguration.START_SURFACE_RETURN_TIME_USE_MODEL.getValue()) {
ReturnToChromeUtil.cacheReturnTimeFromSegmentation();
}
}
}
@Override
public @Nullable TasksView getPrimarySurfaceView() {
if (mTasksSurface != null) {
return (TasksView) mTasksSurface.getView();
}
return mView;
}
@Override
public ObservableSupplier<TabSwitcherCustomViewManager>
getTabSwitcherCustomViewManagerSupplier() {
return mTabSwitcherCustomViewManagerSupplier;
}
/**
* Create the {@link TasksSurface}
* @param activity The {@link Activity} that creates this surface.
* @param scrimCoordinator The {@link ScrimCoordinator} that controls scrim view.
* @param propertyModel The {@link PropertyModel} contains the {@link TasksSurfaceProperties}
* to communicate with this surface.
* @param tabSwitcherType The type of the tab switcher to show.
* @param parentTabSupplier {@link Supplier} to provide parent tab for the
* TasksSurface.
* @param hasMVTiles whether has MV tiles on the surface.
* @param windowAndroid An instance of a {@link WindowAndroid}.
* @param activityLifecycleDispatcher Allows observation of the activity lifecycle.
* @param tabModelSelector Gives access to the current set of {@TabModel}.
* @param snackbarManager Manages the display of snackbars.
* @param dynamicResourceLoaderSupplier Supplies the current {@link DynamicResourceLoader}.
* @param tabContentManager Gives access to the tab content.
* @param modalDialogManager Manages the display of modal dialogs.
* @param browserControlsStateProvider Gives access to the state of the browser controls.
* @param tabCreatorManager Manages creation of tabs.
* @param menuOrKeyboardActionController allows access to menu or keyboard actions.
* @param multiWindowModeStateDispatcher Gives access to the multi window mode state.
* @param rootView The root view of the app.
* @param incognitoReauthControllerSupplier {@link OneshotSupplier<IncognitoReauthController>}
* to detect pending re-auth when tab switcher is shown.
* @return The {@link TasksSurface}.
*/
TasksSurface createTasksSurface(
@NonNull Activity activity,
@NonNull ScrimCoordinator scrimCoordinator,
@NonNull PropertyModel propertyModel,
@TabSwitcherType int tabSwitcherType,
@NonNull Supplier<Tab> parentTabSupplier,
boolean hasMVTiles,
boolean hasQueryTiles,
@NonNull WindowAndroid windowAndroid,
@NonNull ActivityLifecycleDispatcher activityLifecycleDispatcher,
@NonNull TabModelSelector tabModelSelector,
@NonNull SnackbarManager snackbarManager,
@NonNull Supplier<DynamicResourceLoader> dynamicResourceLoaderSupplier,
@NonNull TabContentManager tabContentManager,
@NonNull ModalDialogManager modalDialogManager,
@NonNull BrowserControlsStateProvider browserControlsStateProvider,
@NonNull TabCreatorManager tabCreatorManager,
@NonNull MenuOrKeyboardActionController menuOrKeyboardActionController,
@NonNull MultiWindowModeStateDispatcher multiWindowModeStateDispatcher,
@NonNull ViewGroup rootView,
@Nullable
OneshotSupplier<IncognitoReauthController> incognitoReauthControllerSupplier) {
return new TasksSurfaceCoordinator(
activity,
scrimCoordinator,
propertyModel,
tabSwitcherType,
parentTabSupplier,
hasMVTiles,
hasQueryTiles,
windowAndroid,
activityLifecycleDispatcher,
tabModelSelector,
snackbarManager,
dynamicResourceLoaderSupplier,
tabContentManager,
modalDialogManager,
browserControlsStateProvider,
tabCreatorManager,
menuOrKeyboardActionController,
multiWindowModeStateDispatcher,
rootView,
incognitoReauthControllerSupplier);
}
public boolean isInitPendingForTesting() {
return mIsInitPending;
}
public boolean isInitializedWithNativeForTesting() {
return mIsInitializedWithNative;
}
public boolean isSecondaryTaskInitPendingForTesting() {
return mIsSecondaryTaskInitPending;
}
public boolean isMVTilesCleanedUpForTesting() {
if (mTasksSurface != null) {
return mTasksSurface.isMVTilesCleanedUp();
}
if (mMostVisitedCoordinator != null) {
return mMostVisitedCoordinator.isMVTilesCleanedUp();
}
return false;
}
public boolean isMVTilesInitializedForTesting() {
if (mTasksSurface != null) {
return mTasksSurface.isMVTilesInitialized();
}
return mIsMVTilesInitialized;
}
public TileGroupDelegateImpl getTileGroupDelegateForTesting() {
if (mTasksSurface != null) {
return mTasksSurface.getTileGroupDelegate();
}
return mTileGroupDelegate;
}
/** Called only when Start Surface is enabled. */
private void createAndSetStartSurface(boolean excludeQueryTiles) {
ArrayList<PropertyKey> allProperties =
new ArrayList<>(Arrays.asList(TasksSurfaceProperties.ALL_KEYS));
allProperties.addAll(Arrays.asList(StartSurfaceProperties.ALL_KEYS));
mPropertyModel = new PropertyModel(allProperties);
assert mIsStartSurfaceEnabled;
mTasksSurface =
createTasksSurface(
mActivity,
mScrimCoordinator,
mPropertyModel,
TabSwitcherType.SINGLE,
mParentTabSupplier,
true,
!excludeQueryTiles,
mWindowAndroid,
mActivityLifecycleDispatcher,
mTabModelSelector,
mSnackbarManager,
mDynamicResourceLoaderSupplier,
mTabContentManager,
mModalDialogManager,
mBrowserControlsManager,
mTabCreatorManager,
mMenuOrKeyboardActionController,
mMultiWindowModeStateDispatcher,
mContainerView,
null);
mTasksSurface.getView().setId(R.id.primary_tasks_surface_view);
initializeOffsetChangedListener();
addHeaderOffsetChangeListener(mOffsetChangedListenerToGenerateScrollEvents);
mTasksSurfacePropertyModelChangeProcessor =
PropertyModelChangeProcessor.create(
mPropertyModel,
new StartSurfaceWithParentViewBinder.ViewHolder(
mContainerView, mTasksSurface.getView(), mSwipeRefreshLayout),
StartSurfaceWithParentViewBinder::bind);
}
private void createStartSurfaceWithoutTasksSurface(boolean excludeQueryTiles) {
ArrayList<PropertyKey> allProperties =
new ArrayList<>(Arrays.asList(TasksSurfaceProperties.ALL_KEYS));
allProperties.addAll(Arrays.asList(StartSurfaceProperties.ALL_KEYS));
mPropertyModel = new PropertyModel(allProperties);
assert mIsStartSurfaceEnabled;
int tabSwitcherType = TabSwitcherType.NONE;
if (!mUseMagicSpace) {
tabSwitcherType = TabSwitcherType.SINGLE;
}
if (!mIsSurfacePolishEnabled) {
mView =
(TasksView)
LayoutInflater.from(mActivity)
.inflate(R.layout.tasks_view_layout, null);
} else {
mView =
(TasksView)
LayoutInflater.from(mActivity)
.inflate(R.layout.tasks_view_layout_polish, null);
}
mView.setId(R.id.primary_tasks_surface_view);
mView.initialize(
mActivityLifecycleDispatcher,
mParentTabSupplier.hasValue() && mParentTabSupplier.get().isIncognito(),
mWindowAndroid);
if (tabSwitcherType == TabSwitcherType.SINGLE) {
// We always pass the parameter isTablet to be false here since StartSurfaceCoordinator
// is only created on phones.
mTabSwitcherModule =
new SingleTabSwitcherCoordinator(
mActivity,
mView.getCardTabSwitcherContainer(),
null,
mTabModelSelector,
/* isShownOnNtp= */ false,
/* isTablet= */ false,
/* isScrollableMvtEnabled= */ true,
/* mostRecentTab= */ null,
/* singleTabCardClickedCallback= */ null,
/* snapshotParentViewRunnable= */ null,
mTabContentManager,
null);
}
boolean isScrollableMVTEnabled =
!ReturnToChromeUtil.shouldImproveStartWhenFeedIsDisabled(mActivity);
int maxRowsForGridMVT =
getQueryTilesVisibility()
? QueryTileSection.getMaxRowsForMostVisitedTiles(mActivity)
: MAX_TILE_ROWS_FOR_GRID_MVT;
View mvTilesContainer = mView.findViewById(R.id.mv_tiles_container);
mMostVisitedCoordinator =
new MostVisitedTilesCoordinator(
mActivity,
mActivityLifecycleDispatcher,
mvTilesContainer,
mWindowAndroid,
TabUiFeatureUtilities.supportInstantStart(
DeviceFormFactor.isNonMultiDisplayContextOnTablet(mActivity),
mActivity),
isScrollableMVTEnabled,
isScrollableMVTEnabled ? Integer.MAX_VALUE : maxRowsForGridMVT,
/* snapshotTileGridChangedRunnable= */ null,
/* tileCountChangedRunnable= */ null);
if (!excludeQueryTiles) {
if (ProfileManager.isInitialized()) {
initializeQueryTileSection(Profile.getLastUsedRegularProfile());
} else {
mProfileSupplier.addObserver(this::initializeQueryTileSection);
}
} else {
storeQueryTilesVisibility(false);
}
initializeOffsetChangedListener();
addHeaderOffsetChangeListener(mOffsetChangedListenerToGenerateScrollEvents);
mStartSurfaceWithParentViewPropertyModelChangeProcessor =
PropertyModelChangeProcessor.create(
mPropertyModel,
new StartSurfaceWithParentViewBinder.ViewHolder(
mContainerView, mView, mSwipeRefreshLayout),
StartSurfaceWithParentViewBinder::bind);
mStartSurfacePropertyModelChangeProcessor =
PropertyModelChangeProcessor.create(mPropertyModel, mView, TasksViewBinder::bind);
}
// TODO(crbug.com/1315676): Remove this function once the start surface refactoring is done,
// since the secondary tasks surface will go away.
private TabSwitcher.Controller initializeSecondaryTasksSurface() {
assert mIsStartSurfaceEnabled;
assert mSecondaryTasksSurface == null;
PropertyModel propertyModel = new PropertyModel(TasksSurfaceProperties.ALL_KEYS);
mStartSurfaceMediator.setSecondaryTasksSurfacePropertyModel(propertyModel);
mSecondaryTasksSurface =
createTasksSurface(
mActivity,
mScrimCoordinator,
propertyModel,
TabSwitcherType.GRID,
mParentTabSupplier,
/* hasMVTiles= */ false,
/* hasQueryTiles= */ false,
mWindowAndroid,
mActivityLifecycleDispatcher,
mTabModelSelector,
mSnackbarManager,
mDynamicResourceLoaderSupplier,
mTabContentManager,
mModalDialogManager,
mBrowserControlsManager,
mTabCreatorManager,
mMenuOrKeyboardActionController,
mMultiWindowModeStateDispatcher,
mContainerView,
mIncognitoReauthControllerSupplier);
if (mIsInitializedWithNative) {
mSecondaryTasksSurface.onFinishNativeInitialization(
mActivity, mOmniboxStubSupplier.get(), /* feedReliabilityLogger= */ null);
mSecondaryTasksSurface.initialize();
} else {
mIsSecondaryTaskInitPending = true;
}
mSecondaryTasksSurface.getView().setId(R.id.secondary_tasks_surface_view);
mSecondaryTasksSurfacePropertyModelChangeProcessor =
PropertyModelChangeProcessor.create(
mPropertyModel,
new StartSurfaceWithParentViewBinder.ViewHolder(
mContainerView, mSecondaryTasksSurface.getView(), null),
SecondaryTasksSurfaceViewBinder::bind);
if (mOnTabSelectingListener != null) {
mSecondaryTasksSurface.setOnTabSelectingListener(mOnTabSelectingListener);
mOnTabSelectingListener = null;
}
if (!mTabSwitcherCustomViewManagerSupplier.hasValue()) {
mTabSwitcherCustomViewManagerSupplier.set(
mSecondaryTasksSurface.getTabSwitcherCustomViewManager());
}
return mSecondaryTasksSurface.getController();
}
// TODO(crbug.com/1047488): This is a temporary solution of the issue crbug.com/1047488, which
// has not been reproduced locally. The crash is because we can not find ChromeTabbedActivity's
// ActivityInfo in the ApplicationStatus. However, from the code, ActivityInfo is created in
// ApplicationStatus during AsyncInitializationActivity.onCreate, which happens before
// ChromeTabbedActivity.startNativeInitialization where creates the Start surface. So one
// possible reason is the ChromeTabbedActivity is finishing or destroyed when showing overview.
private boolean isActivityFinishingOrDestroyed() {
boolean finishingOrDestroyed =
mChromeActivityNativeDelegate.isActivityFinishingOrDestroyed()
|| ApplicationStatus.getStateForActivity(mActivity)
== ActivityState.DESTROYED;
// TODO(crbug.com/1047488): Assert false. Do not do that in this CL to keep it small since
// Start surface is eanbled in the fieldtrial_testing_config.json, which requires update of
// the other browser tests.
return finishingOrDestroyed;
}
/** Creates a {@link SwipeRefreshLayout} to do a pull-to-refresh. */
private void createSwipeRefreshLayout() {
assert mSwipeRefreshLayout == null;
mSwipeRefreshLayout = FeedSwipeRefreshLayout.create(mActivity, R.id.toolbar_container);
// If FeedSwipeRefreshLayout is not created because the feature is not enabled, don't create
// another layer.
if (mSwipeRefreshLayout == null) return;
// SwipeRefreshLayout can only support one direct child. So we have to create a FrameLayout
// as a container of possible more than one task views.
mContainerView.addView(mSwipeRefreshLayout);
FrameLayout directChildHolder = new FrameLayout(mActivity);
mSwipeRefreshLayout.addView(directChildHolder);
mSwipeRefreshLayout.setVisibility(View.GONE);
mContainerView = directChildHolder;
}
private int getLogoInSurfaceHeight() {
if (mIsSurfacePolishEnabled
&& StartSurfaceConfiguration.SURFACE_POLISH_MOVE_DOWN_LOGO.getValue()) {
Resources resources = mActivity.getResources();
if (StartSurfaceConfiguration.SURFACE_POLISH_LESS_BRAND_SPACE.getValue()) {
return LogoUtils.getLogoHeightPolishedShort(resources)
+ LogoUtils.getTopMarginPolishedSmall(resources)
+ LogoUtils.getBottomMarginPolishedSmall(resources);
} else {
return LogoUtils.getLogoHeightPolished(resources)
+ LogoUtils.getTopMarginPolished(resources)
+ LogoUtils.getBottomMarginPolished(resources);
}
}
return getPixelSize(R.dimen.ntp_logo_height)
+ getPixelSize(R.dimen.ntp_logo_margin_top)
+ getPixelSize(R.dimen.ntp_logo_margin_bottom);
}
private void initializeOffsetChangedListener() {
// Scroll-dependeng fake box transition of states (e.g., height) undergoes 3 phases:
// Phase 1: Fake box is far from top: Keep original states (e.g., large height). Related
// variables are fake*BeforeAnimation.
// Phase 2: Fake box just reaches top: Interpolate states (e.g., shrink height). Related
// variables are fake*ForAnimation.
// Phase 3: Fake box is at top: Match real box states (e.g., small height). Related
// variables are real*.
// For Surface Polish, Phase 2 is more complex:
// Phase 2A: Fake box *nearing* top: Interpolate height reduction to reach some fixed
// intermediate value (fakeHeightForAnimation). The top border also linear reaches the
// final at a slower rate. This phase encroaches upon Phase 1. Related variables are
// *BeforeRealAnimation or fake*ForAnimation.
// Phase 2B: Fake box reaches top: Same as Phase 2; interpolate all states. Related
// variables are fake*ForAnimation.
int realVerticalMargin = getPixelSize(R.dimen.location_bar_vertical_margin);
int logoInSurfaceHeight = getLogoInSurfaceHeight();
// The following |fake*| values mean the values of the fake search box; |real*| values
// mean the values of the real search box.
int realHeight = getPixelSize(R.dimen.toolbar_height_no_shadow) - realVerticalMargin * 2;
int fakeHeightForAnimation = getPixelSize(R.dimen.ntp_search_box_height);
int fakeHeightBeforeAnimation =
mIsSurfacePolishEnabled
? getPixelSize(R.dimen.ntp_search_box_height_polish)
: fakeHeightForAnimation;
// The gap between the top of fake box and its final position for Phase 2A to start.
int heightReducedBeforeRealAnimation = fakeHeightBeforeAnimation - fakeHeightForAnimation;
int fakeEndPadding =
mIsSurfacePolishEnabled
? getPixelSize(R.dimen.fake_search_box_end_padding)
: getPixelSize(R.dimen.search_box_end_padding);
// realEndPadding is 0 when surface is not polished;
int realEndPadding =
mIsSurfacePolishEnabled
? getPixelSize(R.dimen.location_bar_end_padding)
+ getPixelSize(R.dimen.location_bar_url_action_offset_polish)
: 0;
int endPaddingDiff = fakeEndPadding - realEndPadding;
// fakeTranslationX is 0;
int realTranslationX;
if (mIsSurfacePolishEnabled) {
realTranslationX =
OmniboxResourceProvider.getFocusedStatusViewLeftSpacing(mActivity)
+ getPixelSize(R.dimen.status_view_highlight_size)
+ getPixelSize(
OmniboxFeatures.shouldShowModernizeVisualUpdate(mActivity)
&& OmniboxFeatures.shouldShowSmallBottomMargin()
? R.dimen.location_bar_icon_end_padding_focused_smaller
: R.dimen.location_bar_icon_end_padding_focused)
- getPixelSize(R.dimen.fake_search_box_start_padding);
} else {
realTranslationX =
getPixelSize(R.dimen.location_bar_status_icon_width)
+ getPixelSize(R.dimen.location_bar_icon_end_padding_focused)
+ (getPixelSize(R.dimen.fake_search_box_lateral_padding)
- (getPixelSize(R.dimen.search_box_start_padding)));
}
int fakeButtonSize =
mIsSurfacePolishEnabled
? getPixelSize(R.dimen.location_bar_action_icon_width)
: getPixelSize(R.dimen.tasks_surface_location_bar_url_button_size);
int realButtonSize = getPixelSize(R.dimen.location_bar_action_icon_width);
int fakeLensButtonStartMargin =
mIsSurfacePolishEnabled
? 0
: getPixelSize(R.dimen.tasks_surface_location_bar_url_button_start_margin);
// realLensButtonStartMargin is 0;
float fakeSearchTextSize =
mIsSurfacePolishEnabled
? getTextSizeFromDimen(R.dimen.location_bar_url_text_size_polish)
: getTextSizeFromDimen(R.dimen.location_bar_url_text_size);
float realSearchTextSize = getTextSizeFromDimen(R.dimen.location_bar_url_text_size);
TasksView tasksView = mTasksSurface != null ? (TasksView) mTasksSurface.getView() : mView;
// Explicitly assign fake search box container height, so it won't resizes in Phase 2* along
// with its interior. This prevents content underneath from shifting, which can have adverse
// interaction with scroll from swiping.
tasksView.updateFakeSearchBoxContainer(fakeHeightBeforeAnimation);
mOffsetChangedListenerToGenerateScrollEvents =
(appBarLayout, verticalOffset) -> {
for (ScrollListener scrollListener : mScrollListeners) {
scrollListener.onHeaderOffsetChanged(verticalOffset);
}
int fakeSearchBoxToRealSearchBoxTop =
mStartSurfaceMediator.getTopToolbarPlaceholderHeight()
+ (mStartSurfaceMediator.isLogoVisible()
? logoInSurfaceHeight
: 0)
- realVerticalMargin;
int scrolledHeight = -verticalOffset;
int fakeHeight;
if (mIsSurfacePolishEnabled) {
// Detect and handle Phase 2A. Otherwise reuse the original flow, but tweak
// |fakeHeight| for Phase 2B.
int startPointToReduceHeight =
fakeSearchBoxToRealSearchBoxTop - heightReducedBeforeRealAnimation;
if (scrolledHeight < startPointToReduceHeight) {
// Phase 1.
fakeHeight = fakeHeightBeforeAnimation;
} else if (scrolledHeight < fakeSearchBoxToRealSearchBoxTop) {
// Phase 2A: Shrink height at the same rate as scrolling.
int reducedHeight = scrolledHeight - startPointToReduceHeight;
tasksView.updateFakeSearchBoxHeight(
fakeHeightBeforeAnimation - reducedHeight);
return;
} else {
// Phase 2B and Phase 3.
fakeHeight = fakeHeightForAnimation;
}
} else {
fakeHeight = fakeHeightBeforeAnimation;
}
int fakeAndRealHeightDiff = fakeHeight - realHeight;
// When the fake search box top is scrolled to the search box top, start to
// reduce fake search box's height until it's the same as the real search box.
int reducedHeight =
MathUtils.clamp(
scrolledHeight - fakeSearchBoxToRealSearchBoxTop,
0,
fakeAndRealHeightDiff);
float expansionFraction = (float) reducedHeight / fakeAndRealHeightDiff;
// This function should be called together with
// StartSurfaceToolbarMediator#updateTranslationY, which scroll up the start
// surface toolbar together with the header.
// Note: the logic below may need to be updated if Start is ever showing in
// Incognito mode.
tasksView.updateFakeSearchBox(
fakeHeight - reducedHeight,
reducedHeight,
(int) (endPaddingDiff * (1 - expansionFraction) + realEndPadding),
realTranslationX * expansionFraction,
(int)
(fakeButtonSize
+ (realButtonSize - fakeButtonSize)
* expansionFraction),
(int) (fakeLensButtonStartMargin * (1 - expansionFraction)),
fakeSearchTextSize
+ (realSearchTextSize - fakeSearchTextSize)
* expansionFraction);
if (mIsSurfacePolishEnabled && scrolledHeight > appBarLayout.getHeight()) {
ViewUtils.requestLayout(
appBarLayout,
"StartSurfaceCoordinator#initializeOffsetChangedListener "
+ "AppBarLayout.OnOffsetChangedListener");
}
};
}
private int getPixelSize(int id) {
return mActivity.getResources().getDimensionPixelSize(id);
}
/**
* Gets the text size based on a dimension resource. The return value is in SP.
* @param id The resource ID of the dimension value.
*/
private float getTextSizeFromDimen(int id) {
TypedValue typedValue = new TypedValue();
Resources resources = mActivity.getResources();
resources.getValue(id, typedValue, true);
if (typedValue.type == TypedValue.TYPE_DIMENSION
&& (typedValue.data & TypedValue.COMPLEX_UNIT_MASK) == TypedValue.COMPLEX_UNIT_SP) {
return TypedValue.complexToFloat(typedValue.data);
}
return -1;
}
public void initializeMVTiles() {
if (!LibraryLoader.getInstance().isInitialized()
|| mIsMVTilesInitialized
|| mMostVisitedCoordinator == null) {
return;
}
Profile profile = Profile.getLastUsedRegularProfile();
MostVisitedTileNavigationDelegate navigationDelegate =
new MostVisitedTileNavigationDelegate(mActivity, profile, mParentTabSupplier);
mSuggestionsUiDelegate =
new MostVisitedSuggestionsUiDelegate(
mView, navigationDelegate, profile, mSnackbarManager);
mTileGroupDelegate =
new TileGroupDelegateImpl(
mActivity,
profile,
navigationDelegate,
mSnackbarManager,
BrowserUiUtils.HostSurface.START_SURFACE);
mMostVisitedCoordinator.initWithNative(
mSuggestionsUiDelegate, mTileGroupDelegate, enabled -> {});
mIsMVTilesInitialized = true;
}
private void storeQueryTilesVisibility(boolean isShown) {
ChromeSharedPreferences.getInstance()
.writeBoolean(ChromePreferenceKeys.QUERY_TILES_SHOWN_ON_START_SURFACE, isShown);
}
private boolean getQueryTilesVisibility() {
return ChromeSharedPreferences.getInstance()
.readBoolean(ChromePreferenceKeys.QUERY_TILES_SHOWN_ON_START_SURFACE, false);
}
private void initializeQueryTileSection(Profile profile) {
assert profile != null;
if (profile.isOffTheRecord()) return;
if (!QueryTileUtils.isQueryTilesEnabledOnStartSurface()) {
storeQueryTilesVisibility(false);
return;
}
mQueryTileSection =
new QueryTileSection(
mView.findViewById(R.id.query_tiles_layout),
profile,
query -> performSearchQuery(query.queryText, query.searchParams));
storeQueryTilesVisibility(true);
mProfileSupplier.removeObserver(this::initializeQueryTileSection);
}
/**
* Called to send the search query and params to omnibox to kick off a search.
* @param queryText Text of the search query to perform.
* @param searchParams A list of params to sent along with the search query.
*/
void performSearchQuery(String queryText, List<String> searchParams) {
mStartSurfaceMediator.performSearchQuery(queryText, searchParams);
}
boolean isSecondaryTasksSurfaceEmptyForTesting() {
return mSecondaryTasksSurface == null;
}
FeedSwipeRefreshLayout getFeedSwipeRefreshLayoutForTesting() {
return mSwipeRefreshLayout;
}
TasksSurface getTasksSurfaceForTesting() {
return mTasksSurface;
}
TasksView getViewForTesting() {
return mView;
}
}