/*
* 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.graphics.drawable.Drawable;
import android.support.annotation.ColorInt;
import android.support.v17.leanback.R;
import android.support.v17.leanback.widget.ControlBarPresenter.OnControlClickedListener;
import android.support.v17.leanback.widget.ControlBarPresenter.OnControlSelectedListener;
import android.content.Context;
import android.graphics.Color;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.ImageView;
import android.widget.LinearLayout;
/**
* A PlaybackControlsRowPresenter renders a {@link PlaybackControlsRow} to display a
* series of playback control buttons. Typically this row will be the first row in a fragment
* such as the {@link android.support.v17.leanback.app.PlaybackOverlayFragment}.
*
* <p>The detailed description is rendered using a {@link Presenter} passed in
* {@link #PlaybackControlsRowPresenter(Presenter)}. Typically this will be an instance of
* {@link AbstractDetailsDescriptionPresenter}. The application can access the
* detailed description ViewHolder from {@link ViewHolder#mDescriptionViewHolder}.
* </p>
*/
public class PlaybackControlsRowPresenter extends RowPresenter {
static class BoundData extends PlaybackControlsPresenter.BoundData {
ViewHolder mRowViewHolder;
}
/**
* A ViewHolder for the PlaybackControlsRow.
*/
public class ViewHolder extends RowPresenter.ViewHolder {
public final Presenter.ViewHolder mDescriptionViewHolder;
final ViewGroup mCard;
final ViewGroup mCardRightPanel;
final ImageView mImageView;
final ViewGroup mDescriptionDock;
final ViewGroup mControlsDock;
final ViewGroup mSecondaryControlsDock;
final View mSpacer;
final View mBottomSpacer;
View mBgView;
int mControlsDockMarginStart;
int mControlsDockMarginEnd;
PlaybackControlsPresenter.ViewHolder mControlsVh;
Presenter.ViewHolder mSecondaryControlsVh;
BoundData mControlsBoundData = new BoundData();
BoundData mSecondaryBoundData = new BoundData();
Presenter.ViewHolder mSelectedViewHolder;
Object mSelectedItem;
final PlaybackControlsRow.OnPlaybackStateChangedListener mListener =
new PlaybackControlsRow.OnPlaybackStateChangedListener() {
@Override
public void onCurrentTimeChanged(int ms) {
mPlaybackControlsPresenter.setCurrentTime(mControlsVh, ms);
}
@Override
public void onBufferedProgressChanged(int ms) {
mPlaybackControlsPresenter.setSecondaryProgress(mControlsVh, ms);
}
};
ViewHolder(View rootView, Presenter descriptionPresenter) {
super(rootView);
mCard = (ViewGroup) rootView.findViewById(R.id.controls_card);
mCardRightPanel = (ViewGroup) rootView.findViewById(R.id.controls_card_right_panel);
mImageView = (ImageView) rootView.findViewById(R.id.image);
mDescriptionDock = (ViewGroup) rootView.findViewById(R.id.description_dock);
mControlsDock = (ViewGroup) rootView.findViewById(R.id.controls_dock);
mSecondaryControlsDock =
(ViewGroup) rootView.findViewById(R.id.secondary_controls_dock);
mSpacer = rootView.findViewById(R.id.spacer);
mBottomSpacer = rootView.findViewById(R.id.bottom_spacer);
mDescriptionViewHolder = descriptionPresenter == null ? null :
descriptionPresenter.onCreateViewHolder(mDescriptionDock);
if (mDescriptionViewHolder != null) {
mDescriptionDock.addView(mDescriptionViewHolder.view);
}
}
void dispatchItemSelection() {
if (!isSelected()) {
return;
}
if (mSelectedViewHolder == null) {
if (getOnItemViewSelectedListener() != null) {
getOnItemViewSelectedListener().onItemSelected(null, null,
ViewHolder.this, getRow());
}
} else {
if (getOnItemViewSelectedListener() != null) {
getOnItemViewSelectedListener().onItemSelected(mSelectedViewHolder, mSelectedItem,
ViewHolder.this, getRow());
}
}
};
Presenter getPresenter(boolean primary) {
ObjectAdapter adapter = primary ?
((PlaybackControlsRow) getRow()).getPrimaryActionsAdapter() :
((PlaybackControlsRow) getRow()).getSecondaryActionsAdapter();
if (adapter == null) {
return null;
}
if (adapter.getPresenterSelector() instanceof ControlButtonPresenterSelector) {
ControlButtonPresenterSelector selector =
(ControlButtonPresenterSelector) adapter.getPresenterSelector();
return primary ? selector.getPrimaryPresenter() :
selector.getSecondaryPresenter();
}
return adapter.getPresenter(adapter.size() > 0 ? adapter.get(0) : null);
}
void setOutline(View view) {
if (mBgView != null) {
RoundedRectHelper.getInstance().setClipToRoundedOutline(mBgView, false);
ShadowHelper.getInstance().setZ(mBgView, 0);
}
mBgView = view;
RoundedRectHelper.getInstance().setClipToRoundedOutline(view, true);
if (sShadowZ == 0) {
sShadowZ = view.getResources().getDimensionPixelSize(
R.dimen.lb_playback_controls_z);
}
ShadowHelper.getInstance().setZ(view, sShadowZ);
}
}
private int mBackgroundColor = Color.TRANSPARENT;
private boolean mBackgroundColorSet;
private int mProgressColor = Color.TRANSPARENT;
private boolean mProgressColorSet;
private boolean mSecondaryActionsHidden;
private Presenter mDescriptionPresenter;
private PlaybackControlsPresenter mPlaybackControlsPresenter;
private ControlBarPresenter mSecondaryControlsPresenter;
private OnActionClickedListener mOnActionClickedListener;
private static float sShadowZ;
private final OnControlSelectedListener mOnControlSelectedListener =
new OnControlSelectedListener() {
@Override
public void onControlSelected(Presenter.ViewHolder itemViewHolder, Object item,
ControlBarPresenter.BoundData data) {
ViewHolder vh = ((BoundData) data).mRowViewHolder;
if (vh.mSelectedViewHolder != itemViewHolder || vh.mSelectedItem != item) {
vh.mSelectedViewHolder = itemViewHolder;
vh.mSelectedItem = item;
vh.dispatchItemSelection();
}
}
};
private final OnControlClickedListener mOnControlClickedListener =
new OnControlClickedListener() {
@Override
public void onControlClicked(Presenter.ViewHolder itemViewHolder, Object item,
ControlBarPresenter.BoundData data) {
ViewHolder vh = ((BoundData) data).mRowViewHolder;
if (vh.getOnItemViewClickedListener() != null) {
vh.getOnItemViewClickedListener().onItemClicked(itemViewHolder, item,
vh, vh.getRow());
}
if (mOnActionClickedListener != null && item instanceof Action) {
mOnActionClickedListener.onActionClicked((Action) item);
}
}
};
/**
* Constructor for a PlaybackControlsRowPresenter.
*
* @param descriptionPresenter Presenter for displaying item details.
*/
public PlaybackControlsRowPresenter(Presenter descriptionPresenter) {
setHeaderPresenter(null);
setSelectEffectEnabled(false);
mDescriptionPresenter = descriptionPresenter;
mPlaybackControlsPresenter = new PlaybackControlsPresenter(R.layout.lb_playback_controls);
mSecondaryControlsPresenter = new ControlBarPresenter(R.layout.lb_control_bar);
mPlaybackControlsPresenter.setOnControlSelectedListener(mOnControlSelectedListener);
mSecondaryControlsPresenter.setOnControlSelectedListener(mOnControlSelectedListener);
mPlaybackControlsPresenter.setOnControlClickedListener(mOnControlClickedListener);
mSecondaryControlsPresenter.setOnControlClickedListener(mOnControlClickedListener);
}
/**
* Constructor for a PlaybackControlsRowPresenter.
*/
public PlaybackControlsRowPresenter() {
this(null);
}
/**
* Sets the listener for {@link Action} click events.
*/
public void setOnActionClickedListener(OnActionClickedListener listener) {
mOnActionClickedListener = listener;
}
/**
* Returns the listener for {@link Action} click events.
*/
public OnActionClickedListener getOnActionClickedListener() {
return mOnActionClickedListener;
}
/**
* Sets the background color. If not set, a default from the theme will be used.
*/
public void setBackgroundColor(@ColorInt int color) {
mBackgroundColor = color;
mBackgroundColorSet = true;
}
/**
* Returns the background color. If no background color was set, transparent
* is returned.
*/
@ColorInt
public int getBackgroundColor() {
return mBackgroundColor;
}
/**
* Sets the primary color for the progress bar. If not set, a default from
* the theme will be used.
*/
public void setProgressColor(@ColorInt int color) {
mProgressColor = color;
mProgressColorSet = true;
}
/**
* Returns the primary color for the progress bar. If no color was set, transparent
* is returned.
*/
@ColorInt
public int getProgressColor() {
return mProgressColor;
}
/**
* Sets the secondary actions to be hidden behind a "more actions" button.
* When "more actions" is selected, the primary actions are replaced with
* the secondary actions.
*/
public void setSecondaryActionsHidden(boolean hidden) {
mSecondaryActionsHidden = hidden;
}
/**
* Returns true if secondary actions are hidden.
*/
public boolean areSecondaryActionsHidden() {
return mSecondaryActionsHidden;
}
/**
* Shows or hides space at the bottom of the playback controls row.
* This allows the row to hug the bottom of the display when no
* other rows are present.
*/
public void showBottomSpace(ViewHolder vh, boolean show) {
vh.mBottomSpacer.setVisibility(show ? View.VISIBLE : View.GONE);
}
/**
* Displays the primary actions. This will override the user having selected "more actions"
* to display the secondary actions; see {@link #setSecondaryActionsHidden(boolean)}.
*/
public void showPrimaryActions(ViewHolder vh) {
mPlaybackControlsPresenter.showPrimaryActions(vh.mControlsVh);
mPlaybackControlsPresenter.resetFocus(vh.mControlsVh);
}
private int getDefaultBackgroundColor(Context context) {
TypedValue outValue = new TypedValue();
if (context.getTheme().resolveAttribute(R.attr.defaultBrandColor, outValue, true)) {
return context.getResources().getColor(outValue.resourceId);
}
return context.getResources().getColor(R.color.lb_default_brand_color);
}
private int getDefaultProgressColor(Context context) {
TypedValue outValue = new TypedValue();
if (context.getTheme()
.resolveAttribute(R.attr.playbackProgressPrimaryColor, outValue, true)) {
return context.getResources().getColor(outValue.resourceId);
}
return context.getResources().getColor(R.color.lb_playback_progress_color_no_theme);
}
@Override
protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.lb_playback_controls_row, parent, false);
ViewHolder vh = new ViewHolder(v, mDescriptionPresenter);
initRow(vh);
return vh;
}
private void initRow(final ViewHolder vh) {
MarginLayoutParams lp = (MarginLayoutParams) vh.mControlsDock.getLayoutParams();
vh.mControlsDockMarginStart = lp.getMarginStart();
vh.mControlsDockMarginEnd = lp.getMarginEnd();
vh.mControlsVh = (PlaybackControlsPresenter.ViewHolder)
mPlaybackControlsPresenter.onCreateViewHolder(vh.mControlsDock);
mPlaybackControlsPresenter.setProgressColor(vh.mControlsVh, mProgressColorSet ?
mProgressColor : getDefaultProgressColor(vh.mControlsDock.getContext()));
mPlaybackControlsPresenter.setBackgroundColor(vh.mControlsVh, mBackgroundColorSet ?
mBackgroundColor : getDefaultBackgroundColor(vh.view.getContext()));
vh.mControlsDock.addView(vh.mControlsVh.view);
vh.mSecondaryControlsVh =
mSecondaryControlsPresenter.onCreateViewHolder(vh.mSecondaryControlsDock);
if (!mSecondaryActionsHidden) {
vh.mSecondaryControlsDock.addView(vh.mSecondaryControlsVh.view);
}
((PlaybackControlsRowView) vh.view).setOnUnhandledKeyListener(
new PlaybackControlsRowView.OnUnhandledKeyListener() {
@Override
public boolean onUnhandledKey(KeyEvent event) {
if (vh.getOnKeyListener() != null) {
if (vh.getOnKeyListener().onKey(vh.view, event.getKeyCode(), event)) {
return true;
}
}
return false;
}
});
}
@Override
protected void onBindRowViewHolder(RowPresenter.ViewHolder holder, Object item) {
super.onBindRowViewHolder(holder, item);
ViewHolder vh = (ViewHolder) holder;
PlaybackControlsRow row = (PlaybackControlsRow) vh.getRow();
mPlaybackControlsPresenter.enableSecondaryActions(mSecondaryActionsHidden);
if (row.getItem() == null) {
vh.mDescriptionDock.setVisibility(View.GONE);
vh.mSpacer.setVisibility(View.GONE);
} else {
vh.mDescriptionDock.setVisibility(View.VISIBLE);
if (vh.mDescriptionViewHolder != null) {
mDescriptionPresenter.onBindViewHolder(vh.mDescriptionViewHolder, row.getItem());
}
vh.mSpacer.setVisibility(View.VISIBLE);
}
if (row.getImageDrawable() == null || row.getItem() == null) {
vh.mImageView.setImageDrawable(null);
updateCardLayout(vh, LayoutParams.WRAP_CONTENT);
} else {
vh.mImageView.setImageDrawable(row.getImageDrawable());
updateCardLayout(vh, vh.mImageView.getLayoutParams().height);
}
vh.mControlsBoundData.adapter = row.getPrimaryActionsAdapter();
vh.mControlsBoundData.secondaryActionsAdapter = row.getSecondaryActionsAdapter();
vh.mControlsBoundData.presenter = vh.getPresenter(true);
vh.mControlsBoundData.mRowViewHolder = vh;
mPlaybackControlsPresenter.onBindViewHolder(vh.mControlsVh, vh.mControlsBoundData);
vh.mSecondaryBoundData.adapter = row.getSecondaryActionsAdapter();
vh.mSecondaryBoundData.presenter = vh.getPresenter(false);
vh.mSecondaryBoundData.mRowViewHolder = vh;
mSecondaryControlsPresenter.onBindViewHolder(vh.mSecondaryControlsVh,
vh.mSecondaryBoundData);
mPlaybackControlsPresenter.setTotalTime(vh.mControlsVh, row.getTotalTime());
mPlaybackControlsPresenter.setCurrentTime(vh.mControlsVh, row.getCurrentTime());
mPlaybackControlsPresenter.setSecondaryProgress(vh.mControlsVh, row.getBufferedProgress());
row.setOnPlaybackStateChangedListener(vh.mListener);
}
private void updateCardLayout(ViewHolder vh, int height) {
LayoutParams lp = vh.mCardRightPanel.getLayoutParams();
lp.height = height;
vh.mCardRightPanel.setLayoutParams(lp);
MarginLayoutParams mlp = (MarginLayoutParams) vh.mControlsDock.getLayoutParams();
LinearLayout.LayoutParams llp =
(LinearLayout.LayoutParams) vh.mDescriptionDock.getLayoutParams();
if (height == LayoutParams.WRAP_CONTENT) {
llp.height = LayoutParams.WRAP_CONTENT;
mlp.setMarginStart(0);
mlp.setMarginEnd(0);
vh.mCard.setBackground(null);
vh.setOutline(vh.mControlsDock);
mPlaybackControlsPresenter.enableTimeMargins(vh.mControlsVh, true);
} else {
llp.height = 0;
llp.weight = 1;
mlp.setMarginStart(vh.mControlsDockMarginStart);
mlp.setMarginEnd(vh.mControlsDockMarginEnd);
vh.mCard.setBackgroundColor(mBackgroundColorSet ? mBackgroundColor :
getDefaultBackgroundColor(vh.mCard.getContext()));
vh.setOutline(vh.mCard);
mPlaybackControlsPresenter.enableTimeMargins(vh.mControlsVh, false);
}
vh.mDescriptionDock.setLayoutParams(llp);
vh.mControlsDock.setLayoutParams(mlp);
}
@Override
protected void onUnbindRowViewHolder(RowPresenter.ViewHolder holder) {
ViewHolder vh = (ViewHolder) holder;
PlaybackControlsRow row = (PlaybackControlsRow) vh.getRow();
if (vh.mDescriptionViewHolder != null) {
mDescriptionPresenter.onUnbindViewHolder(vh.mDescriptionViewHolder);
}
mPlaybackControlsPresenter.onUnbindViewHolder(vh.mControlsVh);
mSecondaryControlsPresenter.onUnbindViewHolder(vh.mSecondaryControlsVh);
row.setOnPlaybackStateChangedListener(null);
super.onUnbindRowViewHolder(holder);
}
@Override
protected void onRowViewSelected(RowPresenter.ViewHolder vh, boolean selected) {
super.onRowViewSelected(vh, selected);
if (selected) {
((ViewHolder) vh).dispatchItemSelection();
}
}
@Override
protected void onRowViewAttachedToWindow(RowPresenter.ViewHolder vh) {
super.onRowViewAttachedToWindow(vh);
if (mDescriptionPresenter != null) {
mDescriptionPresenter.onViewAttachedToWindow(
((ViewHolder) vh).mDescriptionViewHolder);
}
}
@Override
protected void onRowViewDetachedFromWindow(RowPresenter.ViewHolder vh) {
super.onRowViewDetachedFromWindow(vh);
if (mDescriptionPresenter != null) {
mDescriptionPresenter.onViewDetachedFromWindow(
((ViewHolder) vh).mDescriptionViewHolder);
}
}
}