Chromium Code Reviews| 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 a9db2057130f2c4c1b736a4af66f4858e6997260..20a49020398d2575a6d2d4a29033509a793094d3 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 |
| @@ -13,6 +13,7 @@ |
| import android.view.ViewGroup; |
| import org.chromium.base.Callback; |
| +import org.chromium.base.ContextUtils; |
| import org.chromium.base.Log; |
| import org.chromium.base.VisibleForTesting; |
| import org.chromium.chrome.R; |
| @@ -28,6 +29,8 @@ |
| import org.chromium.chrome.browser.ntp.snippets.SnippetArticleViewHolder; |
| import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge; |
| import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource; |
| +import org.chromium.chrome.browser.signin.SigninManager; |
| +import org.chromium.chrome.browser.signin.SigninManager.SignInStateObserver; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| @@ -57,6 +60,7 @@ |
| */ |
| private final List<ItemGroup> mGroups = new ArrayList<>(); |
| private final AboveTheFoldItem mAboveTheFold = new AboveTheFoldItem(); |
| + private final SigninPromoItem mSigninPromo = new SigninPromoItem(); |
| private final Footer mFooter = new Footer(); |
| private final SpacingItem mBottomSpacer = new SpacingItem(); |
| @@ -113,13 +117,7 @@ public void onChildDraw(Canvas c, RecyclerView recyclerView, ViewHolder viewHold |
| mRecyclerView.updateViewStateForDismiss(dX, viewHolder); |
| // If there is another item that should be animated at the same time, do the same to it. |
| - int swipePos = viewHolder.getAdapterPosition(); |
| - SuggestionsSection section = (SuggestionsSection) getGroup(swipePos); |
| - int siblingPosDelta = section.getDismissSiblingPosDelta(getItems().get(swipePos)); |
| - if (siblingPosDelta == 0) return; |
| - |
| - ViewHolder siblingViewHolder = |
| - mRecyclerView.findViewHolderForAdapterPosition(siblingPosDelta + swipePos); |
| + ViewHolder siblingViewHolder = getDismissSibling(viewHolder); |
|
dgn
2016/09/30 11:35:31
Added check that the view holder is in a suggestio
|
| if (siblingViewHolder != null) { |
| mRecyclerView.updateViewStateForDismiss(dX, siblingViewHolder); |
| } |
| @@ -137,13 +135,13 @@ public void onChildDraw(Canvas c, RecyclerView recyclerView, ViewHolder viewHold |
| public static NewTabPageAdapter create( |
| NewTabPageManager manager, View aboveTheFoldView, UiConfig uiConfig) { |
| NewTabPageAdapter adapter = new NewTabPageAdapter(manager, aboveTheFoldView, uiConfig); |
| - adapter.initializeSections(); |
| + adapter.finishInitialization(); |
| return adapter; |
| } |
| /** |
| * Constructor for {@link NewTabPageAdapter}. The object is not completely ready to be used |
| - * until {@link #initializeSections()} is called. Usage reserved for testing, prefer calling |
| + * until {@link #finishInitialization()} is called. Usage reserved for testing, prefer calling |
| * {@link NewTabPageAdapter#create(NewTabPageManager, View, UiConfig)} in production code. |
| */ |
| @VisibleForTesting |
| @@ -158,7 +156,31 @@ public static NewTabPageAdapter create( |
| * a section has not been registered at this point will be ignored. |
| */ |
| @VisibleForTesting |
| - void initializeSections() { |
| + void finishInitialization() { |
| + mSigninPromo.setObserver(this); |
| + resetSections(); |
| + mNewTabPageManager.getSuggestionsSource().setObserver(this); |
| + |
| + // TODO(dgn): unregister the observer when the adapter is destroyed. |
| + SigninManager.get(ContextUtils.getApplicationContext()) |
| + .addSignInStateObserver(new SignInStateObserver() { |
| + @Override |
| + public void onSignedIn() { |
| + mSigninPromo.hide(); |
| + resetSections(); |
| + } |
| + |
| + @Override |
| + public void onSignedOut() { |
| + mSigninPromo.maybeShow(); |
| + } |
| + }); |
| + } |
| + |
| + /** Resets the sections, reloading the whole new tab page content. */ |
| + private void resetSections() { |
| + mSections.clear(); |
| + |
| SuggestionsSource suggestionsSource = mNewTabPageManager.getSuggestionsSource(); |
| int[] categories = suggestionsSource.getCategories(); |
| @@ -171,25 +193,37 @@ void initializeSections() { |
| || categoryStatus == CategoryStatus.CATEGORY_EXPLICITLY_DISABLED) |
| continue; |
| - List<SnippetArticle> suggestions = |
| - suggestionsSource.getSuggestionsForCategory(category); |
| - suggestionsPerCategory[i++] = suggestions.size(); |
| - |
| - // Create the new section. |
| - SuggestionsCategoryInfo info = suggestionsSource.getCategoryInfo(category); |
| - if (suggestions.isEmpty() && !info.showIfEmpty()) continue; |
| - mSections.put(category, new SuggestionsSection(category, info, this)); |
| - |
| - // Add the new suggestions. |
| - setSuggestions(category, suggestions, categoryStatus); |
| + suggestionsPerCategory[i++] = resetSection(category, categoryStatus); |
|
dgn
2016/09/30 11:35:31
Split out to be able to reset only the section tha
|
| } |
| mNewTabPageManager.trackSnippetsPageImpression(categories, suggestionsPerCategory); |
| - suggestionsSource.setObserver(this); |
| updateGroups(); |
| } |
| + private int resetSection(@CategoryInt int category, @CategoryStatusEnum int categoryStatus) { |
| + SuggestionsSource suggestionsSource = mNewTabPageManager.getSuggestionsSource(); |
| + List<SnippetArticle> suggestions = suggestionsSource.getSuggestionsForCategory(category); |
| + |
| + // Create the new section. |
| + SuggestionsCategoryInfo info = suggestionsSource.getCategoryInfo(category); |
| + if (suggestions.isEmpty() && !info.showIfEmpty()) { |
| + mSections.remove(category); |
| + return 0; |
| + } |
| + |
| + SuggestionsSection section = mSections.get(category); |
| + if (section == null) { |
| + section = new SuggestionsSection(category, info, this); |
| + mSections.put(category, section); |
| + } |
| + |
| + // Add the new suggestions. |
| + setSuggestions(category, suggestions, categoryStatus); |
| + |
| + return suggestions.size(); |
| + } |
| + |
| /** Returns callbacks to configure the interactions with the RecyclerView's items. */ |
| public ItemTouchHelper.Callback getItemTouchCallbacks() { |
| return mItemTouchCallbacks; |
| @@ -234,15 +268,24 @@ public void onCategoryStatusChanged(@CategoryInt int category, @CategoryStatusEn |
| // If there is no section for this category there is nothing to do. |
| if (!mSections.containsKey(category)) return; |
| - // The section provider has gone away. Keep open UIs as they are. |
| - if (status == CategoryStatus.NOT_PROVIDED) return; |
| + switch (status) { |
| + case CategoryStatus.NOT_PROVIDED: |
| + // The section provider has gone away. Keep open UIs as they are. |
| + return; |
| - if (status == CategoryStatus.CATEGORY_EXPLICITLY_DISABLED |
| - || status == CategoryStatus.LOADING_ERROR) { |
| - // Need to remove the entire section from the UI immediately. |
| - removeSection(mSections.get(category)); |
| - } else { |
| - mSections.get(category).setStatus(status); |
| + case CategoryStatus.CATEGORY_EXPLICITLY_DISABLED: |
| + case CategoryStatus.LOADING_ERROR: |
| + // Need to remove the entire section from the UI immediately. |
| + removeSection(mSections.get(category)); |
| + return; |
| + |
| + case CategoryStatus.SIGNED_OUT: |
| + resetSection(category, status); |
| + return; |
| + |
| + default: |
| + mSections.get(category).setStatus(status); |
| + return; |
| } |
| } |
| @@ -279,7 +322,7 @@ public NewTabPageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { |
| } |
| if (viewType == NewTabPageItem.VIEW_TYPE_STATUS) { |
| - return new StatusItem.ViewHolder(mRecyclerView, mUiConfig); |
| + return new StatusCardViewHolder(mRecyclerView, mUiConfig); |
| } |
| if (viewType == NewTabPageItem.VIEW_TYPE_PROGRESS) { |
| @@ -290,6 +333,10 @@ public NewTabPageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { |
| return new ActionItem.ViewHolder(mRecyclerView, mNewTabPageManager, mUiConfig); |
| } |
| + if (viewType == NewTabPageItem.VIEW_TYPE_PROMO) { |
| + return new SigninPromoItem.ViewHolder(mRecyclerView, mUiConfig); |
| + } |
| + |
| if (viewType == NewTabPageItem.VIEW_TYPE_FOOTER) { |
| return new Footer.ViewHolder(mRecyclerView, mNewTabPageManager); |
| } |
| @@ -369,6 +416,7 @@ private void updateGroups() { |
| mGroups.add(mAboveTheFold); |
| // TODO(treib,bauerb): Preserve the order of categories we got from getCategories. |
| mGroups.addAll(mSections.values()); |
| + mGroups.add(mSigninPromo); |
| if (!mSections.isEmpty()) { |
| mGroups.add(mFooter); |
| mGroups.add(mBottomSpacer); |
| @@ -396,6 +444,7 @@ private void removeSection(SuggestionsSection section) { |
| @Override |
| public void notifyGroupChanged(ItemGroup group, int itemCountBefore, int itemCountAfter) { |
| + if (mGroups.isEmpty()) return; // The sections have not been initialised yet. |
|
dgn
2016/09/30 11:35:31
SuggestionsSection#setSuggestions calls this befor
|
| int startPos = getGroupPositionOffset(group); |
| if (group instanceof SuggestionsSection) { |
| @@ -418,12 +467,14 @@ public void notifyGroupChanged(ItemGroup group, int itemCountBefore, int itemCou |
| @Override |
| public void notifyItemInserted(ItemGroup group, int itemPosition) { |
| + if (mGroups.isEmpty()) return; // The sections have not been initialised yet. |
| notifyItemInserted(getGroupPositionOffset(group) + itemPosition); |
| notifyItemChanged(getItems().size() - 1); // Refresh the spacer too. |
| } |
| @Override |
| public void notifyItemRemoved(ItemGroup group, int itemPosition) { |
| + if (mGroups.isEmpty()) return; // The sections have not been initialised yet. |
| notifyItemRemoved(getGroupPositionOffset(group) + itemPosition); |
| notifyItemChanged(getItems().size() - 1); // Refresh the spacer too. |
| } |
| @@ -442,17 +493,31 @@ public void onAttachedToRecyclerView(RecyclerView recyclerView) { |
| mRecyclerView = (NewTabPageRecyclerView) recyclerView; |
| } |
| + /** |
| + * Dismisses the item at the provided adapter position. Can also cause the dismissal of other |
| + * items or even entire sections. |
| + */ |
| public void dismissItem(int position) { |
| - NewTabPageItem item = getItems().get(position); |
| - if (item instanceof SnippetArticle) { |
| - dismissSuggestion(position); |
| - } else { |
| - // We assume that the item being dismissed is the status card (and/or the more button, |
| - // when it's displayed with the status card). In that case we just dismiss the section |
| - // and it will show up on new NTPs after the status has changed. |
| - ItemGroup group = getGroup(position); |
| - assert group instanceof SuggestionsSection; |
| - dismissSection((SuggestionsSection) group); |
| + int itemViewType = getItemViewType(position); |
| + |
| + // TODO(dgn): Polymorphism is supposed to allow to avoid that kind of stuff. |
| + switch (itemViewType) { |
| + case NewTabPageItem.VIEW_TYPE_STATUS: |
| + case NewTabPageItem.VIEW_TYPE_ACTION: |
| + dismissSection((SuggestionsSection) getGroup(position)); |
| + return; |
| + |
| + case NewTabPageItem.VIEW_TYPE_SNIPPET: |
| + dismissSuggestion(position); |
| + return; |
| + |
| + case NewTabPageItem.VIEW_TYPE_PROMO: |
| + dismissPromo(); |
| + return; |
| + |
| + default: |
| + Log.wtf(TAG, "Unsupported dismissal of item of type %d", itemViewType); |
| + return; |
| } |
| } |
| @@ -489,6 +554,11 @@ public void onResult(Boolean result) { |
| section.removeSuggestion(suggestion); |
| } |
| + private void dismissPromo() { |
| + // TODO(dgn): accessibility announcement. |
| + mSigninPromo.dismiss(); |
| + } |
| + |
| /** |
| * Returns an unmodifiable list containing all items in the adapter. |
| */ |
| @@ -500,6 +570,22 @@ public void onResult(Boolean result) { |
| return Collections.unmodifiableList(items); |
| } |
| + /** |
| + * Returns another view holder that should be dismissed as the same time as the provided one. |
| + */ |
| + private ViewHolder getDismissSibling(ViewHolder viewHolder) { |
| + int swipePos = viewHolder.getAdapterPosition(); |
| + ItemGroup group = getGroup(swipePos); |
| + |
| + if (!(group instanceof SuggestionsSection)) return null; |
| + |
| + SuggestionsSection section = (SuggestionsSection) group; |
| + int siblingPosDelta = section.getDismissSiblingPosDelta(getItems().get(swipePos)); |
| + if (siblingPosDelta == 0) return null; |
| + |
| + return mRecyclerView.findViewHolderForAdapterPosition(siblingPosDelta + swipePos); |
| + } |
| + |
| @VisibleForTesting |
| ItemGroup getGroup(int itemPosition) { |
| int itemsSkipped = 0; |