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

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

Issue 2651673010: 🍝🏠 Refactor MostVisited UI management, extract it for reuse in Home. (Closed)
Patch Set: Rebase. Created 3 years, 10 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 unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.chrome.browser.ntp; 5 package org.chromium.chrome.browser.ntp;
6 6
7 import android.annotation.SuppressLint; 7 import android.annotation.SuppressLint;
8 import android.content.Context; 8 import android.content.Context;
9 import android.content.res.Configuration; 9 import android.content.res.Configuration;
10 import android.content.res.Resources;
11 import android.graphics.Bitmap;
12 import android.graphics.BitmapFactory;
13 import android.graphics.Canvas; 10 import android.graphics.Canvas;
14 import android.graphics.Color;
15 import android.graphics.Point; 11 import android.graphics.Point;
16 import android.graphics.Rect; 12 import android.graphics.Rect;
17 import android.graphics.drawable.BitmapDrawable;
18 import android.support.annotation.Nullable; 13 import android.support.annotation.Nullable;
19 import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
20 import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
21 import android.support.v7.widget.DefaultItemAnimator; 14 import android.support.v7.widget.DefaultItemAnimator;
22 import android.support.v7.widget.RecyclerView; 15 import android.support.v7.widget.RecyclerView;
23 import android.support.v7.widget.RecyclerView.AdapterDataObserver; 16 import android.support.v7.widget.RecyclerView.AdapterDataObserver;
24 import android.support.v7.widget.RecyclerView.ViewHolder; 17 import android.support.v7.widget.RecyclerView.ViewHolder;
25 import android.text.Editable; 18 import android.text.Editable;
26 import android.text.TextUtils;
27 import android.text.TextWatcher; 19 import android.text.TextWatcher;
28 import android.util.AttributeSet; 20 import android.util.AttributeSet;
29 import android.view.LayoutInflater; 21 import android.view.LayoutInflater;
30 import android.view.MotionEvent; 22 import android.view.MotionEvent;
31 import android.view.View; 23 import android.view.View;
32 import android.view.View.OnLayoutChangeListener; 24 import android.view.View.OnLayoutChangeListener;
33 import android.view.ViewGroup; 25 import android.view.ViewGroup;
34 import android.view.ViewStub; 26 import android.view.ViewStub;
35 import android.widget.FrameLayout; 27 import android.widget.FrameLayout;
36 import android.widget.ImageView; 28 import android.widget.ImageView;
37 import android.widget.TextView; 29 import android.widget.TextView;
38 30
39 import org.chromium.base.ApiCompatibilityUtils;
40 import org.chromium.base.Callback;
41 import org.chromium.base.Log;
42 import org.chromium.base.TraceEvent; 31 import org.chromium.base.TraceEvent;
43 import org.chromium.base.VisibleForTesting; 32 import org.chromium.base.VisibleForTesting;
44 import org.chromium.chrome.R; 33 import org.chromium.chrome.R;
45 import org.chromium.chrome.browser.ChromeActivity; 34 import org.chromium.chrome.browser.ChromeActivity;
46 import org.chromium.chrome.browser.favicon.LargeIconBridge.LargeIconCallback;
47 import org.chromium.chrome.browser.ntp.LogoBridge.Logo; 35 import org.chromium.chrome.browser.ntp.LogoBridge.Logo;
48 import org.chromium.chrome.browser.ntp.LogoBridge.LogoObserver; 36 import org.chromium.chrome.browser.ntp.LogoBridge.LogoObserver;
49 import org.chromium.chrome.browser.ntp.MostVisitedItem.MostVisitedItemManager;
50 import org.chromium.chrome.browser.ntp.NewTabPage.DestructionObserver; 37 import org.chromium.chrome.browser.ntp.NewTabPage.DestructionObserver;
51 import org.chromium.chrome.browser.ntp.NewTabPage.OnSearchBoxScrollListener; 38 import org.chromium.chrome.browser.ntp.NewTabPage.OnSearchBoxScrollListener;
52 import org.chromium.chrome.browser.ntp.cards.CardsVariationParameters; 39 import org.chromium.chrome.browser.ntp.cards.CardsVariationParameters;
53 import org.chromium.chrome.browser.ntp.cards.NewTabPageAdapter; 40 import org.chromium.chrome.browser.ntp.cards.NewTabPageAdapter;
54 import org.chromium.chrome.browser.ntp.cards.NewTabPageRecyclerView; 41 import org.chromium.chrome.browser.ntp.cards.NewTabPageRecyclerView;
55 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; 42 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
56 import org.chromium.chrome.browser.profiles.MostVisitedSites.MostVisitedURLsObse rver;
57 import org.chromium.chrome.browser.profiles.Profile; 43 import org.chromium.chrome.browser.profiles.Profile;
58 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate; 44 import org.chromium.chrome.browser.suggestions.SuggestionsUiDelegate;
45 import org.chromium.chrome.browser.suggestions.Tile;
46 import org.chromium.chrome.browser.suggestions.TileGroup;
59 import org.chromium.chrome.browser.tab.Tab; 47 import org.chromium.chrome.browser.tab.Tab;
60 import org.chromium.chrome.browser.util.MathUtils; 48 import org.chromium.chrome.browser.util.MathUtils;
61 import org.chromium.chrome.browser.util.ViewUtils; 49 import org.chromium.chrome.browser.util.ViewUtils;
62 import org.chromium.chrome.browser.widget.RoundedIconGenerator;
63 import org.chromium.chrome.browser.widget.displaystyle.UiConfig; 50 import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
64 import org.chromium.ui.base.DeviceFormFactor; 51 import org.chromium.ui.base.DeviceFormFactor;
65 52
66 import java.util.Arrays;
67 import java.util.HashSet;
68 import java.util.Set;
69
70 import jp.tomorrowkey.android.gifplayer.BaseGifImage; 53 import jp.tomorrowkey.android.gifplayer.BaseGifImage;
71 54
72 /** 55 /**
73 * The native new tab page, represented by some basic data such as title and url , and an Android 56 * The native new tab page, represented by some basic data such as title and url , and an Android
74 * View that displays the page. 57 * View that displays the page.
75 */ 58 */
76 public class NewTabPageView extends FrameLayout 59 public class NewTabPageView
77 implements MostVisitedURLsObserver, OnLayoutChangeListener { 60 extends FrameLayout implements OnLayoutChangeListener, TileGroup.Observe r {
61 private static final String TAG = "NewTabPageView";
78 62
79 private static final long SNAP_SCROLL_DELAY_MS = 30; 63 private static final long SNAP_SCROLL_DELAY_MS = 30;
80 private static final String TAG = "NewTabPageView"; 64
65 private static final int MAX_TILES = 12;
66 private static final int MAX_TILE_ROWS_WITH_LOGO = 2;
67 private static final int MAX_TILE_ROWS_WITHOUT_LOGO = 3;
81 68
82 private NewTabPageRecyclerView mRecyclerView; 69 private NewTabPageRecyclerView mRecyclerView;
83 70
84 private NewTabPageLayout mNewTabPageLayout; 71 private NewTabPageLayout mNewTabPageLayout;
85 private LogoView mSearchProviderLogoView; 72 private LogoView mSearchProviderLogoView;
86 private View mSearchBoxView; 73 private View mSearchBoxView;
87 private ImageView mVoiceSearchButton; 74 private ImageView mVoiceSearchButton;
88 private MostVisitedLayout mMostVisitedLayout; 75 private MostVisitedLayout mMostVisitedLayout;
89 private View mMostVisitedPlaceholder; 76 private View mMostVisitedPlaceholder;
90 private View mNoSearchLogoSpacer; 77 private View mNoSearchLogoSpacer;
91 78
92 private OnSearchBoxScrollListener mSearchBoxScrollListener; 79 private OnSearchBoxScrollListener mSearchBoxScrollListener;
93 80
94 private ChromeActivity mActivity; 81 private ChromeActivity mActivity;
95 private NewTabPageManager mManager; 82 private NewTabPageManager mManager;
83 private TileGroup.Delegate mTileGroupDelegate;
84 private TileGroup mTileGroup;
96 private UiConfig mUiConfig; 85 private UiConfig mUiConfig;
97 private MostVisitedDesign mMostVisitedDesign;
98 private MostVisitedItem[] mMostVisitedItems;
99 private boolean mFirstShow = true; 86 private boolean mFirstShow = true;
100 private boolean mSearchProviderHasLogo = true; 87 private boolean mSearchProviderHasLogo = true;
101 private boolean mHasReceivedMostVisitedSites;
102 private boolean mPendingSnapScroll; 88 private boolean mPendingSnapScroll;
103 89
104 /** 90 /**
105 * The number of asynchronous tasks that need to complete before the page is done loading. 91 * The number of asynchronous tasks that need to complete before the page is done loading.
106 * This starts at one to track when the view is finished attaching to the wi ndow. 92 * This starts at one to track when the view is finished attaching to the wi ndow.
107 */ 93 */
108 private int mPendingLoadTasks = 1; 94 private int mPendingLoadTasks = 1;
109 private boolean mLoadHasCompleted; 95 private boolean mLoadHasCompleted;
110 96
111 private float mUrlFocusChangePercent; 97 private float mUrlFocusChangePercent;
112 private boolean mDisableUrlFocusChangeAnimations; 98 private boolean mDisableUrlFocusChangeAnimations;
113 99
114 /** Flag used to request some layout changes after the next layout pass is c ompleted. */ 100 /** Flag used to request some layout changes after the next layout pass is c ompleted. */
115 private boolean mTileCountChanged; 101 private boolean mTileCountChanged;
116 private boolean mSnapshotMostVisitedChanged; 102 private boolean mSnapshotMostVisitedChanged;
117 private boolean mNewTabPageRecyclerViewChanged; 103 private boolean mNewTabPageRecyclerViewChanged;
118 private int mSnapshotWidth; 104 private int mSnapshotWidth;
119 private int mSnapshotHeight; 105 private int mSnapshotHeight;
120 private int mSnapshotScrollY; 106 private int mSnapshotScrollY;
121 private ContextMenuManager mContextMenuManager; 107 private ContextMenuManager mContextMenuManager;
122 108
123 /** 109 /**
124 * Manages the view interaction with the rest of the system. 110 * Manages the view interaction with the rest of the system.
125 */ 111 */
126 public interface NewTabPageManager extends MostVisitedItemManager, Suggestio nsUiDelegate { 112 public interface NewTabPageManager extends SuggestionsUiDelegate {
127 /** @return Whether the location bar is shown in the NTP. */ 113 /** @return Whether the location bar is shown in the NTP. */
128 boolean isLocationBarShownInNTP(); 114 boolean isLocationBarShownInNTP();
129 115
130 /** @return Whether voice search is enabled and the microphone should be shown. */ 116 /** @return Whether voice search is enabled and the microphone should be shown. */
131 boolean isVoiceSearchEnabled(); 117 boolean isVoiceSearchEnabled();
132 118
133 /** @return Whether the omnibox 'Search or type URL' text should be show n. */ 119 /** @return Whether the omnibox 'Search or type URL' text should be show n. */
134 boolean isFakeOmniboxTextEnabledTablet(); 120 boolean isFakeOmniboxTextEnabledTablet();
135 121
136 /** 122 /**
137 * Animates the search box up into the omnibox and bring up the keyboard . 123 * Animates the search box up into the omnibox and bring up the keyboard .
138 * @param beginVoiceSearch Whether to begin a voice search. 124 * @param beginVoiceSearch Whether to begin a voice search.
139 * @param pastedText Text to paste in the omnibox after it's been focuse d. May be null. 125 * @param pastedText Text to paste in the omnibox after it's been focuse d. May be null.
140 */ 126 */
141 void focusSearchBox(boolean beginVoiceSearch, String pastedText); 127 void focusSearchBox(boolean beginVoiceSearch, String pastedText);
142 128
143 /** 129 /**
144 * Gets the list of most visited sites.
145 * @param observer The observer to be notified with the list of sites.
146 * @param numResults The maximum number of sites to retrieve.
147 */
148 void setMostVisitedURLsObserver(MostVisitedURLsObserver observer, int nu mResults);
149
150 /**
151 * Called when the user clicks on the logo. 130 * Called when the user clicks on the logo.
152 * @param isAnimatedLogoShowing Whether the animated GIF logo is playing . 131 * @param isAnimatedLogoShowing Whether the animated GIF logo is playing .
153 */ 132 */
154 void onLogoClicked(boolean isAnimatedLogoShowing); 133 void onLogoClicked(boolean isAnimatedLogoShowing);
155 134
156 /** 135 /**
157 * Gets the default search provider's logo and calls logoObserver with t he result. 136 * Gets the default search provider's logo and calls logoObserver with t he result.
158 * @param logoObserver The callback to notify when the logo is available . 137 * @param logoObserver The callback to notify when the logo is available .
159 */ 138 */
160 void getSearchProviderLogo(LogoObserver logoObserver); 139 void getSearchProviderLogo(LogoObserver logoObserver);
161 140
162 /** 141 /**
163 * Called when the NTP has completely finished loading (all views will b e inflated
164 * and any dependent resources will have been loaded).
165 * @param mostVisitedItems The MostVisitedItem shown on the NTP. Used to record metrics.
166 */
167 void onLoadingComplete(MostVisitedItem[] mostVisitedItems);
168
169 /**
170 * @return whether the {@link NewTabPage} associated with this manager i s the current page 142 * @return whether the {@link NewTabPage} associated with this manager i s the current page
171 * displayed to the user. 143 * displayed to the user.
172 */ 144 */
173 boolean isCurrentPage(); 145 boolean isCurrentPage();
174 146
175 /** 147 /**
176 * @return The context menu manager. Will be {@code null} if the {@link NewTabPageView} is 148 * @return The context menu manager. Will be {@code null} if the {@link NewTabPageView} is
177 * not done initialising. 149 * not done initialising.
178 */ 150 */
179 @Nullable 151 @Nullable
(...skipping 10 matching lines...) Expand all
190 /** 162 /**
191 * Initializes the NTP. This must be called immediately after inflation, bef ore this object is 163 * Initializes the NTP. This must be called immediately after inflation, bef ore this object is
192 * used in any other way. 164 * used in any other way.
193 * 165 *
194 * @param manager NewTabPageManager used to perform various actions when the user interacts 166 * @param manager NewTabPageManager used to perform various actions when the user interacts
195 * with the page. 167 * with the page.
196 * @param tab The Tab that is showing this new tab page. 168 * @param tab The Tab that is showing this new tab page.
197 * @param searchProviderHasLogo Whether the search provider has a logo. 169 * @param searchProviderHasLogo Whether the search provider has a logo.
198 * @param scrollPosition The adapter scroll position to initialize to. 170 * @param scrollPosition The adapter scroll position to initialize to.
199 */ 171 */
200 public void initialize( 172 public void initialize(NewTabPageManager manager, Tab tab, TileGroup.Delegat e tileGroupDelegate,
201 NewTabPageManager manager, Tab tab, boolean searchProviderHasLogo, i nt scrollPosition) { 173 boolean searchProviderHasLogo, int scrollPosition) {
202 TraceEvent.begin(TAG + ".initialize()"); 174 TraceEvent.begin(TAG + ".initialize()");
203 mActivity = tab.getActivity(); 175 mActivity = tab.getActivity();
204 mManager = manager; 176 mManager = manager;
177 mTileGroupDelegate = tileGroupDelegate;
205 mUiConfig = new UiConfig(this); 178 mUiConfig = new UiConfig(this);
206 179
207 assert manager.getSuggestionsSource() != null; 180 assert manager.getSuggestionsSource() != null;
208 181
209 mRecyclerView = (NewTabPageRecyclerView) findViewById(R.id.new_tab_page_ recycler_view); 182 mRecyclerView = (NewTabPageRecyclerView) findViewById(R.id.new_tab_page_ recycler_view);
210 // Don't attach now, the recyclerView itself will determine when to do i t. 183 // Don't attach now, the recyclerView itself will determine when to do i t.
211 mNewTabPageLayout = 184 mNewTabPageLayout =
212 (NewTabPageLayout) LayoutInflater.from(getContext()) 185 (NewTabPageLayout) LayoutInflater.from(getContext())
213 .inflate(R.layout.new_tab_page_layout, mRecyclerView, fa lse); 186 .inflate(R.layout.new_tab_page_layout, mRecyclerView, fa lse);
214 mRecyclerView.setAboveTheFoldView(mNewTabPageLayout); 187 mRecyclerView.setAboveTheFoldView(mNewTabPageLayout);
(...skipping 17 matching lines...) Expand all
232 mContextMenuManager = 205 mContextMenuManager =
233 new ContextMenuManager(mActivity, mManager.getNavigationDelegate (), mRecyclerView); 206 new ContextMenuManager(mActivity, mManager.getNavigationDelegate (), mRecyclerView);
234 mActivity.getWindowAndroid().addContextMenuCloseListener(mContextMenuMan ager); 207 mActivity.getWindowAndroid().addContextMenuCloseListener(mContextMenuMan ager);
235 manager.addDestructionObserver(new DestructionObserver() { 208 manager.addDestructionObserver(new DestructionObserver() {
236 @Override 209 @Override
237 public void onDestroy() { 210 public void onDestroy() {
238 mActivity.getWindowAndroid().removeContextMenuCloseListener(mCon textMenuManager); 211 mActivity.getWindowAndroid().removeContextMenuCloseListener(mCon textMenuManager);
239 } 212 }
240 }); 213 });
241 214
242 mMostVisitedDesign = new MostVisitedDesign(getContext());
243 mMostVisitedLayout = 215 mMostVisitedLayout =
244 (MostVisitedLayout) mNewTabPageLayout.findViewById(R.id.most_vis ited_layout); 216 (MostVisitedLayout) mNewTabPageLayout.findViewById(R.id.most_vis ited_layout);
245 mMostVisitedDesign.initMostVisitedLayout(searchProviderHasLogo); 217 mMostVisitedLayout.setMaxRows(
218 searchProviderHasLogo ? MAX_TILE_ROWS_WITH_LOGO : MAX_TILE_ROWS_ WITHOUT_LOGO);
219 mTileGroup = new TileGroup(
220 mManager, mContextMenuManager, mTileGroupDelegate, /* observer = */ this);
246 221
247 mSearchProviderLogoView = 222 mSearchProviderLogoView =
248 (LogoView) mNewTabPageLayout.findViewById(R.id.search_provider_l ogo); 223 (LogoView) mNewTabPageLayout.findViewById(R.id.search_provider_l ogo);
249 mSearchBoxView = mNewTabPageLayout.findViewById(R.id.search_box); 224 mSearchBoxView = mNewTabPageLayout.findViewById(R.id.search_box);
250 mNoSearchLogoSpacer = mNewTabPageLayout.findViewById(R.id.no_search_logo _spacer); 225 mNoSearchLogoSpacer = mNewTabPageLayout.findViewById(R.id.no_search_logo _spacer);
251 226
252 initializeSearchBoxTextView(); 227 initializeSearchBoxTextView();
253 initializeVoiceSearchButton(); 228 initializeVoiceSearchButton();
254 229
255 mNewTabPageLayout.addOnLayoutChangeListener(this); 230 mNewTabPageLayout.addOnLayoutChangeListener(this);
256 setSearchProviderHasLogo(searchProviderHasLogo); 231 setSearchProviderHasLogo(searchProviderHasLogo);
257 232
258 mPendingLoadTasks++; 233 mPendingLoadTasks++;
259 mManager.setMostVisitedURLsObserver( 234 mTileGroup.startObserving(MAX_TILES);
260 this, mMostVisitedDesign.getNumberOfTiles(searchProviderHasLogo) );
261 235
262 // Set up snippets 236 // Set up snippets
263 NewTabPageAdapter newTabPageAdapter = new NewTabPageAdapter(mManager, mN ewTabPageLayout, 237 NewTabPageAdapter newTabPageAdapter = new NewTabPageAdapter(mManager, mN ewTabPageLayout,
264 mUiConfig, OfflinePageBridge.getForProfile(Profile.getLastUsedPr ofile()), 238 mUiConfig, OfflinePageBridge.getForProfile(Profile.getLastUsedPr ofile()),
265 mContextMenuManager); 239 mContextMenuManager, /* tileGroupDelegate = */ null);
266 mRecyclerView.setAdapter(newTabPageAdapter); 240 mRecyclerView.setAdapter(newTabPageAdapter);
267 241
268 int scrollOffset; 242 int scrollOffset;
269 if (CardsVariationParameters.isScrollBelowTheFoldEnabled()) { 243 if (CardsVariationParameters.isScrollBelowTheFoldEnabled()) {
270 scrollPosition = newTabPageAdapter.getFirstHeaderPosition(); 244 scrollPosition = newTabPageAdapter.getFirstHeaderPosition();
271 scrollOffset = getResources().getDimensionPixelSize(R.dimen.ntp_sear ch_box_height); 245 scrollOffset = getResources().getDimensionPixelSize(R.dimen.ntp_sear ch_box_height);
272 } else { 246 } else {
273 scrollOffset = 0; 247 scrollOffset = 0;
274 } 248 }
275 mRecyclerView.getLinearLayoutManager().scrollToPositionWithOffset( 249 mRecyclerView.getLinearLayoutManager().scrollToPositionWithOffset(
(...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after
474 * is complete. 448 * is complete.
475 */ 449 */
476 private void loadTaskCompleted() { 450 private void loadTaskCompleted() {
477 assert mPendingLoadTasks > 0; 451 assert mPendingLoadTasks > 0;
478 mPendingLoadTasks--; 452 mPendingLoadTasks--;
479 if (mPendingLoadTasks == 0) { 453 if (mPendingLoadTasks == 0) {
480 if (mLoadHasCompleted) { 454 if (mLoadHasCompleted) {
481 assert false; 455 assert false;
482 } else { 456 } else {
483 mLoadHasCompleted = true; 457 mLoadHasCompleted = true;
484 mManager.onLoadingComplete(mMostVisitedItems); 458 mTileGroupDelegate.onLoadingComplete(mTileGroup.getTiles());
485 // Load the logo after everything else is finished, since it's l ower priority. 459 // Load the logo after everything else is finished, since it's l ower priority.
486 loadSearchProviderLogo(); 460 loadSearchProviderLogo();
487 } 461 }
488 } 462 }
489 } 463 }
490 464
491 /** 465 /**
492 * Loads the search provider logo (e.g. Google doodle), if any. 466 * Loads the search provider logo (e.g. Google doodle), if any.
493 */ 467 */
494 private void loadSearchProviderLogo() { 468 private void loadSearchProviderLogo() {
(...skipping 10 matching lines...) Expand all
505 479
506 /** 480 /**
507 * Changes the layout depending on whether the selected search provider (e.g . Google, Bing) 481 * Changes the layout depending on whether the selected search provider (e.g . Google, Bing)
508 * has a logo. 482 * has a logo.
509 * @param hasLogo Whether the search provider has a logo. 483 * @param hasLogo Whether the search provider has a logo.
510 */ 484 */
511 public void setSearchProviderHasLogo(boolean hasLogo) { 485 public void setSearchProviderHasLogo(boolean hasLogo) {
512 if (hasLogo == mSearchProviderHasLogo) return; 486 if (hasLogo == mSearchProviderHasLogo) return;
513 mSearchProviderHasLogo = hasLogo; 487 mSearchProviderHasLogo = hasLogo;
514 488
515 mMostVisitedDesign.setSearchProviderHasLogo(mMostVisitedLayout, hasLogo) ; 489 // Set a bit more top padding if there is no logo.
490 int paddingTop = getResources().getDimensionPixelSize(hasLogo
491 ? R.dimen.most_visited_layout_padding_top
492 : R.dimen.most_visited_layout_no_logo_padding_top);
493 mMostVisitedLayout.setPadding(0, paddingTop, 0, mMostVisitedLayout.getPa ddingBottom());
516 494
517 // Hide or show all the views above the Most Visited items. 495 // Hide or show all the views above the Most Visited items.
518 int visibility = hasLogo ? View.VISIBLE : View.GONE; 496 int visibility = hasLogo ? View.VISIBLE : View.GONE;
519 int childCount = mNewTabPageLayout.getChildCount(); 497 int childCount = mNewTabPageLayout.getChildCount();
520 for (int i = 0; i < childCount; i++) { 498 for (int i = 0; i < childCount; i++) {
521 View child = mNewTabPageLayout.getChildAt(i); 499 View child = mNewTabPageLayout.getChildAt(i);
522 if (child == mMostVisitedLayout) break; 500 if (child == mMostVisitedLayout) break;
523 // Don't change the visibility of a ViewStub as that will automagica lly inflate it. 501 // Don't change the visibility of a ViewStub as that will automagica lly inflate it.
524 if (child instanceof ViewStub) continue; 502 if (child instanceof ViewStub) continue;
525 child.setVisibility(visibility); 503 child.setVisibility(visibility);
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after
744 onUrlFocusAnimationChanged(); 722 onUrlFocusAnimationChanged();
745 updateSearchBoxOnScroll(); 723 updateSearchBoxOnScroll();
746 724
747 mRecyclerView.updatePeekingCardAndHeader(); 725 mRecyclerView.updatePeekingCardAndHeader();
748 // The positioning of elements may have been changed (since the elements expand to fill 726 // The positioning of elements may have been changed (since the elements expand to fill
749 // the available vertical space), so adjust the scroll. 727 // the available vertical space), so adjust the scroll.
750 mRecyclerView.snapScroll(mSearchBoxView, 728 mRecyclerView.snapScroll(mSearchBoxView,
751 mRecyclerView.computeVerticalScrollOffset(), getHeight()); 729 mRecyclerView.computeVerticalScrollOffset(), getHeight());
752 } 730 }
753 731
754 // MostVisitedURLsObserver implementation
755
756 @Override
757 public void onMostVisitedURLsAvailable(final String[] titles, final String[] urls,
758 final String[] whitelistIconPaths, final int[] sources) {
759 Set<String> urlSet = new HashSet<>(Arrays.asList(urls));
760
761 // If no Most Visited items have been built yet, this is the initial loa d. Build the Most
762 // Visited items immediately so the layout is stable during initial rend ering. They can be
763 // replaced later if there are offline urls, but that will not affect th e layout widths and
764 // heights. A stable layout enables reliable scroll position initializat ion.
765 if (!mHasReceivedMostVisitedSites) {
766 buildMostVisitedItems(titles, urls, whitelistIconPaths, null, source s);
767 }
768
769 // TODO(https://crbug.com/607573): We should show offline-available cont ent in a nonblocking
770 // way so that responsiveness of the NTP does not depend on ready availa bility of offline
771 // pages.
772 mManager.getUrlsAvailableOffline(urlSet, new Callback<Set<String>>() {
773 @Override
774 public void onResult(Set<String> offlineUrls) {
775 buildMostVisitedItems(titles, urls, whitelistIconPaths, offlineU rls, sources);
776 }
777 });
778 }
779
780 private void buildMostVisitedItems(final String[] titles, final String[] url s,
781 final String[] whitelistIconPaths, @Nullable final Set<String> offli neUrls,
782 final int[] sources) {
783 mMostVisitedLayout.removeAllViews();
784
785 MostVisitedItem[] oldItems = mMostVisitedItems;
786 int oldItemCount = oldItems == null ? 0 : oldItems.length;
787 mMostVisitedItems = new MostVisitedItem[titles.length];
788
789 final boolean isInitialLoad = !mHasReceivedMostVisitedSites;
790 LayoutInflater inflater = LayoutInflater.from(getContext());
791
792 // Add the most visited items to the page.
793 for (int i = 0; i < titles.length; i++) {
794 final String url = urls[i];
795 final String title = titles[i];
796 final String whitelistIconPath = whitelistIconPaths[i];
797 final int source = sources[i];
798
799 boolean offlineAvailable = offlineUrls != null && offlineUrls.contai ns(url);
800
801 // Look for an existing item to reuse.
802 MostVisitedItem item = null;
803 for (int j = 0; j < oldItemCount; j++) {
804 MostVisitedItem oldItem = oldItems[j];
805 if (oldItem != null && TextUtils.equals(url, oldItem.getUrl())
806 && TextUtils.equals(title, oldItem.getTitle())
807 && offlineAvailable == oldItem.isOfflineAvailable()
808 && whitelistIconPath.equals(oldItem.getWhitelistIconPath ())) {
809 item = oldItem;
810 item.setIndex(i);
811 oldItems[j] = null;
812 break;
813 }
814 }
815
816 // If nothing can be reused, create a new item.
817 if (item == null) {
818 item = new MostVisitedItem(mManager, title, url, whitelistIconPa th,
819 offlineAvailable, i, source);
820 View view =
821 mMostVisitedDesign.createMostVisitedItemView(inflater, i tem, isInitialLoad);
822 item.initView(view);
823 }
824
825 mMostVisitedItems[i] = item;
826 mMostVisitedLayout.addView(item.getView());
827 }
828
829 mHasReceivedMostVisitedSites = true;
830 updateMostVisitedPlaceholderVisibility();
831
832 if (mUrlFocusChangePercent == 1f && oldItemCount != mMostVisitedItems.le ngth) {
833 // If the number of NTP Tile rows change while the URL bar is focuse d, the icons'
834 // position will be wrong. Schedule the translation to be updated.
835 mTileCountChanged = true;
836 }
837
838 if (isInitialLoad) {
839 loadTaskCompleted();
840 // The page contents are initially hidden; otherwise they'll be draw n centered on the
841 // page before the most visited sites are available and then jump up wards to make space
842 // once the most visited sites are available.
843 mNewTabPageLayout.setVisibility(View.VISIBLE);
844 }
845 mSnapshotMostVisitedChanged = true;
846 }
847
848 @Override
849 public void onIconMadeAvailable(String siteUrl) {
850 mMostVisitedDesign.onIconUpdated(siteUrl);
851 }
852
853 /** 732 /**
854 * Shows the most visited placeholder ("Nothing to see here") if there are n o most visited 733 * Shows the most visited placeholder ("Nothing to see here") if there are n o most visited
855 * items and there is no search provider logo. 734 * items and there is no search provider logo.
856 */ 735 */
857 private void updateMostVisitedPlaceholderVisibility() { 736 private void updateMostVisitedPlaceholderVisibility() {
858 boolean showPlaceholder = mHasReceivedMostVisitedSites 737 boolean showPlaceholder = mTileGroup.hasReceivedData()
859 && mMostVisitedLayout.getChildCount() == 0 738 && mMostVisitedLayout.getChildCount() == 0 && !mSearchProviderHa sLogo;
860 && !mSearchProviderHasLogo;
861 739
862 mNoSearchLogoSpacer.setVisibility( 740 mNoSearchLogoSpacer.setVisibility(
863 (mSearchProviderHasLogo || showPlaceholder) ? View.GONE : View.I NVISIBLE); 741 (mSearchProviderHasLogo || showPlaceholder) ? View.GONE : View.I NVISIBLE);
864 742
865 if (showPlaceholder) { 743 if (showPlaceholder) {
866 if (mMostVisitedPlaceholder == null) { 744 if (mMostVisitedPlaceholder == null) {
867 ViewStub mostVisitedPlaceholderStub = (ViewStub) mNewTabPageLayo ut 745 ViewStub mostVisitedPlaceholderStub = (ViewStub) mNewTabPageLayo ut
868 .findViewById(R.id.most_visited_placeholder_stub); 746 .findViewById(R.id.most_visited_placeholder_stub);
869 747
870 mMostVisitedPlaceholder = mostVisitedPlaceholderStub.inflate(); 748 mMostVisitedPlaceholder = mostVisitedPlaceholderStub.inflate();
871 } 749 }
872 mMostVisitedLayout.setVisibility(GONE); 750 mMostVisitedLayout.setVisibility(GONE);
873 mMostVisitedPlaceholder.setVisibility(VISIBLE); 751 mMostVisitedPlaceholder.setVisibility(VISIBLE);
874 } else if (mMostVisitedPlaceholder != null) { 752 } else if (mMostVisitedPlaceholder != null) {
875 mMostVisitedLayout.setVisibility(VISIBLE); 753 mMostVisitedLayout.setVisibility(VISIBLE);
876 mMostVisitedPlaceholder.setVisibility(GONE); 754 mMostVisitedPlaceholder.setVisibility(GONE);
877 } 755 }
878 } 756 }
879 757
880 /**
881 * The design for most visited tiles: each tile shows a large icon and the s ite's title.
882 */
883 private class MostVisitedDesign {
884
885 private static final int NUM_TILES = 8;
886 private static final int NUM_TILES_NO_LOGO = 12;
887 private static final int MAX_ROWS = 2;
888 private static final int MAX_ROWS_NO_LOGO = 3;
889
890 private static final int ICON_CORNER_RADIUS_DP = 4;
891 private static final int ICON_TEXT_SIZE_DP = 20;
892 private static final int ICON_MIN_SIZE_PX = 48;
893
894 private int mMinIconSize;
895 private int mDesiredIconSize;
896 private RoundedIconGenerator mIconGenerator;
897
898 MostVisitedDesign(Context context) {
899 Resources res = context.getResources();
900 mDesiredIconSize = res.getDimensionPixelSize(R.dimen.most_visited_ic on_size);
901 // On ldpi devices, mDesiredIconSize could be even smaller than ICON _MIN_SIZE_PX.
902 mMinIconSize = Math.min(mDesiredIconSize, ICON_MIN_SIZE_PX);
903 int desiredIconSizeDp = Math.round(
904 mDesiredIconSize / res.getDisplayMetrics().density);
905 int iconColor = ApiCompatibilityUtils.getColor(
906 getResources(), R.color.default_favicon_background_color);
907 mIconGenerator = new RoundedIconGenerator(context, desiredIconSizeDp , desiredIconSizeDp,
908 ICON_CORNER_RADIUS_DP, iconColor, ICON_TEXT_SIZE_DP);
909 }
910
911 public int getNumberOfTiles(boolean searchProviderHasLogo) {
912 return searchProviderHasLogo ? NUM_TILES : NUM_TILES_NO_LOGO;
913 }
914
915 public void initMostVisitedLayout(boolean searchProviderHasLogo) {
916 mMostVisitedLayout.setMaxRows(searchProviderHasLogo ? MAX_ROWS : MAX _ROWS_NO_LOGO);
917 }
918
919 public void setSearchProviderHasLogo(View mostVisitedLayout, boolean has Logo) {
920 int paddingTop = getResources().getDimensionPixelSize(hasLogo
921 ? R.dimen.most_visited_layout_padding_top
922 : R.dimen.most_visited_layout_no_logo_padding_top);
923 mostVisitedLayout.setPadding(0, paddingTop, 0, mMostVisitedLayout.ge tPaddingBottom());
924 }
925
926 class LargeIconCallbackImpl implements LargeIconCallback {
927 private MostVisitedItem mItem;
928 private MostVisitedItemView mItemView;
929 private boolean mIsInitialLoad;
930
931 public LargeIconCallbackImpl(
932 MostVisitedItem item, MostVisitedItemView itemView, boolean isInitialLoad) {
933 mItem = item;
934 mItemView = itemView;
935 mIsInitialLoad = isInitialLoad;
936 }
937
938 @Override
939 public void onLargeIconAvailable(
940 Bitmap icon, int fallbackColor, boolean isFallbackColorDefau lt) {
941 if (icon == null) {
942 mIconGenerator.setBackgroundColor(fallbackColor);
943 icon = mIconGenerator.generateIconForUrl(mItem.getUrl());
944 mItemView.setIcon(new BitmapDrawable(getResources(), icon));
945 mItem.setTileType(isFallbackColorDefault ? MostVisitedTileTy pe.ICON_DEFAULT
946 : MostVisitedTileTy pe.ICON_COLOR);
947 } else {
948 RoundedBitmapDrawable roundedIcon = RoundedBitmapDrawableFac tory.create(
949 getResources(), icon);
950 int cornerRadius = Math.round(ICON_CORNER_RADIUS_DP
951 * getResources().getDisplayMetrics().density * icon. getWidth()
952 / mDesiredIconSize);
953 roundedIcon.setCornerRadius(cornerRadius);
954 roundedIcon.setAntiAlias(true);
955 roundedIcon.setFilterBitmap(true);
956 mItemView.setIcon(roundedIcon);
957 mItem.setTileType(MostVisitedTileType.ICON_REAL);
958 }
959 mSnapshotMostVisitedChanged = true;
960 if (mIsInitialLoad) loadTaskCompleted();
961 }
962 }
963
964 public View createMostVisitedItemView(
965 LayoutInflater inflater, MostVisitedItem item, boolean isInitial Load) {
966 final MostVisitedItemView view = (MostVisitedItemView) inflater.infl ate(
967 R.layout.most_visited_item, mMostVisitedLayout, false);
968 view.setTitle(TitleUtil.getTitleForDisplay(item.getTitle(), item.get Url()));
969 view.setOfflineAvailable(item.isOfflineAvailable());
970
971 LargeIconCallback iconCallback = new LargeIconCallbackImpl(item, vie w, isInitialLoad);
972 if (isInitialLoad) mPendingLoadTasks++;
973 if (!loadWhitelistIcon(item, iconCallback)) {
974 mManager.getLargeIconForUrl(item.getUrl(), mMinIconSize, iconCal lback);
975 }
976
977 return view;
978 }
979
980 private boolean loadWhitelistIcon(MostVisitedItem item, LargeIconCallbac k iconCallback) {
981 if (item.getWhitelistIconPath().isEmpty()) return false;
982
983 Bitmap bitmap = BitmapFactory.decodeFile(item.getWhitelistIconPath() );
984 if (bitmap == null) {
985 Log.d(TAG, "Image decoding failed: %s", item.getWhitelistIconPat h());
986 return false;
987 }
988 iconCallback.onLargeIconAvailable(bitmap, Color.BLACK, false);
989 return true;
990 }
991
992 public void onIconUpdated(final String url) {
993 if (mMostVisitedItems == null) return;
994 // Find a matching most visited item.
995 for (MostVisitedItem item : mMostVisitedItems) {
996 if (item.getUrl().equals(url)) {
997 LargeIconCallback iconCallback = new LargeIconCallbackImpl(
998 item, (MostVisitedItemView) item.getView(), false);
999 mManager.getLargeIconForUrl(url, mMinIconSize, iconCallback) ;
1000 break;
1001 }
1002 }
1003 }
1004 }
1005
1006 @Override 758 @Override
1007 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 759 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1008 if (mNewTabPageLayout != null) { 760 if (mNewTabPageLayout != null) {
1009 mNewTabPageLayout.setParentViewportHeight(MeasureSpec.getSize(height MeasureSpec)); 761 mNewTabPageLayout.setParentViewportHeight(MeasureSpec.getSize(height MeasureSpec));
1010 } 762 }
1011 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 763 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
1012 764
1013 mRecyclerView.updatePeekingCardAndHeader(); 765 mRecyclerView.updatePeekingCardAndHeader();
1014 } 766 }
1015 767
(...skipping 16 matching lines...) Expand all
1032 * @return The adapter position the user has scrolled to. 784 * @return The adapter position the user has scrolled to.
1033 */ 785 */
1034 public int getScrollPosition() { 786 public int getScrollPosition() {
1035 return mRecyclerView.getScrollPosition(); 787 return mRecyclerView.getScrollPosition();
1036 } 788 }
1037 789
1038 /** @return the context menu manager. */ 790 /** @return the context menu manager. */
1039 public ContextMenuManager getContextMenuManager() { 791 public ContextMenuManager getContextMenuManager() {
1040 return mContextMenuManager; 792 return mContextMenuManager;
1041 } 793 }
794
795 // TileGroup.Observer interface.
796
797 @Override
798 public void onInitialTileDataLoaded() {
799 // The page contents are initially hidden; otherwise they'll be drawn ce ntered on the
800 // page before the most visited sites are available and then jump upward s to make space
801 // once the most visited sites are available.
802 onTileDataChanged();
803 mNewTabPageLayout.setVisibility(View.VISIBLE);
804 }
805
806 @Override
807 public void onTileDataChanged() {
808 mTileGroup.renderTileViews(mMostVisitedLayout, !mLoadHasCompleted);
809 mSnapshotMostVisitedChanged = true;
810 }
811
812 @Override
813 public void onTileCountChanged() {
814 // If the number of tile rows change while the URL bar is focused, the i cons'
815 // position will be wrong. Schedule the translation to be updated.
816 if (mUrlFocusChangePercent == 1f) mTileCountChanged = true;
817 updateMostVisitedPlaceholderVisibility();
818 }
819
820 @Override
821 public void onTileIconChanged(Tile tile) {
822 mMostVisitedLayout.updateIconView(tile.getUrl(), tile.getIcon());
823 mSnapshotMostVisitedChanged = true;
824 }
825
826 @Override
827 public void onLoadTaskAdded() {
828 mPendingLoadTasks++;
829 }
830
831 @Override
832 public void onLoadTaskCompleted() {
833 loadTaskCompleted();
834 }
1042 } 835 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698