/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package android.support.v17.leanback.widget; import android.content.Context; import android.support.v17.leanback.R; import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; /** * A presenter that assumes a LinearLayout container for a series * of control buttons backed by objects of type {@link Action}. * * Different layouts may be passed to the presenter constructor. * The layout must contain a view with id control_bar. */ class ControlBarPresenter extends Presenter { private static final int MAX_CONTROLS = 7; /** * The data type expected by this presenter. */ static class BoundData { /** * Adapter containing objects of type {@link Action}. */ ObjectAdapter adapter; /** * The presenter to be used for the adapter objects. */ Presenter presenter; } /** * Listener for control selected events. */ interface OnControlSelectedListener { void onControlSelected(Presenter.ViewHolder controlViewHolder, Object item, BoundData data); } /** * Listener for control clicked events. */ interface OnControlClickedListener { void onControlClicked(Presenter.ViewHolder controlViewHolder, Object item, BoundData data); } class ViewHolder extends Presenter.ViewHolder { ObjectAdapter mAdapter; BoundData mData; Presenter mPresenter; ControlBar mControlBar; View mControlsContainer; SparseArray mViewHolders = new SparseArray(); ObjectAdapter.DataObserver mDataObserver; /** * Constructor for the ViewHolder. */ ViewHolder(View rootView) { super(rootView); mControlsContainer = rootView.findViewById(R.id.controls_container); mControlBar = (ControlBar) rootView.findViewById(R.id.control_bar); if (mControlBar == null) { throw new IllegalStateException("Couldn't find control_bar"); } mControlBar.setOnChildFocusedListener(new ControlBar.OnChildFocusedListener() { @Override public void onChildFocusedListener(View child, View focused) { if (mOnControlSelectedListener == null) { return; } for (int position = 0; position < mViewHolders.size(); position++) { if (mViewHolders.get(position).view == child) { mOnControlSelectedListener.onControlSelected( mViewHolders.get(position), getDisplayedAdapter().get(position), mData); break; } } } }); mDataObserver = new ObjectAdapter.DataObserver() { @Override public void onChanged() { if (mAdapter == getDisplayedAdapter()) { showControls(mPresenter); } } @Override public void onItemRangeChanged(int positionStart, int itemCount) { if (mAdapter == getDisplayedAdapter()) { for (int i = 0; i < itemCount; i++) { bindControlToAction(positionStart + i, mPresenter); } } } }; } int getChildMarginFromCenter(Context context, int numControls) { // Includes margin between icons plus two times half the icon width. return getChildMarginDefault(context) + getControlIconWidth(context); } void showControls(Presenter presenter) { ObjectAdapter adapter = getDisplayedAdapter(); int adapterSize = adapter == null ? 0 : adapter.size(); // Shrink the number of attached views View focusedView = mControlBar.getFocusedChild(); if (focusedView != null && adapterSize > 0 && mControlBar.indexOfChild(focusedView) >= adapterSize) { mControlBar.getChildAt(adapter.size() - 1).requestFocus(); } for (int i = mControlBar.getChildCount() - 1; i >= adapterSize; i--) { mControlBar.removeViewAt(i); } for (int position = 0; position < adapterSize && position < MAX_CONTROLS; position++) { bindControlToAction(position, adapter, presenter); } mControlBar.setChildMarginFromCenter( getChildMarginFromCenter(mControlBar.getContext(), adapterSize)); } void bindControlToAction(int position, Presenter presenter) { bindControlToAction(position, getDisplayedAdapter(), presenter); } private void bindControlToAction(final int position, ObjectAdapter adapter, Presenter presenter) { Presenter.ViewHolder vh = mViewHolders.get(position); Object item = adapter.get(position); if (vh == null) { vh = presenter.onCreateViewHolder(mControlBar); mViewHolders.put(position, vh); final Presenter.ViewHolder itemViewHolder = vh; presenter.setOnClickListener(vh, new View.OnClickListener() { @Override public void onClick(View v) { Object item = getDisplayedAdapter().get(position); if (mOnControlClickedListener != null) { mOnControlClickedListener.onControlClicked(itemViewHolder, item, mData); } } }); } if (vh.view.getParent() == null) { mControlBar.addView(vh.view); } presenter.onBindViewHolder(vh, item); } /** * Returns the adapter currently bound to the displayed controls. * May be overridden in a subclass. */ ObjectAdapter getDisplayedAdapter() { return mAdapter; } } private OnControlClickedListener mOnControlClickedListener; private OnControlSelectedListener mOnControlSelectedListener; private int mLayoutResourceId; private static int sChildMarginDefault; private static int sControlIconWidth; /** * Constructor for a ControlBarPresenter. * * @param layoutResourceId The resource id of the layout for this presenter. */ public ControlBarPresenter(int layoutResourceId) { mLayoutResourceId = layoutResourceId; } /** * Returns the layout resource id. */ public int getLayoutResourceId() { return mLayoutResourceId; } /** * Sets the listener for control clicked events. */ public void setOnControlClickedListener(OnControlClickedListener listener) { mOnControlClickedListener = listener; } /** * Returns the listener for control clicked events. */ public OnControlClickedListener getOnItemViewClickedListener() { return mOnControlClickedListener; } /** * Sets the listener for control selection. */ public void setOnControlSelectedListener(OnControlSelectedListener listener) { mOnControlSelectedListener = listener; } /** * Returns the listener for control selection. */ public OnControlSelectedListener getOnItemControlListener() { return mOnControlSelectedListener; } public void setBackgroundColor(ViewHolder vh, int color) { vh.mControlsContainer.setBackgroundColor(color); } @Override public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) { View v = LayoutInflater.from(parent.getContext()) .inflate(getLayoutResourceId(), parent, false); return new ViewHolder(v); } @Override public void onBindViewHolder(Presenter.ViewHolder holder, Object item) { ViewHolder vh = (ViewHolder) holder; BoundData data = (BoundData) item; if (vh.mAdapter != data.adapter) { vh.mAdapter = data.adapter; if (vh.mAdapter != null) { vh.mAdapter.registerObserver(vh.mDataObserver); } } vh.mPresenter = data.presenter; vh.mData = data; vh.showControls(vh.mPresenter); } @Override public void onUnbindViewHolder(Presenter.ViewHolder holder) { ViewHolder vh = (ViewHolder) holder; if (vh.mAdapter != null) { vh.mAdapter.unregisterObserver(vh.mDataObserver); vh.mAdapter = null; } vh.mData = null; } int getChildMarginDefault(Context context) { if (sChildMarginDefault == 0) { sChildMarginDefault = context.getResources().getDimensionPixelSize( R.dimen.lb_playback_controls_child_margin_default); } return sChildMarginDefault; } int getControlIconWidth(Context context) { if (sControlIconWidth == 0) { sControlIconWidth = context.getResources().getDimensionPixelSize( R.dimen.lb_control_icon_width); } return sControlIconWidth; } }