Gradle dependencies
compile group: 'androidx.leanback', name: 'leanback', version: '1.2.0-alpha04'
- groupId: androidx.leanback
- artifactId: leanback
- version: 1.2.0-alpha04
Artifact androidx.leanback:leanback:1.2.0-alpha04 it located at Google repository (https://maven.google.com/)
Androidx artifact mapping:
androidx.leanback:leanback com.android.support:leanback-v17
Androidx class mapping:
androidx.leanback.app.DetailsSupportFragment android.support.v17.leanback.app.DetailsSupportFragment
Overview
A fragment for creating Leanback details screens.
A DetailsSupportFragment renders the elements of its ObjectAdapter as a set
of rows in a vertical list.The Adapter's PresenterSelector must maintain subclasses
of RowPresenter.
When
FullWidthDetailsOverviewRowPresenter is found in adapter, DetailsSupportFragment will
setup default behavior of the DetailsOverviewRow:
The recommended activity themes to use with a DetailsSupportFragment are
DetailsSupportFragment can use DetailsSupportFragmentBackgroundController to add a parallax drawable
background and embedded video playing fragment.
Summary
Methods |
---|
protected java.lang.Object | createEntranceTransition()
Create entrance transition. |
public ObjectAdapter | getAdapter()
Returns the list of rows. |
public BaseOnItemViewClickedListener | getOnItemViewClickedListener()
Returns the item clicked listener. |
public DetailsParallax | getParallax()
Returns the DetailsParallax instance used by
DetailsSupportFragmentBackgroundController to configure parallax effect of background and
control embedded video playback. |
public RowsSupportFragment | getRowsSupportFragment()
Gets embedded RowsSupportFragment showing multiple rows for DetailsSupportFragment. |
protected View | inflateTitle(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState)
|
public void | onCreate(Bundle savedInstanceState)
Called to do initial creation of a fragment. |
public View | onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
Called to have the fragment instantiate its user interface view. |
public void | onDestroyView()
Called when the view previously created by Fragment.onCreateView(LayoutInflater, ViewGroup, Bundle) has
been detached from the fragment. |
protected void | onEntranceTransitionEnd()
Callback when entrance transition is ended. |
protected void | onEntranceTransitionPrepare()
Callback when entrance transition is prepared. |
protected void | onEntranceTransitionStart()
Callback when entrance transition is started. |
public View | onInflateTitleView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState)
Called by BrandedSupportFragment.installTitleView(LayoutInflater, ViewGroup, Bundle) to inflate
title view. |
protected void | onSetDetailsOverviewRowStatus(FullWidthDetailsOverviewRowPresenter presenter, FullWidthDetailsOverviewRowPresenter.ViewHolder viewHolder, int adapterPosition, int selectedPosition, int selectedSubPosition)
Called to change DetailsOverviewRow view status when current selected row position
or selected sub position changed. |
protected void | onSetRowStatus(RowPresenter presenter, RowPresenter.ViewHolder viewHolder, int adapterPosition, int selectedPosition, int selectedSubPosition)
Called on every visible row to change view status when current selected row position
or selected sub position changed. |
public void | onStart()
Called when the Fragment is visible to the user. |
public void | onStop()
Called when the Fragment is no longer started. |
protected void | runEntranceTransition(java.lang.Object entranceTransition)
Run entrance transition. |
public void | setAdapter(ObjectAdapter adapter)
Sets the list of rows for the fragment. |
public void | setOnItemViewClickedListener(BaseOnItemViewClickedListener listener)
Sets an item clicked listener. |
public void | setOnItemViewSelectedListener(BaseOnItemViewSelectedListener listener)
Sets an item selection listener. |
public void | setSelectedPosition(int position)
Sets the selected row position with smooth animation. |
public void | setSelectedPosition(int position, boolean smooth)
Sets the selected row position. |
protected void | setupDetailsOverviewRowPresenter(FullWidthDetailsOverviewRowPresenter presenter)
Called to setup FullWidthDetailsOverviewRowPresenter. |
protected void | setupPresenter(Presenter rowPresenter)
Called to setup each Presenter of Adapter passed in DetailsSupportFragment.setAdapter(ObjectAdapter).Note
that setup should only change the Presenter behavior that is meaningful in DetailsSupportFragment. |
from BaseSupportFragment | getProgressBarManager, onViewCreated, prepareEntranceTransition, startEntranceTransition |
from BrandedSupportFragment | getBadgeDrawable, getSearchAffordanceColor, getSearchAffordanceColors, getTitle, getTitleView, getTitleViewAdapter, installTitleView, isShowingTitle, onPause, onResume, onSaveInstanceState, setBadgeDrawable, setOnSearchClickedListener, setSearchAffordanceColor, setSearchAffordanceColors, setTitle, setTitleView, showTitle, showTitle |
from Fragment | dump, equals, getActivity, getAllowEnterTransitionOverlap, getAllowReturnTransitionOverlap, getArguments, getChildFragmentManager, getContext, getDefaultViewModelCreationExtras, getDefaultViewModelProviderFactory, getEnterTransition, getExitTransition, getFragmentManager, getHost, getId, getLayoutInflater, getLayoutInflater, getLifecycle, getLoaderManager, getParentFragment, getParentFragmentManager, getReenterTransition, getResources, getRetainInstance, getReturnTransition, getSavedStateRegistry, getSharedElementEnterTransition, getSharedElementReturnTransition, getString, getString, getTag, getTargetFragment, getTargetRequestCode, getText, getUserVisibleHint, getView, getViewLifecycleOwner, getViewLifecycleOwnerLiveData, getViewModelStore, hashCode, hasOptionsMenu, instantiate, instantiate, isAdded, isDetached, isHidden, isInLayout, isMenuVisible, isRemoving, isResumed, isStateSaved, isVisible, onActivityCreated, onActivityResult, onAttach, onAttach, onAttachFragment, onConfigurationChanged, onContextItemSelected, onCreateAnimation, onCreateAnimator, onCreateContextMenu, onCreateOptionsMenu, onDestroy, onDestroyOptionsMenu, onDetach, onGetLayoutInflater, onHiddenChanged, onInflate, onInflate, onLowMemory, onMultiWindowModeChanged, onOptionsItemSelected, onOptionsMenuClosed, onPictureInPictureModeChanged, onPrepareOptionsMenu, onPrimaryNavigationFragmentChanged, onRequestPermissionsResult, onViewStateRestored, postponeEnterTransition, postponeEnterTransition, registerForActivityResult, registerForActivityResult, registerForContextMenu, requestPermissions, requireActivity, requireArguments, requireContext, requireFragmentManager, requireHost, requireParentFragment, requireView, setAllowEnterTransitionOverlap, setAllowReturnTransitionOverlap, setArguments, setEnterSharedElementCallback, setEnterTransition, setExitSharedElementCallback, setExitTransition, setHasOptionsMenu, setInitialSavedState, setMenuVisibility, setReenterTransition, setRetainInstance, setReturnTransition, setSharedElementEnterTransition, setSharedElementReturnTransition, setTargetFragment, setUserVisibleHint, shouldShowRequestPermissionRationale, startActivity, startActivity, startActivityForResult, startActivityForResult, startIntentSenderForResult, startPostponedEnterTransition, toString, unregisterForContextMenu |
from java.lang.Object | clone, finalize, getClass, notify, notifyAll, wait, wait, wait |
Constructors
public
DetailsSupportFragment()
Methods
Sets the list of rows for the fragment.
Returns the list of rows.
Sets an item selection listener.
Sets an item clicked listener.
Returns the item clicked listener.
public void
onCreate(Bundle savedInstanceState)
Called to do initial creation of a fragment. This is called after
Fragment.onAttach(Activity) and before
Fragment.onCreateView(LayoutInflater, ViewGroup, Bundle).
Note that this can be called while the fragment's activity is
still in the process of being created. As such, you can not rely
on things like the activity's content view hierarchy being initialized
at this point. If you want to do work once the activity itself is
created, add a LifecycleObserver on the
activity's Lifecycle, removing it when it receives the
callback.
Any restored child fragments will be created before the base
Fragment.onCreate
method returns.
Parameters:
savedInstanceState: If the fragment is being re-created from
a previous saved state, this is the state.
public View
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
Called to have the fragment instantiate its user interface view.
This is optional, and non-graphical fragments can return null. This will be called between
Fragment.onCreate(Bundle) and Fragment.onViewCreated(View, Bundle).
A default View can be returned by calling Fragment.Fragment(int) in your
constructor. Otherwise, this method returns null.
It is recommended to only inflate the layout in this method and move
logic that operates on the returned View to Fragment.onViewCreated(View, Bundle).
If you return a View from here, you will later be called in
Fragment.onDestroyView() when the view is being released.
Parameters:
inflater: The LayoutInflater object that can be used to inflate
any views in the fragment,
container: If non-null, this is the parent view that the fragment's
UI should be attached to. The fragment should not add the view itself,
but this can be used to generate the LayoutParams of the view.
savedInstanceState: If non-null, this fragment is being re-constructed
from a previous saved state as given here.
Returns:
Return the View for the fragment's UI, or null.
public void
onDestroyView()
Called when the view previously created by Fragment.onCreateView(LayoutInflater, ViewGroup, Bundle) has
been detached from the fragment. The next time the fragment needs
to be displayed, a new view will be created. This is called
after Fragment.onStop() and before Fragment.onDestroy(). It is called
regardless of whether Fragment.onCreateView(LayoutInflater, ViewGroup, Bundle) returned a
non-null view. Internally it is called after the view's state has
been saved but before it has been removed from its parent.
protected View
inflateTitle(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState)
Deprecated: override BrandedSupportFragment.onInflateTitleView(LayoutInflater, ViewGroup, Bundle) instead.
public View
onInflateTitleView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState)
Called by BrandedSupportFragment.installTitleView(LayoutInflater, ViewGroup, Bundle) to inflate
title view. Default implementation uses layout file lb_browse_title.
Subclass may override and use its own layout, the layout must have a descendant with id
browse_title_group that implements . Subclass may return
null if no title is needed.
Parameters:
inflater: The LayoutInflater object that can be used to inflate
any views in the fragment,
parent: Parent of title view.
savedInstanceState: If non-null, this fragment is being re-constructed
from a previous saved state as given here.
Returns:
Title view which must have a descendant with id browse_title_group that implements
, or null for no title view.
protected void
setupPresenter(
Presenter rowPresenter)
Called to setup each Presenter of Adapter passed in DetailsSupportFragment.setAdapter(ObjectAdapter).Note
that setup should only change the Presenter behavior that is meaningful in DetailsSupportFragment.
For example how a row is aligned in details Fragment. The default implementation invokes
DetailsSupportFragment.setupDetailsOverviewRowPresenter(FullWidthDetailsOverviewRowPresenter)
Called to setup FullWidthDetailsOverviewRowPresenter. The default implementation
adds two alignment positions(ItemAlignmentFacet) for ViewHolder of
FullWidthDetailsOverviewRowPresenter to align in fragment.
Gets embedded RowsSupportFragment showing multiple rows for DetailsSupportFragment. If view of
DetailsSupportFragment is not created, the method returns null.
Returns:
Embedded RowsSupportFragment showing multiple rows for DetailsSupportFragment.
public void
setSelectedPosition(int position)
Sets the selected row position with smooth animation.
public void
setSelectedPosition(int position, boolean smooth)
Sets the selected row position.
Called when the Fragment is no longer started. This is generally
tied to of the containing
Activity's lifecycle.
Called on every visible row to change view status when current selected row position
or selected sub position changed. Subclass may override. The default
implementation calls DetailsSupportFragment.onSetDetailsOverviewRowStatus(FullWidthDetailsOverviewRowPresenter, FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int) if presenter is
instance of FullWidthDetailsOverviewRowPresenter.
Parameters:
presenter: The presenter used to create row ViewHolder.
viewHolder: The visible (attached) row ViewHolder, note that it may or may not
be selected.
adapterPosition: The adapter position of viewHolder inside adapter.
selectedPosition: The adapter position of currently selected row.
selectedSubPosition: The sub position within currently selected row. This is used
When a row has multiple alignment positions.
Called to change DetailsOverviewRow view status when current selected row position
or selected sub position changed. Subclass may override. The default
implementation switches between three states based on the positions:
FullWidthDetailsOverviewRowPresenter.STATE_HALF,
FullWidthDetailsOverviewRowPresenter.STATE_FULL and
FullWidthDetailsOverviewRowPresenter.STATE_SMALL.
Parameters:
presenter: The presenter used to create row ViewHolder.
viewHolder: The visible (attached) row ViewHolder, note that it may or may not
be selected.
adapterPosition: The adapter position of viewHolder inside adapter.
selectedPosition: The adapter position of currently selected row.
selectedSubPosition: The sub position within currently selected row. This is used
When a row has multiple alignment positions.
Called when the Fragment is visible to the user. This is generally
tied to of the containing
Activity's lifecycle.
protected java.lang.Object
createEntranceTransition()
Create entrance transition. Subclass can override to load transition from
resource or construct manually. Typically app does not need to
override the default transition that browse and details provides.
protected void
runEntranceTransition(java.lang.Object entranceTransition)
Run entrance transition. Subclass may use TransitionManager to perform
go(Scene) or beginDelayedTransition(). App should not override the default
implementation of browse and details fragment.
protected void
onEntranceTransitionEnd()
Callback when entrance transition is ended.
protected void
onEntranceTransitionPrepare()
Callback when entrance transition is prepared. This is when fragment should
stop user input and animations.
protected void
onEntranceTransitionStart()
Callback when entrance transition is started. This is when fragment should
stop processing layout.
Returns the DetailsParallax instance used by
DetailsSupportFragmentBackgroundController to configure parallax effect of background and
control embedded video playback. App usually does not use this method directly.
App may use this method for other custom parallax tasks.
Returns:
The DetailsParallax instance attached to the DetailsSupportFragment.
Source
// CHECKSTYLE:OFF Generated code
/* This file is auto-generated from DetailsFragment.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 androidx.leanback.app;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentTransaction;
import androidx.leanback.R;
import androidx.leanback.transition.TransitionHelper;
import androidx.leanback.transition.TransitionListener;
import androidx.leanback.util.StateMachine.Event;
import androidx.leanback.util.StateMachine.State;
import androidx.leanback.widget.BaseOnItemViewClickedListener;
import androidx.leanback.widget.BaseOnItemViewSelectedListener;
import androidx.leanback.widget.BrowseFrameLayout;
import androidx.leanback.widget.DetailsParallax;
import androidx.leanback.widget.FullWidthDetailsOverviewRowPresenter;
import androidx.leanback.widget.ItemAlignmentFacet;
import androidx.leanback.widget.ItemBridgeAdapter;
import androidx.leanback.widget.ObjectAdapter;
import androidx.leanback.widget.Presenter;
import androidx.leanback.widget.PresenterSelector;
import androidx.leanback.widget.RowPresenter;
import androidx.leanback.widget.VerticalGridView;
import java.lang.ref.WeakReference;
/**
* A fragment for creating Leanback details screens.
*
* <p>
* A DetailsSupportFragment renders the elements of its {@link ObjectAdapter} as a set
* of rows in a vertical list.The Adapter's {@link PresenterSelector} must maintain subclasses
* of {@link RowPresenter}.
* </p>
*
* When {@link FullWidthDetailsOverviewRowPresenter} is found in adapter, DetailsSupportFragment will
* setup default behavior of the DetailsOverviewRow:
* <ul>
* <li>
* The alignment of FullWidthDetailsOverviewRowPresenter is setup in
* {@link #setupDetailsOverviewRowPresenter(FullWidthDetailsOverviewRowPresenter)}.
* </li>
* <li>
* The view status switching of FullWidthDetailsOverviewRowPresenter is done in
* {@link #onSetDetailsOverviewRowStatus(FullWidthDetailsOverviewRowPresenter,
* FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int)}.
* </li>
* </ul>
*
* <p>
* The recommended activity themes to use with a DetailsSupportFragment are
* <ul>
* <li>
* {@link androidx.leanback.R.style#Theme_Leanback_Details} with activity
* shared element transition for {@link FullWidthDetailsOverviewRowPresenter}.
* </li>
* <li>
* {@link androidx.leanback.R.style#Theme_Leanback_Details_NoSharedElementTransition}
* if shared element transition is not needed, for example if first row is not rendered by
* {@link FullWidthDetailsOverviewRowPresenter}.
* </li>
* </ul>
* </p>
*
* <p>
* DetailsSupportFragment can use {@link DetailsSupportFragmentBackgroundController} to add a parallax drawable
* background and embedded video playing fragment.
* </p>
*/
public class DetailsSupportFragment extends BaseSupportFragment {
static final String TAG = "DetailsSupportFragment";
static final boolean DEBUG = false;
final State STATE_SET_ENTRANCE_START_STATE = new State("STATE_SET_ENTRANCE_START_STATE") {
@Override
public void run() {
mRowsSupportFragment.setEntranceTransitionState(false);
}
};
final State STATE_ENTER_TRANSITION_INIT = new State("STATE_ENTER_TRANSIITON_INIT");
void switchToVideoBeforeVideoSupportFragmentCreated() {
// if the video fragment is not ready: immediately fade out covering drawable,
// hide title and mark mPendingFocusOnVideo and set focus on it later.
mDetailsBackgroundController.switchToVideoBeforeCreate();
showTitle(false);
mPendingFocusOnVideo = true;
slideOutGridView();
}
final State STATE_SWITCH_TO_VIDEO_IN_ON_CREATE = new State("STATE_SWITCH_TO_VIDEO_IN_ON_CREATE",
false, false) {
@Override
public void run() {
switchToVideoBeforeVideoSupportFragmentCreated();
}
};
final State STATE_ENTER_TRANSITION_CANCEL = new State("STATE_ENTER_TRANSITION_CANCEL",
false, false) {
@Override
public void run() {
if (mWaitEnterTransitionTimeout != null) {
mWaitEnterTransitionTimeout.mRef.clear();
}
// clear the activity enter/sharedElement transition, return transitions are kept.
// keep the return transitions and clear enter transition
if (getActivity() != null) {
Window window = getActivity().getWindow();
Object returnTransition = TransitionHelper.getReturnTransition(window);
Object sharedReturnTransition = TransitionHelper
.getSharedElementReturnTransition(window);
TransitionHelper.setEnterTransition(window, null);
TransitionHelper.setSharedElementEnterTransition(window, null);
TransitionHelper.setReturnTransition(window, returnTransition);
TransitionHelper.setSharedElementReturnTransition(window, sharedReturnTransition);
}
}
};
final State STATE_ENTER_TRANSITION_COMPLETE = new State("STATE_ENTER_TRANSIITON_COMPLETE",
true, false);
final State STATE_ENTER_TRANSITION_ADDLISTENER = new State("STATE_ENTER_TRANSITION_PENDING") {
@Override
public void run() {
Object transition = TransitionHelper.getEnterTransition(getActivity().getWindow());
TransitionHelper.addTransitionListener(transition, mEnterTransitionListener);
}
};
final State STATE_ENTER_TRANSITION_PENDING = new State("STATE_ENTER_TRANSITION_PENDING") {
@Override
public void run() {
if (mWaitEnterTransitionTimeout == null) {
new WaitEnterTransitionTimeout(DetailsSupportFragment.this);
}
}
};
/**
* Start this task when first DetailsOverviewRow is created, if there is no entrance transition
* started, it will clear PF_ENTRANCE_TRANSITION_PENDING.
*/
static final class WaitEnterTransitionTimeout implements Runnable {
static final long WAIT_ENTERTRANSITION_START = 200;
final WeakReference<DetailsSupportFragment> mRef;
WaitEnterTransitionTimeout(DetailsSupportFragment f) {
mRef = new WeakReference<>(f);
f.getView().postDelayed(this, WAIT_ENTERTRANSITION_START);
}
@Override
public void run() {
DetailsSupportFragment f = mRef.get();
if (f != null) {
f.mStateMachine.fireEvent(f.EVT_ENTER_TRANSIITON_DONE);
}
}
}
final State STATE_ON_SAFE_START = new State("STATE_ON_SAFE_START") {
@Override
public void run() {
onSafeStart();
}
};
final Event EVT_ONSTART = new Event("onStart");
final Event EVT_NO_ENTER_TRANSITION = new Event("EVT_NO_ENTER_TRANSITION");
final Event EVT_DETAILS_ROW_LOADED = new Event("onFirstRowLoaded");
final Event EVT_ENTER_TRANSIITON_DONE = new Event("onEnterTransitionDone");
final Event EVT_SWITCH_TO_VIDEO = new Event("switchToVideo");
@Override
void createStateMachineStates() {
super.createStateMachineStates();
mStateMachine.addState(STATE_SET_ENTRANCE_START_STATE);
mStateMachine.addState(STATE_ON_SAFE_START);
mStateMachine.addState(STATE_SWITCH_TO_VIDEO_IN_ON_CREATE);
mStateMachine.addState(STATE_ENTER_TRANSITION_INIT);
mStateMachine.addState(STATE_ENTER_TRANSITION_ADDLISTENER);
mStateMachine.addState(STATE_ENTER_TRANSITION_CANCEL);
mStateMachine.addState(STATE_ENTER_TRANSITION_PENDING);
mStateMachine.addState(STATE_ENTER_TRANSITION_COMPLETE);
}
@Override
void createStateMachineTransitions() {
super.createStateMachineTransitions();
/**
* Part 1: Processing enter transitions after fragment.onCreate
*/
mStateMachine.addTransition(STATE_START, STATE_ENTER_TRANSITION_INIT, EVT_ON_CREATE);
// if transition is not supported, skip to complete
mStateMachine.addTransition(STATE_ENTER_TRANSITION_INIT, STATE_ENTER_TRANSITION_COMPLETE,
COND_TRANSITION_NOT_SUPPORTED);
// if transition is not set on Activity, skip to complete
mStateMachine.addTransition(STATE_ENTER_TRANSITION_INIT, STATE_ENTER_TRANSITION_COMPLETE,
EVT_NO_ENTER_TRANSITION);
// if switchToVideo is called before EVT_ON_CREATEVIEW, clear enter transition and skip to
// complete.
mStateMachine.addTransition(STATE_ENTER_TRANSITION_INIT, STATE_ENTER_TRANSITION_CANCEL,
EVT_SWITCH_TO_VIDEO);
mStateMachine.addTransition(STATE_ENTER_TRANSITION_CANCEL, STATE_ENTER_TRANSITION_COMPLETE);
// once after onCreateView, we cannot skip the enter transition, add a listener and wait
// it to finish
mStateMachine.addTransition(STATE_ENTER_TRANSITION_INIT, STATE_ENTER_TRANSITION_ADDLISTENER,
EVT_ON_CREATEVIEW);
// when enter transition finishes, go to complete, however this might never happen if
// the activity is not giving transition options in startActivity, there is no API to query
// if this activity is started in a enter transition mode. So we rely on a timer below:
mStateMachine.addTransition(STATE_ENTER_TRANSITION_ADDLISTENER,
STATE_ENTER_TRANSITION_COMPLETE, EVT_ENTER_TRANSIITON_DONE);
// we are expecting app to start delayed enter transition shortly after details row is
// loaded, so create a timer and wait for enter transition start.
mStateMachine.addTransition(STATE_ENTER_TRANSITION_ADDLISTENER,
STATE_ENTER_TRANSITION_PENDING, EVT_DETAILS_ROW_LOADED);
// if enter transition not started in the timer, skip to DONE, this can be also true when
// startActivity is not giving transition option.
mStateMachine.addTransition(STATE_ENTER_TRANSITION_PENDING, STATE_ENTER_TRANSITION_COMPLETE,
EVT_ENTER_TRANSIITON_DONE);
/**
* Part 2: modification to the entrance transition defined in BaseSupportFragment
*/
// Must finish enter transition before perform entrance transition.
mStateMachine.addTransition(STATE_ENTER_TRANSITION_COMPLETE, STATE_ENTRANCE_PERFORM);
// Calling switch to video would hide immediately and skip entrance transition
mStateMachine.addTransition(STATE_ENTRANCE_INIT, STATE_SWITCH_TO_VIDEO_IN_ON_CREATE,
EVT_SWITCH_TO_VIDEO);
mStateMachine.addTransition(STATE_SWITCH_TO_VIDEO_IN_ON_CREATE, STATE_ENTRANCE_COMPLETE);
// if the entrance transition is skipped to complete by COND_TRANSITION_NOT_SUPPORTED, we
// still need to do the switchToVideo.
mStateMachine.addTransition(STATE_ENTRANCE_COMPLETE, STATE_SWITCH_TO_VIDEO_IN_ON_CREATE,
EVT_SWITCH_TO_VIDEO);
// for once the view is created in onStart and prepareEntranceTransition was called, we
// could setEntranceStartState:
mStateMachine.addTransition(STATE_ENTRANCE_ON_PREPARED,
STATE_SET_ENTRANCE_START_STATE, EVT_ONSTART);
/**
* Part 3: onSafeStart()
*/
// for onSafeStart: the condition is onStart called, entrance transition complete
mStateMachine.addTransition(STATE_START, STATE_ON_SAFE_START, EVT_ONSTART);
mStateMachine.addTransition(STATE_ENTRANCE_COMPLETE, STATE_ON_SAFE_START);
mStateMachine.addTransition(STATE_ENTER_TRANSITION_COMPLETE, STATE_ON_SAFE_START);
}
private class SetSelectionRunnable implements Runnable {
int mPosition;
boolean mSmooth = true;
SetSelectionRunnable() {
}
@Override
public void run() {
if (mRowsSupportFragment == null) {
return;
}
mRowsSupportFragment.setSelectedPosition(mPosition, mSmooth);
}
}
static final class EnterTransitionListener extends TransitionListener {
final WeakReference<DetailsSupportFragment> mFragment;
EnterTransitionListener(DetailsSupportFragment fragment) {
mFragment = new WeakReference<>(fragment);
}
@Override
public void onTransitionStart(Object transition) {
DetailsSupportFragment fragment = mFragment.get();
if (fragment == null) {
return;
}
if (fragment.mWaitEnterTransitionTimeout != null) {
// cancel task of WaitEnterTransitionTimeout, we will clearPendingEnterTransition
// when transition finishes.
fragment.mWaitEnterTransitionTimeout.mRef.clear();
}
}
@Override
public void onTransitionCancel(Object transition) {
DetailsSupportFragment fragment = mFragment.get();
if (fragment == null) {
return;
}
fragment.mStateMachine.fireEvent(fragment.EVT_ENTER_TRANSIITON_DONE);
}
@Override
public void onTransitionEnd(Object transition) {
DetailsSupportFragment fragment = mFragment.get();
if (fragment == null) {
return;
}
fragment.mStateMachine.fireEvent(fragment.EVT_ENTER_TRANSIITON_DONE);
}
}
final TransitionListener mEnterTransitionListener = new EnterTransitionListener(this);
static final class ReturnTransitionListener extends TransitionListener {
final WeakReference<DetailsSupportFragment> mFragment;
ReturnTransitionListener(DetailsSupportFragment fragment) {
mFragment = new WeakReference<>(fragment);
}
@Override
public void onTransitionStart(Object transition) {
DetailsSupportFragment fragment = mFragment.get();
if (fragment == null) {
return;
}
fragment.onReturnTransitionStart();
}
}
final TransitionListener mReturnTransitionListener = new ReturnTransitionListener(this);
BrowseFrameLayout mRootView;
View mBackgroundView;
Drawable mBackgroundDrawable;
Fragment mVideoSupportFragment;
DetailsParallax mDetailsParallax;
RowsSupportFragment mRowsSupportFragment;
ObjectAdapter mAdapter;
int mContainerListAlignTop;
BaseOnItemViewSelectedListener mExternalOnItemViewSelectedListener;
BaseOnItemViewClickedListener mOnItemViewClickedListener;
DetailsSupportFragmentBackgroundController mDetailsBackgroundController;
// A temporarily flag when switchToVideo() is called in onCreate(), if mPendingFocusOnVideo is
// true, we will focus to VideoSupportFragment immediately after video fragment's view is created.
boolean mPendingFocusOnVideo = false;
WaitEnterTransitionTimeout mWaitEnterTransitionTimeout;
Object mSceneAfterEntranceTransition;
final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
final BaseOnItemViewSelectedListener<Object> mOnItemViewSelectedListener =
new BaseOnItemViewSelectedListener<Object>() {
@Override
@SuppressWarnings("unchecked")
public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
RowPresenter.ViewHolder rowViewHolder, Object row) {
int position = mRowsSupportFragment.getVerticalGridView().getSelectedPosition();
int subposition = mRowsSupportFragment.getVerticalGridView().getSelectedSubPosition();
if (DEBUG) Log.v(TAG, "row selected position " + position
+ " subposition " + subposition);
onRowSelected(position, subposition);
if (mExternalOnItemViewSelectedListener != null) {
mExternalOnItemViewSelectedListener.onItemSelected(itemViewHolder, item,
rowViewHolder, row);
}
}
};
/**
* Sets the list of rows for the fragment.
*/
public void setAdapter(ObjectAdapter adapter) {
mAdapter = adapter;
Presenter[] presenters = adapter.getPresenterSelector().getPresenters();
if (presenters != null) {
for (int i = 0; i < presenters.length; i++) {
setupPresenter(presenters[i]);
}
} else {
Log.e(TAG, "PresenterSelector.getPresenters() not implemented");
}
if (mRowsSupportFragment != null) {
mRowsSupportFragment.setAdapter(adapter);
}
}
/**
* Returns the list of rows.
*/
public ObjectAdapter getAdapter() {
return mAdapter;
}
/**
* Sets an item selection listener.
*/
public void setOnItemViewSelectedListener(BaseOnItemViewSelectedListener listener) {
mExternalOnItemViewSelectedListener = listener;
}
/**
* Sets an item clicked listener.
*/
public void setOnItemViewClickedListener(BaseOnItemViewClickedListener listener) {
if (mOnItemViewClickedListener != listener) {
mOnItemViewClickedListener = listener;
if (mRowsSupportFragment != null) {
mRowsSupportFragment.setOnItemViewClickedListener(listener);
}
}
}
/**
* Returns the item clicked listener.
*/
public BaseOnItemViewClickedListener getOnItemViewClickedListener() {
return mOnItemViewClickedListener;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContainerListAlignTop =
getResources().getDimensionPixelSize(R.dimen.lb_details_rows_align_top);
FragmentActivity activity = getActivity();
if (activity != null) {
Object transition = TransitionHelper.getEnterTransition(activity.getWindow());
if (transition == null) {
mStateMachine.fireEvent(EVT_NO_ENTER_TRANSITION);
}
transition = TransitionHelper.getReturnTransition(activity.getWindow());
if (transition != null) {
TransitionHelper.addTransitionListener(transition, mReturnTransitionListener);
}
} else {
mStateMachine.fireEvent(EVT_NO_ENTER_TRANSITION);
}
}
@Override
@Nullable
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
mRootView = (BrowseFrameLayout) inflater.inflate(
R.layout.lb_details_fragment, container, false);
mBackgroundView = mRootView.findViewById(R.id.details_background_view);
if (mBackgroundView != null) {
mBackgroundView.setBackground(mBackgroundDrawable);
}
mRowsSupportFragment = (RowsSupportFragment) getChildFragmentManager().findFragmentById(
R.id.details_rows_dock);
if (mRowsSupportFragment == null) {
mRowsSupportFragment = new RowsSupportFragment();
getChildFragmentManager().beginTransaction()
.replace(R.id.details_rows_dock, mRowsSupportFragment).commit();
}
installTitleView(inflater, mRootView, savedInstanceState);
mRowsSupportFragment.setAdapter(mAdapter);
mRowsSupportFragment.setOnItemViewSelectedListener(mOnItemViewSelectedListener);
mRowsSupportFragment.setOnItemViewClickedListener(mOnItemViewClickedListener);
mSceneAfterEntranceTransition = TransitionHelper.createScene(mRootView, new Runnable() {
@Override
public void run() {
mRowsSupportFragment.setEntranceTransitionState(true);
}
});
setupDpadNavigation();
if (Build.VERSION.SDK_INT >= 21) {
// Setup adapter listener to work with ParallaxTransition (>= API 21).
mRowsSupportFragment.setExternalAdapterListener(new ItemBridgeAdapter.AdapterListener() {
@Override
public void onCreate(ItemBridgeAdapter.ViewHolder vh) {
if (mDetailsParallax != null && vh.getViewHolder()
instanceof FullWidthDetailsOverviewRowPresenter.ViewHolder) {
FullWidthDetailsOverviewRowPresenter.ViewHolder rowVh =
(FullWidthDetailsOverviewRowPresenter.ViewHolder)
vh.getViewHolder();
rowVh.getOverviewView().setTag(R.id.lb_parallax_source,
mDetailsParallax);
}
}
});
}
return mRootView;
}
@Override
public void onDestroyView() {
if (mDetailsParallax != null) {
mDetailsParallax.setRecyclerView(null);
}
mRootView = null;
mBackgroundView = null;
mRowsSupportFragment = null;
mVideoSupportFragment = null;
mSceneAfterEntranceTransition = null;
super.onDestroyView();
}
/**
* @deprecated override {@link BrandedSupportFragment#onInflateTitleView(LayoutInflater, ViewGroup, Bundle)} instead.
*/
@Deprecated
protected View inflateTitle(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
return super.onInflateTitleView(inflater, parent, savedInstanceState);
}
@Override
public @NonNull View onInflateTitleView(
@NonNull LayoutInflater inflater,
@Nullable ViewGroup parent,
@Nullable Bundle savedInstanceState
) {
return inflateTitle(inflater, parent, savedInstanceState);
}
void setVerticalGridViewLayout(VerticalGridView listview) {
// align the top edge of item to a fixed position
listview.setItemAlignmentOffset(-mContainerListAlignTop);
listview.setItemAlignmentOffsetPercent(VerticalGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
listview.setWindowAlignmentOffset(0);
listview.setWindowAlignmentOffsetPercent(VerticalGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
listview.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
}
/**
* Called to setup each Presenter of Adapter passed in {@link #setAdapter(ObjectAdapter)}.Note
* that setup should only change the Presenter behavior that is meaningful in DetailsSupportFragment.
* For example how a row is aligned in details Fragment. The default implementation invokes
* {@link #setupDetailsOverviewRowPresenter(FullWidthDetailsOverviewRowPresenter)}
*
*/
protected void setupPresenter(Presenter rowPresenter) {
if (rowPresenter instanceof FullWidthDetailsOverviewRowPresenter) {
setupDetailsOverviewRowPresenter((FullWidthDetailsOverviewRowPresenter) rowPresenter);
}
}
/**
* Called to setup {@link FullWidthDetailsOverviewRowPresenter}. The default implementation
* adds two alignment positions({@link ItemAlignmentFacet}) for ViewHolder of
* FullWidthDetailsOverviewRowPresenter to align in fragment.
*/
protected void setupDetailsOverviewRowPresenter(FullWidthDetailsOverviewRowPresenter presenter) {
ItemAlignmentFacet facet = new ItemAlignmentFacet();
// by default align details_frame to half window height
ItemAlignmentFacet.ItemAlignmentDef alignDef1 = new ItemAlignmentFacet.ItemAlignmentDef();
alignDef1.setItemAlignmentViewId(R.id.details_frame);
alignDef1.setItemAlignmentOffset(- getResources()
.getDimensionPixelSize(R.dimen.lb_details_v2_align_pos_for_actions));
alignDef1.setItemAlignmentOffsetPercent(0);
// when description is selected, align details_frame to top edge
ItemAlignmentFacet.ItemAlignmentDef alignDef2 = new ItemAlignmentFacet.ItemAlignmentDef();
alignDef2.setItemAlignmentViewId(R.id.details_frame);
alignDef2.setItemAlignmentFocusViewId(R.id.details_overview_description);
alignDef2.setItemAlignmentOffset(- getResources()
.getDimensionPixelSize(R.dimen.lb_details_v2_align_pos_for_description));
alignDef2.setItemAlignmentOffsetPercent(0);
ItemAlignmentFacet.ItemAlignmentDef[] defs =
new ItemAlignmentFacet.ItemAlignmentDef[] {alignDef1, alignDef2};
facet.setAlignmentDefs(defs);
presenter.setFacet(ItemAlignmentFacet.class, facet);
}
VerticalGridView getVerticalGridView() {
return mRowsSupportFragment == null ? null : mRowsSupportFragment.getVerticalGridView();
}
/**
* Gets embedded RowsSupportFragment showing multiple rows for DetailsSupportFragment. If view of
* DetailsSupportFragment is not created, the method returns null.
* @return Embedded RowsSupportFragment showing multiple rows for DetailsSupportFragment.
*/
public RowsSupportFragment getRowsSupportFragment() {
return mRowsSupportFragment;
}
/**
* Setup dimensions that are only meaningful when the child Fragments are inside
* DetailsSupportFragment.
*/
private void setupChildFragmentLayout() {
setVerticalGridViewLayout(mRowsSupportFragment.getVerticalGridView());
}
/**
* 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.mPosition = position;
mSetSelectionRunnable.mSmooth = smooth;
if (getView() != null && getView().getHandler() != null) {
getView().getHandler().post(mSetSelectionRunnable);
}
}
void switchToVideo() {
if (mVideoSupportFragment != null && mVideoSupportFragment.getView() != null) {
mVideoSupportFragment.getView().requestFocus();
} else {
mStateMachine.fireEvent(EVT_SWITCH_TO_VIDEO);
}
}
void switchToRows() {
mPendingFocusOnVideo = false;
VerticalGridView verticalGridView = getVerticalGridView();
if (verticalGridView != null && verticalGridView.getChildCount() > 0) {
verticalGridView.requestFocus();
}
}
/**
* This method asks DetailsSupportFragmentBackgroundController to add a fragment for rendering video.
* In case the fragment is already there, it will return the existing one. The method must be
* called after calling super.onCreate(). App usually does not call this method directly.
*
* @return Fragment the added or restored fragment responsible for rendering video.
* @see DetailsSupportFragmentBackgroundController#onCreateVideoSupportFragment()
*/
final Fragment findOrCreateVideoSupportFragment() {
if (mVideoSupportFragment != null) {
return mVideoSupportFragment;
}
Fragment fragment = getChildFragmentManager()
.findFragmentById(R.id.video_surface_container);
if (fragment == null && mDetailsBackgroundController != null) {
FragmentTransaction ft2 = getChildFragmentManager().beginTransaction();
ft2.add(androidx.leanback.R.id.video_surface_container,
fragment = mDetailsBackgroundController.onCreateVideoSupportFragment());
ft2.commit();
if (mPendingFocusOnVideo) {
// wait next cycle for Fragment view created so we can focus on it.
// This is a bit hack eventually we will do commitNow() which get view immediately.
getView().post(new Runnable() {
@Override
public void run() {
if (getView() != null) {
switchToVideo();
}
mPendingFocusOnVideo = false;
}
});
}
}
mVideoSupportFragment = fragment;
return mVideoSupportFragment;
}
void onRowSelected(int selectedPosition, int selectedSubPosition) {
ObjectAdapter adapter = getAdapter();
if (( mRowsSupportFragment != null && mRowsSupportFragment.getView() != null
&& mRowsSupportFragment.getView().hasFocus() && !mPendingFocusOnVideo)
&& (adapter == null || adapter.size() == 0
|| (getVerticalGridView().getSelectedPosition() == 0
&& getVerticalGridView().getSelectedSubPosition() == 0))) {
showTitle(true);
} else {
showTitle(false);
}
if (adapter != null && adapter.size() > selectedPosition) {
final VerticalGridView gridView = getVerticalGridView();
final int count = gridView.getChildCount();
if (count > 0) {
mStateMachine.fireEvent(EVT_DETAILS_ROW_LOADED);
}
for (int i = 0; i < count; i++) {
ItemBridgeAdapter.ViewHolder bridgeViewHolder = (ItemBridgeAdapter.ViewHolder)
gridView.getChildViewHolder(gridView.getChildAt(i));
RowPresenter rowPresenter = (RowPresenter) bridgeViewHolder.getPresenter();
onSetRowStatus(rowPresenter,
rowPresenter.getRowViewHolder(bridgeViewHolder.getViewHolder()),
bridgeViewHolder.getAbsoluteAdapterPosition(),
selectedPosition, selectedSubPosition);
}
}
}
/**
* Called when onStart and enter transition (postponed/none postponed) and entrance transition
* are all finished.
*/
@CallSuper
void onSafeStart() {
if (mDetailsBackgroundController != null) {
mDetailsBackgroundController.onStart();
}
}
@CallSuper
void onReturnTransitionStart() {
if (mDetailsBackgroundController != null) {
// first disable parallax effect that auto-start PlaybackGlue.
boolean isVideoVisible = mDetailsBackgroundController.disableVideoParallax();
// if video is not visible we can safely remove VideoSupportFragment,
// otherwise let video playing during return transition.
if (!isVideoVisible && mVideoSupportFragment != null) {
FragmentTransaction ft2 = getChildFragmentManager().beginTransaction();
ft2.remove(mVideoSupportFragment);
ft2.commit();
mVideoSupportFragment = null;
}
}
}
@Override
public void onStop() {
if (mDetailsBackgroundController != null) {
mDetailsBackgroundController.onStop();
}
super.onStop();
}
/**
* Called on every visible row to change view status when current selected row position
* or selected sub position changed. Subclass may override. The default
* implementation calls {@link #onSetDetailsOverviewRowStatus(FullWidthDetailsOverviewRowPresenter,
* FullWidthDetailsOverviewRowPresenter.ViewHolder, int, int, int)} if presenter is
* instance of {@link FullWidthDetailsOverviewRowPresenter}.
*
* @param presenter The presenter used to create row ViewHolder.
* @param viewHolder The visible (attached) row ViewHolder, note that it may or may not
* be selected.
* @param adapterPosition The adapter position of viewHolder inside adapter.
* @param selectedPosition The adapter position of currently selected row.
* @param selectedSubPosition The sub position within currently selected row. This is used
* When a row has multiple alignment positions.
*/
protected void onSetRowStatus(RowPresenter presenter, RowPresenter.ViewHolder viewHolder, int
adapterPosition, int selectedPosition, int selectedSubPosition) {
if (presenter instanceof FullWidthDetailsOverviewRowPresenter) {
onSetDetailsOverviewRowStatus((FullWidthDetailsOverviewRowPresenter) presenter,
(FullWidthDetailsOverviewRowPresenter.ViewHolder) viewHolder,
adapterPosition, selectedPosition, selectedSubPosition);
}
}
/**
* Called to change DetailsOverviewRow view status when current selected row position
* or selected sub position changed. Subclass may override. The default
* implementation switches between three states based on the positions:
* {@link FullWidthDetailsOverviewRowPresenter#STATE_HALF},
* {@link FullWidthDetailsOverviewRowPresenter#STATE_FULL} and
* {@link FullWidthDetailsOverviewRowPresenter#STATE_SMALL}.
*
* @param presenter The presenter used to create row ViewHolder.
* @param viewHolder The visible (attached) row ViewHolder, note that it may or may not
* be selected.
* @param adapterPosition The adapter position of viewHolder inside adapter.
* @param selectedPosition The adapter position of currently selected row.
* @param selectedSubPosition The sub position within currently selected row. This is used
* When a row has multiple alignment positions.
*/
protected void onSetDetailsOverviewRowStatus(FullWidthDetailsOverviewRowPresenter presenter,
FullWidthDetailsOverviewRowPresenter.ViewHolder viewHolder, int adapterPosition,
int selectedPosition, int selectedSubPosition) {
if (selectedPosition > adapterPosition) {
presenter.setState(viewHolder, FullWidthDetailsOverviewRowPresenter.STATE_HALF);
} else if (selectedPosition == adapterPosition && selectedSubPosition == 1) {
presenter.setState(viewHolder, FullWidthDetailsOverviewRowPresenter.STATE_HALF);
} else if (selectedPosition == adapterPosition && selectedSubPosition == 0){
presenter.setState(viewHolder, FullWidthDetailsOverviewRowPresenter.STATE_FULL);
} else {
presenter.setState(viewHolder,
FullWidthDetailsOverviewRowPresenter.STATE_SMALL);
}
}
@Override
public void onStart() {
super.onStart();
setupChildFragmentLayout();
mStateMachine.fireEvent(EVT_ONSTART);
if (mDetailsParallax != null) {
mDetailsParallax.setRecyclerView(mRowsSupportFragment.getVerticalGridView());
}
if (mPendingFocusOnVideo) {
slideOutGridView();
} else if (!getView().hasFocus()) {
mRowsSupportFragment.getVerticalGridView().requestFocus();
}
}
@Override
protected Object createEntranceTransition() {
return TransitionHelper.loadTransition(getContext(),
R.transition.lb_details_enter_transition);
}
@Override
protected void runEntranceTransition(Object entranceTransition) {
TransitionHelper.runTransition(mSceneAfterEntranceTransition, entranceTransition);
}
@Override
protected void onEntranceTransitionEnd() {
mRowsSupportFragment.onTransitionEnd();
}
@Override
protected void onEntranceTransitionPrepare() {
mRowsSupportFragment.onTransitionPrepare();
}
@Override
protected void onEntranceTransitionStart() {
mRowsSupportFragment.onTransitionStart();
}
/**
* Returns the {@link DetailsParallax} instance used by
* {@link DetailsSupportFragmentBackgroundController} to configure parallax effect of background and
* control embedded video playback. App usually does not use this method directly.
* App may use this method for other custom parallax tasks.
*
* @return The DetailsParallax instance attached to the DetailsSupportFragment.
*/
public DetailsParallax getParallax() {
if (mDetailsParallax == null) {
mDetailsParallax = new DetailsParallax();
if (mRowsSupportFragment != null && mRowsSupportFragment.getView() != null) {
mDetailsParallax.setRecyclerView(mRowsSupportFragment.getVerticalGridView());
}
}
return mDetailsParallax;
}
/**
* Set background drawable shown below foreground rows UI and above
* {@link #findOrCreateVideoSupportFragment()}.
*
* @see DetailsSupportFragmentBackgroundController
*/
void setBackgroundDrawable(Drawable drawable) {
if (mBackgroundView != null) {
mBackgroundView.setBackground(drawable);
}
mBackgroundDrawable = drawable;
}
/**
* This method does the following
* <ul>
* <li>sets up focus search handling logic in the root view to enable transitioning between
* half screen/full screen/no video mode.</li>
*
* <li>Sets up the key listener in the root view to intercept events like UP/DOWN and
* transition to appropriate mode like half/full screen video.</li>
* </ul>
*/
void setupDpadNavigation() {
mRootView.setOnChildFocusListener(new BrowseFrameLayout.OnChildFocusListener() {
@Override
public boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
return false;
}
@Override
public void onRequestChildFocus(View child, View focused) {
if (child != mRootView.getFocusedChild()) {
if (child.getId() == R.id.details_fragment_root) {
if (!mPendingFocusOnVideo) {
slideInGridView();
showTitle(true);
}
} else if (child.getId() == R.id.video_surface_container) {
slideOutGridView();
showTitle(false);
} else {
showTitle(true);
}
}
}
});
mRootView.setOnFocusSearchListener(new BrowseFrameLayout.OnFocusSearchListener() {
@Override
public View onFocusSearch(View focused, int direction) {
if (mRowsSupportFragment.getVerticalGridView() != null
&& mRowsSupportFragment.getVerticalGridView().hasFocus()) {
if (direction == View.FOCUS_UP) {
if (mDetailsBackgroundController != null
&& mDetailsBackgroundController.canNavigateToVideoSupportFragment()
&& mVideoSupportFragment != null && mVideoSupportFragment.getView() != null) {
return mVideoSupportFragment.getView();
} else if (getTitleView() != null && getTitleView().hasFocusable()) {
return getTitleView();
}
}
} else if (getTitleView() != null && getTitleView().hasFocus()) {
if (direction == View.FOCUS_DOWN) {
if (mRowsSupportFragment.getVerticalGridView() != null) {
return mRowsSupportFragment.getVerticalGridView();
}
}
}
return focused;
}
});
// If we press BACK on remote while in full screen video mode, we should
// transition back to half screen video playback mode.
mRootView.setOnDispatchKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// This is used to check if we are in full screen video mode. This is somewhat
// hacky and relies on the behavior of the video helper class to update the
// focusability of the video surface view.
if (mVideoSupportFragment != null && mVideoSupportFragment.getView() != null
&& mVideoSupportFragment.getView().hasFocus()) {
if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) {
if (getVerticalGridView().getChildCount() > 0) {
getVerticalGridView().requestFocus();
return true;
}
}
}
return false;
}
});
}
/**
* Slides vertical grid view (displaying media item details) out of the screen from below.
*/
void slideOutGridView() {
if (getVerticalGridView() != null) {
getVerticalGridView().animateOut();
}
}
void slideInGridView() {
if (getVerticalGridView() != null) {
getVerticalGridView().animateIn();
}
}
}