package tv.emby.embyatv.presentation; /** * Created by Eric on 8/17/2015. */ import android.support.v17.leanback.widget.FocusHighlight; import android.support.v17.leanback.widget.FocusHighlightHelper; import android.support.v17.leanback.widget.HorizontalGridView; import android.support.v17.leanback.widget.ItemBridgeAdapter; import android.support.v17.leanback.widget.ObjectAdapter; import android.support.v17.leanback.widget.OnChildSelectedListener; import android.support.v17.leanback.widget.OnItemViewClickedListener; import android.support.v17.leanback.widget.OnItemViewSelectedListener; import android.support.v17.leanback.widget.Presenter; import android.support.v17.leanback.widget.ShadowOverlayContainer; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import tv.emby.embyatv.R; import tv.emby.embyatv.TvApp; /** * A presenter that renders objects in a horizontal grid. * */ public class HorizontalGridPresenter extends Presenter { private static final String TAG = "GridPresenter"; private static final boolean DEBUG = false; public static class ViewHolder extends Presenter.ViewHolder { final ItemBridgeAdapter mItemBridgeAdapter = new ItemBridgeAdapter(); final HorizontalGridView mGridView; boolean mInitialized; public ViewHolder(HorizontalGridView view) { super(view); mGridView = view; } public HorizontalGridView getGridView() { return mGridView; } } private int mNumRows = -1; private int mZoomFactor; private boolean mShadowEnabled = true; private OnItemViewSelectedListener mOnItemViewSelectedListener; private OnItemViewClickedListener mOnItemViewClickedListener; private boolean mRoundedCornersEnabled = true; public HorizontalGridPresenter() { this(FocusHighlight.ZOOM_FACTOR_LARGE); } public HorizontalGridPresenter(int zoomFactor) { mZoomFactor = zoomFactor; } public void setPosition(int ndx) { if (mViewHolder != null) mViewHolder.getGridView().setSelectedPosition(ndx); } /** * Sets the number of rows in the grid. */ public void setNumberOfRows(int numRows) { if (numRows < 0) { throw new IllegalArgumentException("Invalid number of rows"); } if (mNumRows != numRows) { mNumRows = numRows; } } /** * Returns the number of rows in the grid. */ public int getNumberOfRows() { return mNumRows; } /** * Enable or disable child shadow. * This is not only for enable/disable default shadow implementation but also subclass must * respect this flag. */ public final void setShadowEnabled(boolean enabled) { mShadowEnabled = enabled; } /** * Returns true if child shadow is enabled. * This is not only for enable/disable default shadow implementation but also subclass must * respect this flag. */ public final boolean getShadowEnabled() { return mShadowEnabled; } /** * Returns true if opticalBounds is supported (SDK >= 18) so that default shadow * is applied to each individual child of {@link HorizontalGridView}. * Subclass may return false to disable. */ public boolean isUsingDefaultShadow() { return ShadowOverlayContainer.supportsShadow(); } /** * Enables or disabled rounded corners on children of this row. * Supported on Android SDK >= L. */ public final void enableChildRoundedCorners(boolean enable) { mRoundedCornersEnabled = enable; } /** * Returns true if rounded corners are enabled for children of this row. */ public final boolean areChildRoundedCornersEnabled() { return mRoundedCornersEnabled; } /** * Returns true if SDK >= L, where Z shadow is enabled so that Z order is enabled * on each child of vertical grid. If subclass returns false in isUsingDefaultShadow() * and does not use Z-shadow on SDK >= L, it should override isUsingZOrder() return false. */ public boolean isUsingZOrder() { return false; } final boolean needsDefaultShadow() { return isUsingDefaultShadow() && getShadowEnabled(); } @Override public final ViewHolder onCreateViewHolder(ViewGroup parent) { ViewHolder vh = createGridViewHolder(parent); vh.mInitialized = false; initializeGridViewHolder(vh); if (!vh.mInitialized) { throw new RuntimeException("super.initializeGridViewHolder() must be called"); } return vh; } /** * Subclass may override this to inflate a different layout. */ protected ViewHolder createGridViewHolder(ViewGroup parent) { View root = LayoutInflater.from(parent.getContext()).inflate( R.layout.horizontal_grid, parent, false); return new ViewHolder((HorizontalGridView) root.findViewById(R.id.horizontal_grid)); } private ItemBridgeAdapter.Wrapper mWrapper = new ItemBridgeAdapter.Wrapper() { @Override public View createWrapper(View root) { ShadowOverlayContainer wrapper = new ShadowOverlayContainer(root.getContext()); wrapper.setLayoutParams( new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); wrapper.initialize(needsDefaultShadow(), true, areChildRoundedCornersEnabled()); return wrapper; } @Override public void wrap(View wrapper, View wrapped) { ((ShadowOverlayContainer) wrapper).wrap(wrapped); } }; /** * Called after a {@link HorizontalGridPresenter.ViewHolder} is created. * Subclasses may override this method and start by calling * super.initializeGridViewHolder(ViewHolder). * * @param vh The ViewHolder to initialize for the vertical grid. */ protected void initializeGridViewHolder(ViewHolder vh) { if (mNumRows == -1) { throw new IllegalStateException("Number of rows must be set"); } if (DEBUG) Log.v(TAG, "mNumRows " + mNumRows); vh.getGridView().setNumRows(mNumRows); vh.getGridView().setFadingRightEdge(true); vh.getGridView().setFadingRightEdgeLength(100); vh.getGridView().setItemMargin(10); vh.mInitialized = true; vh.mItemBridgeAdapter.setWrapper(mWrapper); if (needsDefaultShadow() || areChildRoundedCornersEnabled()) { ShadowOverlayContainer.prepareParentForShadow(vh.getGridView()); ((ViewGroup) vh.view).setClipChildren(false); } vh.getGridView().setFocusDrawingOrderEnabled(!isUsingZOrder()); FocusHighlightHelper.setupBrowseItemFocusHighlight(vh.mItemBridgeAdapter, mZoomFactor, true); final ViewHolder gridViewHolder = vh; vh.getGridView().setOnChildSelectedListener(new OnChildSelectedListener() { @Override public void onChildSelected(ViewGroup parent, View view, int position, long id) { selectChildView(gridViewHolder, view); } }); vh.mItemBridgeAdapter.setAdapterListener(new ItemBridgeAdapter.AdapterListener() { @Override public void onBind(final ItemBridgeAdapter.ViewHolder itemViewHolder) { // Only when having an OnItemClickListner, we attach the OnClickListener. if (getOnItemViewClickedListener() != null) { final View itemView = itemViewHolder.getViewHolder().view; itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (getOnItemViewClickedListener() != null) { // Row is always null getOnItemViewClickedListener().onItemClicked( itemViewHolder.getViewHolder(), itemViewHolder.getItem(), null, null); } } }); } } @Override public void onUnbind(ItemBridgeAdapter.ViewHolder viewHolder) { if (getOnItemViewClickedListener() != null) { viewHolder.getViewHolder().view.setOnClickListener(null); } } @Override public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder viewHolder) { viewHolder.itemView.setActivated(true); } }); } ViewHolder mViewHolder; @Override public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) { if (DEBUG) Log.v(TAG, "onBindViewHolder " + item); mViewHolder = (ViewHolder) viewHolder; mViewHolder.mItemBridgeAdapter.setAdapter((ObjectAdapter) item); mViewHolder.getGridView().setAdapter(mViewHolder.mItemBridgeAdapter); } @Override public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) { if (DEBUG) Log.v(TAG, "onUnbindViewHolder"); ViewHolder vh = (ViewHolder) viewHolder; vh.mItemBridgeAdapter.setAdapter(null); vh.getGridView().setAdapter(null); } /** * Sets the item selected listener. * Since this is a grid the row parameter is always null. */ public final void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) { mOnItemViewSelectedListener = listener; } /** * Returns the item selected listener. */ public final OnItemViewSelectedListener getOnItemViewSelectedListener() { return mOnItemViewSelectedListener; } /** * Sets the item clicked listener. * OnItemViewClickedListener will override {@link View.OnClickListener} that * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}. * So in general, developer should choose one of the listeners but not both. */ public final void setOnItemViewClickedListener(OnItemViewClickedListener listener) { mOnItemViewClickedListener = listener; } /** * Returns the item clicked listener. */ public final OnItemViewClickedListener getOnItemViewClickedListener() { return mOnItemViewClickedListener; } private void selectChildView(ViewHolder vh, View view) { if (getOnItemViewSelectedListener() != null) { ItemBridgeAdapter.ViewHolder ibh = (view == null) ? null : (ItemBridgeAdapter.ViewHolder) vh.getGridView().getChildViewHolder(view); if (ibh == null) { getOnItemViewSelectedListener().onItemSelected(null, null, null, null); } else { getOnItemViewSelectedListener().onItemSelected(ibh.getViewHolder(), ibh.getItem(), null, null); } } } }