Commit 7f1a5c00 authored by Becky Zhou's avatar Becky Zhou Committed by Commit Bot

[Feed] Move scroll logic to Mediator

Bug: 863171
Change-Id: Ibf9c93098fef18bbda60370d6bdd771442debd9f
Reviewed-on: https://chromium-review.googlesource.com/1145788Reviewed-by: default avatarTheresa <twellington@chromium.org>
Commit-Queue: Becky Zhou <huayinz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#577209}
parent a99a420b
......@@ -15,7 +15,6 @@ import android.widget.FrameLayout;
import com.google.android.libraries.feed.api.scope.FeedProcessScope;
import com.google.android.libraries.feed.api.scope.FeedStreamScope;
import com.google.android.libraries.feed.api.stream.ScrollListener;
import com.google.android.libraries.feed.api.stream.Stream;
import com.google.android.libraries.feed.host.stream.CardConfiguration;
import com.google.android.libraries.feed.host.stream.SnackbarApi;
......@@ -45,13 +44,10 @@ import java.util.Arrays;
/**
* Provides a new tab page that displays an interest feed rendered list of content suggestions.
*/
public class FeedNewTabPage
extends NewTabPage implements TouchEnabledDelegate, NewTabPageLayout.ScrollDelegate {
public class FeedNewTabPage extends NewTabPage implements TouchEnabledDelegate {
private final FeedNewTabPageMediator mMediator;
private final StreamLifecycleManager mStreamLifecycleManager;
private final Stream mStream;
private final ScrollListener mStreamScrollListener;
private final SnapScrollHelper mSnapScrollHelper;
private FrameLayout mRootView;
private SectionHeaderView mSectionHeaderView;
......@@ -152,44 +148,14 @@ public class FeedNewTabPage
mStream = streamScope.getStream();
mStreamLifecycleManager = new StreamLifecycleManager(mStream, activity, tab);
mSnapScrollHelper =
new SnapScrollHelper(mNewTabPageManager, mNewTabPageLayout, mStream.getView());
mSectionHeaderView = (SectionHeaderView) LayoutInflater.from(activity).inflate(
R.layout.new_tab_page_snippets_expandable_header, null);
mMediator = new FeedNewTabPageMediator(this);
mStream.getView().setBackgroundColor(Color.WHITE);
mRootView.addView(mStream.getView());
mStream.setHeaderViews(Arrays.asList(mNewTabPageLayout, mSectionHeaderView));
// Listen for layout changes on the NewTabPageView itself to catch changes in scroll
// position that are due to layout changes after e.g. device rotation. This contrasts with
// regular scrolling, which is observed through an OnScrollListener.
mRootView.addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
mSnapScrollHelper.handleScroll();
});
mStreamScrollListener = new ScrollListener() {
@Override
public void onScrollStateChanged(int state) {}
@Override
public void onScrolled(int dx, int dy) {
mSnapScrollHelper.handleScroll();
}
};
mStream.addScrollListener(mStreamScrollListener);
}
LayoutInflater inflater = LayoutInflater.from(activity);
mNewTabPageLayout = (NewTabPageLayout) inflater.inflate(R.layout.new_tab_page_layout, null);
mSectionHeaderView = (SectionHeaderView) inflater.inflate(
R.layout.new_tab_page_snippets_expandable_header, null);
@Override
protected void initializeMainView(Context context) {
int topPadding = context.getResources().getDimensionPixelOffset(R.dimen.tab_strip_height);
mRootView = new FrameLayout(context);
mRootView.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
mRootView.setPadding(0, topPadding, 0, 0);
mMediator = new FeedNewTabPageMediator(this,
new SnapScrollHelper(mNewTabPageManager, mNewTabPageLayout, mStream.getView()));
// Don't store a direct reference to the activity, because it might change later if the tab
// is reparented.
......@@ -200,19 +166,30 @@ public class FeedNewTabPage
this::setTouchEnabled, closeContextMenuCallback);
mTab.getWindowAndroid().addContextMenuCloseListener(contextMenuManager);
LayoutInflater inflater = LayoutInflater.from(context);
mNewTabPageLayout = (NewTabPageLayout) inflater.inflate(R.layout.new_tab_page_layout, null);
mNewTabPageLayout.initialize(mNewTabPageManager, mTab, mTileGroupDelegate,
mSearchProviderHasLogo,
TemplateUrlService.getInstance().isDefaultSearchEngineGoogle(), this,
TemplateUrlService.getInstance().isDefaultSearchEngineGoogle(), mMediator,
contextMenuManager, new UiConfig(mRootView));
mStream.getView().setBackgroundColor(Color.WHITE);
mRootView.addView(mStream.getView());
mStream.setHeaderViews(Arrays.asList(mNewTabPageLayout, mSectionHeaderView));
}
@Override
protected void initializeMainView(Context context) {
int topPadding = context.getResources().getDimensionPixelOffset(R.dimen.tab_strip_height);
mRootView = new FrameLayout(context);
mRootView.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
mRootView.setPadding(0, topPadding, 0, 0);
}
@Override
public void destroy() {
super.destroy();
mMediator.destroy();
mStream.removeScrollListener(mStreamScrollListener);
mImageLoader.destroy();
mStreamLifecycleManager.destroy();
}
......@@ -261,40 +238,4 @@ public class FeedNewTabPage
public void setTouchEnabled(boolean enabled) {
// TODO(twellington): implement this method.
}
// ScrollDelegate interface.
@Override
public boolean isScrollViewInitialized() {
// During startup the view may not be fully initialized, so we check to see if some basic
// view properties (height of the RecyclerView) are sane.
return mStream != null && mStream.getView().getHeight() > 0;
}
@Override
public int getVerticalScrollOffset() {
// This method returns a valid vertical scroll offset only when the first header view in the
// Stream is visible. In all other cases, this returns 0.
if (!isScrollViewInitialized()) return 0;
int firstChildTop = mStream.getChildTopAt(0);
return firstChildTop != Stream.POSITION_NOT_KNOWN ? -firstChildTop : 0;
}
@Override
public boolean isChildVisibleAtPosition(int position) {
return isScrollViewInitialized() && mStream.isChildAtPositionVisible(position);
}
@Override
public void snapScroll() {
if (!isScrollViewInitialized()) return;
int initialScroll = getVerticalScrollOffset();
int scrollTo = mSnapScrollHelper.calculateSnapPosition(initialScroll);
// Calculating the snap position should be idempotent.
assert scrollTo == mSnapScrollHelper.calculateSnapPosition(scrollTo);
mStream.smoothScrollBy(0, scrollTo - initialScroll);
}
}
......@@ -6,7 +6,12 @@ package org.chromium.chrome.browser.feed;
import android.content.res.Resources;
import com.google.android.libraries.feed.api.stream.ScrollListener;
import com.google.android.libraries.feed.api.stream.Stream;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ntp.NewTabPageLayout;
import org.chromium.chrome.browser.ntp.SnapScrollHelper;
import org.chromium.chrome.browser.ntp.snippets.SectionHeader;
import org.chromium.chrome.browser.preferences.Pref;
import org.chromium.chrome.browser.preferences.PrefChangeRegistrar;
......@@ -16,17 +21,20 @@ import org.chromium.chrome.browser.preferences.PrefServiceBridge;
* A mediator for the {@link FeedNewTabPage} responsible for interacting with the
* native library and handling business logic.
*/
class FeedNewTabPageMediator {
class FeedNewTabPageMediator implements NewTabPageLayout.ScrollDelegate {
private final FeedNewTabPage mCoordinator;
private final SnapScrollHelper mSnapScrollHelper;
private final PrefChangeRegistrar mPrefChangeRegistrar;
private ScrollListener mStreamScrollListener;
private SectionHeader mSectionHeader;
/**
* @param feedNewTabPage The {@link FeedNewTabPage} that interacts with this class.
*/
FeedNewTabPageMediator(FeedNewTabPage feedNewTabPage) {
FeedNewTabPageMediator(FeedNewTabPage feedNewTabPage, SnapScrollHelper snapScrollHelper) {
mCoordinator = feedNewTabPage;
mSnapScrollHelper = snapScrollHelper;
initializeProperties();
mPrefChangeRegistrar = new PrefChangeRegistrar();
......@@ -36,6 +44,7 @@ class FeedNewTabPageMediator {
/** Clears any dependencies. */
void destroy() {
mCoordinator.getStream().removeScrollListener(mStreamScrollListener);
mPrefChangeRegistrar.destroy();
}
......@@ -44,6 +53,25 @@ class FeedNewTabPageMediator {
* TODO(huayinz): Introduce a Model for these properties.
*/
private void initializeProperties() {
// Listen for layout changes on the NewTabPageView itself to catch changes in scroll
// position that are due to layout changes after e.g. device rotation. This contrasts with
// regular scrolling, which is observed through an OnScrollListener.
mCoordinator.getView().addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
mSnapScrollHelper.handleScroll();
});
mStreamScrollListener = new ScrollListener() {
@Override
public void onScrollStateChanged(int state) {}
@Override
public void onScrolled(int dx, int dy) {
mSnapScrollHelper.handleScroll();
}
};
mCoordinator.getStream().addScrollListener(mStreamScrollListener);
Resources res = mCoordinator.getSectionHeaderView().getResources();
mSectionHeader =
new SectionHeader(res.getString(R.string.ntp_article_suggestions_section_header),
......@@ -72,4 +100,42 @@ class FeedNewTabPageMediator {
// TODO(huayinz): Update the section header view through a ModelChangeProcessor.
mCoordinator.getSectionHeaderView().updateIconDrawable();
}
// ScrollDelegate interface.
@Override
public boolean isScrollViewInitialized() {
Stream stream = mCoordinator.getStream();
// During startup the view may not be fully initialized, so we check to see if some basic
// view properties (height of the RecyclerView) are sane.
return stream != null && stream.getView().getHeight() > 0;
}
@Override
public int getVerticalScrollOffset() {
// This method returns a valid vertical scroll offset only when the first header view in the
// Stream is visible. In all other cases, this returns 0.
if (!isScrollViewInitialized()) return 0;
int firstChildTop = mCoordinator.getStream().getChildTopAt(0);
return firstChildTop != Stream.POSITION_NOT_KNOWN ? -firstChildTop : 0;
}
@Override
public boolean isChildVisibleAtPosition(int position) {
return isScrollViewInitialized()
&& mCoordinator.getStream().isChildAtPositionVisible(position);
}
@Override
public void snapScroll() {
if (!isScrollViewInitialized()) return;
int initialScroll = getVerticalScrollOffset();
int scrollTo = mSnapScrollHelper.calculateSnapPosition(initialScroll);
// Calculating the snap position should be idempotent.
assert scrollTo == mSnapScrollHelper.calculateSnapPosition(scrollTo);
mCoordinator.getStream().smoothScrollBy(0, scrollTo - initialScroll);
}
}
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