Index: chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java |
index bf63bf3b8b316c64e9a6a31c50b618ffea643fbc..2ef4889879f95248354d99dc97177b7201aa4420 100644 |
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageRecyclerView.java |
@@ -32,10 +32,11 @@ public class NewTabPageRecyclerView extends RecyclerView { |
private static final int MIN_BOTTOM_SPACING = 0; |
/** |
- * Position in the adapter of the item we snap the scroll at, when switching between above and |
- * below the fold. |
+ * Positions of key items in the RecyclerView. |
*/ |
- private static final int SNAP_ITEM_ADAPTER_POSITION = 1; |
+ private static final int ABOVE_THE_FOLD_ITEM_POSITION = 0; |
+ private static final int ARTICLES_HEADER_ITEM_POSITION = 1; |
+ private static final int PEEKING_CARD_ITEM_POSITION = 2; |
private final GestureDetector mGestureDetector; |
private final LinearLayoutManager mLayoutManager; |
@@ -159,13 +160,13 @@ public class NewTabPageRecyclerView extends RecyclerView { |
int firstVisiblePos = mLayoutManager.findFirstVisibleItemPosition(); |
// We have enough items to fill the view, since the snap point item is not even visible. |
- if (firstVisiblePos > SNAP_ITEM_ADAPTER_POSITION) return MIN_BOTTOM_SPACING; |
+ if (firstVisiblePos > ARTICLES_HEADER_ITEM_POSITION) return MIN_BOTTOM_SPACING; |
// The spacing item is the last item, the last content item is directly above that. |
int lastContentItemPosition = getAdapter().getItemCount() - 2; |
ViewHolder lastContentItem = findViewHolderForAdapterPosition(lastContentItemPosition); |
- ViewHolder snapItem = findViewHolderForAdapterPosition(SNAP_ITEM_ADAPTER_POSITION); |
+ ViewHolder snapItem = findViewHolderForAdapterPosition(ARTICLES_HEADER_ITEM_POSITION); |
int bottomSpacing = getHeight() - mToolbarHeight; |
if (lastContentItem == null || snapItem == null) { |
@@ -209,7 +210,7 @@ public class NewTabPageRecyclerView extends RecyclerView { |
* @return The viewholder for the first card or null if no card is available. |
*/ |
private CardViewHolder findFirstCard() { |
- int firstCardIndex = 2; // 0 => above-the-fold, 1 => header, 2 => card |
+ int firstCardIndex = PEEKING_CARD_ITEM_POSITION; |
ViewHolder viewHolder = findViewHolderForAdapterPosition(firstCardIndex); |
if (!(viewHolder instanceof CardViewHolder)) return null; |
@@ -239,7 +240,7 @@ public class NewTabPageRecyclerView extends RecyclerView { |
*/ |
private SnippetHeaderViewHolder findHeaderView() { |
// Get the snippet header view. It is always at position 1 |
- ViewHolder viewHolder = findViewHolderForAdapterPosition(1); |
+ ViewHolder viewHolder = findViewHolderForAdapterPosition(ARTICLES_HEADER_ITEM_POSITION); |
if (!(viewHolder instanceof SnippetHeaderViewHolder)) return null; |
return (SnippetHeaderViewHolder) viewHolder; |
@@ -256,4 +257,68 @@ public class NewTabPageRecyclerView extends RecyclerView { |
mCompensationHeight -= itemView.getHeight(); |
assert mCompensationHeight >= 0; |
} |
+ |
+ /** |
+ * If the RecyclerView is currently scrolled to between regionStart and regionEnd, smooth scroll |
+ * out of the region. flipPoint is the threshold used to decide which bound of the region to |
+ * scroll to. It returns whether the view was scrolled. |
+ */ |
+ private boolean scrollOutOfRegion(int regionStart, int flipPoint, int regionEnd) { |
+ // This function is only called when we are using the RecyclerView. |
+ final int currentScroll = computeVerticalScrollOffset(); |
+ |
+ if (currentScroll < regionStart || currentScroll > regionEnd) return false; |
+ |
+ if (currentScroll < flipPoint) { |
+ smoothScrollBy(0, regionStart - currentScroll); |
+ } else { |
+ smoothScrollBy(0, regionEnd - currentScroll); |
+ } |
+ return true; |
+ } |
+ |
+ /** |
+ * If the RecyclerView is currently scrolled to between regionStart and regionEnd, smooth scroll |
+ * out of the region to the nearest edge. |
+ */ |
+ private boolean scrollOutOfRegion(int regionStart, int regionEnd) { |
+ return scrollOutOfRegion(regionStart, (regionStart + regionEnd) / 2, regionEnd); |
+ } |
+ |
+ /** |
+ * Snaps the scroll point of the RecyclerView to prevent the user from scrolling to midway |
+ * through a transition and to allow peeking card behaviour. |
+ */ |
+ public void snapScroll(View fakeBox, int parentScrollY, int parentHeight) { |
+ // Snap scroll to prevent resting in the middle of the omnibox transition. |
+ final int searchBoxTransitionLength = getResources() |
+ .getDimensionPixelSize(R.dimen.ntp_search_box_transition_length); |
+ if (scrollOutOfRegion(fakeBox.getTop() - searchBoxTransitionLength, fakeBox.getTop())) { |
+ // The snap scrolling regions should never overlap. |
+ return; |
+ } |
+ |
+ // Snap scroll to prevent resting in the middle of the peeking card transition |
+ // and to allow the peeking card to peek a bit before snapping back. |
+ if (findFirstCard() != null && isFirstItemVisible()) { |
+ View peekingCard = findFirstCard().itemView; |
+ final int peekingHeight = getResources().getDimensionPixelSize( |
+ R.dimen.snippets_padding_and_peeking_card_height); |
+ |
+ // |A + B| gives the offset of the peeking card relative to the Recycler View, |
+ // so scrolling to this point would put the peeking card at the top of the |
+ // screen. |
+ // |A + B - C| will scroll us so that the peeking card is just off the bottom |
+ // of the screen. |
+ // Finally, we get |A + B - C + D| because the transition starts from the |
+ // peeking card's resting point, which is |D| from the bottom of the screen. |
+ int start = peekingCard.getTop() // A. |
+ + parentScrollY // B. |
+ - parentHeight // C. |
+ + peekingHeight; // D. |
+ scrollOutOfRegion(start, |
+ start + peekingCard.getHeight() / 2, |
+ start + peekingCard.getHeight() / 2); |
+ } |
+ } |
} |