Chromium Code Reviews| 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..93025852288a0b11a8f583991a971ffb05b8c90a 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.snippets.SnippetsBridge; |
| +import org.chromium.chrome.browser.ntp.v2.NewTabPageAdapter; |
| +import org.chromium.chrome.browser.ntp.v2.NewTabPageRecyclerView; |
| 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 mUseNewUi; |
|
newt (away)
2016/03/31 04:16:22
I think "mUseSnippetsUi" would be clearer
(There
dgn
2016/03/31 13:38:31
Okay. There are going to be further experiments wi
newt (away)
2016/03/31 17:06:32
In that case, maybe mUseCardsUi or mUseRecyclerVie
dgn
2016/03/31 17:56:13
Done.
|
| + |
| + // 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; |
| @@ -234,22 +245,36 @@ 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 snippetsBridge SnippetsBridge that provides the snippets to display. The UI type used |
| + * will depend on whether {@code null}, |
|
newt (away)
2016/03/31 04:16:22
fix content and indentation
dgn
2016/03/31 13:38:31
Done.
|
| + * the old NTP UI (ScrollView-based) will be used. |
| */ |
| public void initialize(NewTabPageManager manager, boolean isSingleUrlBarMode, |
| - boolean searchProviderHasLogo, SnippetsManager snippetsManager) { |
| + boolean searchProviderHasLogo, SnippetsBridge snippetsBridge) { |
| mManager = manager; |
| - |
| - mScrollView = (NewTabScrollView) findViewById(R.id.ntp_scrollview); |
| - mScrollView.enableBottomShadow(SHADOW_COLOR); |
| - mContentView = (ViewGroup) findViewById(R.id.ntp_content); |
| + ViewStub stub = (ViewStub) findViewById(R.id.new_tab_page_layout_stub); |
| + |
| + mUseNewUi = snippetsBridge != null; |
| + if (mUseNewUi) { |
| + stub.setLayoutResource(R.layout.new_tab_page_recycler_view); |
| + mRecyclerView = (NewTabPageRecyclerView) stub.inflate(); |
| + mContentView = (NewTabPageLayout) LayoutInflater.from(getContext()) |
| + .inflate(R.layout.new_tab_page_layout, null); |
| + } 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 +306,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 +342,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 (!mUseNewUi) { |
| + // 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 +360,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 (mUseNewUi) { |
| + mNtpAdapter = new NewTabPageAdapter(snippetsBridge, 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 +373,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 +388,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 = mUseNewUi ? 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 +401,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 +423,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 +583,7 @@ float getUrlFocusChangeAnimationPercent() { |
| */ |
| private void setUrlFocusChangeAnimationPercentInternal(float percent) { |
| mContentView.setTranslationY(percent * (-mMostVisitedLayout.getTop() |
| - + mScrollView.getScrollY() + mContentView.getPaddingTop())); |
| + + getVerticalScroll() + mContentView.getPaddingTop())); |
| updateVisualsForToolbarTransition(percent); |
| } |
| @@ -586,7 +621,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 +680,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 +687,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 +700,7 @@ void captureThumbnail(Canvas canvas) { |
| ViewUtils.captureBitmap(this, canvas); |
| mSnapshotWidth = getWidth(); |
| mSnapshotHeight = getHeight(); |
| - mSnapshotScrollY = mScrollView.getScrollY(); |
| + mSnapshotScrollY = getVerticalScroll(); |
| mSnapshotMostVisitedChanged = false; |
| } |
| @@ -825,15 +846,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 +866,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 +956,21 @@ public void onIconUpdated(final String url) { |
| } |
| } |
| } |
| + |
| + private int getVerticalScroll() { |
| + if (mUseNewUi) { |
| + return mRecyclerView.computeVerticalScrollOffset(); |
| + } else { |
| + return mScrollView.getScrollY(); |
| + } |
| + } |
| + |
| + /** |
| + * Called when the associated {@link NewTabPage} is destroyed. |
| + */ |
| + public void onPageDestroyed() { |
| + if (mUseNewUi) { |
| + mNtpAdapter.stopObserving(); |
| + } |
| + } |
| } |