blob: fa7f61ec9f4a8101555dcf49c0be01eb9d2e9bb0 [file] [log] [blame]
/* This file is auto-generated from BrowseFragment.java. DO NOT MODIFY. */
/*
* 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.annotation.ColorInt;
import android.support.v17.leanback.R;
import android.support.v17.leanback.transition.LeanbackTransitionHelper;
import android.support.v17.leanback.transition.TransitionHelper;
import android.support.v17.leanback.transition.TransitionListener;
import android.support.v17.leanback.widget.BrowseFrameLayout;
import android.support.v17.leanback.widget.HorizontalGridView;
import android.support.v17.leanback.widget.ItemBridgeAdapter;
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.PresenterSelector;
import android.support.v17.leanback.widget.RowHeaderPresenter;
import android.support.v17.leanback.widget.RowPresenter;
import android.support.v17.leanback.widget.TitleView;
import android.support.v17.leanback.widget.VerticalGridView;
import android.support.v17.leanback.widget.Row;
import android.support.v17.leanback.widget.ObjectAdapter;
import android.support.v17.leanback.widget.SearchOrbView;
import android.support.v4.view.ViewCompat;
import android.util.Log;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentManager.BackStackEntry;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.ViewTreeObserver;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import static android.support.v7.widget.RecyclerView.NO_POSITION;
/**
* A fragment for creating Leanback browse screens. It is composed of a
* RowsSupportFragment and a HeadersSupportFragment.
* <p>
* A BrowseSupportFragment renders the elements of its {@link ObjectAdapter} as a set
* of rows in a vertical list. The elements in this adapter must be subclasses
* of {@link Row}.
* <p>
* The HeadersSupportFragment can be set to be either shown or hidden by default, or
* may be disabled entirely. See {@link #setHeadersState} for details.
* <p>
* By default the BrowseSupportFragment includes support for returning to the headers
* when the user presses Back. For Activities that customize {@link
* android.support.v4.app.FragmentActivity#onBackPressed()}, you must disable this default Back key support by
* calling {@link #setHeadersTransitionOnBackEnabled(boolean)} with false and
* use {@link BrowseSupportFragment.BrowseTransitionListener} and
* {@link #startHeadersTransition(boolean)}.
* <p>
* The recommended theme to use with a BrowseSupportFragment is
* {@link android.support.v17.leanback.R.style#Theme_Leanback_Browse}.
* </p>
*/
public class BrowseSupportFragment extends BaseSupportFragment {
// BUNDLE attribute for saving header show/hide status when backstack is used:
static final String HEADER_STACK_INDEX = "headerStackIndex";
// BUNDLE attribute for saving header show/hide status when backstack is not used:
static final String HEADER_SHOW = "headerShow";
final class BackStackListener implements FragmentManager.OnBackStackChangedListener {
int mLastEntryCount;
int mIndexOfHeadersBackStack;
BackStackListener() {
mLastEntryCount = getFragmentManager().getBackStackEntryCount();
mIndexOfHeadersBackStack = -1;
}
void load(Bundle savedInstanceState) {
if (savedInstanceState != null) {
mIndexOfHeadersBackStack = savedInstanceState.getInt(HEADER_STACK_INDEX, -1);
mShowingHeaders = mIndexOfHeadersBackStack == -1;
} else {
if (!mShowingHeaders) {
getFragmentManager().beginTransaction()
.addToBackStack(mWithHeadersBackStackName).commit();
}
}
}
void save(Bundle outState) {
outState.putInt(HEADER_STACK_INDEX, mIndexOfHeadersBackStack);
}
@Override
public void onBackStackChanged() {
if (getFragmentManager() == null) {
Log.w(TAG, "getFragmentManager() is null, stack:", new Exception());
return;
}
int count = getFragmentManager().getBackStackEntryCount();
// if backstack is growing and last pushed entry is "headers" backstack,
// remember the index of the entry.
if (count > mLastEntryCount) {
BackStackEntry entry = getFragmentManager().getBackStackEntryAt(count - 1);
if (mWithHeadersBackStackName.equals(entry.getName())) {
mIndexOfHeadersBackStack = count - 1;
}
} else if (count < mLastEntryCount) {
// if popped "headers" backstack, initiate the show header transition if needed
if (mIndexOfHeadersBackStack >= count) {
mIndexOfHeadersBackStack = -1;
if (!mShowingHeaders) {
startHeadersTransitionInternal(true);
}
}
}
mLastEntryCount = count;
}
}
/**
* Listener for transitions between browse headers and rows.
*/
public static class BrowseTransitionListener {
/**
* Callback when headers transition starts.
*
* @param withHeaders True if the transition will result in headers
* being shown, false otherwise.
*/
public void onHeadersTransitionStart(boolean withHeaders) {
}
/**
* Callback when headers transition stops.
*
* @param withHeaders True if the transition will result in headers
* being shown, false otherwise.
*/
public void onHeadersTransitionStop(boolean withHeaders) {
}
}
private class SetSelectionRunnable implements Runnable {
static final int TYPE_INVALID = -1;
static final int TYPE_INTERNAL_SYNC = 0;
static final int TYPE_USER_REQUEST = 1;
private int mPosition;
private int mType;
private boolean mSmooth;
SetSelectionRunnable() {
reset();
}
void post(int position, int type, boolean smooth) {
// Posting the set selection, rather than calling it immediately, prevents an issue
// with adapter changes. Example: a row is added before the current selected row;
// first the fast lane view updates its selection, then the rows fragment has that
// new selection propagated immediately; THEN the rows view processes the same adapter
// change and moves the selection again.
if (type >= mType) {
mPosition = position;
mType = type;
mSmooth = smooth;
mBrowseFrame.removeCallbacks(this);
mBrowseFrame.post(this);
}
}
@Override
public void run() {
setSelection(mPosition, mSmooth);
reset();
}
private void reset() {
mPosition = -1;
mType = TYPE_INVALID;
mSmooth = false;
}
}
private static final String TAG = "BrowseSupportFragment";
private static final String LB_HEADERS_BACKSTACK = "lbHeadersBackStack_";
private static boolean DEBUG = false;
/** The headers fragment is enabled and shown by default. */
public static final int HEADERS_ENABLED = 1;
/** The headers fragment is enabled and hidden by default. */
public static final int HEADERS_HIDDEN = 2;
/** The headers fragment is disabled and will never be shown. */
public static final int HEADERS_DISABLED = 3;
private RowsSupportFragment mRowsSupportFragment;
private HeadersSupportFragment mHeadersSupportFragment;
private ObjectAdapter mAdapter;
private int mHeadersState = HEADERS_ENABLED;
private int mBrandColor = Color.TRANSPARENT;
private boolean mBrandColorSet;
private BrowseFrameLayout mBrowseFrame;
private boolean mHeadersBackStackEnabled = true;
private String mWithHeadersBackStackName;
private boolean mShowingHeaders = true;
private boolean mCanShowHeaders = true;
private int mContainerListMarginStart;
private int mContainerListAlignTop;
private boolean mRowScaleEnabled = true;
private OnItemViewSelectedListener mExternalOnItemViewSelectedListener;
private OnItemViewClickedListener mOnItemViewClickedListener;
private int mSelectedPosition = -1;
private PresenterSelector mHeaderPresenterSelector;
private final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
// transition related:
private Object mSceneWithHeaders;
private Object mSceneWithoutHeaders;
private Object mSceneAfterEntranceTransition;
private Object mHeadersTransition;
private BackStackListener mBackStackChangedListener;
private BrowseTransitionListener mBrowseTransitionListener;
private static final String ARG_TITLE = BrowseSupportFragment.class.getCanonicalName() + ".title";
private static final String ARG_BADGE_URI = BrowseSupportFragment.class.getCanonicalName() + ".badge";
private static final String ARG_HEADERS_STATE =
BrowseSupportFragment.class.getCanonicalName() + ".headersState";
/**
* Creates arguments for a browse fragment.
*
* @param args The Bundle to place arguments into, or null if the method
* should return a new Bundle.
* @param title The title of the BrowseSupportFragment.
* @param headersState The initial state of the headers of the
* BrowseSupportFragment. Must be one of {@link #HEADERS_ENABLED}, {@link
* #HEADERS_HIDDEN}, or {@link #HEADERS_DISABLED}.
* @return A Bundle with the given arguments for creating a BrowseSupportFragment.
*/
public static Bundle createArgs(Bundle args, String title, int headersState) {
if (args == null) {
args = new Bundle();
}
args.putString(ARG_TITLE, title);
args.putInt(ARG_HEADERS_STATE, headersState);
return args;
}
/**
* Sets the brand color for the browse fragment. The brand color is used as
* the primary color for UI elements in the browse fragment. For example,
* the background color of the headers fragment uses the brand color.
*
* @param color The color to use as the brand color of the fragment.
*/
public void setBrandColor(@ColorInt int color) {
mBrandColor = color;
mBrandColorSet = true;
if (mHeadersSupportFragment != null) {
mHeadersSupportFragment.setBackgroundColor(mBrandColor);
}
}
/**
* Returns the brand color for the browse fragment.
* The default is transparent.
*/
@ColorInt
public int getBrandColor() {
return mBrandColor;
}
/**
* Sets the adapter containing the rows for the fragment.
*
* <p>The items referenced by the adapter must be be derived from
* {@link Row}. These rows will be used by the rows fragment and the headers
* fragment (if not disabled) to render the browse rows.
*
* @param adapter An ObjectAdapter for the browse rows. All items must
* derive from {@link Row}.
*/
public void setAdapter(ObjectAdapter adapter) {
mAdapter = adapter;
if (mRowsSupportFragment != null) {
mRowsSupportFragment.setAdapter(adapter);
mHeadersSupportFragment.setAdapter(adapter);
}
}
/**
* Returns the adapter containing the rows for the fragment.
*/
public ObjectAdapter getAdapter() {
return mAdapter;
}
/**
* Sets an item selection listener.
*/
public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
mExternalOnItemViewSelectedListener = listener;
}
/**
* Returns an item selection listener.
*/
public OnItemViewSelectedListener getOnItemViewSelectedListener() {
return mExternalOnItemViewSelectedListener;
}
/**
* Sets an item clicked listener on the fragment.
* 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 void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
mOnItemViewClickedListener = listener;
if (mRowsSupportFragment != null) {
mRowsSupportFragment.setOnItemViewClickedListener(listener);
}
}
/**
* Returns the item Clicked listener.
*/
public OnItemViewClickedListener getOnItemViewClickedListener() {
return mOnItemViewClickedListener;
}
/**
* Starts a headers transition.
*
* <p>This method will begin a transition to either show or hide the
* headers, depending on the value of withHeaders. If headers are disabled
* for this browse fragment, this method will throw an exception.
*
* @param withHeaders True if the headers should transition to being shown,
* false if the transition should result in headers being hidden.
*/
public void startHeadersTransition(boolean withHeaders) {
if (!mCanShowHeaders) {
throw new IllegalStateException("Cannot start headers transition");
}
if (isInHeadersTransition() || mShowingHeaders == withHeaders) {
return;
}
startHeadersTransitionInternal(withHeaders);
}
/**
* Returns true if the headers transition is currently running.
*/
public boolean isInHeadersTransition() {
return mHeadersTransition != null;
}
/**
* Returns true if headers are shown.
*/
public boolean isShowingHeaders() {
return mShowingHeaders;
}
/**
* Sets a listener for browse fragment transitions.
*
* @param listener The listener to call when a browse headers transition
* begins or ends.
*/
public void setBrowseTransitionListener(BrowseTransitionListener listener) {
mBrowseTransitionListener = listener;
}
/**
* Enables scaling of rows when headers are present.
* By default enabled to increase density.
*
* @param enable true to enable row scaling
*/
public void enableRowScaling(boolean enable) {
mRowScaleEnabled = enable;
if (mRowsSupportFragment != null) {
mRowsSupportFragment.enableRowScaling(mRowScaleEnabled);
}
}
private void startHeadersTransitionInternal(final boolean withHeaders) {
if (getFragmentManager().isDestroyed()) {
return;
}
mShowingHeaders = withHeaders;
mRowsSupportFragment.onExpandTransitionStart(!withHeaders, new Runnable() {
@Override
public void run() {
mHeadersSupportFragment.onTransitionPrepare();
mHeadersSupportFragment.onTransitionStart();
createHeadersTransition();
if (mBrowseTransitionListener != null) {
mBrowseTransitionListener.onHeadersTransitionStart(withHeaders);
}
TransitionHelper.runTransition(withHeaders ? mSceneWithHeaders : mSceneWithoutHeaders,
mHeadersTransition);
if (mHeadersBackStackEnabled) {
if (!withHeaders) {
getFragmentManager().beginTransaction()
.addToBackStack(mWithHeadersBackStackName).commit();
} else {
int index = mBackStackChangedListener.mIndexOfHeadersBackStack;
if (index >= 0) {
BackStackEntry entry = getFragmentManager().getBackStackEntryAt(index);
getFragmentManager().popBackStackImmediate(entry.getId(),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}
}
}
});
}
private boolean isVerticalScrolling() {
// don't run transition
return mHeadersSupportFragment.getVerticalGridView().getScrollState()
!= HorizontalGridView.SCROLL_STATE_IDLE
|| mRowsSupportFragment.getVerticalGridView().getScrollState()
!= HorizontalGridView.SCROLL_STATE_IDLE;
}
private final BrowseFrameLayout.OnFocusSearchListener mOnFocusSearchListener =
new BrowseFrameLayout.OnFocusSearchListener() {
@Override
public View onFocusSearch(View focused, int direction) {
// if headers is running transition, focus stays
if (mCanShowHeaders && isInHeadersTransition()) {
return focused;
}
if (DEBUG) Log.v(TAG, "onFocusSearch focused " + focused + " + direction " + direction);
if (getTitleView() != null && focused != getTitleView() &&
direction == View.FOCUS_UP) {
return getTitleView();
}
if (getTitleView() != null && getTitleView().hasFocus() &&
direction == View.FOCUS_DOWN) {
return mCanShowHeaders && mShowingHeaders ?
mHeadersSupportFragment.getVerticalGridView() :
mRowsSupportFragment.getVerticalGridView();
}
boolean isRtl = ViewCompat.getLayoutDirection(focused) == View.LAYOUT_DIRECTION_RTL;
int towardStart = isRtl ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
int towardEnd = isRtl ? View.FOCUS_LEFT : View.FOCUS_RIGHT;
if (mCanShowHeaders && direction == towardStart) {
if (isVerticalScrolling() || mShowingHeaders) {
return focused;
}
return mHeadersSupportFragment.getVerticalGridView();
} else if (direction == towardEnd) {
if (isVerticalScrolling()) {
return focused;
}
return mRowsSupportFragment.getVerticalGridView();
} else {
return null;
}
}
};
private final BrowseFrameLayout.OnChildFocusListener mOnChildFocusListener =
new BrowseFrameLayout.OnChildFocusListener() {
@Override
public boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
if (getChildFragmentManager().isDestroyed()) {
return true;
}
// Make sure not changing focus when requestFocus() is called.
if (mCanShowHeaders && mShowingHeaders) {
if (mHeadersSupportFragment != null && mHeadersSupportFragment.getView() != null &&
mHeadersSupportFragment.getView().requestFocus(direction, previouslyFocusedRect)) {
return true;
}
}
if (mRowsSupportFragment != null && mRowsSupportFragment.getView() != null &&
mRowsSupportFragment.getView().requestFocus(direction, previouslyFocusedRect)) {
return true;
}
if (getTitleView() != null &&
getTitleView().requestFocus(direction, previouslyFocusedRect)) {
return true;
}
return false;
};
@Override
public void onRequestChildFocus(View child, View focused) {
if (getChildFragmentManager().isDestroyed()) {
return;
}
if (!mCanShowHeaders || isInHeadersTransition()) return;
int childId = child.getId();
if (childId == R.id.browse_container_dock && mShowingHeaders) {
startHeadersTransitionInternal(false);
} else if (childId == R.id.browse_headers_dock && !mShowingHeaders) {
startHeadersTransitionInternal(true);
}
}
};
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mBackStackChangedListener != null) {
mBackStackChangedListener.save(outState);
} else {
outState.putBoolean(HEADER_SHOW, mShowingHeaders);
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TypedArray ta = getActivity().obtainStyledAttributes(R.styleable.LeanbackTheme);
mContainerListMarginStart = (int) ta.getDimension(
R.styleable.LeanbackTheme_browseRowsMarginStart, getActivity().getResources()
.getDimensionPixelSize(R.dimen.lb_browse_rows_margin_start));
mContainerListAlignTop = (int) ta.getDimension(
R.styleable.LeanbackTheme_browseRowsMarginTop, getActivity().getResources()
.getDimensionPixelSize(R.dimen.lb_browse_rows_margin_top));
ta.recycle();
readArguments(getArguments());
if (mCanShowHeaders) {
if (mHeadersBackStackEnabled) {
mWithHeadersBackStackName = LB_HEADERS_BACKSTACK + this;
mBackStackChangedListener = new BackStackListener();
getFragmentManager().addOnBackStackChangedListener(mBackStackChangedListener);
mBackStackChangedListener.load(savedInstanceState);
} else {
if (savedInstanceState != null) {
mShowingHeaders = savedInstanceState.getBoolean(HEADER_SHOW);
}
}
}
}
@Override
public void onDestroy() {
if (mBackStackChangedListener != null) {
getFragmentManager().removeOnBackStackChangedListener(mBackStackChangedListener);
}
super.onDestroy();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (getChildFragmentManager().findFragmentById(R.id.browse_container_dock) == null) {
mRowsSupportFragment = new RowsSupportFragment();
mHeadersSupportFragment = new HeadersSupportFragment();
getChildFragmentManager().beginTransaction()
.replace(R.id.browse_headers_dock, mHeadersSupportFragment)
.replace(R.id.browse_container_dock, mRowsSupportFragment).commit();
} else {
mHeadersSupportFragment = (HeadersSupportFragment) getChildFragmentManager()
.findFragmentById(R.id.browse_headers_dock);
mRowsSupportFragment = (RowsSupportFragment) getChildFragmentManager()
.findFragmentById(R.id.browse_container_dock);
}
mHeadersSupportFragment.setHeadersGone(!mCanShowHeaders);
mRowsSupportFragment.setAdapter(mAdapter);
if (mHeaderPresenterSelector != null) {
mHeadersSupportFragment.setPresenterSelector(mHeaderPresenterSelector);
}
mHeadersSupportFragment.setAdapter(mAdapter);
mRowsSupportFragment.enableRowScaling(mRowScaleEnabled);
mRowsSupportFragment.setOnItemViewSelectedListener(mRowViewSelectedListener);
mHeadersSupportFragment.setOnHeaderViewSelectedListener(mHeaderViewSelectedListener);
mHeadersSupportFragment.setOnHeaderClickedListener(mHeaderClickedListener);
mRowsSupportFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
View root = inflater.inflate(R.layout.lb_browse_fragment, container, false);
setTitleView((TitleView) root.findViewById(R.id.browse_title_group));
mBrowseFrame = (BrowseFrameLayout) root.findViewById(R.id.browse_frame);
mBrowseFrame.setOnChildFocusListener(mOnChildFocusListener);
mBrowseFrame.setOnFocusSearchListener(mOnFocusSearchListener);
if (mBrandColorSet) {
mHeadersSupportFragment.setBackgroundColor(mBrandColor);
}
mSceneWithHeaders = TransitionHelper.createScene(mBrowseFrame, new Runnable() {
@Override
public void run() {
showHeaders(true);
}
});
mSceneWithoutHeaders = TransitionHelper.createScene(mBrowseFrame, new Runnable() {
@Override
public void run() {
showHeaders(false);
}
});
mSceneAfterEntranceTransition = TransitionHelper.createScene(mBrowseFrame, new Runnable() {
@Override
public void run() {
setEntranceTransitionEndState();
}
});
return root;
}
private void createHeadersTransition() {
mHeadersTransition = TransitionHelper.loadTransition(getActivity(),
mShowingHeaders ?
R.transition.lb_browse_headers_in : R.transition.lb_browse_headers_out);
TransitionHelper.addTransitionListener(mHeadersTransition, new TransitionListener() {
@Override
public void onTransitionStart(Object transition) {
}
@Override
public void onTransitionEnd(Object transition) {
mHeadersTransition = null;
mRowsSupportFragment.onTransitionEnd();
mHeadersSupportFragment.onTransitionEnd();
if (mShowingHeaders) {
VerticalGridView headerGridView = mHeadersSupportFragment.getVerticalGridView();
if (headerGridView != null && !headerGridView.hasFocus()) {
headerGridView.requestFocus();
}
} else {
VerticalGridView rowsGridView = mRowsSupportFragment.getVerticalGridView();
if (rowsGridView != null && !rowsGridView.hasFocus()) {
rowsGridView.requestFocus();
}
}
if (mBrowseTransitionListener != null) {
mBrowseTransitionListener.onHeadersTransitionStop(mShowingHeaders);
}
}
});
}
/**
* Sets the {@link PresenterSelector} used to render the row headers.
*
* @param headerPresenterSelector The PresenterSelector that will determine
* the Presenter for each row header.
*/
public void setHeaderPresenterSelector(PresenterSelector headerPresenterSelector) {
mHeaderPresenterSelector = headerPresenterSelector;
if (mHeadersSupportFragment != null) {
mHeadersSupportFragment.setPresenterSelector(mHeaderPresenterSelector);
}
}
private void setRowsAlignedLeft(boolean alignLeft) {
MarginLayoutParams lp;
View containerList;
containerList = mRowsSupportFragment.getView();
lp = (MarginLayoutParams) containerList.getLayoutParams();
lp.setMarginStart(alignLeft ? 0 : mContainerListMarginStart);
containerList.setLayoutParams(lp);
}
private void setHeadersOnScreen(boolean onScreen) {
MarginLayoutParams lp;
View containerList;
containerList = mHeadersSupportFragment.getView();
lp = (MarginLayoutParams) containerList.getLayoutParams();
lp.setMarginStart(onScreen ? 0 : -mContainerListMarginStart);
containerList.setLayoutParams(lp);
}
private void showHeaders(boolean show) {
if (DEBUG) Log.v(TAG, "showHeaders " + show);
mHeadersSupportFragment.setHeadersEnabled(show);
setHeadersOnScreen(show);
setRowsAlignedLeft(!show);
mRowsSupportFragment.setExpand(!show);
}
private HeadersSupportFragment.OnHeaderClickedListener mHeaderClickedListener =
new HeadersSupportFragment.OnHeaderClickedListener() {
@Override
public void onHeaderClicked() {
if (!mCanShowHeaders || !mShowingHeaders || isInHeadersTransition()) {
return;
}
startHeadersTransitionInternal(false);
mRowsSupportFragment.getVerticalGridView().requestFocus();
}
};
private OnItemViewSelectedListener mRowViewSelectedListener = new OnItemViewSelectedListener() {
@Override
public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
RowPresenter.ViewHolder rowViewHolder, Row row) {
int position = mRowsSupportFragment.getVerticalGridView().getSelectedPosition();
if (DEBUG) Log.v(TAG, "row selected position " + position);
onRowSelected(position);
if (mExternalOnItemViewSelectedListener != null) {
mExternalOnItemViewSelectedListener.onItemSelected(itemViewHolder, item,
rowViewHolder, row);
}
}
};
private HeadersSupportFragment.OnHeaderViewSelectedListener mHeaderViewSelectedListener =
new HeadersSupportFragment.OnHeaderViewSelectedListener() {
@Override
public void onHeaderSelected(RowHeaderPresenter.ViewHolder viewHolder, Row row) {
int position = mHeadersSupportFragment.getVerticalGridView().getSelectedPosition();
if (DEBUG) Log.v(TAG, "header selected position " + position);
onRowSelected(position);
}
};
private void onRowSelected(int position) {
if (position != mSelectedPosition) {
mSetSelectionRunnable.post(
position, SetSelectionRunnable.TYPE_INTERNAL_SYNC, true);
if (getAdapter() == null || getAdapter().size() == 0 || position == 0) {
showTitle(true);
} else {
showTitle(false);
}
}
}
private void setSelection(int position, boolean smooth) {
if (position != NO_POSITION) {
mRowsSupportFragment.setSelectedPosition(position, smooth);
mHeadersSupportFragment.setSelectedPosition(position, smooth);
}
mSelectedPosition = position;
}
/**
* Sets the selected row position with smooth animation.
*/
public void setSelectedPosition(int position) {
setSelectedPosition(position, true);
}
/**
* Sets the selected row position.
*/
public void setSelectedPosition(int position, boolean smooth) {
mSetSelectionRunnable.post(
position, SetSelectionRunnable.TYPE_USER_REQUEST, smooth);
}
@Override
public void onStart() {
super.onStart();
mHeadersSupportFragment.setWindowAlignmentFromTop(mContainerListAlignTop);
mHeadersSupportFragment.setItemAlignment();
mRowsSupportFragment.setWindowAlignmentFromTop(mContainerListAlignTop);
mRowsSupportFragment.setItemAlignment();
mRowsSupportFragment.setScalePivots(0, mContainerListAlignTop);
if (mCanShowHeaders && mShowingHeaders && mHeadersSupportFragment.getView() != null) {
mHeadersSupportFragment.getView().requestFocus();
} else if ((!mCanShowHeaders || !mShowingHeaders)
&& mRowsSupportFragment.getView() != null) {
mRowsSupportFragment.getView().requestFocus();
}
if (mCanShowHeaders) {
showHeaders(mShowingHeaders);
}
if (isEntranceTransitionEnabled()) {
setEntranceTransitionStartState();
}
}
/**
* Enables/disables headers transition on back key support. This is enabled by
* default. The BrowseSupportFragment will add a back stack entry when headers are
* showing. Running a headers transition when the back key is pressed only
* works when the headers state is {@link #HEADERS_ENABLED} or
* {@link #HEADERS_HIDDEN}.
* <p>
* NOTE: If an Activity has its own onBackPressed() handling, you must
* disable this feature. You may use {@link #startHeadersTransition(boolean)}
* and {@link BrowseTransitionListener} in your own back stack handling.
*/
public final void setHeadersTransitionOnBackEnabled(boolean headersBackStackEnabled) {
mHeadersBackStackEnabled = headersBackStackEnabled;
}
/**
* Returns true if headers transition on back key support is enabled.
*/
public final boolean isHeadersTransitionOnBackEnabled() {
return mHeadersBackStackEnabled;
}
private void readArguments(Bundle args) {
if (args == null) {
return;
}
if (args.containsKey(ARG_TITLE)) {
setTitle(args.getString(ARG_TITLE));
}
if (args.containsKey(ARG_HEADERS_STATE)) {
setHeadersState(args.getInt(ARG_HEADERS_STATE));
}
}
/**
* Sets the state for the headers column in the browse fragment. Must be one
* of {@link #HEADERS_ENABLED}, {@link #HEADERS_HIDDEN}, or
* {@link #HEADERS_DISABLED}.
*
* @param headersState The state of the headers for the browse fragment.
*/
public void setHeadersState(int headersState) {
if (headersState < HEADERS_ENABLED || headersState > HEADERS_DISABLED) {
throw new IllegalArgumentException("Invalid headers state: " + headersState);
}
if (DEBUG) Log.v(TAG, "setHeadersState " + headersState);
if (headersState != mHeadersState) {
mHeadersState = headersState;
switch (headersState) {
case HEADERS_ENABLED:
mCanShowHeaders = true;
mShowingHeaders = true;
break;
case HEADERS_HIDDEN:
mCanShowHeaders = true;
mShowingHeaders = false;
break;
case HEADERS_DISABLED:
mCanShowHeaders = false;
mShowingHeaders = false;
break;
default:
Log.w(TAG, "Unknown headers state: " + headersState);
break;
}
if (mHeadersSupportFragment != null) {
mHeadersSupportFragment.setHeadersGone(!mCanShowHeaders);
}
}
}
/**
* Returns the state of the headers column in the browse fragment.
*/
public int getHeadersState() {
return mHeadersState;
}
@Override
protected Object createEntranceTransition() {
return TransitionHelper.loadTransition(getActivity(),
R.transition.lb_browse_entrance_transition);
}
@Override
protected void runEntranceTransition(Object entranceTransition) {
TransitionHelper.runTransition(mSceneAfterEntranceTransition, entranceTransition);
}
@Override
protected void onEntranceTransitionPrepare() {
mHeadersSupportFragment.onTransitionPrepare();
mRowsSupportFragment.onTransitionPrepare();
}
@Override
protected void onEntranceTransitionStart() {
mHeadersSupportFragment.onTransitionStart();
mRowsSupportFragment.onTransitionStart();
}
@Override
protected void onEntranceTransitionEnd() {
mRowsSupportFragment.onTransitionEnd();
mHeadersSupportFragment.onTransitionEnd();
}
void setSearchOrbViewOnScreen(boolean onScreen) {
View searchOrbView = getTitleView().getSearchAffordanceView();
MarginLayoutParams lp = (MarginLayoutParams) searchOrbView.getLayoutParams();
lp.setMarginStart(onScreen ? 0 : -mContainerListMarginStart);
searchOrbView.setLayoutParams(lp);
}
void setEntranceTransitionStartState() {
setHeadersOnScreen(false);
setSearchOrbViewOnScreen(false);
mRowsSupportFragment.setEntranceTransitionState(false);
}
void setEntranceTransitionEndState() {
setHeadersOnScreen(mShowingHeaders);
setSearchOrbViewOnScreen(true);
mRowsSupportFragment.setEntranceTransitionState(true);
}
}