Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1445)

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java

Issue 1833763002: Add new NTP layout with snippet cards and hide it behind a flag (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: address more comments Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
index e2b074a2d2f9a38bbbe412a1bdbaf4973062b053..c43625bee6ceef310c98a4c7dc07972b36a73952 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java
@@ -16,7 +16,6 @@
import android.net.Uri;
import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
-import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Editable;
import android.text.TextUtils;
@@ -34,10 +33,8 @@
import org.chromium.base.Log;
import org.chromium.base.VisibleForTesting;
-import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.RecordUserAction;
import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ChromeFeatureList;
import org.chromium.chrome.browser.favicon.FaviconHelper.FaviconImageCallback;
import org.chromium.chrome.browser.favicon.FaviconHelper.IconAvailabilityCallback;
import org.chromium.chrome.browser.favicon.LargeIconBridge.LargeIconCallback;
@@ -45,7 +42,9 @@
import org.chromium.chrome.browser.ntp.LogoBridge.LogoObserver;
import org.chromium.chrome.browser.ntp.MostVisitedItem.MostVisitedItemManager;
import org.chromium.chrome.browser.ntp.NewTabPage.OnSearchBoxScrollListener;
-import org.chromium.chrome.browser.ntp.snippets.SnippetsManager;
+import org.chromium.chrome.browser.ntp.cards.NewTabPageAdapter;
+import org.chromium.chrome.browser.ntp.cards.NewTabPageRecyclerView;
+import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge.SnippetsObserver;
import org.chromium.chrome.browser.profiles.MostVisitedSites.MostVisitedURLsObserver;
import org.chromium.chrome.browser.profiles.MostVisitedSites.ThumbnailCallback;
import org.chromium.chrome.browser.util.ViewUtils;
@@ -62,10 +61,20 @@
private static final int SHADOW_COLOR = 0x11000000;
private static final long SNAP_SCROLL_DELAY_MS = 30;
- private static final String TAG = "NewTabPageView";
+ private static final String TAG = "Ntp";
- private ViewGroup mContentView;
- private NewTabScrollView mScrollView;
+ /**
+ * Indicates which UI mode we are using. Should be checked when manipulating some members, as
+ * they may be unused or {@code null} depending on the mode.
+ */
+ private boolean mUseCardsUi;
+
+ // Note: Only one of these will be valid at a time, depending on if we are using the old NTP
+ // (NewTabPageScrollView) or the new NTP with cards (NewTabPageRecyclerView).
+ private NewTabPageScrollView mScrollView;
+ private NewTabPageRecyclerView mRecyclerView;
+
+ private NewTabPageLayout mContentView;
private LogoView mSearchProviderLogoView;
private View mSearchBoxView;
private TextView mSearchBoxTextView;
@@ -73,7 +82,9 @@
private MostVisitedLayout mMostVisitedLayout;
private View mMostVisitedPlaceholder;
private View mNoSearchLogoSpacer;
- private RecyclerView mSnippetsView;
+
+ /** Adapter for {@link #mRecyclerView}. Will be {@code null} when using the old UI */
+ private NewTabPageAdapter mNtpAdapter;
private OnSearchBoxScrollListener mSearchBoxScrollListener;
@@ -142,6 +153,9 @@
*/
void setMostVisitedURLsObserver(MostVisitedURLsObserver observer, int numResults);
+ /** Sets the observer that will be notified of new snippets. */
+ void setSnippetsObserver(SnippetsObserver observer);
+
/**
* Gets a cached thumbnail of a URL.
* @param url The URL whose thumbnail is being retrieved.
@@ -234,22 +248,41 @@ public NewTabPageView(Context context, AttributeSet attrs) {
* with the page.
* @param isSingleUrlBarMode Whether the NTP is in single URL bar mode.
* @param searchProviderHasLogo Whether the search provider has a logo.
+ * @param useCardsUi Whether to use the new cards based UI or the old one.
*/
public void initialize(NewTabPageManager manager, boolean isSingleUrlBarMode,
- boolean searchProviderHasLogo, SnippetsManager snippetsManager) {
+ boolean searchProviderHasLogo, boolean useCardsUi) {
mManager = manager;
+ ViewStub stub = (ViewStub) findViewById(R.id.new_tab_page_layout_stub);
+
+ mUseCardsUi = useCardsUi;
+ if (mUseCardsUi) {
+ stub.setLayoutResource(R.layout.new_tab_page_recycler_view);
+ mRecyclerView = (NewTabPageRecyclerView) stub.inflate();
+
+ // Don't attach now, the recyclerView itself will determine when to do it.
+ mContentView = (NewTabPageLayout) LayoutInflater.from(getContext())
+ .inflate(R.layout.new_tab_page_layout, mRecyclerView, false);
- mScrollView = (NewTabScrollView) findViewById(R.id.ntp_scrollview);
- mScrollView.enableBottomShadow(SHADOW_COLOR);
- mContentView = (ViewGroup) findViewById(R.id.ntp_content);
+ // Tailor the LayoutParams for the snippets UI, as the configuration in the XML is
+ // made for the ScrollView UI.
+ ViewGroup.LayoutParams params = mContentView.getLayoutParams();
+ params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ } else {
+ stub.setLayoutResource(R.layout.new_tab_page_scroll_view);
+ mScrollView = (NewTabPageScrollView) stub.inflate();
+ mScrollView.enableBottomShadow(SHADOW_COLOR);
+ mContentView = (NewTabPageLayout) findViewById(R.id.ntp_content);
+ }
mMostVisitedDesign = new MostVisitedDesign(getContext());
- mMostVisitedLayout = (MostVisitedLayout) findViewById(R.id.most_visited_layout);
+ mMostVisitedLayout =
+ (MostVisitedLayout) mContentView.findViewById(R.id.most_visited_layout);
mMostVisitedDesign.initMostVisitedLayout(mMostVisitedLayout, searchProviderHasLogo);
- mSearchProviderLogoView = (LogoView) findViewById(R.id.search_provider_logo);
- mSearchBoxView = findViewById(R.id.search_box);
- mNoSearchLogoSpacer = findViewById(R.id.no_search_logo_spacer);
+ mSearchProviderLogoView = (LogoView) mContentView.findViewById(R.id.search_provider_logo);
+ mSearchBoxView = mContentView.findViewById(R.id.search_box);
+ mNoSearchLogoSpacer = mContentView.findViewById(R.id.no_search_logo_spacer);
mSearchBoxTextView = (TextView) mSearchBoxView.findViewById(R.id.search_box_text);
String hintText = getResources().getString(R.string.search_or_type_url);
@@ -281,7 +314,7 @@ public void afterTextChanged(Editable s) {
}
});
- mVoiceSearchButton = (ImageView) findViewById(R.id.voice_search_button);
+ mVoiceSearchButton = (ImageView) mContentView.findViewById(R.id.voice_search_button);
mVoiceSearchButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -317,13 +350,16 @@ public void onClick(View v) {
}
} else {
((ViewGroup) toolbar.getParent()).removeView(toolbar);
- FrameLayout.LayoutParams params =
- (FrameLayout.LayoutParams) mScrollView.getLayoutParams();
- params.bottomMargin = 0;
- mScrollView.setLayoutParams(params);
+ if (!mUseCardsUi) {
+ // Only remove if we're using the old NTP view, the new one does not use a
+ // ScrollView
+ FrameLayout.LayoutParams params =
+ (FrameLayout.LayoutParams) mScrollView.getLayoutParams();
+ params.bottomMargin = 0;
+ mScrollView.setLayoutParams(params);
+ }
}
- initializeSearchBoxScrollHandling();
addOnLayoutChangeListener(this);
setSearchProviderHasLogo(searchProviderHasLogo);
@@ -332,13 +368,12 @@ public void onClick(View v) {
mMostVisitedDesign.getNumberOfTiles(searchProviderHasLogo));
// Set up snippets
- if (ChromeFeatureList.isEnabled(ChromeFeatureList.NTP_SNIPPETS)) {
- mSnippetsView = (RecyclerView) findViewById(R.id.snippets_card_list);
- mSnippetsView.setVisibility(View.VISIBLE);
- RecordHistogram.recordEnumeratedHistogram(SnippetsManager.SNIPPETS_STATE_HISTOGRAM,
- SnippetsManager.SNIPPETS_SHOWN, SnippetsManager.NUM_SNIPPETS_ACTIONS);
- mSnippetsView.setLayoutManager(new LinearLayoutManager(getContext()));
- mSnippetsView.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ if (mUseCardsUi) {
+ mNtpAdapter = new NewTabPageAdapter(mManager, mContentView);
+ mRecyclerView.setAdapter(mNtpAdapter);
+
+ NewTabPageUma.recordSnippetAction(NewTabPageUma.SNIPPETS_ACTION_SHOWN);
+ mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
private boolean mScrolledOnce = false;
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
@@ -346,13 +381,12 @@ public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
RecordUserAction.record("MobileNTP.Snippets.Scrolled");
if (mScrolledOnce) return;
mScrolledOnce = true;
- RecordHistogram.recordEnumeratedHistogram(
- SnippetsManager.SNIPPETS_STATE_HISTOGRAM,
- SnippetsManager.SNIPPETS_SCROLLED,
- SnippetsManager.NUM_SNIPPETS_ACTIONS);
+ NewTabPageUma.recordSnippetAction(NewTabPageUma.SNIPPETS_ACTION_SCROLLED);
}
});
- snippetsManager.setSnippetsView(mSnippetsView);
+ initializeSearchBoxRecyclerViewScrollHandling();
+ } else {
+ initializeSearchBoxScrollHandling();
}
}
@@ -362,10 +396,10 @@ private void updateSearchBoxOnScroll() {
float percentage = 0;
// During startup the view may not be fully initialized, so we only calculate the current
// percentage if some basic view properties are sane.
- if (mScrollView.getHeight() != 0 && mSearchBoxView.getTop() != 0) {
- int scrollY = mScrollView.getScrollY();
- percentage = Math.max(
- 0f, Math.min(1f, scrollY / (float) mSearchBoxView.getTop()));
+ View wrapperView = mUseCardsUi ? mRecyclerView : mScrollView;
+ if (wrapperView.getHeight() != 0 && mSearchBoxView.getTop() != 0) {
+ int scrollY = getVerticalScroll();
+ percentage = Math.max(0f, Math.min(1f, scrollY / (float) mSearchBoxView.getTop()));
}
updateVisualsForToolbarTransition(percentage);
@@ -375,6 +409,15 @@ private void updateSearchBoxOnScroll() {
}
}
+ private void initializeSearchBoxRecyclerViewScrollHandling() {
+ mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ updateSearchBoxOnScroll();
+ }
+ });
+ }
+
private void initializeSearchBoxScrollHandling() {
final Runnable mSnapScrollRunnable = new Runnable() {
@Override
@@ -388,7 +431,7 @@ public void run() {
mPendingSnapScroll = false;
}
};
- mScrollView.setOnScrollListener(new NewTabScrollView.OnScrollListener() {
+ mScrollView.setOnScrollListener(new NewTabPageScrollView.OnScrollListener() {
@Override
public void onScrollChanged(int l, int t, int oldl, int oldt) {
if (mPendingSnapScroll) {
@@ -548,7 +591,7 @@ float getUrlFocusChangeAnimationPercent() {
*/
private void setUrlFocusChangeAnimationPercentInternal(float percent) {
mContentView.setTranslationY(percent * (-mMostVisitedLayout.getTop()
- + mScrollView.getScrollY() + mContentView.getPaddingTop()));
+ + getVerticalScroll() + mContentView.getPaddingTop()));
updateVisualsForToolbarTransition(percent);
}
@@ -586,7 +629,12 @@ void getSearchBoxBounds(Rect originalBounds, Rect transformedBounds) {
transformedBounds.set(originalBounds);
View view = (View) mSearchBoxView.getParent();
while (view != null) {
- transformedBounds.offset(-view.getScrollX(), -view.getScrollY());
+ if (view instanceof RecyclerView) {
+ transformedBounds.offset(-((RecyclerView) view).computeHorizontalScrollOffset(),
+ -((RecyclerView) view).computeVerticalScrollOffset());
+ } else {
+ transformedBounds.offset(-view.getScrollX(), -view.getScrollY());
+ }
if (view == this) break;
transformedBounds.offset((int) view.getX(), (int) view.getY());
view = (View) view.getParent();
@@ -640,23 +688,6 @@ protected void onWindowVisibilityChanged(int visibility) {
}
}
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- // Make the search box and logo the same width as the most visited tiles.
- if (mMostVisitedLayout.getVisibility() != GONE) {
- int mostVisitedWidth = MeasureSpec.makeMeasureSpec(mMostVisitedLayout.getMeasuredWidth()
- - mMostVisitedDesign.getMostVisitedLayoutBleed(), MeasureSpec.EXACTLY);
- int searchBoxHeight = MeasureSpec.makeMeasureSpec(
- mSearchBoxView.getMeasuredHeight(), MeasureSpec.EXACTLY);
- int logoHeight = MeasureSpec.makeMeasureSpec(
- mSearchProviderLogoView.getMeasuredHeight(), MeasureSpec.EXACTLY);
- mSearchBoxView.measure(mostVisitedWidth, searchBoxHeight);
- mSearchProviderLogoView.measure(mostVisitedWidth, logoHeight);
- }
- }
-
/**
* @see org.chromium.chrome.browser.compositor.layouts.content.
* InvalidationAwareThumbnailProvider#shouldCaptureThumbnail()
@@ -664,10 +695,8 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
boolean shouldCaptureThumbnail() {
if (getWidth() == 0 || getHeight() == 0) return false;
- return mSnapshotMostVisitedChanged
- || getWidth() != mSnapshotWidth
- || getHeight() != mSnapshotHeight
- || mScrollView.getScrollY() != mSnapshotScrollY;
+ return mSnapshotMostVisitedChanged || getWidth() != mSnapshotWidth
+ || getHeight() != mSnapshotHeight || getVerticalScroll() != mSnapshotScrollY;
}
/**
@@ -679,7 +708,7 @@ void captureThumbnail(Canvas canvas) {
ViewUtils.captureBitmap(this, canvas);
mSnapshotWidth = getWidth();
mSnapshotHeight = getHeight();
- mSnapshotScrollY = mScrollView.getScrollY();
+ mSnapshotScrollY = getVerticalScroll();
mSnapshotMostVisitedChanged = false;
}
@@ -825,15 +854,12 @@ private void updateMostVisitedPlaceholderVisibility() {
private static final int ICON_BACKGROUND_COLOR = 0xff787878;
private static final int ICON_MIN_SIZE_PX = 48;
- private int mMostVisitedLayoutBleed;
private int mMinIconSize;
private int mDesiredIconSize;
private RoundedIconGenerator mIconGenerator;
MostVisitedDesign(Context context) {
Resources res = context.getResources();
- mMostVisitedLayoutBleed = res.getDimensionPixelSize(
- R.dimen.most_visited_layout_bleed);
mDesiredIconSize = res.getDimensionPixelSize(R.dimen.most_visited_icon_size);
// On ldpi devices, mDesiredIconSize could be even smaller than ICON_MIN_SIZE_PX.
mMinIconSize = Math.min(mDesiredIconSize, ICON_MIN_SIZE_PX);
@@ -848,10 +874,6 @@ public int getNumberOfTiles(boolean searchProviderHasLogo) {
return searchProviderHasLogo ? NUM_TILES : NUM_TILES_NO_LOGO;
}
- public int getMostVisitedLayoutBleed() {
- return mMostVisitedLayoutBleed;
- }
-
public void initMostVisitedLayout(MostVisitedLayout mostVisitedLayout,
boolean searchProviderHasLogo) {
mostVisitedLayout.setMaxRows(searchProviderHasLogo ? MAX_ROWS : MAX_ROWS_NO_LOGO);
@@ -942,4 +964,12 @@ public void onIconUpdated(final String url) {
}
}
}
+
+ private int getVerticalScroll() {
+ if (mUseCardsUi) {
+ return mRecyclerView.computeVerticalScrollOffset();
+ } else {
+ return mScrollView.getScrollY();
+ }
+ }
}

Powered by Google App Engine
This is Rietveld 408576698