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
......@@ -5,6 +5,8 @@
package org.chromium.chrome.browser.feed;
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.ScrollListener;
......@@ -36,6 +38,7 @@ class FeedNewTabPageMediator
private SectionHeader mSectionHeader;
private MemoryPressureCallback mMemoryPressureCallback;
private boolean mFeedEnabled;
private boolean mTouchEnabled = true;
private boolean mStreamContentChanged;
private int mThumbnailWidth;
......@@ -44,31 +47,26 @@ class FeedNewTabPageMediator
/**
* @param feedNewTabPage The {@link FeedNewTabPage} that interacts with this class.
* @param snapScrollHelper The {@link SnapScrollHelper} that handles snap scrolling.
*/
FeedNewTabPageMediator(FeedNewTabPage feedNewTabPage, SnapScrollHelper snapScrollHelper) {
mCoordinator = feedNewTabPage;
mSnapScrollHelper = snapScrollHelper;
initializeProperties();
mPrefChangeRegistrar = new PrefChangeRegistrar();
mPrefChangeRegistrar.addObserver(
Pref.NTP_ARTICLES_LIST_VISIBLE, this::updateSectionHeader);
mPrefChangeRegistrar.addObserver(Pref.NTP_ARTICLES_SECTION_ENABLED, this::updateContent);
initialize();
// Create the content.
updateContent();
}
/** Clears any dependencies. */
void destroy() {
Stream stream = mCoordinator.getStream();
stream.removeScrollListener(mStreamScrollListener);
stream.removeOnContentChangedListener(mStreamContentChangedListener);
destroyPropertiesForStream();
mPrefChangeRegistrar.destroy();
MemoryPressureListener.removeCallback(mMemoryPressureCallback);
}
/**
* Initialize properties for UI components in the {@link FeedNewTabPage}.
* TODO(huayinz): Introduce a Model for these properties.
*/
private void initializeProperties() {
private void initialize() {
// 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.
......@@ -76,7 +74,33 @@ class FeedNewTabPageMediator
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
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();
mStreamScrollListener = new ScrollListener() {
@Override
......@@ -97,6 +121,8 @@ class FeedNewTabPageMediator
new SectionHeader(res.getString(R.string.ntp_article_suggestions_section_header),
PrefServiceBridge.getInstance().getBoolean(Pref.NTP_ARTICLES_LIST_VISIBLE),
this::onSectionHeaderToggled);
mPrefChangeRegistrar.addObserver(
Pref.NTP_ARTICLES_LIST_VISIBLE, this::updateSectionHeader);
mCoordinator.getSectionHeaderView().setHeader(mSectionHeader);
stream.setStreamContentVisibility(mSectionHeader.isExpanded());
......@@ -104,6 +130,28 @@ class FeedNewTabPageMediator
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. */
private void updateSectionHeader() {
if (mSectionHeader.isExpanded()
......@@ -158,26 +206,42 @@ class FeedNewTabPageMediator
@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;
if (mFeedEnabled) {
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;
} else {
ScrollView scrollView = mCoordinator.getScrollViewForPolicy();
return scrollView != null && scrollView.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.
// Stream is visible.
if (!isScrollViewInitialized()) return 0;
int firstChildTop = mCoordinator.getStream().getChildTopAt(0);
return firstChildTop != Stream.POSITION_NOT_KNOWN ? -firstChildTop : Integer.MIN_VALUE;
if (mFeedEnabled) {
int firstChildTop = mCoordinator.getStream().getChildTopAt(0);
return firstChildTop != Stream.POSITION_NOT_KNOWN ? -firstChildTop : Integer.MIN_VALUE;
} else {
return mCoordinator.getScrollViewForPolicy().getScrollY();
}
}
@Override
public boolean isChildVisibleAtPosition(int position) {
return isScrollViewInitialized()
&& mCoordinator.getStream().isChildAtPositionVisible(position);
if (mFeedEnabled) {
return isScrollViewInitialized()
&& mCoordinator.getStream().isChildAtPositionVisible(position);
} else {
ScrollView scrollView = mCoordinator.getScrollViewForPolicy();
Rect rect = new Rect();
scrollView.getHitRect(rect);
return scrollView.getChildAt(position).getLocalVisibleRect(rect);
}
}
@Override
......@@ -189,6 +253,11 @@ class FeedNewTabPageMediator
// Calculating the snap position should be idempotent.
assert scrollTo == mSnapScrollHelper.calculateSnapPosition(scrollTo);
mCoordinator.getStream().smoothScrollBy(0, scrollTo - initialScroll);
if (mFeedEnabled) {
mCoordinator.getStream().smoothScrollBy(0, scrollTo - initialScroll);
} else {
mCoordinator.getScrollViewForPolicy().smoothScrollBy(0, scrollTo - initialScroll);
}
}
}
......@@ -133,7 +133,8 @@ public class NewTabPageView extends FrameLayout {
mNewTabPageLayout.initialize(manager, tab, tileGroupDelegate, searchProviderHasLogo,
searchProviderIsGoogle, mRecyclerView, mContextMenuManager, mUiConfig);
mSnapScrollHelper = new SnapScrollHelper(mManager, mNewTabPageLayout, mRecyclerView);
mSnapScrollHelper = new SnapScrollHelper(mManager, mNewTabPageLayout);
mSnapScrollHelper.setView(mRecyclerView);
mRecyclerView.setSnapScrollHelper(mSnapScrollHelper);
addView(mRecyclerView);
......
......@@ -6,6 +6,7 @@ package org.chromium.chrome.browser.ntp;
import android.annotation.SuppressLint;
import android.content.res.Resources;
import android.support.annotation.NonNull;
import android.view.MotionEvent;
import android.view.View;
......@@ -21,11 +22,11 @@ public class SnapScrollHelper {
private final NewTabPageManager mManager;
private final NewTabPageLayout mNewTabPageLayout;
private final View mView;
private final Runnable mSnapScrollRunnable;
private final int mToolbarHeight;
private final int mSearchBoxTransitionLength;
private View mView;
private boolean mPendingSnapScroll;
private int mLastScrollY = -1;
......@@ -33,20 +34,30 @@ public class SnapScrollHelper {
* @param manager The {@link NewTabPageManager} to get information about user interactions on
* 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,
NewTabPageLayout newTabPageLayout, View view) {
public SnapScrollHelper(
NewTabPageView.NewTabPageManager manager, NewTabPageLayout newTabPageLayout) {
mManager = manager;
mNewTabPageLayout = newTabPageLayout;
mView = view;
mSnapScrollRunnable = new SnapScrollRunnable();
Resources res = view.getResources();
Resources res = newTabPageLayout.getResources();
mToolbarHeight = res.getDimensionPixelSize(R.dimen.toolbar_height_no_shadow)
+ res.getDimensionPixelSize(R.dimen.toolbar_progress_bar_height);
mSearchBoxTransitionLength =
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")
View.OnTouchListener onTouchListener = (v, event) -> {
......
......@@ -45,6 +45,17 @@ public class PrefChangeRegistrar {
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.
*/
......@@ -64,5 +75,6 @@ public class PrefChangeRegistrar {
private native long nativeInit();
private native void nativeAdd(long nativePrefChangeRegistrarAndroid, int preference);
private native void nativeRemove(long nativePrefChangeRegistrarAndroid, int preference);
private native void nativeDestroy(long nativePrefChangeRegistrarAndroid);
}
......@@ -40,6 +40,13 @@ void PrefChangeRegistrarAndroid::Add(JNIEnv* env,
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) {
JNIEnv* env = AttachCurrentThread();
Java_PrefChangeRegistrar_onPreferenceChange(
......
......@@ -23,6 +23,9 @@ class PrefChangeRegistrarAndroid {
void Add(JNIEnv* env,
const JavaParamRef<jobject>& obj,
const jint j_pref_index);
void Remove(JNIEnv* env,
const JavaParamRef<jobject>& obj,
const jint j_pref_index);
private:
~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