Commit a049ad82 authored by Becky Zhou's avatar Becky Zhou Committed by Commit Bot

[Feed] Handle supervised user/enterprise policy on Feed NTP

Use a ScrollView instead of the Stream for the NTP if supervised
user or enterprise policy is enabled.

Bug: 862832
Change-Id: I8987db14dac17ef525860817fcc341b254fea358
Reviewed-on: https://chromium-review.googlesource.com/1208814
Commit-Queue: Becky Zhou <huayinz@chromium.org>
Reviewed-by: default avatarTheresa <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#589609}
parent 5b59b7dd
...@@ -4,16 +4,19 @@ ...@@ -4,16 +4,19 @@
package org.chromium.chrome.browser.feed; package org.chromium.chrome.browser.feed;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ScrollView;
import com.google.android.libraries.feed.api.scope.FeedProcessScope; 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.scope.FeedStreamScope;
...@@ -39,13 +42,12 @@ import org.chromium.chrome.browser.profiles.Profile; ...@@ -39,13 +42,12 @@ import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.search_engines.TemplateUrlService; import org.chromium.chrome.browser.search_engines.TemplateUrlService;
import org.chromium.chrome.browser.snackbar.Snackbar; import org.chromium.chrome.browser.snackbar.Snackbar;
import org.chromium.chrome.browser.snackbar.SnackbarManager; import org.chromium.chrome.browser.snackbar.SnackbarManager;
import org.chromium.chrome.browser.suggestions.SuggestionsNavigationDelegateImpl;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.util.ViewUtils; import org.chromium.chrome.browser.util.ViewUtils;
import org.chromium.chrome.browser.widget.displaystyle.HorizontalDisplayStyle; import org.chromium.chrome.browser.widget.displaystyle.HorizontalDisplayStyle;
import org.chromium.chrome.browser.widget.displaystyle.MarginResizer; import org.chromium.chrome.browser.widget.displaystyle.MarginResizer;
import org.chromium.chrome.browser.widget.displaystyle.UiConfig; import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
import org.chromium.ui.UiUtils;
import java.util.Arrays; import java.util.Arrays;
...@@ -54,13 +56,18 @@ import java.util.Arrays; ...@@ -54,13 +56,18 @@ import java.util.Arrays;
*/ */
public class FeedNewTabPage extends NewTabPage { public class FeedNewTabPage extends NewTabPage {
private final FeedNewTabPageMediator mMediator; private final FeedNewTabPageMediator mMediator;
private final StreamLifecycleManager mStreamLifecycleManager;
private final Stream mStream;
private UiConfig mUiConfig; private UiConfig mUiConfig;
private FrameLayout mRootView; private FrameLayout mRootView;
private SectionHeaderView mSectionHeaderView;
private FeedImageLoader mImageLoader; // Used when Feed is enabled.
private @Nullable Stream mStream;
private @Nullable FeedImageLoader mImageLoader;
private @Nullable StreamLifecycleManager mStreamLifecycleManager;
private @Nullable SectionHeaderView mSectionHeaderView;
// Used when Feed is disabled by policy.
private @Nullable ScrollView mScrollViewForPolicy;
private class BasicSnackbarApi implements SnackbarApi { private class BasicSnackbarApi implements SnackbarApi {
@Override @Override
...@@ -175,42 +182,12 @@ public class FeedNewTabPage extends NewTabPage { ...@@ -175,42 +182,12 @@ public class FeedNewTabPage extends NewTabPage {
TabModelSelector tabModelSelector) { TabModelSelector tabModelSelector) {
super(activity, nativePageHost, tabModelSelector); super(activity, nativePageHost, tabModelSelector);
FeedProcessScope feedProcessScope = FeedProcessScopeFactory.getFeedProcessScope();
Tab tab = nativePageHost.getActiveTab();
Profile profile = tab.getProfile();
mImageLoader = new FeedImageLoader(profile, activity);
SuggestionsNavigationDelegateImpl navigationDelegate =
new SuggestionsNavigationDelegateImpl(
activity, profile, nativePageHost, tabModelSelector);
ActionApi actionApi = new FeedActionHandler(navigationDelegate,
() -> FeedProcessScopeFactory.getFeedScheduler().onSuggestionConsumed());
FeedOfflineIndicator offlineIndicator = FeedProcessScopeFactory.getFeedOfflineIndicator();
FeedStreamScope streamScope =
feedProcessScope
.createFeedStreamScopeBuilder(activity, mImageLoader, actionApi,
new BasicStreamConfiguration(),
new BasicCardConfiguration(activity.getResources(), mUiConfig),
new BasicSnackbarApi(), new FeedBasicLogging(), offlineIndicator)
.build();
mStream = streamScope.getStream();
mStreamLifecycleManager = new StreamLifecycleManager(mStream, activity, tab);
LayoutInflater inflater = LayoutInflater.from(activity); LayoutInflater inflater = LayoutInflater.from(activity);
mNewTabPageLayout = (NewTabPageLayout) inflater.inflate(R.layout.new_tab_page_layout, null); mNewTabPageLayout = (NewTabPageLayout) inflater.inflate(R.layout.new_tab_page_layout, null);
mSectionHeaderView = (SectionHeaderView) inflater.inflate(
R.layout.new_tab_page_snippets_expandable_header, null);
// TODO(huayinz): Add MarginResizer for sign-in promo under issue 860051 or 860043,
// depending on which one lands first.
Resources resources = mSectionHeaderView.getResources();
int defaultMargin =
resources.getDimensionPixelSize(R.dimen.content_suggestions_card_modern_margin);
int wideMargin = resources.getDimensionPixelSize(R.dimen.ntp_wide_card_lateral_margins);
MarginResizer.createAndAttach(mSectionHeaderView, mUiConfig, defaultMargin, wideMargin);
mMediator = new FeedNewTabPageMediator(this, // Mediator should be created before any Stream changes.
new SnapScrollHelper(mNewTabPageManager, mNewTabPageLayout, mStream.getView())); mMediator = new FeedNewTabPageMediator(
this, new SnapScrollHelper(mNewTabPageManager, mNewTabPageLayout));
// Don't store a direct reference to the activity, because it might change later if the tab // Don't store a direct reference to the activity, because it might change later if the tab
// is reparented. // is reparented.
...@@ -225,12 +202,6 @@ public class FeedNewTabPage extends NewTabPage { ...@@ -225,12 +202,6 @@ public class FeedNewTabPage extends NewTabPage {
mSearchProviderHasLogo, mSearchProviderHasLogo,
TemplateUrlService.getInstance().isDefaultSearchEngineGoogle(), mMediator, TemplateUrlService.getInstance().isDefaultSearchEngineGoogle(), mMediator,
contextMenuManager, mUiConfig); contextMenuManager, mUiConfig);
mStream.getView().setBackgroundColor(Color.WHITE);
mRootView.addView(mStream.getView());
mStream.setHeaderViews(Arrays.asList(new NonDismissibleHeader(mNewTabPageLayout),
new NonDismissibleHeader(mSectionHeaderView)));
} }
@Override @Override
...@@ -247,8 +218,8 @@ public class FeedNewTabPage extends NewTabPage { ...@@ -247,8 +218,8 @@ public class FeedNewTabPage extends NewTabPage {
public void destroy() { public void destroy() {
super.destroy(); super.destroy();
mMediator.destroy(); mMediator.destroy();
mImageLoader.destroy(); if (mImageLoader != null) mImageLoader.destroy();
mStreamLifecycleManager.destroy(); if (mStreamLifecycleManager != null) mStreamLifecycleManager.destroy();
} }
@Override @Override
...@@ -283,6 +254,94 @@ public class FeedNewTabPage extends NewTabPage { ...@@ -283,6 +254,94 @@ public class FeedNewTabPage extends NewTabPage {
return mStream; return mStream;
} }
/**
* Create a {@link Stream} for this class.
*/
void createStream() {
if (mScrollViewForPolicy != null) {
mRootView.removeView(mScrollViewForPolicy);
mScrollViewForPolicy = null;
}
FeedProcessScope feedProcessScope = FeedProcessScopeFactory.getFeedProcessScope();
Activity activity = mTab.getActivity();
Profile profile = mTab.getProfile();
mImageLoader = new FeedImageLoader(profile, activity);
ActionApi actionApi = new FeedActionHandler(mNewTabPageManager.getNavigationDelegate(),
() -> FeedProcessScopeFactory.getFeedScheduler().onSuggestionConsumed());
FeedOfflineIndicator offlineIndicator = FeedProcessScopeFactory.getFeedOfflineIndicator();
FeedStreamScope streamScope =
feedProcessScope
.createFeedStreamScopeBuilder(activity, mImageLoader, actionApi,
new BasicStreamConfiguration(),
new BasicCardConfiguration(activity.getResources(), mUiConfig),
new BasicSnackbarApi(), new FeedBasicLogging(), offlineIndicator)
.build();
mStream = streamScope.getStream();
mStreamLifecycleManager = new StreamLifecycleManager(mStream, activity, mTab);
LayoutInflater inflater = LayoutInflater.from(activity);
mSectionHeaderView = (SectionHeaderView) inflater.inflate(
R.layout.new_tab_page_snippets_expandable_header, null);
// TODO(huayinz): Add MarginResizer for sign-in promo under issue 860051 or 860043,
// depending on which one lands first.
Resources resources = mSectionHeaderView.getResources();
int defaultMargin =
resources.getDimensionPixelSize(R.dimen.content_suggestions_card_modern_margin);
int wideMargin = resources.getDimensionPixelSize(R.dimen.ntp_wide_card_lateral_margins);
MarginResizer.createAndAttach(mSectionHeaderView, mUiConfig, defaultMargin, wideMargin);
View view = mStream.getView();
view.setBackgroundColor(Color.WHITE);
mRootView.addView(view);
UiUtils.removeViewFromParent(mNewTabPageLayout);
UiUtils.removeViewFromParent(mSectionHeaderView);
mStream.setHeaderViews(Arrays.asList(new NonDismissibleHeader(mNewTabPageLayout),
new NonDismissibleHeader(mSectionHeaderView)));
// Explicitly request focus on the scroll container to avoid UrlBar being focused after
// the scroll container for policy is removed.
view.requestFocus();
}
/**
* @return The {@link ScrollView} for displaying content for supervised user or enterprise
* policy.
*/
ScrollView getScrollViewForPolicy() {
return mScrollViewForPolicy;
}
/**
* Create a {@link ScrollView} for displaying content for supervised user or enterprise policy.
*/
void createScrollViewForPolicy() {
if (mStream != null) {
mRootView.removeView(mStream.getView());
mStreamLifecycleManager.destroy();
mStream = null;
mSectionHeaderView = null;
}
mScrollViewForPolicy = new ScrollView(mTab.getActivity());
mScrollViewForPolicy.setBackgroundColor(Color.WHITE);
// Make scroll view focusable so that it is the next focusable view when the url bar clears
// focus.
mScrollViewForPolicy.setFocusable(true);
mScrollViewForPolicy.setFocusableInTouchMode(true);
mScrollViewForPolicy.setContentDescription(
mScrollViewForPolicy.getResources().getString(R.string.accessibility_new_tab_page));
UiUtils.removeViewFromParent(mNewTabPageLayout);
mScrollViewForPolicy.addView(mNewTabPageLayout);
mRootView.addView(mScrollViewForPolicy);
mScrollViewForPolicy.requestFocus();
}
/** @return The {@link SectionHeaderView} for the Feed section header. */ /** @return The {@link SectionHeaderView} for the Feed section header. */
SectionHeaderView getSectionHeaderView() { SectionHeaderView getSectionHeaderView() {
return mSectionHeaderView; return mSectionHeaderView;
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
package org.chromium.chrome.browser.feed; package org.chromium.chrome.browser.feed;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Rect;
import android.widget.ScrollView;
import com.google.android.libraries.feed.api.stream.ContentChangedListener; import com.google.android.libraries.feed.api.stream.ContentChangedListener;
import com.google.android.libraries.feed.api.stream.ScrollListener; import com.google.android.libraries.feed.api.stream.ScrollListener;
...@@ -36,6 +38,7 @@ class FeedNewTabPageMediator ...@@ -36,6 +38,7 @@ class FeedNewTabPageMediator
private SectionHeader mSectionHeader; private SectionHeader mSectionHeader;
private MemoryPressureCallback mMemoryPressureCallback; private MemoryPressureCallback mMemoryPressureCallback;
private boolean mFeedEnabled;
private boolean mTouchEnabled = true; private boolean mTouchEnabled = true;
private boolean mStreamContentChanged; private boolean mStreamContentChanged;
private int mThumbnailWidth; private int mThumbnailWidth;
...@@ -44,31 +47,26 @@ class FeedNewTabPageMediator ...@@ -44,31 +47,26 @@ class FeedNewTabPageMediator
/** /**
* @param feedNewTabPage The {@link FeedNewTabPage} that interacts with this class. * @param feedNewTabPage The {@link FeedNewTabPage} that interacts with this class.
* @param snapScrollHelper The {@link SnapScrollHelper} that handles snap scrolling.
*/ */
FeedNewTabPageMediator(FeedNewTabPage feedNewTabPage, SnapScrollHelper snapScrollHelper) { FeedNewTabPageMediator(FeedNewTabPage feedNewTabPage, SnapScrollHelper snapScrollHelper) {
mCoordinator = feedNewTabPage; mCoordinator = feedNewTabPage;
mSnapScrollHelper = snapScrollHelper; mSnapScrollHelper = snapScrollHelper;
initializeProperties();
mPrefChangeRegistrar = new PrefChangeRegistrar(); mPrefChangeRegistrar = new PrefChangeRegistrar();
mPrefChangeRegistrar.addObserver( mPrefChangeRegistrar.addObserver(Pref.NTP_ARTICLES_SECTION_ENABLED, this::updateContent);
Pref.NTP_ARTICLES_LIST_VISIBLE, this::updateSectionHeader); initialize();
// Create the content.
updateContent();
} }
/** Clears any dependencies. */ /** Clears any dependencies. */
void destroy() { void destroy() {
Stream stream = mCoordinator.getStream(); destroyPropertiesForStream();
stream.removeScrollListener(mStreamScrollListener);
stream.removeOnContentChangedListener(mStreamContentChangedListener);
mPrefChangeRegistrar.destroy(); mPrefChangeRegistrar.destroy();
MemoryPressureListener.removeCallback(mMemoryPressureCallback);
} }
/** private void initialize() {
* Initialize properties for UI components in the {@link FeedNewTabPage}.
* TODO(huayinz): Introduce a Model for these properties.
*/
private void initializeProperties() {
// Listen for layout changes on the NewTabPageView itself to catch changes in scroll // 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 // position that are due to layout changes after e.g. device rotation. This contrasts with
// regular scrolling, which is observed through an OnScrollListener. // regular scrolling, which is observed through an OnScrollListener.
...@@ -76,7 +74,33 @@ class FeedNewTabPageMediator ...@@ -76,7 +74,33 @@ class FeedNewTabPageMediator
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
mSnapScrollHelper.handleScroll(); mSnapScrollHelper.handleScroll();
}); });
}
/** Update the content based on supervised user or enterprise policy. */
private void updateContent() {
mFeedEnabled =
PrefServiceBridge.getInstance().getBoolean(Pref.NTP_ARTICLES_SECTION_ENABLED);
if ((mFeedEnabled && mCoordinator.getStream() != null)
|| (!mFeedEnabled && mCoordinator.getScrollViewForPolicy() != null))
return;
if (mFeedEnabled) {
mCoordinator.createStream();
mSnapScrollHelper.setView(mCoordinator.getStream().getView());
initializePropertiesForStream();
} else {
destroyPropertiesForStream();
mCoordinator.createScrollViewForPolicy();
mSnapScrollHelper.setView(mCoordinator.getScrollViewForPolicy());
initializePropertiesForPolicy();
}
}
/**
* Initialize properties for UI components in the {@link FeedNewTabPage}.
* TODO(huayinz): Introduce a Model for these properties.
*/
private void initializePropertiesForStream() {
Stream stream = mCoordinator.getStream(); Stream stream = mCoordinator.getStream();
mStreamScrollListener = new ScrollListener() { mStreamScrollListener = new ScrollListener() {
@Override @Override
...@@ -97,6 +121,8 @@ class FeedNewTabPageMediator ...@@ -97,6 +121,8 @@ class FeedNewTabPageMediator
new SectionHeader(res.getString(R.string.ntp_article_suggestions_section_header), new SectionHeader(res.getString(R.string.ntp_article_suggestions_section_header),
PrefServiceBridge.getInstance().getBoolean(Pref.NTP_ARTICLES_LIST_VISIBLE), PrefServiceBridge.getInstance().getBoolean(Pref.NTP_ARTICLES_LIST_VISIBLE),
this::onSectionHeaderToggled); this::onSectionHeaderToggled);
mPrefChangeRegistrar.addObserver(
Pref.NTP_ARTICLES_LIST_VISIBLE, this::updateSectionHeader);
mCoordinator.getSectionHeaderView().setHeader(mSectionHeader); mCoordinator.getSectionHeaderView().setHeader(mSectionHeader);
stream.setStreamContentVisibility(mSectionHeader.isExpanded()); stream.setStreamContentVisibility(mSectionHeader.isExpanded());
...@@ -104,6 +130,28 @@ class FeedNewTabPageMediator ...@@ -104,6 +130,28 @@ class FeedNewTabPageMediator
MemoryPressureListener.addCallback(mMemoryPressureCallback); MemoryPressureListener.addCallback(mMemoryPressureCallback);
} }
/** Clear any dependencies related to the {@link Stream}. */
private void destroyPropertiesForStream() {
Stream stream = mCoordinator.getStream();
if (stream == null) return;
stream.removeScrollListener(mStreamScrollListener);
stream.removeOnContentChangedListener(mStreamContentChangedListener);
MemoryPressureListener.removeCallback(mMemoryPressureCallback);
mPrefChangeRegistrar.removeObserver(Pref.NTP_ARTICLES_LIST_VISIBLE);
mStreamScrollListener = null;
mStreamContentChangedListener = null;
mMemoryPressureCallback = null;
}
/**
* Initialize properties for the scroll view shown under supervised user or enterprise policy.
*/
private void initializePropertiesForPolicy() {
ScrollView view = mCoordinator.getScrollViewForPolicy();
view.getViewTreeObserver().addOnScrollChangedListener(mSnapScrollHelper::handleScroll);
}
/** Update whether the section header should be expanded. */ /** Update whether the section header should be expanded. */
private void updateSectionHeader() { private void updateSectionHeader() {
if (mSectionHeader.isExpanded() if (mSectionHeader.isExpanded()
...@@ -158,26 +206,42 @@ class FeedNewTabPageMediator ...@@ -158,26 +206,42 @@ class FeedNewTabPageMediator
@Override @Override
public boolean isScrollViewInitialized() { public boolean isScrollViewInitialized() {
if (mFeedEnabled) {
Stream stream = mCoordinator.getStream(); Stream stream = mCoordinator.getStream();
// During startup the view may not be fully initialized, so we check to see if some basic // During startup the view may not be fully initialized, so we check to see if some
// view properties (height of the RecyclerView) are sane. // basic view properties (height of the RecyclerView) are sane.
return stream != null && stream.getView().getHeight() > 0; return stream != null && stream.getView().getHeight() > 0;
} else {
ScrollView scrollView = mCoordinator.getScrollViewForPolicy();
return scrollView != null && scrollView.getHeight() > 0;
}
} }
@Override @Override
public int getVerticalScrollOffset() { public int getVerticalScrollOffset() {
// This method returns a valid vertical scroll offset only when the first header view in the // 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. // Stream is visible.
if (!isScrollViewInitialized()) return 0; if (!isScrollViewInitialized()) return 0;
if (mFeedEnabled) {
int firstChildTop = mCoordinator.getStream().getChildTopAt(0); int firstChildTop = mCoordinator.getStream().getChildTopAt(0);
return firstChildTop != Stream.POSITION_NOT_KNOWN ? -firstChildTop : Integer.MIN_VALUE; return firstChildTop != Stream.POSITION_NOT_KNOWN ? -firstChildTop : Integer.MIN_VALUE;
} else {
return mCoordinator.getScrollViewForPolicy().getScrollY();
}
} }
@Override @Override
public boolean isChildVisibleAtPosition(int position) { public boolean isChildVisibleAtPosition(int position) {
if (mFeedEnabled) {
return isScrollViewInitialized() return isScrollViewInitialized()
&& mCoordinator.getStream().isChildAtPositionVisible(position); && mCoordinator.getStream().isChildAtPositionVisible(position);
} else {
ScrollView scrollView = mCoordinator.getScrollViewForPolicy();
Rect rect = new Rect();
scrollView.getHitRect(rect);
return scrollView.getChildAt(position).getLocalVisibleRect(rect);
}
} }
@Override @Override
...@@ -189,6 +253,11 @@ class FeedNewTabPageMediator ...@@ -189,6 +253,11 @@ class FeedNewTabPageMediator
// Calculating the snap position should be idempotent. // Calculating the snap position should be idempotent.
assert scrollTo == mSnapScrollHelper.calculateSnapPosition(scrollTo); assert scrollTo == mSnapScrollHelper.calculateSnapPosition(scrollTo);
if (mFeedEnabled) {
mCoordinator.getStream().smoothScrollBy(0, scrollTo - initialScroll); mCoordinator.getStream().smoothScrollBy(0, scrollTo - initialScroll);
} else {
mCoordinator.getScrollViewForPolicy().smoothScrollBy(0, scrollTo - initialScroll);
}
} }
} }
...@@ -133,7 +133,8 @@ public class NewTabPageView extends FrameLayout { ...@@ -133,7 +133,8 @@ public class NewTabPageView extends FrameLayout {
mNewTabPageLayout.initialize(manager, tab, tileGroupDelegate, searchProviderHasLogo, mNewTabPageLayout.initialize(manager, tab, tileGroupDelegate, searchProviderHasLogo,
searchProviderIsGoogle, mRecyclerView, mContextMenuManager, mUiConfig); searchProviderIsGoogle, mRecyclerView, mContextMenuManager, mUiConfig);
mSnapScrollHelper = new SnapScrollHelper(mManager, mNewTabPageLayout, mRecyclerView); mSnapScrollHelper = new SnapScrollHelper(mManager, mNewTabPageLayout);
mSnapScrollHelper.setView(mRecyclerView);
mRecyclerView.setSnapScrollHelper(mSnapScrollHelper); mRecyclerView.setSnapScrollHelper(mSnapScrollHelper);
addView(mRecyclerView); addView(mRecyclerView);
......
...@@ -6,6 +6,7 @@ package org.chromium.chrome.browser.ntp; ...@@ -6,6 +6,7 @@ package org.chromium.chrome.browser.ntp;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.res.Resources; import android.content.res.Resources;
import android.support.annotation.NonNull;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
...@@ -21,11 +22,11 @@ public class SnapScrollHelper { ...@@ -21,11 +22,11 @@ public class SnapScrollHelper {
private final NewTabPageManager mManager; private final NewTabPageManager mManager;
private final NewTabPageLayout mNewTabPageLayout; private final NewTabPageLayout mNewTabPageLayout;
private final View mView;
private final Runnable mSnapScrollRunnable; private final Runnable mSnapScrollRunnable;
private final int mToolbarHeight; private final int mToolbarHeight;
private final int mSearchBoxTransitionLength; private final int mSearchBoxTransitionLength;
private View mView;
private boolean mPendingSnapScroll; private boolean mPendingSnapScroll;
private int mLastScrollY = -1; private int mLastScrollY = -1;
...@@ -33,20 +34,30 @@ public class SnapScrollHelper { ...@@ -33,20 +34,30 @@ public class SnapScrollHelper {
* @param manager The {@link NewTabPageManager} to get information about user interactions on * @param manager The {@link NewTabPageManager} to get information about user interactions on
* the {@link NewTabPage}. * the {@link NewTabPage}.
* @param newTabPageLayout The {@link NewTabPageLayout} associated with the {@link NewTabPage}. * @param newTabPageLayout The {@link NewTabPageLayout} associated with the {@link NewTabPage}.
* @param view The view on which this class needs to handle snap scroll.
*/ */
public SnapScrollHelper(NewTabPageView.NewTabPageManager manager, public SnapScrollHelper(
NewTabPageLayout newTabPageLayout, View view) { NewTabPageView.NewTabPageManager manager, NewTabPageLayout newTabPageLayout) {
mManager = manager; mManager = manager;
mNewTabPageLayout = newTabPageLayout; mNewTabPageLayout = newTabPageLayout;
mView = view;
mSnapScrollRunnable = new SnapScrollRunnable(); mSnapScrollRunnable = new SnapScrollRunnable();
Resources res = view.getResources(); Resources res = newTabPageLayout.getResources();
mToolbarHeight = res.getDimensionPixelSize(R.dimen.toolbar_height_no_shadow) mToolbarHeight = res.getDimensionPixelSize(R.dimen.toolbar_height_no_shadow)
+ res.getDimensionPixelSize(R.dimen.toolbar_progress_bar_height); + res.getDimensionPixelSize(R.dimen.toolbar_progress_bar_height);
mSearchBoxTransitionLength = mSearchBoxTransitionLength =
res.getDimensionPixelSize(R.dimen.ntp_search_box_transition_length); res.getDimensionPixelSize(R.dimen.ntp_search_box_transition_length);
}
/** @param view The view on which this class needs to handle snap scroll. */
public void setView(@NonNull View view) {
if (mView != null) {
mPendingSnapScroll = false;
mLastScrollY = -1;
mView.removeCallbacks(mSnapScrollRunnable);
mView.setOnTouchListener(null);
}
mView = view;
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
View.OnTouchListener onTouchListener = (v, event) -> { View.OnTouchListener onTouchListener = (v, event) -> {
......
...@@ -45,6 +45,17 @@ public class PrefChangeRegistrar { ...@@ -45,6 +45,17 @@ public class PrefChangeRegistrar {
nativeAdd(mNativeRegistrar, preference); nativeAdd(mNativeRegistrar, preference);
} }
/**
* Remove an observer for the specified preference if it has previously been added.
* @param preference The specified preference.
*/
public void removeObserver(@Pref int preference) {
PrefObserver observer = mObservers.get(preference);
if (observer == null) return;
mObservers.remove(preference);
nativeRemove(mNativeRegistrar, preference);
}
/** /**
* Destroy native PrefChangeRegistrar. * Destroy native PrefChangeRegistrar.
*/ */
...@@ -64,5 +75,6 @@ public class PrefChangeRegistrar { ...@@ -64,5 +75,6 @@ public class PrefChangeRegistrar {
private native long nativeInit(); private native long nativeInit();
private native void nativeAdd(long nativePrefChangeRegistrarAndroid, int preference); private native void nativeAdd(long nativePrefChangeRegistrarAndroid, int preference);
private native void nativeRemove(long nativePrefChangeRegistrarAndroid, int preference);
private native void nativeDestroy(long nativePrefChangeRegistrarAndroid); private native void nativeDestroy(long nativePrefChangeRegistrarAndroid);
} }
...@@ -40,6 +40,13 @@ void PrefChangeRegistrarAndroid::Add(JNIEnv* env, ...@@ -40,6 +40,13 @@ void PrefChangeRegistrarAndroid::Add(JNIEnv* env,
base::Unretained(this), j_pref_index)); base::Unretained(this), j_pref_index));
} }
void PrefChangeRegistrarAndroid::Remove(JNIEnv* env,
const JavaParamRef<jobject>& obj,
const jint j_pref_index) {
pref_change_registrar_.Remove(
PrefServiceBridge::GetPrefNameExposedToJava(j_pref_index));
}
void PrefChangeRegistrarAndroid::OnPreferenceChange(const int pref_index) { void PrefChangeRegistrarAndroid::OnPreferenceChange(const int pref_index) {
JNIEnv* env = AttachCurrentThread(); JNIEnv* env = AttachCurrentThread();
Java_PrefChangeRegistrar_onPreferenceChange( Java_PrefChangeRegistrar_onPreferenceChange(
......
...@@ -23,6 +23,9 @@ class PrefChangeRegistrarAndroid { ...@@ -23,6 +23,9 @@ class PrefChangeRegistrarAndroid {
void Add(JNIEnv* env, void Add(JNIEnv* env,
const JavaParamRef<jobject>& obj, const JavaParamRef<jobject>& obj,
const jint j_pref_index); const jint j_pref_index);
void Remove(JNIEnv* env,
const JavaParamRef<jobject>& obj,
const jint j_pref_index);
private: private:
~PrefChangeRegistrarAndroid(); ~PrefChangeRegistrarAndroid();
......
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