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

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

Issue 2004893002: Scroll to articles when peeking card is tapped. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 7 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/cards/CardsLayoutOperations.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/CardsLayoutOperations.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/CardsLayoutOperations.java
new file mode 100644
index 0000000000000000000000000000000000000000..b088af7ec0fcecd37270770864d4209fb7b730c0
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/CardsLayoutOperations.java
@@ -0,0 +1,211 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.ntp.cards;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ntp.MostVisitedLayout;
+import org.chromium.chrome.browser.ntp.NewTabPageLayout;
+import org.chromium.chrome.browser.ntp.NewTabPageUma;
+import org.chromium.chrome.browser.ntp.snippets.SnippetArticleViewHolder;
+import org.chromium.chrome.browser.util.MathUtils;
+
+/**
+ * This is a class to organise the various layout operations of the New Tab Page while using the
+ * cards layout. For example, updating the size of the peeking card or controlling the scroll
+ * behaviour.
+ */
+public class CardsLayoutOperations {
+
+ /**
+ * Change the width, padding and child opacity of the first card (the peeking card) to give a
+ * smooth transition as the user scrolls. The peeking card will scroll to the Articles on touch.
+ * @param recyclerView The NewTabPageRecyclerView that contains the peeking card.
+ * @param ntpLayout The NewTabPageLayout to scroll beyond when the peeking card is tapped.
+ * @param viewportHeight The height of the containing view, to calculate when the transition
+ * should start.
+ */
+ public static void updatePeekingCard(final NewTabPageRecyclerView recyclerView,
+ final NewTabPageLayout ntpLayout, int viewportHeight) {
+ // Get the first snippet that could display to make the peeking card transition.
+ ViewGroup firstSnippet = (ViewGroup) getFirstViewMatchingViewType(recyclerView,
+ NewTabPageListItem.VIEW_TYPE_SNIPPET);
+
+ if (firstSnippet == null || !firstSnippet.isShown()) return;
+
+ // If first snippet exists change the peeking card margin and padding to change its
+ // width when scrolling.
+ // Value used for max peeking card height and padding.
+ int maxPadding = recyclerView.getResources().getDimensionPixelSize(
+ R.dimen.snippets_padding_and_peeking_card_height);
+
+ // The peeking card's resting position is |maxPadding| from the bottom of the screen hence
+ // |viewportHeight - maxPadding|, and it grows the further it gets from this.
+ // Also, make sure the |padding| is between 0 and |maxPadding|.
+ final int padding =
+ MathUtils.clamp(viewportHeight - maxPadding - firstSnippet.getTop(), 0, maxPadding);
+
+ // Modify the padding so as the margin increases, the padding decreases, keeping the card's
+ // contents in the same position. The top and bottom remain the same.
+ firstSnippet.setPadding(padding, maxPadding, padding, maxPadding);
+
+ RecyclerView.LayoutParams params =
+ (RecyclerView.LayoutParams) firstSnippet.getLayoutParams();
+ params.leftMargin = maxPadding - padding;
+ params.rightMargin = maxPadding - padding;
+
+ // Set the opacity of the card content to be 0 when peeking and 1 when full width.
+ int firstSnippetChildCount = firstSnippet.getChildCount();
+ for (int i = 0; i < firstSnippetChildCount; ++i) {
+ View snippetChild = firstSnippet.getChildAt(i);
+ snippetChild.setAlpha(padding / (float) maxPadding);
+ }
+
+ // If the peeking card is peeking, set its onClick to scroll to the articles section.
+ // If not, reset its onClick to its ViewHolder.
+ if (padding < maxPadding) {
+ firstSnippet.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Offset the target scroll by the height of the omnibox (the top padding).
+ final int targetScroll = ntpLayout.getHeight() - ntpLayout.getPaddingTop();
+ // If (somehow) the peeking card is tapped while midway through the transition,
+ // we need to account for how much we have already scrolled.
+ recyclerView.smoothScrollBy(0, targetScroll - padding);
+ }
+ });
+ } else {
+ SnippetArticleViewHolder vh =
+ (SnippetArticleViewHolder) recyclerView.findContainingViewHolder(firstSnippet);
+ firstSnippet.setOnClickListener(vh);
+ }
+ }
+
+ /**
+ * Show the snippets header when the user scrolls down and snippet articles starts reaching the
+ * top of the screen.
+ * @param recyclerView The NewTabPageRecyclerView that contains the header card.
+ * @param omniBoxHeight The height of the omnibox displayed over the NTP, we use this to offset
+ * the start point of the transition.
+ */
+ public static void updateSnippetsHeaderDisplay(NewTabPageRecyclerView recyclerView,
+ int omniBoxHeight) {
+ // Get the snippet header view.
+ View snippetHeader = getFirstViewMatchingViewType(recyclerView,
+ NewTabPageListItem.VIEW_TYPE_HEADER);
+
+ if (snippetHeader == null || !snippetHeader.isShown()) return;
+
+ // Start doing the calculations if the snippet header is currently shown on screen.
+ RecyclerView.LayoutParams params =
+ (RecyclerView.LayoutParams) snippetHeader.getLayoutParams();
+ float headerAlpha = 0;
+ int headerHeight = 0;
+
+ // Get the max snippet header height.
+ int maxSnippetHeaderHeight = recyclerView.getResources()
+ .getDimensionPixelSize(R.dimen.snippets_article_header_height);
+ // Measurement used to multiply the max snippet height to get a range on when to start
+ // modifying the display of article header.
+ final int numberHeaderHeight = 2;
+ // Used to indicate when to start modifying the snippet header.
+ int heightToStartChangingHeader = maxSnippetHeaderHeight * numberHeaderHeight;
+ int snippetHeaderTop = snippetHeader.getTop();
+
+ // Check if snippet header top is within range to start showing the snippet header.
+ if (snippetHeaderTop < omniBoxHeight + heightToStartChangingHeader) {
+ // The amount of space the article header has scrolled into the
+ // |heightToStartChangingHeader|.
+ int amountScrolledIntoHeaderSpace =
+ heightToStartChangingHeader - (snippetHeaderTop - omniBoxHeight);
+
+ // Remove the |numberHeaderHeight| to get the actual header height we want to
+ // display. Never let the height be more than the |maxSnippetHeaderHeight|.
+ headerHeight = Math.min(
+ amountScrolledIntoHeaderSpace / numberHeaderHeight, maxSnippetHeaderHeight);
+
+ // Get the alpha for the snippet header.
+ headerAlpha = (float) headerHeight / maxSnippetHeaderHeight;
+ }
+ snippetHeader.setAlpha(headerAlpha);
+ params.height = headerHeight;
+ snippetHeader.setLayoutParams(params);
+
+ // Update the space at the bottom, which needs to know about the height of the header.
+ recyclerView.refreshBottomSpacing();
+ }
+
+ /**
+ * Snaps the scroll point to the top of the screen, the top of most visited or to articles
+ * depending on the current scroll. This function makes gross assumptions about the layout, so
+ * will probably need to be changed if the new tab page changes.
+ * @param recyclerView The NewTabPageRecyclerView containing everything.
+ * @param newTabPageLayout The above the fold content.
+ * @param mostVisitedLayout The view to snap to while snapping to most visited.
+ * @param currentScroll The current scroll.
+ * @return Whether the snap point was above or below the fold.
+ */
+ public static NewTabPageUma.SnapState snapScroll(NewTabPageRecyclerView recyclerView,
+ NewTabPageLayout newTabPageLayout, MostVisitedLayout mostVisitedLayout,
+ int currentScroll) {
+ // These calculations only work if the first item is visible (since
+ // computeVerticalScrollOffset only takes into account visible items).
+ // Luckily, we only need to perform the calculations if the first item is visible.
+ if (!recyclerView.isFirstItemVisible()) {
+ return NewTabPageUma.SnapState.BELOW_THE_FOLD;
+ }
+
+ // If snapping to Most Likely or to Articles, the omnibox will be at the top of the
+ // page, so offset the scroll so the scroll targets appear below it.
+ final int omniBoxHeight = newTabPageLayout.getPaddingTop();
+ final int topOfMostLikelyScroll = mostVisitedLayout.getTop() - omniBoxHeight;
+ final int topOfSnippetsScroll = newTabPageLayout.getHeight() - omniBoxHeight;
+
+ assert currentScroll >= 0;
+ // Do not do any scrolling if the user is currently viewing articles.
+ if (currentScroll >= topOfSnippetsScroll) {
+ return NewTabPageUma.SnapState.BELOW_THE_FOLD;
+ }
+
+ // If Most Likely is fully visible when we are scrolled to the top, we have two
+ // snap points: the Top and Articles.
+ // If not, we have three snap points, the Top, Most Likely and Articles.
+ boolean snapToMostLikely =
+ recyclerView.getHeight() < mostVisitedLayout.getBottom();
+
+ int targetScroll;
+ NewTabPageUma.SnapState snapState = NewTabPageUma.SnapState.ABOVE_THE_FOLD;
+ if (currentScroll < newTabPageLayout.getHeight() / 3) {
+ // In either case, if in the top 1/3 of the original NTP, snap to the top.
+ targetScroll = 0;
+ } else if (snapToMostLikely
+ && currentScroll < newTabPageLayout.getHeight() * 2 / 3) {
+ // If in the middle 1/3 and we are snapping to Most Likely, snap to it.
+ targetScroll = topOfMostLikelyScroll;
+ } else {
+ // Otherwise, snap to the Articles.
+ targetScroll = topOfSnippetsScroll;
+ snapState = NewTabPageUma.SnapState.BELOW_THE_FOLD;
+ }
+ recyclerView.smoothScrollBy(0, targetScroll - currentScroll);
+ return snapState;
+ }
+
+ private static View getFirstViewMatchingViewType(RecyclerView recyclerView,
+ int newTabPageListItemViewType) {
+ int adapterSize = recyclerView.getAdapter().getItemCount();
+ for (int i = 0; i < adapterSize; i++) {
+ if (recyclerView.getAdapter().getItemViewType(i) == newTabPageListItemViewType) {
+ return recyclerView.getLayoutManager().findViewByPosition(i);
+ }
+ }
+ return null;
+ }
+}
+
« no previous file with comments | « chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageView.java ('k') | chrome/android/java_sources.gni » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698