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

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

Issue 2396523002: Unify NewTabPageItem and ItemGroup into a single tree-structured interface. (Closed)
Patch Set: oo Created 4 years, 2 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 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.v7.widget.RecyclerView; 9 import android.support.v7.widget.RecyclerView;
10 import android.support.v7.widget.RecyclerView.Adapter; 10 import android.support.v7.widget.RecyclerView.Adapter;
11 import android.support.v7.widget.RecyclerView.ViewHolder; 11 import android.support.v7.widget.RecyclerView.ViewHolder;
12 import android.support.v7.widget.helper.ItemTouchHelper; 12 import android.support.v7.widget.helper.ItemTouchHelper;
13 import android.view.View; 13 import android.view.View;
14 import android.view.ViewGroup; 14 import android.view.ViewGroup;
15 15
16 import org.chromium.base.Log; 16 import org.chromium.base.Log;
17 import org.chromium.base.VisibleForTesting; 17 import org.chromium.base.VisibleForTesting;
18 import org.chromium.chrome.R; 18 import org.chromium.chrome.R;
19 import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager; 19 import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager;
20 import org.chromium.chrome.browser.ntp.UiConfig; 20 import org.chromium.chrome.browser.ntp.UiConfig;
21 import org.chromium.chrome.browser.ntp.snippets.CategoryInt; 21 import org.chromium.chrome.browser.ntp.snippets.CategoryInt;
22 import org.chromium.chrome.browser.ntp.snippets.CategoryStatus; 22 import org.chromium.chrome.browser.ntp.snippets.CategoryStatus;
23 import org.chromium.chrome.browser.ntp.snippets.CategoryStatus.CategoryStatusEnu m; 23 import org.chromium.chrome.browser.ntp.snippets.CategoryStatus.CategoryStatusEnu m;
24 import org.chromium.chrome.browser.ntp.snippets.SectionHeader;
25 import org.chromium.chrome.browser.ntp.snippets.SectionHeaderViewHolder; 24 import org.chromium.chrome.browser.ntp.snippets.SectionHeaderViewHolder;
26 import org.chromium.chrome.browser.ntp.snippets.SnippetArticle; 25 import org.chromium.chrome.browser.ntp.snippets.SnippetArticle;
27 import org.chromium.chrome.browser.ntp.snippets.SnippetArticleViewHolder; 26 import org.chromium.chrome.browser.ntp.snippets.SnippetArticleViewHolder;
28 import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge; 27 import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge;
29 import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource; 28 import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource;
30 import org.chromium.chrome.browser.signin.SigninManager.SignInStateObserver; 29 import org.chromium.chrome.browser.signin.SigninManager.SignInStateObserver;
31 30
32 import java.util.ArrayList; 31 import java.util.ArrayList;
33 import java.util.Collections; 32 import java.util.Collections;
34 import java.util.LinkedHashMap; 33 import java.util.LinkedHashMap;
35 import java.util.List; 34 import java.util.List;
36 import java.util.Map; 35 import java.util.Map;
37 36
38 /** 37 /**
39 * A class that handles merging above the fold elements and below the fold cards into an adapter 38 * A class that handles merging above the fold elements and below the fold cards into an adapter
40 * that will be used to back the NTP RecyclerView. The first element in the adap ter should always be 39 * that will be used to back the NTP RecyclerView. The first element in the adap ter should always be
41 * the above-the-fold view (containing the logo, search box, and most visited ti les) and subsequent 40 * the above-the-fold view (containing the logo, search box, and most visited ti les) and subsequent
42 * elements will be the cards shown to the user 41 * elements will be the cards shown to the user
43 */ 42 */
44 public class NewTabPageAdapter extends Adapter<NewTabPageViewHolder> 43 public class NewTabPageAdapter
45 implements SuggestionsSource.Observer, ItemGroup.Observer { 44 extends Adapter<NewTabPageViewHolder> implements SuggestionsSource.Obser ver, NodeParent {
46 private static final String TAG = "Ntp"; 45 private static final String TAG = "Ntp";
47 46
48 private final NewTabPageManager mNewTabPageManager; 47 private final NewTabPageManager mNewTabPageManager;
49 private final View mAboveTheFoldView; 48 private final View mAboveTheFoldView;
50 private final UiConfig mUiConfig; 49 private final UiConfig mUiConfig;
51 private final ItemTouchCallbacks mItemTouchCallbacks = new ItemTouchCallback s(); 50 private final ItemTouchCallbacks mItemTouchCallbacks = new ItemTouchCallback s();
52 private NewTabPageRecyclerView mRecyclerView; 51 private NewTabPageRecyclerView mRecyclerView;
53 52
54 /** 53 /**
55 * List of all item groups (which can themselves contain multiple items. Whe n flattened, this 54 * List of all item groups (which can themselves contain multiple items. Whe n flattened, this
56 * will be a list of all items the adapter exposes. 55 * will be a list of all items the adapter exposes.
57 */ 56 */
58 private final List<ItemGroup> mGroups = new ArrayList<>(); 57 private final List<TreeNode> mGroups = new ArrayList<>();
59 private final AboveTheFoldItem mAboveTheFold = new AboveTheFoldItem(); 58 private final AboveTheFoldItem mAboveTheFold = new AboveTheFoldItem();
60 private final SignInPromo mSigninPromo = new SignInPromo(); 59 private final SignInPromo mSigninPromo;
61 private final AllDismissedItem mAllDismissed = new AllDismissedItem(); 60 private final AllDismissedItem mAllDismissed = new AllDismissedItem();
62 private final Footer mFooter = new Footer(); 61 private final Footer mFooter = new Footer();
63 private final SpacingItem mBottomSpacer = new SpacingItem(); 62 private final SpacingItem mBottomSpacer = new SpacingItem();
63 private final InnerNode mRoot;
64 64
65 /** Maps suggestion categories to sections, with stable iteration ordering. */ 65 /** Maps suggestion categories to sections, with stable iteration ordering. */
66 private final Map<Integer, SuggestionsSection> mSections = new LinkedHashMap <>(); 66 private final Map<Integer, SuggestionsSection> mSections = new LinkedHashMap <>();
67 67
68 private class ItemTouchCallbacks extends ItemTouchHelper.Callback { 68 private class ItemTouchCallbacks extends ItemTouchHelper.Callback {
69 @Override 69 @Override
70 public void onSwiped(ViewHolder viewHolder, int direction) { 70 public void onSwiped(ViewHolder viewHolder, int direction) {
71 mRecyclerView.onItemDismissStarted(viewHolder); 71 mRecyclerView.onItemDismissStarted(viewHolder);
72 NewTabPageAdapter.this.dismissItem(viewHolder.getAdapterPosition()); 72 NewTabPageAdapter.this.dismissItem(viewHolder.getAdapterPosition());
73 } 73 }
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
127 * 127 *
128 * @param manager the NewTabPageManager to use to interact with the rest of the system. 128 * @param manager the NewTabPageManager to use to interact with the rest of the system.
129 * @param aboveTheFoldView the layout encapsulating all the above-the-fold e lements 129 * @param aboveTheFoldView the layout encapsulating all the above-the-fold e lements
130 * (logo, search box, most visited tiles) 130 * (logo, search box, most visited tiles)
131 * @param uiConfig the NTP UI configuration, to be passed to created views. 131 * @param uiConfig the NTP UI configuration, to be passed to created views.
132 */ 132 */
133 public NewTabPageAdapter(NewTabPageManager manager, View aboveTheFoldView, U iConfig uiConfig) { 133 public NewTabPageAdapter(NewTabPageManager manager, View aboveTheFoldView, U iConfig uiConfig) {
134 mNewTabPageManager = manager; 134 mNewTabPageManager = manager;
135 mAboveTheFoldView = aboveTheFoldView; 135 mAboveTheFoldView = aboveTheFoldView;
136 mUiConfig = uiConfig; 136 mUiConfig = uiConfig;
137 mSigninPromo.setObserver(this); 137 mRoot = new InnerNode(this) {
138 @Override
139 protected List<TreeNode> getChildren() {
140 return mGroups;
141 }
142 };
143
144 mSigninPromo = new SignInPromo(mRoot);
138 resetSections(/*alwaysAllowEmptySections=*/false); 145 resetSections(/*alwaysAllowEmptySections=*/false);
139 mNewTabPageManager.getSuggestionsSource().setObserver(this); 146 mNewTabPageManager.getSuggestionsSource().setObserver(this);
140 147
141 mNewTabPageManager.registerSignInStateObserver(new SignInStateObserver() { 148 mNewTabPageManager.registerSignInStateObserver(new SignInStateObserver() {
142 @Override 149 @Override
143 public void onSignedIn() { 150 public void onSignedIn() {
144 mSigninPromo.hide(); 151 mSigninPromo.hide();
145 resetSections(/*alwaysAllowEmptySections=*/false); 152 resetSections(/*alwaysAllowEmptySections=*/false);
146 } 153 }
147 154
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
198 205
199 // Do not show an empty section if not allowed. 206 // Do not show an empty section if not allowed.
200 if (suggestions.isEmpty() && !info.showIfEmpty() && !alwaysAllowEmptySec tions) { 207 if (suggestions.isEmpty() && !info.showIfEmpty() && !alwaysAllowEmptySec tions) {
201 mSections.remove(category); 208 mSections.remove(category);
202 return 0; 209 return 0;
203 } 210 }
204 211
205 // Create the section if needed. 212 // Create the section if needed.
206 SuggestionsSection section = mSections.get(category); 213 SuggestionsSection section = mSections.get(category);
207 if (section == null) { 214 if (section == null) {
208 section = new SuggestionsSection(info, this); 215 section = new SuggestionsSection(mRoot, info);
209 mSections.put(category, section); 216 mSections.put(category, section);
210 } 217 }
211 218
212 // Add the new suggestions. 219 // Add the new suggestions.
213 setSuggestions(category, suggestions, categoryStatus); 220 setSuggestions(category, suggestions, categoryStatus);
214 221
215 return suggestions.size(); 222 return suggestions.size();
216 } 223 }
217 224
218 /** Returns callbacks to configure the interactions with the RecyclerView's items. */ 225 /** Returns callbacks to configure the interactions with the RecyclerView's items. */
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
278 } 285 }
279 } 286 }
280 287
281 @Override 288 @Override
282 public void onSuggestionInvalidated(@CategoryInt int category, String idWith inCategory) { 289 public void onSuggestionInvalidated(@CategoryInt int category, String idWith inCategory) {
283 if (!mSections.containsKey(category)) return; 290 if (!mSections.containsKey(category)) return;
284 mSections.get(category).removeSuggestionById(idWithinCategory); 291 mSections.get(category).removeSuggestionById(idWithinCategory);
285 } 292 }
286 293
287 @Override 294 @Override
288 @NewTabPageItem.ViewType 295 @ItemViewType
289 public int getItemViewType(int position) { 296 public int getItemViewType(int position) {
290 return getItems().get(position).getType(); 297 return mRoot.getItemViewType(position);
291 } 298 }
292 299
293 @Override 300 @Override
294 public NewTabPageViewHolder onCreateViewHolder(ViewGroup parent, int viewTyp e) { 301 public NewTabPageViewHolder onCreateViewHolder(ViewGroup parent, int viewTyp e) {
295 assert parent == mRecyclerView; 302 assert parent == mRecyclerView;
296 303
dgn 2016/10/13 15:42:41 change it to as switch while we're at it?
Bernhard Bauer 2016/10/13 16:13:14 Done.
297 if (viewType == NewTabPageItem.VIEW_TYPE_ABOVE_THE_FOLD) { 304 if (viewType == ItemViewType.VIEW_TYPE_ABOVE_THE_FOLD) {
298 return new NewTabPageViewHolder(mAboveTheFoldView); 305 return new NewTabPageViewHolder(mAboveTheFoldView);
299 } 306 }
300 307
301 if (viewType == NewTabPageItem.VIEW_TYPE_HEADER) { 308 if (viewType == ItemViewType.VIEW_TYPE_HEADER) {
302 return new SectionHeaderViewHolder(mRecyclerView, mUiConfig); 309 return new SectionHeaderViewHolder(mRecyclerView, mUiConfig);
303 } 310 }
304 311
305 if (viewType == NewTabPageItem.VIEW_TYPE_SNIPPET) { 312 if (viewType == ItemViewType.VIEW_TYPE_SNIPPET) {
306 return new SnippetArticleViewHolder(mRecyclerView, mNewTabPageManage r, mUiConfig); 313 return new SnippetArticleViewHolder(mRecyclerView, mNewTabPageManage r, mUiConfig);
307 } 314 }
308 315
309 if (viewType == NewTabPageItem.VIEW_TYPE_SPACING) { 316 if (viewType == ItemViewType.VIEW_TYPE_SPACING) {
310 return new NewTabPageViewHolder(SpacingItem.createView(parent)); 317 return new NewTabPageViewHolder(SpacingItem.createView(parent));
311 } 318 }
312 319
313 if (viewType == NewTabPageItem.VIEW_TYPE_STATUS) { 320 if (viewType == ItemViewType.VIEW_TYPE_STATUS) {
314 return new StatusCardViewHolder(mRecyclerView, mUiConfig); 321 return new StatusCardViewHolder(mRecyclerView, mUiConfig);
315 } 322 }
316 323
317 if (viewType == NewTabPageItem.VIEW_TYPE_PROGRESS) { 324 if (viewType == ItemViewType.VIEW_TYPE_PROGRESS) {
318 return new ProgressViewHolder(mRecyclerView); 325 return new ProgressViewHolder(mRecyclerView);
319 } 326 }
320 327
321 if (viewType == NewTabPageItem.VIEW_TYPE_ACTION) { 328 if (viewType == ItemViewType.VIEW_TYPE_ACTION) {
322 return new ActionItem.ViewHolder(mRecyclerView, mNewTabPageManager, mUiConfig); 329 return new ActionItem.ViewHolder(mRecyclerView, mNewTabPageManager, mUiConfig);
323 } 330 }
324 331
325 if (viewType == NewTabPageItem.VIEW_TYPE_PROMO) { 332 if (viewType == ItemViewType.VIEW_TYPE_PROMO) {
326 return new SignInPromo.ViewHolder(mRecyclerView, mUiConfig); 333 return new SignInPromo.ViewHolder(mRecyclerView, mUiConfig);
327 } 334 }
328 335
329 if (viewType == NewTabPageItem.VIEW_TYPE_FOOTER) { 336 if (viewType == ItemViewType.VIEW_TYPE_FOOTER) {
330 return new Footer.ViewHolder(mRecyclerView, mNewTabPageManager); 337 return new Footer.ViewHolder(mRecyclerView, mNewTabPageManager);
331 } 338 }
332 339
333 if (viewType == NewTabPageItem.VIEW_TYPE_ALL_DISMISSED) { 340 if (viewType == ItemViewType.VIEW_TYPE_ALL_DISMISSED) {
334 return new AllDismissedItem.ViewHolder(mRecyclerView, mNewTabPageMan ager, this); 341 return new AllDismissedItem.ViewHolder(mRecyclerView, mNewTabPageMan ager, this);
335 } 342 }
336 343
337 return null; 344 return null;
338 } 345 }
339 346
340 @Override 347 @Override
341 public void onBindViewHolder(NewTabPageViewHolder holder, final int position ) { 348 public void onBindViewHolder(NewTabPageViewHolder holder, final int position ) {
342 getItems().get(position).onBindViewHolder(holder); 349 mRoot.onBindViewHolder(holder, position);
343 } 350 }
344 351
345 @Override 352 @Override
346 public int getItemCount() { 353 public int getItemCount() {
347 return getItems().size(); 354 return mRoot.getItemCount();
348 } 355 }
349 356
350 public int getAboveTheFoldPosition() { 357 public int getAboveTheFoldPosition() {
351 return getGroupPositionOffset(mAboveTheFold); 358 return getGroupPositionOffset(mAboveTheFold);
352 } 359 }
353 360
354 public int getFirstHeaderPosition() { 361 public int getFirstHeaderPosition() {
355 List<NewTabPageItem> items = getItems(); 362 int count = getItemCount();
356 for (int i = 0; i < items.size(); i++) { 363 for (int i = 0; i < count; i++) {
357 if (items.get(i) instanceof SectionHeader) return i; 364 if (getItemViewType(i) == ItemViewType.VIEW_TYPE_HEADER) return i;
358 } 365 }
359 return RecyclerView.NO_POSITION; 366 return RecyclerView.NO_POSITION;
360 } 367 }
361 368
362 public int getFirstCardPosition() { 369 public int getFirstCardPosition() {
363 for (int i = 0; i < getItemCount(); ++i) { 370 for (int i = 0; i < getItemCount(); ++i) {
364 if (CardViewHolder.isCard(getItemViewType(i))) return i; 371 if (CardViewHolder.isCard(getItemViewType(i))) return i;
365 } 372 }
366 return RecyclerView.NO_POSITION; 373 return RecyclerView.NO_POSITION;
367 } 374 }
368 375
369 public int getFooterPosition() { 376 public int getFooterPosition() {
370 return getGroupPositionOffset(mFooter); 377 return getGroupPositionOffset(mFooter);
371 } 378 }
372 379
373 public int getBottomSpacerPosition() { 380 public int getBottomSpacerPosition() {
374 return getGroupPositionOffset(mBottomSpacer); 381 return getGroupPositionOffset(mBottomSpacer);
375 } 382 }
376 383
377 public int getSuggestionPosition(SnippetArticle article) { 384 public int getSuggestionPosition(SnippetArticle foo) {
dgn 2016/10/13 15:42:41 foo?
Bernhard Bauer 2016/10/13 16:13:14 Oops... renamed.
378 List<NewTabPageItem> items = getItems(); 385 for (int i = 0; i < mRoot.getItemCount(); i++) {
379 for (int i = 0; i < items.size(); i++) { 386 SnippetArticle article = mRoot.getSuggestionAt(i);
380 NewTabPageItem item = items.get(i); 387 if (article != null && article.equals(foo)) return i;
381 if (article.equals(item)) return i;
382 } 388 }
383 return RecyclerView.NO_POSITION; 389 return RecyclerView.NO_POSITION;
384 } 390 }
385 391
386 /** Start a request for new snippets. */ 392 /** Start a request for new snippets. */
387 public void reloadSnippets() { 393 public void reloadSnippets() {
388 SnippetsBridge.fetchSnippets(/*forceRequest=*/true); 394 SnippetsBridge.fetchSnippets(/*forceRequest=*/true);
389 } 395 }
390 396
391 private void setSuggestions(@CategoryInt int category, List<SnippetArticle> suggestions, 397 private void setSuggestions(@CategoryInt int category, List<SnippetArticle> suggestions,
(...skipping 22 matching lines...) Expand all
414 420
415 // TODO(mvanouwerkerk): Notify about the subset of changed items. At lea st |mAboveTheFold| 421 // TODO(mvanouwerkerk): Notify about the subset of changed items. At lea st |mAboveTheFold|
416 // has not changed when refreshing from the all dismissed state. 422 // has not changed when refreshing from the all dismissed state.
417 notifyDataSetChanged(); 423 notifyDataSetChanged();
418 } 424 }
419 425
420 private void removeSection(SuggestionsSection section) { 426 private void removeSection(SuggestionsSection section) {
421 mSections.remove(section.getCategory()); 427 mSections.remove(section.getCategory());
422 int startPos = getGroupPositionOffset(section); 428 int startPos = getGroupPositionOffset(section);
423 mGroups.remove(section); 429 mGroups.remove(section);
424 notifyItemRangeRemoved(startPos, section.getItems().size()); 430 notifyItemRangeRemoved(startPos, section.getItemCount());
425 431
426 if (hasAllBeenDismissed()) { 432 if (hasAllBeenDismissed()) {
427 int footerPosition = getFooterPosition(); 433 int footerPosition = getFooterPosition();
428 mGroups.set(mGroups.indexOf(mFooter), mAllDismissed); 434 mGroups.set(mGroups.indexOf(mFooter), mAllDismissed);
429 notifyItemChanged(footerPosition); 435 notifyItemChanged(footerPosition);
430 } 436 }
431 437
432 notifyItemChanged(getBottomSpacerPosition()); 438 notifyItemChanged(getBottomSpacerPosition());
433 } 439 }
434 440
435 @Override 441 @Override
436 public void onItemRangeChanged(ItemGroup group, int itemPosition, int itemCo unt) { 442 public void onItemRangeChanged(TreeNode child, int itemPosition, int itemCou nt) {
443 assert child == mRoot;
437 if (mGroups.isEmpty()) return; // The sections have not been initialised yet. 444 if (mGroups.isEmpty()) return; // The sections have not been initialised yet.
438 notifyItemRangeChanged(getGroupPositionOffset(group) + itemPosition, ite mCount); 445 notifyItemRangeChanged(itemPosition, itemCount);
439 } 446 }
440 447
441 @Override 448 @Override
442 public void onItemRangeInserted(ItemGroup group, int itemPosition, int itemC ount) { 449 public void onItemRangeInserted(TreeNode child, int itemPosition, int itemCo unt) {
450 assert child == mRoot;
443 if (mGroups.isEmpty()) return; // The sections have not been initialised yet. 451 if (mGroups.isEmpty()) return; // The sections have not been initialised yet.
444 notifyItemRangeInserted(getGroupPositionOffset(group) + itemPosition, it emCount); 452 notifyItemRangeInserted(itemPosition, itemCount);
445 notifyItemChanged(getItems().size() - 1); // Refresh the spacer too. 453 notifyItemChanged(getItemCount() - 1); // Refresh the spacer too.
446 } 454 }
447 455
448 @Override 456 @Override
449 public void onItemRangeRemoved(ItemGroup group, int itemPosition, int itemCo unt) { 457 public void onItemRangeRemoved(TreeNode child, int itemPosition, int itemCou nt) {
458 assert child == mRoot;
450 if (mGroups.isEmpty()) return; // The sections have not been initialised yet. 459 if (mGroups.isEmpty()) return; // The sections have not been initialised yet.
451 notifyItemRangeRemoved(getGroupPositionOffset(group) + itemPosition, ite mCount); 460 notifyItemRangeRemoved(itemPosition, itemCount);
452 notifyItemChanged(getItems().size() - 1); // Refresh the spacer too. 461 notifyItemChanged(getItemCount() - 1); // Refresh the spacer too.
453 } 462 }
454 463
455 @Override 464 @Override
456 public void onAttachedToRecyclerView(RecyclerView recyclerView) { 465 public void onAttachedToRecyclerView(RecyclerView recyclerView) {
457 super.onAttachedToRecyclerView(recyclerView); 466 super.onAttachedToRecyclerView(recyclerView);
458 467
459 // We are assuming for now that the adapter is used with a single Recycl erView. 468 // We are assuming for now that the adapter is used with a single Recycl erView.
460 // Getting the reference as we are doing here is going to be broken if t hat changes. 469 // Getting the reference as we are doing here is going to be broken if t hat changes.
461 assert mRecyclerView == null; 470 assert mRecyclerView == null;
462 471
463 // FindBugs chokes on the cast below when not checked, raising BC_UNCONF IRMED_CAST 472 // FindBugs chokes on the cast below when not checked, raising BC_UNCONF IRMED_CAST
464 assert recyclerView instanceof NewTabPageRecyclerView; 473 assert recyclerView instanceof NewTabPageRecyclerView;
465 474
466 mRecyclerView = (NewTabPageRecyclerView) recyclerView; 475 mRecyclerView = (NewTabPageRecyclerView) recyclerView;
467 } 476 }
468 477
469 /** 478 /**
470 * Dismisses the item at the provided adapter position. Can also cause the d ismissal of other 479 * Dismisses the item at the provided adapter position. Can also cause the d ismissal of other
471 * items or even entire sections. 480 * items or even entire sections.
472 */ 481 */
473 // TODO(crbug.com/635567): Fix this properly. 482 // TODO(crbug.com/635567): Fix this properly.
474 @SuppressLint("SwitchIntDef") 483 @SuppressLint("SwitchIntDef")
475 public void dismissItem(int position) { 484 public void dismissItem(int position) {
476 int itemViewType = getItemViewType(position); 485 int itemViewType = getItemViewType(position);
477 486
478 // TODO(dgn): Polymorphism is supposed to allow to avoid that kind of st uff. 487 // TODO(dgn): Polymorphism is supposed to allow to avoid that kind of st uff.
479 switch (itemViewType) { 488 switch (itemViewType) {
480 case NewTabPageItem.VIEW_TYPE_STATUS: 489 case ItemViewType.VIEW_TYPE_STATUS:
481 case NewTabPageItem.VIEW_TYPE_ACTION: 490 case ItemViewType.VIEW_TYPE_ACTION:
482 dismissSection((SuggestionsSection) getGroup(position)); 491 dismissSection(getSuggestionsSection(position));
483 return; 492 return;
484 493
485 case NewTabPageItem.VIEW_TYPE_SNIPPET: 494 case ItemViewType.VIEW_TYPE_SNIPPET:
486 dismissSuggestion(position); 495 dismissSuggestion(position);
487 return; 496 return;
488 497
489 case NewTabPageItem.VIEW_TYPE_PROMO: 498 case ItemViewType.VIEW_TYPE_PROMO:
490 dismissPromo(); 499 dismissPromo();
491 return; 500 return;
492 501
493 default: 502 default:
494 Log.wtf(TAG, "Unsupported dismissal of item of type %d", itemVie wType); 503 Log.wtf(TAG, "Unsupported dismissal of item of type %d", itemVie wType);
495 return; 504 return;
496 } 505 }
497 } 506 }
498 507
499 private void dismissSection(SuggestionsSection section) { 508 private void dismissSection(SuggestionsSection section) {
500 mNewTabPageManager.getSuggestionsSource().dismissCategory(section.getCat egory()); 509 mNewTabPageManager.getSuggestionsSource().dismissCategory(section.getCat egory());
501 removeSection(section); 510 removeSection(section);
502 } 511 }
503 512
504 private void dismissSuggestion(int position) { 513 private void dismissSuggestion(int position) {
505 SnippetArticle suggestion = (SnippetArticle) getItems().get(position); 514 SnippetArticle suggestion = mRoot.getSuggestionAt(position);
506
507 SuggestionsSource suggestionsSource = mNewTabPageManager.getSuggestionsS ource(); 515 SuggestionsSource suggestionsSource = mNewTabPageManager.getSuggestionsS ource();
508 if (suggestionsSource == null) { 516 if (suggestionsSource == null) {
509 // It is possible for this method to be called after the NewTabPage has had destroy() 517 // It is possible for this method to be called after the NewTabPage has had destroy()
510 // called. This can happen when NewTabPageRecyclerView.dismissWithAn imation() is called 518 // called. This can happen when NewTabPageRecyclerView.dismissWithAn imation() is called
511 // and the animation ends after the user has navigated away. In this case we cannot 519 // and the animation ends after the user has navigated away. In this case we cannot
512 // inform the native side that the snippet has been dismissed (http: //crbug.com/649299). 520 // inform the native side that the snippet has been dismissed (http: //crbug.com/649299).
513 return; 521 return;
514 } 522 }
515 523
516 announceItemRemoved(suggestion.mTitle); 524 announceItemRemoved(suggestion.mTitle);
517 525
518 suggestionsSource.dismissSuggestion(suggestion); 526 suggestionsSource.dismissSuggestion(suggestion);
519 SuggestionsSection section = (SuggestionsSection) getGroup(position); 527 SuggestionsSection section = getSuggestionsSection(position);
520 section.removeSuggestion(suggestion); 528 section.removeSuggestion(suggestion);
521 } 529 }
522 530
523 private void dismissPromo() { 531 private void dismissPromo() {
524 // TODO(dgn): accessibility announcement. 532 // TODO(dgn): accessibility announcement.
525 mSigninPromo.dismiss(); 533 mSigninPromo.dismiss();
526 534
527 if (hasAllBeenDismissed()) { 535 if (hasAllBeenDismissed()) {
528 int footerPosition = getFooterPosition(); 536 int footerPosition = getFooterPosition();
529 mGroups.set(mGroups.indexOf(mFooter), mAllDismissed); 537 mGroups.set(mGroups.indexOf(mFooter), mAllDismissed);
530 notifyItemChanged(footerPosition); 538 notifyItemChanged(footerPosition);
531 } 539 }
532 } 540 }
533 541
534 /** 542 /**
535 * Returns an unmodifiable list containing all items in the adapter.
536 */
537 private List<NewTabPageItem> getItems() {
538 List<NewTabPageItem> items = new ArrayList<>();
539 for (ItemGroup group : mGroups) {
540 items.addAll(group.getItems());
541 }
542 return Collections.unmodifiableList(items);
543 }
544
545 /**
546 * Returns another view holder that should be dismissed at the same time as the provided one. 543 * Returns another view holder that should be dismissed at the same time as the provided one.
547 */ 544 */
548 public ViewHolder getDismissSibling(ViewHolder viewHolder) { 545 public ViewHolder getDismissSibling(ViewHolder viewHolder) {
549 int swipePos = viewHolder.getAdapterPosition(); 546 int swipePos = viewHolder.getAdapterPosition();
550 ItemGroup group = getGroup(swipePos); 547 SuggestionsSection section = getSuggestionsSection(swipePos);
dgn 2016/10/13 15:42:41 You can swipe the signin promo and it's not a sugg
Bernhard Bauer 2016/10/13 16:13:14 Good catch! Done.
551 548 int siblingPosDelta =
552 if (!(group instanceof SuggestionsSection)) return null; 549 section.getDismissSiblingPosDelta(swipePos - getGroupPositionOff set(section));
553
554 SuggestionsSection section = (SuggestionsSection) group;
555 int siblingPosDelta = section.getDismissSiblingPosDelta(getItems().get(s wipePos));
556 if (siblingPosDelta == 0) return null; 550 if (siblingPosDelta == 0) return null;
557 551
558 return mRecyclerView.findViewHolderForAdapterPosition(siblingPosDelta + swipePos); 552 return mRecyclerView.findViewHolderForAdapterPosition(siblingPosDelta + swipePos);
559 } 553 }
560 554
561 private boolean hasAllBeenDismissed() { 555 private boolean hasAllBeenDismissed() {
562 return mSections.isEmpty() && !mSigninPromo.isShown(); 556 return mSections.isEmpty() && !mSigninPromo.isShown();
563 } 557 }
564 558
565 @VisibleForTesting 559 @VisibleForTesting
566 ItemGroup getGroup(int itemPosition) { 560 SuggestionsSection getSuggestionsSection(int itemPosition) {
567 int itemsSkipped = 0; 561 return (SuggestionsSection) mGroups.get(mRoot.getChildIndexForPosition(i temPosition));
568 for (ItemGroup group : mGroups) {
569 List<NewTabPageItem> items = group.getItems();
570 itemsSkipped += items.size();
571 if (itemPosition < itemsSkipped) return group;
572 }
573 return null;
574 } 562 }
575 563
576 @VisibleForTesting 564 @VisibleForTesting
577 List<ItemGroup> getGroups() { 565 List<TreeNode> getGroups() {
578 return Collections.unmodifiableList(mGroups); 566 return Collections.unmodifiableList(mGroups);
579 } 567 }
580 568
581 @VisibleForTesting 569 @VisibleForTesting
582 int getGroupPositionOffset(ItemGroup group) { 570 int getGroupPositionOffset(TreeNode group) {
583 int positionOffset = 0; 571 return mRoot.getStartingOffsetForChild(group);
584 for (ItemGroup candidateGroup : mGroups) {
585 if (candidateGroup == group) return positionOffset;
586 positionOffset += candidateGroup.getItems().size();
587 }
588 Log.d(TAG, "Group not found: %s", group);
589 return RecyclerView.NO_POSITION;
590 } 572 }
591 573
592 @VisibleForTesting 574 @VisibleForTesting
593 SnippetArticle getSuggestionAt(int position) { 575 SnippetArticle getSuggestionAt(int position) {
594 return (SnippetArticle) getItems().get(position); 576 return mRoot.getSuggestionAt(position);
595 } 577 }
596 578
597 private void announceItemRemoved(String suggestionTitle) { 579 private void announceItemRemoved(String suggestionTitle) {
598 // In tests the RecyclerView can be null. 580 // In tests the RecyclerView can be null.
599 if (mRecyclerView == null) return; 581 if (mRecyclerView == null) return;
600 582
601 mRecyclerView.announceForAccessibility(mRecyclerView.getResources().getS tring( 583 mRecyclerView.announceForAccessibility(mRecyclerView.getResources().getS tring(
602 R.string.ntp_accessibility_item_removed, suggestionTitle)); 584 R.string.ntp_accessibility_item_removed, suggestionTitle));
603 } 585 }
604 } 586 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698