/*
* 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<Presenter.ViewHolder> mViewHolders =
new SparseArray<Presenter.ViewHolder>();
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;
}
}