blob: d75c327227be97573c47fb93c9bd5fc81d69e8ee [file] [log] [blame]
/*
* 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.app;
import android.support.v17.leanback.R;
import android.support.v17.leanback.transition.TransitionHelper;
import android.support.v17.leanback.widget.BrowseFrameLayout;
import android.support.v17.leanback.widget.OnChildLaidOutListener;
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.Row;
import android.support.v17.leanback.widget.RowPresenter;
import android.support.v17.leanback.widget.TitleView;
import android.support.v17.leanback.widget.VerticalGridPresenter;
import android.support.v17.leanback.widget.ObjectAdapter;
import android.support.v17.leanback.widget.OnItemClickedListener;
import android.support.v17.leanback.widget.OnItemSelectedListener;
import android.support.v17.leanback.widget.SearchOrbView;
import android.support.v4.view.ViewCompat;
import android.app.Fragment;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.ImageView;
import android.widget.TextView;
/**
* A fragment for creating leanback vertical grids.
*
* <p>Renders a vertical grid of objects given a {@link VerticalGridPresenter} and
* an {@link ObjectAdapter}.
*/
public class VerticalGridFragment extends Fragment {
private static final String TAG = "VerticalGridFragment";
private static boolean DEBUG = false;
private BrowseFrameLayout mBrowseFrame;
private String mTitle;
private Drawable mBadgeDrawable;
private ObjectAdapter mAdapter;
private VerticalGridPresenter mGridPresenter;
private VerticalGridPresenter.ViewHolder mGridViewHolder;
private OnItemSelectedListener mOnItemSelectedListener;
private OnItemClickedListener mOnItemClickedListener;
private OnItemViewSelectedListener mOnItemViewSelectedListener;
private OnItemViewClickedListener mOnItemViewClickedListener;
private View.OnClickListener mExternalOnSearchClickedListener;
private int mSelectedPosition = -1;
private TitleView mTitleView;
private SearchOrbView.Colors mSearchAffordanceColors;
private boolean mSearchAffordanceColorSet;
private boolean mShowingTitle = true;
// transition related
private static TransitionHelper sTransitionHelper = TransitionHelper.getInstance();
private Object mTitleUpTransition;
private Object mTitleDownTransition;
private Object mSceneWithTitle;
private Object mSceneWithoutTitle;
/**
* Sets the badge drawable displayed in the title area.
*/
public void setBadgeDrawable(Drawable drawable) {
if (drawable != mBadgeDrawable) {
mBadgeDrawable = drawable;
if (mTitleView != null) {
mTitleView.setBadgeDrawable(drawable);
}
}
}
/**
* Returns the badge drawable.
*/
public Drawable getBadgeDrawable() {
return mBadgeDrawable;
}
/**
* Sets a title for the fragment.
*/
public void setTitle(String title) {
mTitle = title;
if (mTitleView != null) {
mTitleView.setTitle(mTitle);
}
}
/**
* Returns the title for the fragment.
*/
public String getTitle() {
return mTitle;
}
/**
* Sets the grid presenter.
*/
public void setGridPresenter(VerticalGridPresenter gridPresenter) {
if (gridPresenter == null) {
throw new IllegalArgumentException("Grid presenter may not be null");
}
mGridPresenter = gridPresenter;
mGridPresenter.setOnItemViewSelectedListener(mViewSelectedListener);
if (mOnItemViewClickedListener != null) {
mGridPresenter.setOnItemViewClickedListener(mOnItemViewClickedListener);
}
if (mOnItemClickedListener != null) {
mGridPresenter.setOnItemClickedListener(mOnItemClickedListener);
}
}
/**
* Returns the grid presenter.
*/
public VerticalGridPresenter getGridPresenter() {
return mGridPresenter;
}
/**
* Sets the object adapter for the fragment.
*/
public void setAdapter(ObjectAdapter adapter) {
mAdapter = adapter;
updateAdapter();
}
/**
* Returns the object adapter.
*/
public ObjectAdapter getAdapter() {
return mAdapter;
}
final private OnItemViewSelectedListener mViewSelectedListener =
new OnItemViewSelectedListener() {
@Override
public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
RowPresenter.ViewHolder rowViewHolder, Row row) {
int position = mGridViewHolder.getGridView().getSelectedPosition();
if (DEBUG) Log.v(TAG, "grid selected position " + position);
gridOnItemSelected(position);
if (mOnItemSelectedListener != null) {
mOnItemSelectedListener.onItemSelected(item, row);
}
if (mOnItemViewSelectedListener != null) {
mOnItemViewSelectedListener.onItemSelected(itemViewHolder, item,
rowViewHolder, row);
}
}
};
final private OnChildLaidOutListener mChildLaidOutListener =
new OnChildLaidOutListener() {
@Override
public void onChildLaidOut(ViewGroup parent, View view, int position, long id) {
if (position == 0) {
showOrHideTitle();
}
}
};
/**
* Sets an item selection listener.
* @deprecated Use {@link #setOnItemViewSelectedListener(OnItemViewSelectedListener)}
*/
public void setOnItemSelectedListener(OnItemSelectedListener listener) {
mOnItemSelectedListener = listener;
}
/**
* Sets an item selection listener.
*/
public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
mOnItemViewSelectedListener = listener;
}
private void gridOnItemSelected(int position) {
if (position != mSelectedPosition) {
mSelectedPosition = position;
showOrHideTitle();
}
}
private void showOrHideTitle() {
if (mGridViewHolder.getGridView().findViewHolderForAdapterPosition(mSelectedPosition)
== null) {
return;
}
if (!mGridViewHolder.getGridView().hasPreviousViewInSameRow(mSelectedPosition)) {
// if has no sibling in front of it, show title
if (!mShowingTitle) {
sTransitionHelper.runTransition(mSceneWithTitle, mTitleDownTransition);
mShowingTitle = true;
}
} else if (mShowingTitle) {
sTransitionHelper.runTransition(mSceneWithoutTitle, mTitleUpTransition);
mShowingTitle = false;
}
}
/**
* Sets an item clicked listener.
* @deprecated Use {@link #setOnItemViewClickedListener(OnItemViewClickedListener)}
*/
public void setOnItemClickedListener(OnItemClickedListener listener) {
mOnItemClickedListener = listener;
if (mGridPresenter != null) {
mGridPresenter.setOnItemClickedListener(mOnItemClickedListener);
}
}
/**
* Returns the item clicked listener.
* @deprecated Use {@link #getOnItemViewClickedListener()}
*/
public OnItemClickedListener getOnItemClickedListener() {
return mOnItemClickedListener;
}
/**
* Sets an item clicked listener.
*/
public void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
mOnItemViewClickedListener = listener;
if (mGridPresenter != null) {
mGridPresenter.setOnItemViewClickedListener(mOnItemViewClickedListener);
}
}
/**
* Returns the item clicked listener.
*/
public OnItemViewClickedListener getOnItemViewClickedListener() {
return mOnItemViewClickedListener;
}
/**
* Sets a click listener for the search affordance.
*
* <p>The presence of a listener will change the visibility of the search
* affordance in the title area. When set to non-null, the title area will
* contain a call to search action.
*
* <p>The listener's onClick method will be invoked when the user clicks on
* the search action.
*
* @param listener The listener to invoke when the search affordance is
* clicked, or null to hide the search affordance.
*/
public void setOnSearchClickedListener(View.OnClickListener listener) {
mExternalOnSearchClickedListener = listener;
if (mTitleView != null) {
mTitleView.setOnSearchClickedListener(listener);
}
}
/**
* Sets the {@link SearchOrbView.Colors} used to draw the search affordance.
*/
public void setSearchAffordanceColors(SearchOrbView.Colors colors) {
mSearchAffordanceColors = colors;
mSearchAffordanceColorSet = true;
if (mTitleView != null) {
mTitleView.setSearchAffordanceColors(mSearchAffordanceColors);
}
}
/**
* Returns the {@link SearchOrbView.Colors} used to draw the search affordance.
*/
public SearchOrbView.Colors getSearchAffordanceColors() {
if (mSearchAffordanceColorSet) {
return mSearchAffordanceColors;
}
if (mTitleView == null) {
throw new IllegalStateException("Fragment views not yet created");
}
return mTitleView.getSearchAffordanceColors();
}
/**
* Sets the color used to draw the search affordance.
* A default brighter color will be set by the framework.
*
* @param color The color to use for the search affordance.
*/
public void setSearchAffordanceColor(int color) {
setSearchAffordanceColors(new SearchOrbView.Colors(color));
}
/**
* Returns the color used to draw the search affordance.
*/
public int getSearchAffordanceColor() {
return getSearchAffordanceColors().color;
}
private final BrowseFrameLayout.OnFocusSearchListener mOnFocusSearchListener =
new BrowseFrameLayout.OnFocusSearchListener() {
@Override
public View onFocusSearch(View focused, int direction) {
if (DEBUG) Log.v(TAG, "onFocusSearch focused " + focused + " + direction " + direction);
final View searchOrbView = mTitleView.getSearchAffordanceView();
final boolean isRtl = ViewCompat.getLayoutDirection(focused) ==
View.LAYOUT_DIRECTION_RTL;
final int forward = isRtl ? View.FOCUS_LEFT : View.FOCUS_RIGHT;
if (focused == searchOrbView && (
direction == View.FOCUS_DOWN || direction == forward)) {
return mGridViewHolder.view;
} else if (focused != searchOrbView && searchOrbView.getVisibility() == View.VISIBLE
&& direction == View.FOCUS_UP) {
return searchOrbView;
} else {
return null;
}
}
};
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.lb_vertical_grid_fragment,
container, false);
mBrowseFrame = (BrowseFrameLayout) root.findViewById(R.id.browse_frame);
mBrowseFrame.setOnFocusSearchListener(mOnFocusSearchListener);
mTitleView = (TitleView) root.findViewById(R.id.browse_title_group);
mTitleView.setBadgeDrawable(mBadgeDrawable);
mTitleView.setTitle(mTitle);
if (mSearchAffordanceColorSet) {
mTitleView.setSearchAffordanceColors(mSearchAffordanceColors);
}
if (mExternalOnSearchClickedListener != null) {
mTitleView.setOnSearchClickedListener(mExternalOnSearchClickedListener);
}
mSceneWithTitle = sTransitionHelper.createScene(root, new Runnable() {
@Override
public void run() {
mTitleView.setVisibility(View.VISIBLE);
}
});
mSceneWithoutTitle = sTransitionHelper.createScene(root, new Runnable() {
@Override
public void run() {
mTitleView.setVisibility(View.INVISIBLE);
}
});
Context context = getActivity();
mTitleUpTransition = sTransitionHelper.loadTransition(context, R.transition.lb_title_out);
mTitleDownTransition = sTransitionHelper.loadTransition(context, R.transition.lb_title_in);
return root;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
ViewGroup gridDock = (ViewGroup) view.findViewById(R.id.browse_grid_dock);
mGridViewHolder = mGridPresenter.onCreateViewHolder(gridDock);
gridDock.addView(mGridViewHolder.view);
mGridViewHolder.getGridView().setOnChildLaidOutListener(mChildLaidOutListener);
updateAdapter();
}
@Override
public void onStart() {
super.onStart();
mGridViewHolder.getGridView().requestFocus();
}
@Override
public void onPause() {
mTitleView.enableAnimation(false);
super.onPause();
}
@Override
public void onResume() {
super.onResume();
mTitleView.enableAnimation(true);
}
@Override
public void onDestroyView() {
super.onDestroyView();
mGridViewHolder = null;
}
/**
* Sets the selected item position.
*/
public void setSelectedPosition(int position) {
mSelectedPosition = position;
if(mGridViewHolder != null && mGridViewHolder.getGridView().getAdapter() != null) {
mGridViewHolder.getGridView().setSelectedPositionSmooth(position);
}
}
private void updateAdapter() {
if (mGridViewHolder != null) {
mGridPresenter.onBindViewHolder(mGridViewHolder, mAdapter);
if (mSelectedPosition != -1) {
mGridViewHolder.getGridView().setSelectedPosition(mSelectedPosition);
}
}
}
}