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

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

Issue 2513453004: [Android NTP] Move suggestion sections into a separate node. (Closed)
Patch Set: sync Created 4 years 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 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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.cards; 5 package org.chromium.chrome.browser.ntp.cards;
6 6
7 import android.annotation.SuppressLint; 7 import android.annotation.SuppressLint;
8 import android.graphics.Canvas; 8 import android.graphics.Canvas;
9 import android.support.annotation.StringRes; 9 import android.support.annotation.StringRes;
10 import android.support.v7.widget.RecyclerView; 10 import android.support.v7.widget.RecyclerView;
11 import android.support.v7.widget.RecyclerView.Adapter; 11 import android.support.v7.widget.RecyclerView.Adapter;
12 import android.support.v7.widget.RecyclerView.ViewHolder; 12 import android.support.v7.widget.RecyclerView.ViewHolder;
13 import android.support.v7.widget.helper.ItemTouchHelper; 13 import android.support.v7.widget.helper.ItemTouchHelper;
14 import android.view.View; 14 import android.view.View;
15 import android.view.ViewGroup; 15 import android.view.ViewGroup;
16 16
17 import org.chromium.base.Log; 17 import org.chromium.base.Log;
18 import org.chromium.base.VisibleForTesting; 18 import org.chromium.base.VisibleForTesting;
19 import org.chromium.chrome.R; 19 import org.chromium.chrome.R;
20 import org.chromium.chrome.browser.ntp.NewTabPage.DestructionObserver;
21 import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager; 20 import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager;
22 import org.chromium.chrome.browser.ntp.UiConfig; 21 import org.chromium.chrome.browser.ntp.UiConfig;
23 import org.chromium.chrome.browser.ntp.snippets.CategoryInt;
24 import org.chromium.chrome.browser.ntp.snippets.CategoryStatus;
25 import org.chromium.chrome.browser.ntp.snippets.CategoryStatus.CategoryStatusEnu m;
26 import org.chromium.chrome.browser.ntp.snippets.SectionHeaderViewHolder; 22 import org.chromium.chrome.browser.ntp.snippets.SectionHeaderViewHolder;
27 import org.chromium.chrome.browser.ntp.snippets.SnippetArticle; 23 import org.chromium.chrome.browser.ntp.snippets.SnippetArticle;
28 import org.chromium.chrome.browser.ntp.snippets.SnippetArticleViewHolder; 24 import org.chromium.chrome.browser.ntp.snippets.SnippetArticleViewHolder;
29 import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge;
30 import org.chromium.chrome.browser.ntp.snippets.SnippetsConfig;
31 import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource; 25 import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource;
32 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; 26 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
33 27
34 import java.util.ArrayList; 28 import java.util.Arrays;
35 import java.util.LinkedHashMap;
36 import java.util.List; 29 import java.util.List;
37 import java.util.Map;
38 30
39 /** 31 /**
40 * A class that handles merging above the fold elements and below the fold cards into an adapter 32 * A class that handles merging above the fold elements and below the fold cards into an adapter
41 * that will be used to back the NTP RecyclerView. The first element in the adap ter should always be 33 * that will be used to back the NTP RecyclerView. The first element in the adap ter should always be
42 * the above-the-fold view (containing the logo, search box, and most visited ti les) and subsequent 34 * the above-the-fold view (containing the logo, search box, and most visited ti les) and subsequent
43 * elements will be the cards shown to the user 35 * elements will be the cards shown to the user
44 */ 36 */
45 public class NewTabPageAdapter 37 public class NewTabPageAdapter extends Adapter<NewTabPageViewHolder> implements NodeParent {
46 extends Adapter<NewTabPageViewHolder> implements SuggestionsSource.Obser ver, NodeParent {
47 private static final String TAG = "Ntp"; 38 private static final String TAG = "Ntp";
48 39
49 private final NewTabPageManager mNewTabPageManager; 40 private final NewTabPageManager mNewTabPageManager;
50 private final View mAboveTheFoldView; 41 private final View mAboveTheFoldView;
51 private final UiConfig mUiConfig; 42 private final UiConfig mUiConfig;
52 private final ItemTouchCallbacks mItemTouchCallbacks = new ItemTouchCallback s(); 43 private final ItemTouchCallbacks mItemTouchCallbacks = new ItemTouchCallback s();
53 private final OfflinePageBridge mOfflinePageBridge;
54 private NewTabPageRecyclerView mRecyclerView; 44 private NewTabPageRecyclerView mRecyclerView;
55 45
56 /** 46 /**
57 * List of all child nodes (which can themselves contain multiple child node s). 47 * List of all child nodes (which can themselves contain multiple child node s).
58 */ 48 */
59 private final List<TreeNode> mChildren = new ArrayList<>(); 49 private final List<TreeNode> mChildren;
50 private final InnerNode mRoot;
51
60 private final AboveTheFoldItem mAboveTheFold = new AboveTheFoldItem(); 52 private final AboveTheFoldItem mAboveTheFold = new AboveTheFoldItem();
53 private final SectionList mSections;
61 private final SignInPromo mSigninPromo; 54 private final SignInPromo mSigninPromo;
62 private final AllDismissedItem mAllDismissed; 55 private final AllDismissedItem mAllDismissed;
63 private final Footer mFooter; 56 private final Footer mFooter;
64 private final SpacingItem mBottomSpacer = new SpacingItem(); 57 private final SpacingItem mBottomSpacer = new SpacingItem();
65 private final InnerNode mRoot;
66
67 /** Maps suggestion categories to sections, with stable iteration ordering. */
68 private final Map<Integer, SuggestionsSection> mSections = new LinkedHashMap <>();
69 58
70 private class ItemTouchCallbacks extends ItemTouchHelper.Callback { 59 private class ItemTouchCallbacks extends ItemTouchHelper.Callback {
71 @Override 60 @Override
72 public void onSwiped(ViewHolder viewHolder, int direction) { 61 public void onSwiped(ViewHolder viewHolder, int direction) {
73 mRecyclerView.onItemDismissStarted(viewHolder); 62 mRecyclerView.onItemDismissStarted(viewHolder);
74 NewTabPageAdapter.this.dismissItem(viewHolder.getAdapterPosition()); 63 NewTabPageAdapter.this.dismissItem(viewHolder.getAdapterPosition());
75 } 64 }
76 65
77 @Override 66 @Override
78 public void clearView(RecyclerView recyclerView, ViewHolder viewHolder) { 67 public void clearView(RecyclerView recyclerView, ViewHolder viewHolder) {
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
136 * @param uiConfig the NTP UI configuration, to be passed to created views. 125 * @param uiConfig the NTP UI configuration, to be passed to created views.
137 * @param offlinePageBridge the OfflinePageBridge used to determine if artic les are available 126 * @param offlinePageBridge the OfflinePageBridge used to determine if artic les are available
138 * offline. 127 * offline.
139 * 128 *
140 */ 129 */
141 public NewTabPageAdapter(NewTabPageManager manager, View aboveTheFoldView, U iConfig uiConfig, 130 public NewTabPageAdapter(NewTabPageManager manager, View aboveTheFoldView, U iConfig uiConfig,
142 OfflinePageBridge offlinePageBridge) { 131 OfflinePageBridge offlinePageBridge) {
143 mNewTabPageManager = manager; 132 mNewTabPageManager = manager;
144 mAboveTheFoldView = aboveTheFoldView; 133 mAboveTheFoldView = aboveTheFoldView;
145 mUiConfig = uiConfig; 134 mUiConfig = uiConfig;
146 mOfflinePageBridge = offlinePageBridge;
147 mRoot = new InnerNode(this) { 135 mRoot = new InnerNode(this) {
148 @Override 136 @Override
149 protected List<TreeNode> getChildren() { 137 protected List<TreeNode> getChildren() {
150 return mChildren; 138 return mChildren;
151 } 139 }
152
153 @Override
154 public void onItemRangeChanged(TreeNode child, int index, int count) {
155 if (mChildren.isEmpty()) return; // The sections have not been i nitialised yet.
156 super.onItemRangeChanged(child, index, count);
157 }
158
159 @Override
160 public void onItemRangeInserted(TreeNode child, int index, int count ) {
161 if (mChildren.isEmpty()) return; // The sections have not been i nitialised yet.
162 super.onItemRangeInserted(child, index, count);
163 }
164
165 @Override
166 public void onItemRangeRemoved(TreeNode child, int index, int count) {
167 if (mChildren.isEmpty()) return; // The sections have not been i nitialised yet.
168 super.onItemRangeRemoved(child, index, count);
169 }
170 }; 140 };
171 141
172 mSigninPromo = new SignInPromo(mRoot); 142 mSections = new SectionList(mRoot, mNewTabPageManager, offlinePageBridge );
143 mSigninPromo = new SignInPromo(mRoot, mNewTabPageManager);
173 mAllDismissed = new AllDismissedItem(mRoot); 144 mAllDismissed = new AllDismissedItem(mRoot);
174 mFooter = new Footer(mRoot); 145 mFooter = new Footer(mRoot);
175 DestructionObserver signInObserver = mSigninPromo.getObserver();
176 if (signInObserver != null) mNewTabPageManager.addDestructionObserver(si gnInObserver);
177 146
178 resetSections(/*alwaysAllowEmptySections=*/false); 147 mChildren = Arrays.asList(
179 mNewTabPageManager.getSuggestionsSource().setObserver(this); 148 mAboveTheFold, mSections, mSigninPromo, mAllDismissed, mFooter, mBottomSpacer);
180 } 149 mRoot.init();
181 150
182 /** 151 updateAllDismissedVisibility();
183 * Resets the sections, reloading the whole new tab page content.
184 * @param alwaysAllowEmptySections Whether sections are always allowed to be displayed when
185 * they are empty, even when they are normally not.
186 */
187 public void resetSections(boolean alwaysAllowEmptySections) {
188 mSections.clear();
189 mChildren.clear();
190
191 SuggestionsSource suggestionsSource = mNewTabPageManager.getSuggestionsS ource();
192 int[] categories = suggestionsSource.getCategories();
193 int[] suggestionsPerCategory = new int[categories.length];
194 int i = 0;
195 for (int category : categories) {
196 int categoryStatus = suggestionsSource.getCategoryStatus(category);
197 if (categoryStatus == CategoryStatus.LOADING_ERROR
198 || categoryStatus == CategoryStatus.NOT_PROVIDED
199 || categoryStatus == CategoryStatus.CATEGORY_EXPLICITLY_DISA BLED)
200 continue;
201
202 suggestionsPerCategory[i++] =
203 resetSection(category, categoryStatus, alwaysAllowEmptySecti ons);
204 }
205
206 mNewTabPageManager.trackSnippetsPageImpression(categories, suggestionsPe rCategory);
207
208 updateChildren();
209 }
210
211 /**
212 * Resets the section for {@code category}. Removes the section if there are no suggestions for
213 * it and it is not allowed to be empty. Otherwise, creates the section if i t is not present
214 * yet. Sets the available suggestions on the section.
215 * @param category The category for which the section must be reset.
216 * @param categoryStatus The category status.
217 * @param alwaysAllowEmptySections Whether sections are always allowed to be displayed when
218 * they are empty, even when they are normally not.
219 * @return The number of suggestions for the section.
220 */
221 private int resetSection(@CategoryInt int category, @CategoryStatusEnum int categoryStatus,
222 boolean alwaysAllowEmptySections) {
223 SuggestionsSource suggestionsSource = mNewTabPageManager.getSuggestionsS ource();
224 List<SnippetArticle> suggestions = suggestionsSource.getSuggestionsForCa tegory(category);
225 SuggestionsCategoryInfo info = suggestionsSource.getCategoryInfo(categor y);
226
227 // Do not show an empty section if not allowed.
228 if (suggestions.isEmpty() && !info.showIfEmpty() && !alwaysAllowEmptySec tions) {
229 mSections.remove(category);
230 return 0;
231 }
232
233 // Create the section if needed.
234 SuggestionsSection section = mSections.get(category);
235 if (section == null) {
236 section = new SuggestionsSection(mRoot, info, mNewTabPageManager, mO fflinePageBridge);
237 mSections.put(category, section);
238 }
239
240 // Add the new suggestions.
241 setSuggestions(category, suggestions, categoryStatus);
242
243 return suggestions.size();
244 } 152 }
245 153
246 /** Returns callbacks to configure the interactions with the RecyclerView's items. */ 154 /** Returns callbacks to configure the interactions with the RecyclerView's items. */
247 public ItemTouchHelper.Callback getItemTouchCallbacks() { 155 public ItemTouchHelper.Callback getItemTouchCallbacks() {
248 return mItemTouchCallbacks; 156 return mItemTouchCallbacks;
249 } 157 }
250 158
251 @Override 159 @Override
252 public void onNewSuggestions(@CategoryInt int category) {
253 @CategoryStatusEnum
254 int status = mNewTabPageManager.getSuggestionsSource().getCategoryStatus (category);
255
256 if (!canLoadSuggestions(category, status)) return;
257
258 // We never want to refresh the suggestions if we already have some cont ent.
259 if (mSections.get(category).hasSuggestions()) return;
260
261 List<SnippetArticle> suggestions =
262 mNewTabPageManager.getSuggestionsSource().getSuggestionsForCateg ory(category);
263
264 Log.d(TAG, "Received %d new suggestions for category %d.", suggestions.s ize(), category);
265
266 // At first, there might be no suggestions available, we wait until they have been fetched.
267 if (suggestions.isEmpty()) return;
268
269 setSuggestions(category, suggestions, status);
270 }
271
272 @Override
273 public void onMoreSuggestions(@CategoryInt int category, List<SnippetArticle > suggestions) {
274 @CategoryStatusEnum
275 int status = mNewTabPageManager.getSuggestionsSource().getCategoryStatus (category);
276 if (!canLoadSuggestions(category, status)) return;
277
278 setSuggestions(category, suggestions, status);
279 }
280
281 @Override
282 public void onCategoryStatusChanged(@CategoryInt int category, @CategoryStat usEnum int status) {
283 // Observers should not be registered for this state.
284 assert status != CategoryStatus.ALL_SUGGESTIONS_EXPLICITLY_DISABLED;
285
286 // If there is no section for this category there is nothing to do.
287 if (!mSections.containsKey(category)) return;
288
289 switch (status) {
290 case CategoryStatus.NOT_PROVIDED:
291 // The section provider has gone away. Keep open UIs as they are .
292 return;
293
294 case CategoryStatus.CATEGORY_EXPLICITLY_DISABLED:
295 case CategoryStatus.LOADING_ERROR:
296 // Need to remove the entire section from the UI immediately.
297 removeSection(mSections.get(category));
298 return;
299
300 case CategoryStatus.SIGNED_OUT:
301 // TODO(dgn): We currently can only reach this through an old va riation parameter.
302 default:
303 mSections.get(category).setStatus(status);
304 return;
305 }
306 }
307
308 @Override
309 public void onSuggestionInvalidated(@CategoryInt int category, String idWith inCategory) {
310 if (!mSections.containsKey(category)) return;
311 mSections.get(category).removeSuggestionById(idWithinCategory);
312 }
313
314 @Override
315 public void onFullRefreshRequired() {
316 resetSections(/*alwaysAllowEmptySections=*/false);
317 }
318
319 @Override
320 @ItemViewType 160 @ItemViewType
321 public int getItemViewType(int position) { 161 public int getItemViewType(int position) {
322 return mRoot.getItemViewType(position); 162 return mRoot.getItemViewType(position);
323 } 163 }
324 164
325 @Override 165 @Override
326 public NewTabPageViewHolder onCreateViewHolder(ViewGroup parent, int viewTyp e) { 166 public NewTabPageViewHolder onCreateViewHolder(ViewGroup parent, int viewTyp e) {
327 assert parent == mRecyclerView; 167 assert parent == mRecyclerView;
328 168
329 switch (viewType) { 169 switch (viewType) {
(...skipping 18 matching lines...) Expand all
348 case ItemViewType.ACTION: 188 case ItemViewType.ACTION:
349 return new ActionItem.ViewHolder(mRecyclerView, mNewTabPageManag er, mUiConfig); 189 return new ActionItem.ViewHolder(mRecyclerView, mNewTabPageManag er, mUiConfig);
350 190
351 case ItemViewType.PROMO: 191 case ItemViewType.PROMO:
352 return new SignInPromo.ViewHolder(mRecyclerView, mNewTabPageMana ger, mUiConfig); 192 return new SignInPromo.ViewHolder(mRecyclerView, mNewTabPageMana ger, mUiConfig);
353 193
354 case ItemViewType.FOOTER: 194 case ItemViewType.FOOTER:
355 return new Footer.ViewHolder(mRecyclerView, mNewTabPageManager); 195 return new Footer.ViewHolder(mRecyclerView, mNewTabPageManager);
356 196
357 case ItemViewType.ALL_DISMISSED: 197 case ItemViewType.ALL_DISMISSED:
358 return new AllDismissedItem.ViewHolder(mRecyclerView, mNewTabPag eManager, this); 198 return new AllDismissedItem.ViewHolder(mRecyclerView, mSections) ;
359 } 199 }
360 200
361 assert false : viewType; 201 assert false : viewType;
362 return null; 202 return null;
363 } 203 }
364 204
365 @Override 205 @Override
366 public void onBindViewHolder(NewTabPageViewHolder holder, final int position ) { 206 public void onBindViewHolder(NewTabPageViewHolder holder, final int position ) {
367 mRoot.onBindViewHolder(holder, position); 207 mRoot.onBindViewHolder(holder, position);
368 } 208 }
(...skipping 11 matching lines...) Expand all
380 return getFirstPositionForType(ItemViewType.HEADER); 220 return getFirstPositionForType(ItemViewType.HEADER);
381 } 221 }
382 222
383 public int getFirstCardPosition() { 223 public int getFirstCardPosition() {
384 for (int i = 0; i < getItemCount(); ++i) { 224 for (int i = 0; i < getItemCount(); ++i) {
385 if (CardViewHolder.isCard(getItemViewType(i))) return i; 225 if (CardViewHolder.isCard(getItemViewType(i))) return i;
386 } 226 }
387 return RecyclerView.NO_POSITION; 227 return RecyclerView.NO_POSITION;
388 } 228 }
389 229
230 int getLastContentItemPosition() {
231 return getChildPositionOffset(hasAllBeenDismissed() ? mAllDismissed : mF ooter);
232 }
233
390 int getBottomSpacerPosition() { 234 int getBottomSpacerPosition() {
391 return getChildPositionOffset(mBottomSpacer); 235 return getChildPositionOffset(mBottomSpacer);
392 } 236 }
393 237
394 int getLastContentItemPosition() {
395 return getChildPositionOffset(hasAllBeenDismissed() ? mAllDismissed : mF ooter);
396 }
397
398 private void setSuggestions(@CategoryInt int category, List<SnippetArticle> suggestions,
399 @CategoryStatusEnum int status) {
400 // Count the number of suggestions before this category.
401 int globalPositionOffset = 0;
402 for (Map.Entry<Integer, SuggestionsSection> entry : mSections.entrySet() ) {
403 if (entry.getKey() == category) break;
404 globalPositionOffset += entry.getValue().getSuggestionsCount();
405 }
406 // Assign global indices to the new suggestions.
407 for (SnippetArticle suggestion : suggestions) {
408 suggestion.mGlobalPosition = globalPositionOffset + suggestion.mPosi tion;
409 }
410
411 mSections.get(category).addSuggestions(suggestions, status);
412 }
413
414 private void updateChildren() {
415 mChildren.clear();
416 mChildren.add(mAboveTheFold);
417 mChildren.addAll(mSections.values());
418 mChildren.add(mSigninPromo);
419 mChildren.add(mAllDismissed);
420 mChildren.add(mFooter);
421 mChildren.add(mBottomSpacer);
422
423 updateAllDismissedVisibility();
424
425 // TODO(mvanouwerkerk): Notify about the subset of changed items. At lea st |mAboveTheFold|
426 // has not changed when refreshing from the all dismissed state.
427 notifyDataSetChanged();
428 }
429
430 private void updateAllDismissedVisibility() { 238 private void updateAllDismissedVisibility() {
431 boolean showAllDismissed = hasAllBeenDismissed(); 239 boolean showAllDismissed = hasAllBeenDismissed();
432 mAllDismissed.setVisible(showAllDismissed); 240 mAllDismissed.setVisible(showAllDismissed);
433 mFooter.setVisible(!showAllDismissed); 241 mFooter.setVisible(!showAllDismissed);
434 } 242 }
435 243
436 private void removeSection(SuggestionsSection section) {
437 mSections.remove(section.getCategory());
438 int startPos = getChildPositionOffset(section);
439 mChildren.remove(section);
440 notifyItemRangeRemoved(startPos, section.getItemCount());
441
442 updateAllDismissedVisibility();
443
444 notifyItemChanged(getBottomSpacerPosition());
445 }
446
447 @Override 244 @Override
448 public void onItemRangeChanged(TreeNode child, int itemPosition, int itemCou nt) { 245 public void onItemRangeChanged(TreeNode child, int itemPosition, int itemCou nt) {
449 assert child == mRoot; 246 assert child == mRoot;
450 notifyItemRangeChanged(itemPosition, itemCount); 247 notifyItemRangeChanged(itemPosition, itemCount);
451 } 248 }
452 249
453 @Override 250 @Override
454 public void onItemRangeInserted(TreeNode child, int itemPosition, int itemCo unt) { 251 public void onItemRangeInserted(TreeNode child, int itemPosition, int itemCo unt) {
455 assert child == mRoot; 252 assert child == mRoot;
456 notifyItemRangeInserted(itemPosition, itemCount); 253 notifyItemRangeInserted(itemPosition, itemCount);
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
488 */ 285 */
489 // TODO(crbug.com/635567): Fix this properly. 286 // TODO(crbug.com/635567): Fix this properly.
490 @SuppressLint("SwitchIntDef") 287 @SuppressLint("SwitchIntDef")
491 public void dismissItem(int position) { 288 public void dismissItem(int position) {
492 int itemViewType = getItemViewType(position); 289 int itemViewType = getItemViewType(position);
493 290
494 // TODO(dgn): Polymorphism is supposed to allow to avoid that kind of st uff. 291 // TODO(dgn): Polymorphism is supposed to allow to avoid that kind of st uff.
495 switch (itemViewType) { 292 switch (itemViewType) {
496 case ItemViewType.STATUS: 293 case ItemViewType.STATUS:
497 case ItemViewType.ACTION: 294 case ItemViewType.ACTION:
498 dismissSection(getSuggestionsSection(position)); 295 dismissSection(position);
499 return; 296 return;
500 297
501 case ItemViewType.SNIPPET: 298 case ItemViewType.SNIPPET:
502 dismissSuggestion(position); 299 dismissSuggestion(position);
503 return; 300 return;
504 301
505 case ItemViewType.PROMO: 302 case ItemViewType.PROMO:
506 dismissPromo(); 303 dismissPromo();
507 return; 304 return;
508 305
509 default: 306 default:
510 Log.wtf(TAG, "Unsupported dismissal of item of type %d", itemVie wType); 307 Log.wtf(TAG, "Unsupported dismissal of item of type %d", itemVie wType);
511 return; 308 return;
512 } 309 }
513 } 310 }
514 311
515 private void dismissSection(SuggestionsSection section) { 312 private void dismissSection(int position) {
516 assert SnippetsConfig.isSectionDismissalEnabled(); 313 SuggestionsSection section = getSuggestionsSection(position);
517 314 mSections.dismissSection(section);
518 announceItemRemoved(section.getHeaderText()); 315 announceItemRemoved(section.getHeaderText());
519
520 mNewTabPageManager.getSuggestionsSource().dismissCategory(section.getCat egory());
521 removeSection(section);
522 } 316 }
523 317
524 private void dismissSuggestion(int position) { 318 private void dismissSuggestion(int position) {
525 SnippetArticle suggestion = mRoot.getSuggestionAt(position); 319 SnippetArticle suggestion = mRoot.getSuggestionAt(position);
526 SuggestionsSource suggestionsSource = mNewTabPageManager.getSuggestionsS ource(); 320 SuggestionsSource suggestionsSource = mNewTabPageManager.getSuggestionsS ource();
527 if (suggestionsSource == null) { 321 if (suggestionsSource == null) {
528 // It is possible for this method to be called after the NewTabPage has had destroy() 322 // It is possible for this method to be called after the NewTabPage has had destroy()
529 // called. This can happen when NewTabPageRecyclerView.dismissWithAn imation() is called 323 // called. This can happen when NewTabPageRecyclerView.dismissWithAn imation() is called
530 // and the animation ends after the user has navigated away. In this case we cannot 324 // and the animation ends after the user has navigated away. In this case we cannot
531 // inform the native side that the snippet has been dismissed (http: //crbug.com/649299). 325 // inform the native side that the snippet has been dismissed (http: //crbug.com/649299).
(...skipping 21 matching lines...) Expand all
553 if (siblingPosDelta == 0) return null; 347 if (siblingPosDelta == 0) return null;
554 348
555 return (NewTabPageViewHolder) mRecyclerView.findViewHolderForAdapterPosi tion( 349 return (NewTabPageViewHolder) mRecyclerView.findViewHolderForAdapterPosi tion(
556 siblingPosDelta + swipePos); 350 siblingPosDelta + swipePos);
557 } 351 }
558 352
559 private boolean hasAllBeenDismissed() { 353 private boolean hasAllBeenDismissed() {
560 return mSections.isEmpty() && !mSigninPromo.isVisible(); 354 return mSections.isEmpty() && !mSigninPromo.isVisible();
561 } 355 }
562 356
563 private boolean canLoadSuggestions(@CategoryInt int category, @CategoryStatu sEnum int status) {
564 // We never want to add suggestions from unknown categories.
565 if (!mSections.containsKey(category)) return false;
566
567 // The status may have changed while the suggestions were loading, perha ps they should not
568 // be displayed any more.
569 if (!SnippetsBridge.isCategoryEnabled(status)) {
570 Log.w(TAG, "Received suggestions for a disabled category (id=%d, sta tus=%d)", category,
571 status);
572 return false;
573 }
574
575 return true;
576 }
577
578 /** 357 /**
579 * @param itemPosition The position of an item in the adapter. 358 * @param itemPosition The position of an item in the adapter.
580 * @return Returns the {@link SuggestionsSection} that contains the item at 359 * @return Returns the {@link SuggestionsSection} that contains the item at
581 * {@code itemPosition}, or null if the item is not part of one. 360 * {@code itemPosition}, or null if the item is not part of one.
582 */ 361 */
583 private SuggestionsSection getSuggestionsSection(int itemPosition) { 362 private SuggestionsSection getSuggestionsSection(int itemPosition) {
584 TreeNode child = mRoot.getChildForPosition(itemPosition); 363 int relativePosition = itemPosition - mRoot.getStartingOffsetForChild(mS ections);
364 assert relativePosition >= 0;
365 TreeNode child = mSections.getChildForPosition(relativePosition);
585 if (!(child instanceof SuggestionsSection)) return null; 366 if (!(child instanceof SuggestionsSection)) return null;
586 return (SuggestionsSection) child; 367 return (SuggestionsSection) child;
587 } 368 }
588 369
589 private int getChildPositionOffset(TreeNode child) { 370 private int getChildPositionOffset(TreeNode child) {
590 return mRoot.getStartingOffsetForChild(child); 371 return mRoot.getStartingOffsetForChild(child);
591 } 372 }
592 373
593 @VisibleForTesting 374 @VisibleForTesting
594 SnippetArticle getSuggestionAt(int position) { 375 SnippetArticle getSuggestionAt(int position) {
595 return mRoot.getSuggestionAt(position); 376 return mRoot.getSuggestionAt(position);
596 } 377 }
597 378
598 @VisibleForTesting 379 @VisibleForTesting
599 int getFirstPositionForType(@ItemViewType int viewType) { 380 int getFirstPositionForType(@ItemViewType int viewType) {
600 int count = getItemCount(); 381 int count = getItemCount();
601 for (int i = 0; i < count; i++) { 382 for (int i = 0; i < count; i++) {
602 if (getItemViewType(i) == viewType) return i; 383 if (getItemViewType(i) == viewType) return i;
603 } 384 }
604 return RecyclerView.NO_POSITION; 385 return RecyclerView.NO_POSITION;
605 } 386 }
606 387
607 SuggestionsSection getSectionForTesting(@CategoryInt int category) { 388 SectionList getSectionListForTesting() {
608 return mSections.get(category); 389 return mSections;
609 } 390 }
610 391
611 InnerNode getRootForTesting() { 392 InnerNode getRootForTesting() {
612 return mRoot; 393 return mRoot;
613 } 394 }
614 395
615 private void announceItemRemoved(String itemTitle) { 396 private void announceItemRemoved(String itemTitle) {
616 // In tests the RecyclerView can be null. 397 // In tests the RecyclerView can be null.
617 if (mRecyclerView == null) return; 398 if (mRecyclerView == null) return;
618 399
619 mRecyclerView.announceForAccessibility(mRecyclerView.getResources().getS tring( 400 mRecyclerView.announceForAccessibility(mRecyclerView.getResources().getS tring(
620 R.string.ntp_accessibility_item_removed, itemTitle)); 401 R.string.ntp_accessibility_item_removed, itemTitle));
621 } 402 }
622 403
623 private void announceItemRemoved(@StringRes int stringToAnnounce) { 404 private void announceItemRemoved(@StringRes int stringToAnnounce) {
624 // In tests the RecyclerView can be null. 405 // In tests the RecyclerView can be null.
625 if (mRecyclerView == null) return; 406 if (mRecyclerView == null) return;
626 407
627 announceItemRemoved(mRecyclerView.getResources().getString(stringToAnnou nce)); 408 announceItemRemoved(mRecyclerView.getResources().getString(stringToAnnou nce));
628 } 409 }
629 } 410 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698