blob: df292ff84c6475cec22da244c0ac2f7f1e98f2cc [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.ntp.cards;
import android.content.res.Resources;
import android.support.annotation.CallSuper;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.ViewGroup;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.native_page.ContextMenuManager;
import org.chromium.chrome.browser.native_page.ContextMenuManager.ContextMenuItemId;
import org.chromium.chrome.browser.suggestions.SuggestionsRecyclerView;
import org.chromium.chrome.browser.widget.displaystyle.HorizontalDisplayStyle;
import org.chromium.chrome.browser.widget.displaystyle.MarginResizer;
import org.chromium.chrome.browser.widget.displaystyle.UiConfig;
/**
* Holder for a generic card.
*
* Specific behaviors added to the cards:
*
* - Tap events will be routed through {@link #onCardTapped()} for subclasses to override.
*
* - Cards will get some lateral margins when the viewport is sufficiently wide.
* (see {@link HorizontalDisplayStyle#WIDE})
*
* Note: If a subclass overrides {@link #onBindViewHolder()}, it should call the
* parent implementation to reset the private state when a card is recycled.
*/
public abstract class CardViewHolder
extends NewTabPageViewHolder implements ContextMenuManager.Delegate {
protected final SuggestionsRecyclerView mRecyclerView;
protected final UiConfig mUiConfig;
private final MarginResizer mMarginResizer;
/**
* @param layoutId resource id of the layout to inflate and to use as card.
* @param recyclerView ViewGroup that will contain the newly created view.
* @param uiConfig The NTP UI configuration object used to adjust the card UI.
* @param contextMenuManager The manager responsible for the context menu.
*/
public CardViewHolder(int layoutId, final SuggestionsRecyclerView recyclerView,
UiConfig uiConfig, final ContextMenuManager contextMenuManager) {
super(inflateView(layoutId, recyclerView));
Resources resources = recyclerView.getResources();
mRecyclerView = recyclerView;
itemView.setOnClickListener(v -> onCardTapped());
itemView.setOnCreateContextMenuListener(
(menu, view, menuInfo)
-> contextMenuManager.createContextMenu(
menu, itemView, CardViewHolder.this));
mUiConfig = uiConfig;
final int defaultLateralMargin =
resources.getDimensionPixelSize(R.dimen.content_suggestions_card_modern_margin);
int wideLateralMargin =
resources.getDimensionPixelSize(R.dimen.ntp_wide_card_lateral_margins);
mMarginResizer =
new MarginResizer(itemView, uiConfig, defaultLateralMargin, wideLateralMargin);
}
@Override
public boolean isItemSupported(@ContextMenuItemId int menuItemId) {
return menuItemId == ContextMenuManager.ContextMenuItemId.REMOVE && isDismissable();
}
@Override
public void removeItem() {
getRecyclerView().dismissItemWithAnimation(this);
}
@Override
public void openItem(int windowDisposition) {
throw new UnsupportedOperationException();
}
@Override
public String getUrl() {
return null;
}
@Override
public boolean isDismissable() {
int position = getAdapterPosition();
if (position == RecyclerView.NO_POSITION) return false;
return !mRecyclerView.getNewTabPageAdapter().getItemDismissalGroup(position).isEmpty();
}
@Override
public void onContextMenuCreated() {}
/**
* Called when the NTP cards adapter is requested to update the currently visible ViewHolder
* with data.
*/
@CallSuper
public void onBindViewHolder() {
// Reset the transparency and translation in case a dismissed card is being recycled.
itemView.setAlpha(1f);
itemView.setTranslationX(0f);
itemView.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View view) {}
@Override
public void onViewDetachedFromWindow(View view) {
// In some cases a view can be removed while a user is interacting with it, without
// calling ItemTouchHelper.Callback#clearView(), which we rely on for bottomSpacer
// calculations. So we call this explicitly here instead.
// See https://crbug.com/664466, b/32900699
mRecyclerView.onItemDismissFinished(mRecyclerView.findContainingViewHolder(view));
itemView.removeOnAttachStateChangeListener(this);
}
});
// Make sure we use the right background.
updateLayoutParams();
mMarginResizer.attach();
}
@Override
public void recycle() {
mMarginResizer.detach();
super.recycle();
}
/**
* Override this to react when the card is tapped. This method will not be called if the card is
* currently peeking.
*/
protected void onCardTapped() {}
private static View inflateView(int resourceId, ViewGroup parent) {
return LayoutInflater.from(parent.getContext()).inflate(resourceId, parent, false);
}
public static boolean isCard(@ItemViewType int type) {
switch (type) {
case ItemViewType.SNIPPET:
case ItemViewType.STATUS:
case ItemViewType.ACTION:
case ItemViewType.PROMO:
return true;
case ItemViewType.ABOVE_THE_FOLD:
case ItemViewType.HEADER:
case ItemViewType.PROGRESS:
case ItemViewType.FOOTER:
case ItemViewType.ALL_DISMISSED:
return false;
}
assert false;
return false;
}
public SuggestionsRecyclerView getRecyclerView() {
return mRecyclerView;
}
}