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

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

Issue 2194433002: Zine: support multiple sections in the ui (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Cleanups. Created 4 years, 5 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 side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
index 723636d10b3c3e4ed074aa9d1b61c30d170242a9..3348fcc0d3f1bb9e30ef2bc693e78ee73a5bfe89 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
@@ -17,6 +17,7 @@
import org.chromium.chrome.browser.ntp.NewTabPageUma;
import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager;
import org.chromium.chrome.browser.ntp.UiConfig;
+import org.chromium.chrome.browser.ntp.snippets.ContentSuggestionsCategory;
import org.chromium.chrome.browser.ntp.snippets.ContentSuggestionsCategoryStatus;
import org.chromium.chrome.browser.ntp.snippets.SnippetArticleListItem;
import org.chromium.chrome.browser.ntp.snippets.SnippetArticleViewHolder;
@@ -24,9 +25,14 @@
import org.chromium.chrome.browser.ntp.snippets.SnippetHeaderViewHolder;
import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge;
import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge.SnippetsObserver;
+import org.chromium.chrome.browser.ntp.snippets.SuggestionsCategory;
+import org.chromium.chrome.browser.ntp.snippets.SuggestionsCategoryStatus;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
/**
* A class that handles merging above the fold elements and below the fold cards into an adapter
@@ -39,17 +45,17 @@
private final NewTabPageManager mNewTabPageManager;
private final NewTabPageLayout mNewTabPageLayout;
- private final AboveTheFoldListItem mAboveTheFold;
- private final SnippetHeaderListItem mHeader;
+ private final SnippetsBridge mSnippetsBridge;
private final UiConfig mUiConfig;
- private StatusListItem mStatusCard;
- private final SpacingListItem mBottomSpacer;
- private final List<NewTabPageListItem> mItems;
private final ItemTouchCallbacks mItemTouchCallbacks;
private NewTabPageRecyclerView mRecyclerView;
- private int mProviderStatus;
- private SnippetsBridge mSnippetsBridge;
+ private final List<NewTabPageItemGroup> mGroups;
+ private final AboveTheFoldListItem mAboveTheFold;
+ private final SpacingListItem mBottomSpacer;
+
+ // Maps suggestion categories to sections, with stable iteration ordering.
+ private final Map<Integer, SuggestionsSection> mSections;
private class ItemTouchCallbacks extends ItemTouchHelper.Callback {
@Override
@@ -57,7 +63,6 @@ public void onSwiped(ViewHolder viewHolder, int direction) {
mRecyclerView.onItemDismissStarted(viewHolder.itemView);
NewTabPageAdapter.this.dismissItem(viewHolder);
- addStatusCardIfNecessary();
}
@Override
@@ -113,17 +118,20 @@ public NewTabPageAdapter(NewTabPageManager manager, NewTabPageLayout newTabPageL
SnippetsBridge snippetsBridge, UiConfig uiConfig) {
mNewTabPageManager = manager;
mNewTabPageLayout = newTabPageLayout;
- mAboveTheFold = new AboveTheFoldListItem();
- mHeader = new SnippetHeaderListItem();
- mBottomSpacer = new SpacingListItem();
- mItemTouchCallbacks = new ItemTouchCallbacks();
- mItems = new ArrayList<>();
- mProviderStatus = ContentSuggestionsCategoryStatus.INITIALIZING;
mSnippetsBridge = snippetsBridge;
mUiConfig = uiConfig;
- mStatusCard = StatusListItem.create(snippetsBridge.getCategoryStatus(), this, manager);
+ mItemTouchCallbacks = new ItemTouchCallbacks();
- loadSnippets(new ArrayList<SnippetArticleListItem>());
+ mGroups = new ArrayList<>();
+ mAboveTheFold = new AboveTheFoldListItem();
+ mBottomSpacer = new SpacingListItem();
+ mSections = new TreeMap<>();
+
+ // TODO(mvanouwerkerk): Do not hard code ARTICLES. Maybe do not initialize an empty
+ // section in the constructor.
+ setSuggestions(ContentSuggestionsCategory.ARTICLES,
+ Collections.<SnippetArticleListItem>emptyList(),
+ ContentSuggestionsCategoryStatus.INITIALIZING);
mSnippetsBridge.setObserver(this);
}
@@ -133,54 +141,71 @@ public NewTabPageAdapter(NewTabPageManager manager, NewTabPageLayout newTabPageL
}
@Override
- public void onSnippetsReceived(List<SnippetArticleListItem> snippets) {
+ public void onSuggestionsReceived(
+ @SuggestionsCategory int category, List<SnippetArticleListItem> suggestions) {
// We never want to refresh the suggestions if we already have some content.
- if (hasSuggestions()) return;
+ if (mSections.containsKey(category) && mSections.get(category).hasSuggestions()) return;
- if (!SnippetsBridge.isCategoryStatusInitOrAvailable(mProviderStatus)) {
+ // The status may have changed while the suggestions were loading, perhaps they should not
+ // be displayed any more.
+ if (!SnippetsBridge.isCategoryStatusInitOrAvailable(
+ mSnippetsBridge.getCategoryStatus(category))) {
return;
}
- Log.d(TAG, "Received %d new snippets.", snippets.size());
+ Log.d(TAG, "Received %d new suggestions for category %d.", suggestions.size(), category);
- // At first, there might be no snippets available, we wait until they have been fetched.
- if (snippets.isEmpty()) return;
+ // At first, there might be no suggestions available, we wait until they have been fetched.
+ if (suggestions.isEmpty()) return;
- loadSnippets(snippets);
+ setSuggestions(category, suggestions, mSnippetsBridge.getCategoryStatus(category));
NewTabPageUma.recordSnippetAction(NewTabPageUma.SNIPPETS_ACTION_SHOWN);
}
@Override
- public void onCategoryStatusChanged(int categoryStatus) {
- // Observers should not be registered for that state
- assert categoryStatus
- != ContentSuggestionsCategoryStatus.ALL_SUGGESTIONS_EXPLICITLY_DISABLED;
-
- mProviderStatus = categoryStatus;
- mStatusCard = StatusListItem.create(mProviderStatus, this, mNewTabPageManager);
-
- // We had suggestions but we just got notified about the provider being enabled. Nothing to
- // do then.
- if (SnippetsBridge.isCategoryStatusAvailable(mProviderStatus) && hasSuggestions()) return;
-
- if (hasSuggestions()) {
- // We have suggestions, this implies that the service was previously enabled and just
- // transitioned to a disabled state. Clear them.
- loadSnippets(new ArrayList<SnippetArticleListItem>());
+ public void onCategoryStatusChanged(
+ @SuggestionsCategory int category, @SuggestionsCategoryStatus int status) {
+ // Observers should not be registered for this state.
+ assert status != ContentSuggestionsCategoryStatus.ALL_SUGGESTIONS_EXPLICITLY_DISABLED;
+
+ // If there is no section for this category there is nothing to do.
+ if (!mSections.containsKey(category)) return;
+
+ SuggestionsSection section = mSections.get(category);
+
+ // The section already has suggestions but we just got notified about the provider being
+ // enabled. Nothing to do.
+ if (SnippetsBridge.isCategoryStatusAvailable(status) && section.hasSuggestions()) return;
+
+ if (section.hasSuggestions()) {
+ // The section has suggestions, this implies that the service was previously enabled
+ // and just transitioned to a disabled state. Clear it.
+ setSuggestions(category, Collections.<SnippetArticleListItem>emptyList(), status);
} else {
- // If there are no suggestions there is an old status card that must be replaced.
- int firstCardPosition = getFirstCardPosition();
- mItems.set(firstCardPosition, mStatusCard);
- // Update both the status card and the spacer after it.
- notifyItemRangeChanged(firstCardPosition, 2);
+ // If the section has no suggestions, it has an old status card that must be replaced.
+ section.setSuggestions(Collections.<SnippetArticleListItem>emptyList(), status, this,
+ mNewTabPageManager);
+
+ // Update both the status card, and the bottom spacer, which may get a different height
+ // if the status card height changed.
+ int statusItemPosition = getGroupPositionOffset(section);
+ List<NewTabPageListItem> sectionItems = section.getItems();
+ for (int i = 0; i < sectionItems.size(); i++) {
+ if (sectionItems.get(i) instanceof StatusListItem) {
+ statusItemPosition += i;
+ break;
+ }
+ }
+ notifyItemChanged(statusItemPosition);
+ notifyItemChanged(getBottomSpacerPosition());
}
}
@Override
@NewTabPageListItem.ViewType
public int getItemViewType(int position) {
- return mItems.get(position).getType();
+ return getItems().get(position).getType();
}
@Override
@@ -213,20 +238,24 @@ public NewTabPageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
@Override
public void onBindViewHolder(NewTabPageViewHolder holder, final int position) {
- holder.onBindViewHolder(mItems.get(position));
+ holder.onBindViewHolder(getItems().get(position));
}
@Override
public int getItemCount() {
- return mItems.size();
+ return getItems().size();
}
- public int getHeaderPosition() {
- return mItems.indexOf(mHeader);
+ public int getFirstHeaderPosition() {
+ List<NewTabPageListItem> items = getItems();
+ for (int i = 0; i < items.size(); i++) {
+ if (items.get(i) instanceof SnippetHeaderListItem) return i;
+ }
+ return RecyclerView.NO_POSITION;
}
public int getFirstCardPosition() {
- return getHeaderPosition() + 1;
+ return getFirstHeaderPosition() + 1;
}
public int getLastCardPosition() {
@@ -234,7 +263,7 @@ public int getLastCardPosition() {
}
public int getBottomSpacerPosition() {
- return mItems.indexOf(mBottomSpacer);
+ return getItems().indexOf(mBottomSpacer);
}
/** Start a request for new snippets. */
@@ -242,36 +271,43 @@ public void reloadSnippets() {
SnippetsBridge.fetchSnippets(/*forceRequest=*/true);
}
- private void loadSnippets(List<SnippetArticleListItem> snippets) {
- // Copy thumbnails over
- for (SnippetArticleListItem snippet : snippets) {
- int existingSnippetIdx = mItems.indexOf(snippet);
- if (existingSnippetIdx == -1) continue;
-
- snippet.setThumbnailBitmap(
- ((SnippetArticleListItem) mItems.get(existingSnippetIdx)).getThumbnailBitmap());
- }
-
- boolean hasContentToShow = !snippets.isEmpty();
+ private void setSuggestions(@SuggestionsCategory int category,
+ List<SnippetArticleListItem> suggestions, @SuggestionsCategoryStatus int status) {
+ copyThumbnails(category, suggestions);
- // TODO(mvanouwerkerk): Make it so that the header does not need to be manipulated
- // separately from the cards to which it belongs - crbug.com/616090.
- mHeader.setVisible(hasContentToShow);
+ mGroups.clear();
+ mGroups.add(mAboveTheFold);
- mItems.clear();
- mItems.add(mAboveTheFold);
- mItems.add(mHeader);
- if (hasContentToShow) {
- mItems.addAll(snippets);
+ if (!mSections.containsKey(category)) {
+ mSections.put(category,
+ new SuggestionsSection(suggestions, status, this, mNewTabPageManager));
} else {
- mItems.add(mStatusCard);
+ mSections.get(category).setSuggestions(suggestions, status, this, mNewTabPageManager);
}
- mItems.add(mBottomSpacer);
+ for (@SuggestionsCategory Integer key : mSections.keySet()) {
+ mGroups.add(mSections.get(key));
+ }
+
+ mGroups.add(mBottomSpacer);
notifyDataSetChanged();
}
+ private void copyThumbnails(
+ @SuggestionsCategory int category, List<SnippetArticleListItem> suggestions) {
+ if (!mSections.containsKey(category)) return;
+
+ List<NewTabPageListItem> items = mSections.get(category).getItems();
+ for (SnippetArticleListItem suggestion : suggestions) {
+ int index = items.indexOf(suggestion);
+ if (index == -1) continue;
+
+ suggestion.setThumbnailBitmap(
+ ((SnippetArticleListItem) items.get(index)).getThumbnailBitmap());
+ }
+ }
+
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
@@ -290,9 +326,9 @@ private void dismissItem(ViewHolder itemViewHolder) {
assert itemViewHolder.getItemViewType() == NewTabPageListItem.VIEW_TYPE_SNIPPET;
int position = itemViewHolder.getAdapterPosition();
- SnippetArticleListItem dismissedSnippet = (SnippetArticleListItem) mItems.get(position);
+ SnippetArticleListItem suggestion = (SnippetArticleListItem) getItems().get(position);
- mSnippetsBridge.getSnippedVisited(dismissedSnippet, new Callback<Boolean>() {
+ mSnippetsBridge.getSnippedVisited(suggestion, new Callback<Boolean>() {
@Override
public void onResult(Boolean result) {
NewTabPageUma.recordSnippetAction(result
@@ -301,30 +337,47 @@ public void onResult(Boolean result) {
}
});
- mSnippetsBridge.discardSnippet(dismissedSnippet);
- mItems.remove(position);
- notifyItemRemoved(position);
- }
+ mSnippetsBridge.discardSnippet(suggestion);
+ SuggestionsSection section = (SuggestionsSection) getGroup(position);
+ section.dismissSuggestion(suggestion);
- private void addStatusCardIfNecessary() {
- if (!hasSuggestions() && !mItems.contains(mStatusCard)) {
- mItems.add(getFirstCardPosition(), mStatusCard);
-
- // We also want to refresh the header and the bottom padding.
- mHeader.setVisible(false);
+ if (section.hasSuggestions()) {
+ // If one of many suggestions was dismissed, it's a simple item removal, which can be
+ // animated smoothly by the RecyclerView.
+ notifyItemRemoved(position);
+ } else {
+ // If the last suggestion was dismissed, multiple items will have changed, so mark
+ // everything as changed.
notifyDataSetChanged();
}
}
- /** Returns whether we have some suggested content to display. */
- private boolean hasSuggestions() {
- for (NewTabPageListItem item : mItems) {
- if (item instanceof SnippetArticleListItem) return true;
+ /**
+ * Returns an unmodifiable list containing all items in the adapter.
+ */
+ List<NewTabPageListItem> getItems() {
+ List<NewTabPageListItem> items = new ArrayList<>();
+ for (NewTabPageItemGroup group : mGroups) {
+ items.addAll(group.getItems());
}
- return false;
+ return Collections.unmodifiableList(items);
}
- List<NewTabPageListItem> getItemsForTesting() {
- return mItems;
+ private NewTabPageItemGroup getGroup(int itemPosition) {
+ int itemsSkipped = 0;
+ for (NewTabPageItemGroup group : mGroups) {
+ List<NewTabPageListItem> items = group.getItems();
+ if (itemPosition < itemsSkipped + items.size()) return group;
+ }
+ return null;
+ }
+
+ private int getGroupPositionOffset(NewTabPageItemGroup group) {
+ int positionOffset = 0;
+ for (NewTabPageItemGroup candidateGroup : mGroups) {
+ if (candidateGroup == group) return positionOffset;
+ positionOffset += candidateGroup.getItems().size();
+ }
+ return RecyclerView.NO_POSITION;
}
}

Powered by Google App Engine
This is Rietveld 408576698