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.GuidedStepSupportFragment android.support.v17.leanback.app.GuidedStepSupportFragment
Overview
A GuidedStepSupportFragment is used to guide the user through a decision or series of decisions.
It is composed of a guidance view on the left and a view on the right containing a list of
possible actions.
Basic Usage
Clients of GuidedStepSupportFragment must create a custom subclass to attach to their Activities.
This custom subclass provides the information necessary to construct the user interface and
respond to user actions. At a minimum, subclasses should override:
Clients use following helper functions to add GuidedStepSupportFragment to Activity or FragmentManager:
Theming and Stylists
GuidedStepSupportFragment delegates its visual styling to classes called stylists. The GuidanceStylist is responsible for the left guidance view, while the GuidedActionsStylist is responsible for the right actions view. The stylists use theme
attributes to derive values associated with the presentation, such as colors, animations, etc.
Most simple visual aspects of GuidanceStylist and GuidedActionsStylist can be customized
via theming; see their documentation for more information.
GuidedStepSupportFragments must have access to an appropriate theme in order for the stylists to
function properly. Specifically, the fragment must receive , or a theme whose parent is
is set to that theme. Themes can be provided in one of three ways:
- The simplest way is to set the theme for the host Activity to the GuidedStep theme or a
theme that derives from it.
- If the Activity already has a theme and setting its parent theme is inconvenient, the
existing Activity theme can have an entry added for the attribute . If present,
this theme will be used by GuidedStepSupportFragment as an overlay to the Activity's theme.
- Finally, custom subclasses of GuidedStepSupportFragment may provide a theme through the GuidedStepSupportFragment.onProvideTheme() method. This can be useful if a subclass is used across multiple
Activities.
If the theme is provided in multiple ways, the onProvideTheme override has priority, followed by
the Activity's theme. (Themes whose parent theme is already set to the guided step theme do not
need to set the guidedStepTheme attribute; if set, it will be ignored.)
If themes do not provide enough customizability, the stylists themselves may be subclassed and
provided to the GuidedStepSupportFragment through the GuidedStepSupportFragment.onCreateGuidanceStylist() and GuidedStepSupportFragment.onCreateActionsStylist() methods. The stylists have simple hooks so that subclasses
may override layout files; subclasses may also have more complex logic to determine styling.
Guided sequences
GuidedStepSupportFragments can be grouped together to provide a guided sequence. GuidedStepSupportFragments
grouped as a sequence use custom animations provided by GuidanceStylist and
GuidedActionsStylist (or subclasses) during transitions between steps. Clients
should use GuidedStepSupportFragment.add(FragmentManager, GuidedStepSupportFragment) to place subsequent GuidedFragments onto the fragment stack so that
custom animations are properly configured. (Custom animations are triggered automatically when
the fragment stack is subsequently popped by any normal mechanism.)
Note: Currently GuidedStepSupportFragments grouped in this way must all be defined programmatically,
rather than in XML. This restriction may be removed in the future.
Summary
Methods |
---|
public static int | add(FragmentManager fragmentManager, GuidedStepSupportFragment fragment)
Adds the specified GuidedStepSupportFragment to the fragment stack, replacing any existing
GuidedStepSupportFragments in the stack, and configuring the fragment-to-fragment custom
transitions. |
public static int | add(FragmentManager fragmentManager, GuidedStepSupportFragment fragment, int id)
Adds the specified GuidedStepSupportFragment to the fragment stack, replacing any existing
GuidedStepSupportFragments in the stack, and configuring the fragment-to-fragment custom
transitions. |
public static int | addAsRoot(FragmentActivity activity, GuidedStepSupportFragment fragment, int id)
Adds the specified GuidedStepSupportFragment as content of Activity; no backstack entry is added so
the activity will be dismissed when BACK key is pressed. |
public void | collapseAction(boolean withTransition)
Collapse action which either has a sub actions list or action with
GuidedAction.hasEditableActivatorView() is true. |
public void | collapseSubActions()
Collapse sub actions list. |
public void | expandAction(GuidedAction action, boolean withTransition)
Expand a given action with sub actions list or
GuidedAction.hasEditableActivatorView() is true. |
public void | expandSubActions(GuidedAction action)
Expand a given action's sub actions list. |
public GuidedAction | findActionById(long id)
Find GuidedAction by Id. |
public int | findActionPositionById(long id)
Find GuidedAction position in array by Id. |
public GuidedAction | findButtonActionById(long id)
Find button GuidedAction by Id. |
public int | findButtonActionPositionById(long id)
Find button GuidedAction position in array by Id. |
public void | finishGuidedStepSupportFragments()
Convenient method to close GuidedStepSupportFragments on top of other content or finish Activity if
GuidedStepSupportFragments were started in a separate activity. |
public View | getActionItemView(int position)
Returns the view corresponding to the action at the indicated position in the list of
actions for this fragment. |
public java.util.List<GuidedAction> | getActions()
Returns the list of GuidedActions that the user may take in this fragment. |
public View | getButtonActionItemView(int position)
Returns the view corresponding to the button action at the indicated position in the list of
actions for this fragment. |
public java.util.List<GuidedAction> | getButtonActions()
Returns the list of button GuidedActions that the user may take in this fragment. |
public static GuidedStepSupportFragment | getCurrentGuidedStepSupportFragment(FragmentManager fm)
Returns the current GuidedStepSupportFragment on the fragment transaction stack. |
public GuidanceStylist | getGuidanceStylist()
Returns the GuidanceStylist that displays guidance information for the user. |
public GuidedActionsStylist | getGuidedActionsStylist()
Returns the GuidedActionsStylist that displays the actions the user may take. |
public GuidedActionsStylist | getGuidedButtonActionsStylist()
Returns the GuidedActionsStylist that displays the button actions the user may take. |
public int | getSelectedActionPosition()
Returns the position if the currently selected GuidedAction. |
public int | getSelectedButtonActionPosition()
Returns the position if the currently selected button GuidedAction. |
public int | getUiStyle()
Read UI style from fragment arguments. |
public boolean | isExpanded()
|
public boolean | isFocusOutEndAllowed()
Returns true if allows focus out of end edge of GuidedStepSupportFragment, false otherwise. |
public boolean | isFocusOutStartAllowed()
Returns true if allows focus out of start edge of GuidedStepSupportFragment, false otherwise. |
public boolean | isSubActionsExpanded()
|
public void | notifyActionChanged(int position)
Notify an action has changed and update its UI. |
public void | notifyButtonActionChanged(int position)
Notify an button action has changed and update its UI. |
protected void | onAddSharedElementTransition(FragmentTransaction ft, GuidedStepSupportFragment disappearing)
Called when this fragment is added to FragmentTransaction with GuidedStepSupportFragment.UI_STYLE_REPLACE (aka
when the GuidedStepSupportFragment replacing an existing GuidedStepSupportFragment). |
public void | onCreate(Bundle savedInstanceState)
|
public void | onCreateActions(java.util.List<GuidedAction> actions, Bundle savedInstanceState)
Fills out the set of actions available to the user. |
public GuidedActionsStylist | onCreateActionsStylist()
Creates the presenter used to style the guided actions panel. |
public View | onCreateBackgroundView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
Called by onCreateView to inflate background view. |
public void | onCreateButtonActions(java.util.List<GuidedAction> actions, Bundle savedInstanceState)
Fills out the set of actions shown at right available to the user. |
public GuidedActionsStylist | onCreateButtonActionsStylist()
Creates the presenter used to style a sided actions panel for button only. |
public GuidanceStylist.Guidance | onCreateGuidance(Bundle savedInstanceState)
Returns the information required to provide guidance to the user. |
public GuidanceStylist | onCreateGuidanceStylist()
Creates the presenter used to style the guidance panel. |
public View | onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
|
public void | onDestroyView()
|
public void | onGuidedActionClicked(GuidedAction action)
Callback invoked when an action is taken by the user. |
public void | onGuidedActionEditCanceled(GuidedAction action)
Callback invoked when an action has been canceled editing, for example when user closes
IME window by BACK key. |
public void | onGuidedActionEdited(GuidedAction action)
Callback invoked when an action's title or description has been edited, this happens either
when user clicks confirm button in IME or user closes IME window by BACK key. |
public long | onGuidedActionEditedAndProceed(GuidedAction action)
Callback invoked when an action has been edited, for example when user clicks confirm button
in IME window. |
public void | onGuidedActionFocused(GuidedAction action)
Callback invoked when an action is focused (made to be the current selection) by the user. |
protected void | onProvideFragmentTransitions()
Called by Constructor to provide fragment transitions. |
public int | onProvideTheme()
Returns the theme used for styling the fragment. |
public void | onResume()
Called when the fragment is visible to the user and actively running. |
public void | onSaveInstanceState(Bundle outState)
|
public boolean | onSubGuidedActionClicked(GuidedAction action)
Callback invoked when an action in sub actions is taken by the user. |
public void | openInEditMode(GuidedAction action)
Opens the provided action in edit mode and raises ime. |
public void | popBackStackToGuidedStepSupportFragment(java.lang.Class<java.lang.Object> guidedStepFragmentClass, int flags)
Convenient method to pop to fragment with Given class. |
public void | setActions(java.util.List<GuidedAction> actions)
Sets the list of GuidedActions that the user may take in this fragment. |
public void | setActionsDiffCallback(DiffCallback<GuidedAction> diffCallback)
Sets the RecyclerView DiffCallback used when GuidedStepSupportFragment.setActions(List) is called. |
public void | setButtonActions(java.util.List<GuidedAction> actions)
Sets the list of button GuidedActions that the user may take in this fragment. |
public void | setEntranceTransitionType(int transitionType)
Sets the transition type to be used for GuidedStepSupportFragment.UI_STYLE_ENTRANCE animation. |
public void | setSelectedActionPosition(int position)
Scrolls the action list to the position indicated, selecting that action's view. |
public void | setSelectedButtonActionPosition(int position)
Scrolls the action list to the position indicated, selecting that button action's view. |
public void | setUiStyle(int style)
Set UI style to fragment arguments. |
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, onPause, onPictureInPictureModeChanged, onPrepareOptionsMenu, onPrimaryNavigationFragmentChanged, onRequestPermissionsResult, onStart, onStop, onViewCreated, 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 |
Fields
public static final java.lang.String
EXTRA_UI_STYLEFragment argument name for UI style. The argument value is persisted in fragment state and
used to select fragment transition. The value is initially GuidedStepSupportFragment.UI_STYLE_ENTRANCE and
might be changed in one of the three helper functions:
Argument value can be either:
public static final int
UI_STYLE_REPLACEThis is the case that we use GuidedStepSupportFragment to replace another existing
GuidedStepSupportFragment when moving forward to next step. Default behavior of this style is:
public static final int
UI_STYLE_DEFAULTDeprecated: Same value as GuidedStepSupportFragment.UI_STYLE_REPLACE.
public static final int
UI_STYLE_ENTRANCEDefault value for argument GuidedStepSupportFragment.EXTRA_UI_STYLE. The default value is assigned in
GuidedStepSupportFragment constructor. This is the case that we show GuidedStepSupportFragment on top of
other content. The default behavior of this style:
- Enter transition slides in from two sides, exit transition slide out to START(left).
Background will be faded in. Note: Changing exit transition by UI style is not working
because fragment transition asks for exit transition before UI style is restored in Fragment
.onCreate().
When popping multiple GuidedStepSupportFragment,
GuidedStepSupportFragment.finishGuidedStepSupportFragments() also changes
the top GuidedStepSupportFragment to UI_STYLE_ENTRANCE in order to run the return transition
(reverse of enter transition) of UI_STYLE_ENTRANCE.
public static final int
UI_STYLE_ACTIVITY_ROOTOne possible value of argument GuidedStepSupportFragment.EXTRA_UI_STYLE. This is the case that we show first
GuidedStepSupportFragment in a separate activity. The default behavior of this style:
- Enter transition is assigned null (will rely on activity transition), exit transition is
same as GuidedStepSupportFragment.UI_STYLE_ENTRANCE. Note: Changing exit transition by UI style is not working
because fragment transition asks for exit transition before UI style is restored in
Fragment.onCreate().
public static final int
SLIDE_FROM_SIDEAnimation to slide the contents from the side (left/right).
public static final int
SLIDE_FROM_BOTTOMAnimation to slide the contents from the bottom.
Constructors
public
GuidedStepSupportFragment()
Methods
Creates the presenter used to style the guidance panel. The default implementation returns
a basic GuidanceStylist.
Returns:
The GuidanceStylist used in this fragment.
Creates the presenter used to style the guided actions panel. The default implementation
returns a basic GuidedActionsStylist.
Returns:
The GuidedActionsStylist used in this fragment.
Creates the presenter used to style a sided actions panel for button only.
The default implementation returns a basic GuidedActionsStylist.
Returns:
The GuidedActionsStylist used in this fragment.
public int
onProvideTheme()
Returns the theme used for styling the fragment. The default returns -1, indicating that the
host Activity's theme should be used.
Returns:
The theme resource ID of the theme to use in this fragment, or -1 to use the
host Activity's theme.
Returns the information required to provide guidance to the user. This hook is called during
GuidedStepSupportFragment.onCreateView(LayoutInflater, ViewGroup, Bundle). May be overridden to return a custom subclass of for use in a subclass of GuidanceStylist. The default
returns a Guidance object with empty fields; subclasses should override.
Parameters:
savedInstanceState: The saved instance state from onCreateView.
Returns:
The Guidance object representing the information used to guide the user.
public void
onCreateActions(java.util.List<GuidedAction> actions, Bundle savedInstanceState)
Fills out the set of actions available to the user. This hook is called during GuidedStepSupportFragment.onCreate(Bundle). The default leaves the list of actions empty; subclasses should override.
Parameters:
actions: A non-null, empty list ready to be populated.
savedInstanceState: The saved instance state from onCreate.
public void
onCreateButtonActions(java.util.List<GuidedAction> actions, Bundle savedInstanceState)
Fills out the set of actions shown at right available to the user. This hook is called during
GuidedStepSupportFragment.onCreate(Bundle). The default leaves the list of actions empty; subclasses may override.
Parameters:
actions: A non-null, empty list ready to be populated.
savedInstanceState: The saved instance state from onCreate.
Callback invoked when an action is taken by the user. Subclasses should override in
order to act on the user's decisions.
Parameters:
action: The chosen action.
public boolean
onSubGuidedActionClicked(
GuidedAction action)
Callback invoked when an action in sub actions is taken by the user. Subclasses should
override in order to act on the user's decisions. Default return value is true to close
the sub actions list.
Parameters:
action: The chosen action.
Returns:
true to collapse the sub actions list, false to keep it expanded.
public boolean
isExpanded()
Returns:
True if is current expanded including subactions list or
action with GuidedAction.hasEditableActivatorView() is true.
public boolean
isSubActionsExpanded()
Returns:
True if the sub actions list is expanded, false otherwise.
Expand a given action's sub actions list.
Parameters:
action: GuidedAction to expand.
See also: GuidedStepSupportFragment.expandAction(GuidedAction, boolean)
public void
expandAction(
GuidedAction action, boolean withTransition)
Expand a given action with sub actions list or
GuidedAction.hasEditableActivatorView() is true. The method must be called after
GuidedStepSupportFragment.onCreateView(LayoutInflater, ViewGroup, Bundle) creates fragment view.
Parameters:
action: GuidedAction to expand.
withTransition: True to run transition animation, false otherwise.
public void
collapseSubActions()
Collapse sub actions list.
See also: GuidedAction.getSubActions()
public void
collapseAction(boolean withTransition)
Collapse action which either has a sub actions list or action with
GuidedAction.hasEditableActivatorView() is true.
Parameters:
withTransition: True to run transition animation, false otherwise.
Callback invoked when an action is focused (made to be the current selection) by the user.
Deprecated: Override GuidedStepSupportFragment.onGuidedActionEditedAndProceed(GuidedAction) and/or
GuidedStepSupportFragment.onGuidedActionEditCanceled(GuidedAction).
Callback invoked when an action's title or description has been edited, this happens either
when user clicks confirm button in IME or user closes IME window by BACK key.
Callback invoked when an action has been canceled editing, for example when user closes
IME window by BACK key. Default implementation calls deprecated method
GuidedStepSupportFragment.onGuidedActionEdited(GuidedAction).
Parameters:
action: The action which has been canceled editing.
public long
onGuidedActionEditedAndProceed(
GuidedAction action)
Callback invoked when an action has been edited, for example when user clicks confirm button
in IME window. Default implementation calls deprecated method
GuidedStepSupportFragment.onGuidedActionEdited(GuidedAction) and returns GuidedAction.ACTION_ID_NEXT.
Parameters:
action: The action that has been edited.
Returns:
ID of the action will be focused or GuidedAction.ACTION_ID_NEXT,
GuidedAction.ACTION_ID_CURRENT.
Adds the specified GuidedStepSupportFragment to the fragment stack, replacing any existing
GuidedStepSupportFragments in the stack, and configuring the fragment-to-fragment custom
transitions. A backstack entry is added, so the fragment will be dismissed when BACK key
is pressed.
Note: currently fragments added using this method must be created programmatically rather
than via XML.
Parameters:
fragmentManager: The FragmentManager to be used in the transaction.
fragment: The GuidedStepSupportFragment to be inserted into the fragment stack.
Returns:
The ID returned by the call FragmentTransaction.commit.
Adds the specified GuidedStepSupportFragment to the fragment stack, replacing any existing
GuidedStepSupportFragments in the stack, and configuring the fragment-to-fragment custom
transitions. A backstack entry is added, so the fragment will be dismissed when BACK key
is pressed.
Note: currently fragments added using this method must be created programmatically rather
than via XML.
Parameters:
fragmentManager: The FragmentManager to be used in the transaction.
fragment: The GuidedStepSupportFragment to be inserted into the fragment stack.
id: The id of container to add GuidedStepSupportFragment, can be android.R.id.content.
Returns:
The ID returned by the call FragmentTransaction.commit.
Called when this fragment is added to FragmentTransaction with GuidedStepSupportFragment.UI_STYLE_REPLACE (aka
when the GuidedStepSupportFragment replacing an existing GuidedStepSupportFragment). Default implementation
establishes connections between action background views to morph action background bounds
change from disappearing GuidedStepSupportFragment into this GuidedStepSupportFragment. The default
implementation heavily relies on GuidedActionsStylist's layout, app may override this
method when modifying the default layout of GuidedActionsStylist.
Parameters:
ft: The FragmentTransaction to add shared element.
disappearing: The disappearing fragment.
See also: GuidedActionsStylist, GuidedStepSupportFragment.onProvideFragmentTransitions()
Adds the specified GuidedStepSupportFragment as content of Activity; no backstack entry is added so
the activity will be dismissed when BACK key is pressed. The method is typically called in
Activity.onCreate() when savedInstanceState is null. When savedInstanceState is not null,
the Activity is being restored, do not call addAsRoot() to duplicate the Fragment restored
by FragmentManager.
GuidedStepSupportFragment.UI_STYLE_ACTIVITY_ROOT is assigned.
Note: currently fragments added using this method must be created programmatically rather
than via XML.
Parameters:
activity: The Activity to be used to insert GuidedstepFragment.
fragment: The GuidedStepSupportFragment to be inserted into the fragment stack.
id: The id of container to add GuidedStepSupportFragment, can be android.R.id.content.
Returns:
The ID returned by the call FragmentTransaction.commit, or -1 there is already
GuidedStepSupportFragment.
Returns the current GuidedStepSupportFragment on the fragment transaction stack.
Returns:
The current GuidedStepSupportFragment, if any, on the fragment transaction stack.
Returns the GuidanceStylist that displays guidance information for the user.
Returns:
The GuidanceStylist for this fragment.
Returns the GuidedActionsStylist that displays the actions the user may take.
Returns:
The GuidedActionsStylist for this fragment.
public java.util.List<GuidedAction>
getButtonActions()
Returns the list of button GuidedActions that the user may take in this fragment.
Returns:
The list of button GuidedActions for this fragment.
Find button GuidedAction by Id.
Parameters:
id: Id of the button action to search.
Returns:
GuidedAction object or null if not found.
public int
findButtonActionPositionById(long id)
Find button GuidedAction position in array by Id.
Parameters:
id: Id of the button action to search.
Returns:
position of GuidedAction object in array or -1 if not found.
Returns the GuidedActionsStylist that displays the button actions the user may take.
Returns:
The GuidedActionsStylist for this fragment.
public void
setButtonActions(java.util.List<GuidedAction> actions)
Sets the list of button GuidedActions that the user may take in this fragment.
Parameters:
actions: The list of button GuidedActions for this fragment.
public void
notifyButtonActionChanged(int position)
Notify an button action has changed and update its UI.
Parameters:
position: Position of the button GuidedAction in array.
public View
getButtonActionItemView(int position)
Returns the view corresponding to the button action at the indicated position in the list of
actions for this fragment.
Parameters:
position: The integer position of the button action of interest.
Returns:
The View corresponding to the button action at the indicated position, or null if
that action is not currently onscreen.
public void
setSelectedButtonActionPosition(int position)
Scrolls the action list to the position indicated, selecting that button action's view.
Parameters:
position: The integer position of the button action of interest.
public int
getSelectedButtonActionPosition()
Returns the position if the currently selected button GuidedAction.
Returns:
position The integer position of the currently selected button action.
public java.util.List<GuidedAction>
getActions()
Returns the list of GuidedActions that the user may take in this fragment.
Returns:
The list of GuidedActions for this fragment.
Find GuidedAction by Id.
Parameters:
id: Id of the action to search.
Returns:
GuidedAction object or null if not found.
public int
findActionPositionById(long id)
Find GuidedAction position in array by Id.
Parameters:
id: Id of the action to search.
Returns:
position of GuidedAction object in array or -1 if not found.
public void
setActions(java.util.List<GuidedAction> actions)
Sets the list of GuidedActions that the user may take in this fragment.
Uses DiffCallback set by GuidedStepSupportFragment.setActionsDiffCallback(DiffCallback).
Parameters:
actions: The list of GuidedActions for this fragment.
public void
setActionsDiffCallback(
DiffCallback<GuidedAction> diffCallback)
Sets the RecyclerView DiffCallback used when GuidedStepSupportFragment.setActions(List) is called. By default
GuidedStepSupportFragment uses
GuidedActionDiffCallback.
Sets it to null if app wants to refresh the whole list.
Parameters:
diffCallback: DiffCallback used in GuidedStepSupportFragment.setActions(List).
public void
notifyActionChanged(int position)
Notify an action has changed and update its UI.
Parameters:
position: Position of the GuidedAction in array.
public View
getActionItemView(int position)
Returns the view corresponding to the action at the indicated position in the list of
actions for this fragment.
Parameters:
position: The integer position of the action of interest.
Returns:
The View corresponding to the action at the indicated position, or null if that
action is not currently onscreen.
public void
setSelectedActionPosition(int position)
Scrolls the action list to the position indicated, selecting that action's view.
Parameters:
position: The integer position of the action of interest.
public int
getSelectedActionPosition()
Returns the position if the currently selected GuidedAction.
Returns:
position The integer position of the currently selected action.
protected void
onProvideFragmentTransitions()
Called by Constructor to provide fragment transitions. The default implementation assigns
transitions based on GuidedStepSupportFragment.getUiStyle():
The default implementation heavily relies on GuidedActionsStylist and
GuidanceStylist layout, app may override this method when modifying the default
layout of GuidedActionsStylist or GuidanceStylist.
TIP: because the fragment view is removed during fragment transition, in general app cannot
use two Visibility transition together. Workaround is to create your own Visibility
transition that controls multiple animators (e.g. slide and fade animation in one Transition
class).
public View
onCreateBackgroundView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
Called by onCreateView to inflate background view. Default implementation loads view
from which holds a reference to
guidedStepBackground.
Parameters:
inflater: LayoutInflater to load background view.
container: Parent view of background view.
savedInstanceState:
Returns:
Created background view or null if no background.
public void
setUiStyle(int style)
Set UI style to fragment arguments. Default value is GuidedStepSupportFragment.UI_STYLE_ENTRANCE when fragment
is first initialized. UI style is used to choose different fragment transition animations and
determine if this is the first GuidedStepSupportFragment on backstack. In most cases app does not
directly call this method, app calls helper function
GuidedStepSupportFragment.add(FragmentManager, GuidedStepSupportFragment, int). However if the app creates Fragment
transaction and controls backstack by itself, it would need call setUiStyle() to select the
fragment transition to use.
Parameters:
style: GuidedStepSupportFragment.UI_STYLE_ACTIVITY_ROOT GuidedStepSupportFragment.UI_STYLE_REPLACE or
GuidedStepSupportFragment.UI_STYLE_ENTRANCE.
Read UI style from fragment arguments. Default value is GuidedStepSupportFragment.UI_STYLE_ENTRANCE when
fragment is first initialized. UI style is used to choose different fragment transition
animations and determine if this is the first GuidedStepSupportFragment on backstack.
Returns:
GuidedStepSupportFragment.UI_STYLE_ACTIVITY_ROOT GuidedStepSupportFragment.UI_STYLE_REPLACE or
GuidedStepSupportFragment.UI_STYLE_ENTRANCE.
See also: GuidedStepSupportFragment.onProvideFragmentTransitions()
public void
onCreate(Bundle savedInstanceState)
public void
onDestroyView()
public View
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
Called when the fragment is visible to the user and actively running.
This is generally
tied to of the containing
Activity's lifecycle.
public void
onSaveInstanceState(Bundle outState)
public void
finishGuidedStepSupportFragments()
Convenient method to close GuidedStepSupportFragments on top of other content or finish Activity if
GuidedStepSupportFragments were started in a separate activity. Pops all stack entries including
GuidedStepSupportFragment.UI_STYLE_ENTRANCE; if GuidedStepSupportFragment.UI_STYLE_ENTRANCE is not found, finish the activity.
Note that this method must be paired with GuidedStepSupportFragment.add(FragmentManager, GuidedStepSupportFragment, int) which sets up the stack entry name for finding which fragment we need to pop back to.
public void
popBackStackToGuidedStepSupportFragment(java.lang.Class<java.lang.Object> guidedStepFragmentClass, int flags)
Convenient method to pop to fragment with Given class.
Parameters:
guidedStepFragmentClass: Name of the Class of GuidedStepSupportFragment to pop to.
flags: Either 0 or FragmentManager.POP_BACK_STACK_INCLUSIVE.
public boolean
isFocusOutStartAllowed()
Returns true if allows focus out of start edge of GuidedStepSupportFragment, false otherwise.
Default value is false, the reason is to disable FocusFinder to find focusable views
beneath content of GuidedStepSupportFragment. Subclass may override.
Returns:
True if allows focus out of start edge of GuidedStepSupportFragment.
public boolean
isFocusOutEndAllowed()
Returns true if allows focus out of end edge of GuidedStepSupportFragment, false otherwise.
Default value is false, the reason is to disable FocusFinder to find focusable views
beneath content of GuidedStepSupportFragment. Subclass may override.
Returns:
True if allows focus out of end edge of GuidedStepSupportFragment.
public void
setEntranceTransitionType(int transitionType)
Sets the transition type to be used for GuidedStepSupportFragment.UI_STYLE_ENTRANCE animation.
Currently we provide 2 different variations for animation - slide in from
side (default) or bottom.
Ideally we can retrieve the screen mode settings from the theme attribute
Theme.Leanback.GuidedStep#guidedStepHeightWeight and use that to
determine the transition. But the fragment context to retrieve the theme
isn't available on platform v23 or earlier.
For now clients(subclasses) can call this method inside the constructor.
Opens the provided action in edit mode and raises ime. This can be
used to programmatically skip the extra click required to go into edit mode. This method
can be invoked in GuidedStepSupportFragment.onCreateView(LayoutInflater, ViewGroup, Bundle).
Source
/*
* Copyright (C) 2015 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 static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentManager.BackStackEntry;
import androidx.fragment.app.FragmentTransaction;
import androidx.leanback.R;
import androidx.leanback.transition.TransitionHelper;
import androidx.leanback.widget.DiffCallback;
import androidx.leanback.widget.GuidanceStylist;
import androidx.leanback.widget.GuidanceStylist.Guidance;
import androidx.leanback.widget.GuidedAction;
import androidx.leanback.widget.GuidedActionAdapter;
import androidx.leanback.widget.GuidedActionAdapterGroup;
import androidx.leanback.widget.GuidedActionsStylist;
import androidx.leanback.widget.NonOverlappingLinearLayout;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
/**
* A GuidedStepSupportFragment is used to guide the user through a decision or series of decisions.
* It is composed of a guidance view on the left and a view on the right containing a list of
* possible actions.
* <p>
* <h3>Basic Usage</h3>
* <p>
* Clients of GuidedStepSupportFragment must create a custom subclass to attach to their Activities.
* This custom subclass provides the information necessary to construct the user interface and
* respond to user actions. At a minimum, subclasses should override:
* <ul>
* <li>{@link #onCreateGuidance}, to provide instructions to the user</li>
* <li>{@link #onCreateActions}, to provide a set of {@link GuidedAction}s the user can take</li>
* <li>{@link #onGuidedActionClicked}, to respond to those actions</li>
* </ul>
* <p>
* Clients use following helper functions to add GuidedStepSupportFragment to Activity or FragmentManager:
* <ul>
* <li>{@link #addAsRoot(FragmentActivity, GuidedStepSupportFragment, int)}, to be called during Activity onCreate,
* adds GuidedStepSupportFragment as the first Fragment in activity.</li>
* <li>{@link #add(FragmentManager, GuidedStepSupportFragment)} or {@link #add(FragmentManager,
* GuidedStepSupportFragment, int)}, to add GuidedStepSupportFragment on top of existing Fragments or
* replacing existing GuidedStepSupportFragment when moving forward to next step.</li>
* <li>{@link #finishGuidedStepSupportFragments()} can either finish the activity or pop all
* GuidedStepSupportFragment from stack.</li>
* <li>If app chooses not to use the helper function, it is the app's responsibility to call
* {@link #setUiStyle(int)} to select fragment transition and remember the stack entry where it
* need pops to.</li>
* </ul>
* <h3>Theming and Stylists</h3>
* <p>
* GuidedStepSupportFragment delegates its visual styling to classes called stylists. The {@link
* GuidanceStylist} is responsible for the left guidance view, while the {@link
* GuidedActionsStylist} is responsible for the right actions view. The stylists use theme
* attributes to derive values associated with the presentation, such as colors, animations, etc.
* Most simple visual aspects of GuidanceStylist and GuidedActionsStylist can be customized
* via theming; see their documentation for more information.
* <p>
* GuidedStepSupportFragments must have access to an appropriate theme in order for the stylists to
* function properly. Specifically, the fragment must receive {@link
* androidx.leanback.R.style#Theme_Leanback_GuidedStep}, or a theme whose parent is
* is set to that theme. Themes can be provided in one of three ways:
* <ul>
* <li>The simplest way is to set the theme for the host Activity to the GuidedStep theme or a
* theme that derives from it.</li>
* <li>If the Activity already has a theme and setting its parent theme is inconvenient, the
* existing Activity theme can have an entry added for the attribute {@link
* androidx.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepTheme}. If present,
* this theme will be used by GuidedStepSupportFragment as an overlay to the Activity's theme.</li>
* <li>Finally, custom subclasses of GuidedStepSupportFragment may provide a theme through the {@link
* #onProvideTheme} method. This can be useful if a subclass is used across multiple
* Activities.</li>
* </ul>
* <p>
* If the theme is provided in multiple ways, the onProvideTheme override has priority, followed by
* the Activity's theme. (Themes whose parent theme is already set to the guided step theme do not
* need to set the guidedStepTheme attribute; if set, it will be ignored.)
* <p>
* If themes do not provide enough customizability, the stylists themselves may be subclassed and
* provided to the GuidedStepSupportFragment through the {@link #onCreateGuidanceStylist} and {@link
* #onCreateActionsStylist} methods. The stylists have simple hooks so that subclasses
* may override layout files; subclasses may also have more complex logic to determine styling.
* <p>
* <h3>Guided sequences</h3>
* <p>
* GuidedStepSupportFragments can be grouped together to provide a guided sequence. GuidedStepSupportFragments
* grouped as a sequence use custom animations provided by {@link GuidanceStylist} and
* {@link GuidedActionsStylist} (or subclasses) during transitions between steps. Clients
* should use {@link #add} to place subsequent GuidedFragments onto the fragment stack so that
* custom animations are properly configured. (Custom animations are triggered automatically when
* the fragment stack is subsequently popped by any normal mechanism.)
* <p>
* <i>Note: Currently GuidedStepSupportFragments grouped in this way must all be defined programmatically,
* rather than in XML. This restriction may be removed in the future.</i>
*
* {@link androidx.leanback.R.attr#guidedStepTheme}
* {@link androidx.leanback.R.attr#guidedStepBackground}
* {@link androidx.leanback.R.attr#guidedActionContentWidthWeight}
* {@link androidx.leanback.R.attr#guidedActionContentWidthWeightTwoPanels}
* {@link androidx.leanback.R.attr#guidedActionsBackground}
* {@link androidx.leanback.R.attr#guidedActionsBackgroundDark}
* {@link androidx.leanback.R.attr#guidedActionsElevation}
* @see GuidanceStylist
* @see GuidanceStylist.Guidance
* @see GuidedAction
* @see GuidedActionsStylist
*/
@SuppressWarnings("HiddenSuperclass")
public class GuidedStepSupportFragment extends Fragment implements GuidedActionAdapter.FocusListener {
private static final String TAG_LEAN_BACK_ACTIONS_FRAGMENT = "leanBackGuidedStepSupportFragment";
private static final String EXTRA_ACTION_PREFIX = "action_";
private static final String EXTRA_BUTTON_ACTION_PREFIX = "buttonaction_";
private static final String ENTRY_NAME_REPLACE = "GuidedStepDefault";
private static final String ENTRY_NAME_ENTRANCE = "GuidedStepEntrance";
private static final boolean IS_FRAMEWORK_FRAGMENT = false;
/**
* Fragment argument name for UI style. The argument value is persisted in fragment state and
* used to select fragment transition. The value is initially {@link #UI_STYLE_ENTRANCE} and
* might be changed in one of the three helper functions:
* <ul>
* <li>{@link #addAsRoot(FragmentActivity, GuidedStepSupportFragment, int)} sets to
* {@link #UI_STYLE_ACTIVITY_ROOT}</li>
* <li>{@link #add(FragmentManager, GuidedStepSupportFragment)} or {@link #add(FragmentManager,
* GuidedStepSupportFragment, int)} sets it to {@link #UI_STYLE_REPLACE} if there is already a
* GuidedStepSupportFragment on stack.</li>
* <li>{@link #finishGuidedStepSupportFragments()} changes current GuidedStepSupportFragment to
* {@link #UI_STYLE_ENTRANCE} for the non activity case. This is a special case that changes
* the transition settings after fragment has been created, in order to force current
* GuidedStepSupportFragment run a return transition of {@link #UI_STYLE_ENTRANCE}</li>
* </ul>
* <p>
* Argument value can be either:
* <ul>
* <li>{@link #UI_STYLE_REPLACE}</li>
* <li>{@link #UI_STYLE_ENTRANCE}</li>
* <li>{@link #UI_STYLE_ACTIVITY_ROOT}</li>
* </ul>
*/
public static final String EXTRA_UI_STYLE = "uiStyle";
/**
* This is the case that we use GuidedStepSupportFragment to replace another existing
* GuidedStepSupportFragment when moving forward to next step. Default behavior of this style is:
* <ul>
* <li>Enter transition slides in from END(right), exit transition same as
* {@link #UI_STYLE_ENTRANCE}.
* </li>
* </ul>
*/
public static final int UI_STYLE_REPLACE = 0;
/**
* @deprecated Same value as {@link #UI_STYLE_REPLACE}.
*/
@Deprecated
public static final int UI_STYLE_DEFAULT = 0;
/**
* Default value for argument {@link #EXTRA_UI_STYLE}. The default value is assigned in
* GuidedStepSupportFragment constructor. This is the case that we show GuidedStepSupportFragment on top of
* other content. The default behavior of this style:
* <ul>
* <li>Enter transition slides in from two sides, exit transition slide out to START(left).
* Background will be faded in. Note: Changing exit transition by UI style is not working
* because fragment transition asks for exit transition before UI style is restored in Fragment
* .onCreate().</li>
* </ul>
* When popping multiple GuidedStepSupportFragment, {@link #finishGuidedStepSupportFragments()} also changes
* the top GuidedStepSupportFragment to UI_STYLE_ENTRANCE in order to run the return transition
* (reverse of enter transition) of UI_STYLE_ENTRANCE.
*/
public static final int UI_STYLE_ENTRANCE = 1;
/**
* One possible value of argument {@link #EXTRA_UI_STYLE}. This is the case that we show first
* GuidedStepSupportFragment in a separate activity. The default behavior of this style:
* <ul>
* <li>Enter transition is assigned null (will rely on activity transition), exit transition is
* same as {@link #UI_STYLE_ENTRANCE}. Note: Changing exit transition by UI style is not working
* because fragment transition asks for exit transition before UI style is restored in
* Fragment.onCreate().</li>
* </ul>
*/
public static final int UI_STYLE_ACTIVITY_ROOT = 2;
/**
* Animation to slide the contents from the side (left/right).
*/
@RestrictTo(LIBRARY_GROUP_PREFIX)
public static final int SLIDE_FROM_SIDE = 0;
/**
* Animation to slide the contents from the bottom.
*/
@RestrictTo(LIBRARY_GROUP_PREFIX)
public static final int SLIDE_FROM_BOTTOM = 1;
private static final String TAG = "GuidedStepF";
private static final boolean DEBUG = false;
/**
*/
@RestrictTo(LIBRARY_GROUP_PREFIX)
public static class DummyFragment extends Fragment {
@NonNull
@Override
public View onCreateView(
@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState
) {
final View v = new View(inflater.getContext());
v.setVisibility(View.GONE);
return v;
}
}
private ContextThemeWrapper mThemeWrapper;
private GuidanceStylist mGuidanceStylist;
GuidedActionsStylist mActionsStylist;
private GuidedActionsStylist mButtonActionsStylist;
private GuidedActionAdapter mAdapter;
private GuidedActionAdapter mSubAdapter;
private GuidedActionAdapter mButtonAdapter;
private GuidedActionAdapterGroup mAdapterGroup;
private List<GuidedAction> mActions = new ArrayList<GuidedAction>();
private List<GuidedAction> mButtonActions = new ArrayList<GuidedAction>();
private int entranceTransitionType = SLIDE_FROM_SIDE;
public GuidedStepSupportFragment() {
onProvideFragmentTransitions();
}
/**
* Creates the presenter used to style the guidance panel. The default implementation returns
* a basic GuidanceStylist.
* @return The GuidanceStylist used in this fragment.
*/
@NonNull
public GuidanceStylist onCreateGuidanceStylist() {
return new GuidanceStylist();
}
/**
* Creates the presenter used to style the guided actions panel. The default implementation
* returns a basic GuidedActionsStylist.
* @return The GuidedActionsStylist used in this fragment.
*/
@NonNull
public GuidedActionsStylist onCreateActionsStylist() {
return new GuidedActionsStylist();
}
/**
* Creates the presenter used to style a sided actions panel for button only.
* The default implementation returns a basic GuidedActionsStylist.
* @return The GuidedActionsStylist used in this fragment.
*/
@NonNull
public GuidedActionsStylist onCreateButtonActionsStylist() {
GuidedActionsStylist stylist = new GuidedActionsStylist();
stylist.setAsButtonActions();
return stylist;
}
/**
* Returns the theme used for styling the fragment. The default returns -1, indicating that the
* host Activity's theme should be used.
* @return The theme resource ID of the theme to use in this fragment, or -1 to use the
* host Activity's theme.
*/
public int onProvideTheme() {
return -1;
}
/**
* Returns the information required to provide guidance to the user. This hook is called during
* {@link #onCreateView}. May be overridden to return a custom subclass of {@link
* GuidanceStylist.Guidance} for use in a subclass of {@link GuidanceStylist}. The default
* returns a Guidance object with empty fields; subclasses should override.
* @param savedInstanceState The saved instance state from onCreateView.
* @return The Guidance object representing the information used to guide the user.
*/
public @NonNull Guidance onCreateGuidance(@Nullable Bundle savedInstanceState) {
return new Guidance("", "", "", null);
}
/**
* Fills out the set of actions available to the user. This hook is called during {@link
* #onCreate}. The default leaves the list of actions empty; subclasses should override.
* @param actions A non-null, empty list ready to be populated.
* @param savedInstanceState The saved instance state from onCreate.
*/
public void onCreateActions(
@NonNull List<GuidedAction> actions,
@Nullable Bundle savedInstanceState
) {
}
/**
* Fills out the set of actions shown at right available to the user. This hook is called during
* {@link #onCreate}. The default leaves the list of actions empty; subclasses may override.
* @param actions A non-null, empty list ready to be populated.
* @param savedInstanceState The saved instance state from onCreate.
*/
public void onCreateButtonActions(
@NonNull List<GuidedAction> actions,
@Nullable Bundle savedInstanceState
) {
}
/**
* Callback invoked when an action is taken by the user. Subclasses should override in
* order to act on the user's decisions.
* @param action The chosen action.
*/
public void onGuidedActionClicked(@NonNull GuidedAction action) {
}
/**
* Callback invoked when an action in sub actions is taken by the user. Subclasses should
* override in order to act on the user's decisions. Default return value is true to close
* the sub actions list.
* @param action The chosen action.
* @return true to collapse the sub actions list, false to keep it expanded.
*/
public boolean onSubGuidedActionClicked(@NonNull GuidedAction action) {
return true;
}
/**
* @return True if is current expanded including subactions list or
* action with {@link GuidedAction#hasEditableActivatorView()} is true.
*/
public boolean isExpanded() {
return mActionsStylist.isExpanded();
}
/**
* @return True if the sub actions list is expanded, false otherwise.
*/
public boolean isSubActionsExpanded() {
return mActionsStylist.isSubActionsExpanded();
}
/**
* Expand a given action's sub actions list.
* @param action GuidedAction to expand.
* @see #expandAction(GuidedAction, boolean)
*/
public void expandSubActions(@NonNull GuidedAction action) {
if (!action.hasSubActions()) {
return;
}
expandAction(action, true);
}
/**
* Expand a given action with sub actions list or
* {@link GuidedAction#hasEditableActivatorView()} is true. The method must be called after
* {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} creates fragment view.
*
* @param action GuidedAction to expand.
* @param withTransition True to run transition animation, false otherwise.
*/
public void expandAction(@NonNull GuidedAction action, boolean withTransition) {
mActionsStylist.expandAction(action, withTransition);
}
/**
* Collapse sub actions list.
* @see GuidedAction#getSubActions()
*/
public void collapseSubActions() {
collapseAction(true);
}
/**
* Collapse action which either has a sub actions list or action with
* {@link GuidedAction#hasEditableActivatorView()} is true.
*
* @param withTransition True to run transition animation, false otherwise.
*/
public void collapseAction(boolean withTransition) {
if (mActionsStylist != null && mActionsStylist.getActionsGridView() != null) {
mActionsStylist.collapseAction(withTransition);
}
}
/**
* Callback invoked when an action is focused (made to be the current selection) by the user.
*/
@Override
public void onGuidedActionFocused(@NonNull GuidedAction action) {
}
/**
* Callback invoked when an action's title or description has been edited, this happens either
* when user clicks confirm button in IME or user closes IME window by BACK key.
* @deprecated Override {@link #onGuidedActionEditedAndProceed(GuidedAction)} and/or
* {@link #onGuidedActionEditCanceled(GuidedAction)}.
*/
@Deprecated
public void onGuidedActionEdited(GuidedAction action) {
}
/**
* Callback invoked when an action has been canceled editing, for example when user closes
* IME window by BACK key. Default implementation calls deprecated method
* {@link #onGuidedActionEdited(GuidedAction)}.
* @param action The action which has been canceled editing.
*/
public void onGuidedActionEditCanceled(@NonNull GuidedAction action) {
onGuidedActionEdited(action);
}
/**
* Callback invoked when an action has been edited, for example when user clicks confirm button
* in IME window. Default implementation calls deprecated method
* {@link #onGuidedActionEdited(GuidedAction)} and returns {@link GuidedAction#ACTION_ID_NEXT}.
*
* @param action The action that has been edited.
* @return ID of the action will be focused or {@link GuidedAction#ACTION_ID_NEXT},
* {@link GuidedAction#ACTION_ID_CURRENT}.
*/
public long onGuidedActionEditedAndProceed(@NonNull GuidedAction action) {
onGuidedActionEdited(action);
return GuidedAction.ACTION_ID_NEXT;
}
/**
* Adds the specified GuidedStepSupportFragment to the fragment stack, replacing any existing
* GuidedStepSupportFragments in the stack, and configuring the fragment-to-fragment custom
* transitions. A backstack entry is added, so the fragment will be dismissed when BACK key
* is pressed.
* <ul>
* <li>
* If current fragment on stack is GuidedStepSupportFragment: assign
* {@link #UI_STYLE_REPLACE}
* </li>
* <li>
* If current fragment on stack is not GuidedStepSupportFragment: assign
* {@link #UI_STYLE_ENTRANCE}
* </li>
* </ul>
* <p>
* Note: currently fragments added using this method must be created programmatically rather
* than via XML.
* @param fragmentManager The FragmentManager to be used in the transaction.
* @param fragment The GuidedStepSupportFragment to be inserted into the fragment stack.
* @return The ID returned by the call FragmentTransaction.commit.
*/
public static int add(
@NonNull FragmentManager fragmentManager,
@NonNull GuidedStepSupportFragment fragment) {
return add(fragmentManager, fragment, android.R.id.content);
}
/**
* Adds the specified GuidedStepSupportFragment to the fragment stack, replacing any existing
* GuidedStepSupportFragments in the stack, and configuring the fragment-to-fragment custom
* transitions. A backstack entry is added, so the fragment will be dismissed when BACK key
* is pressed.
* <ul>
* <li>
* If current fragment on stack is GuidedStepSupportFragment: assign
* {@link #UI_STYLE_REPLACE} and
* {@link #onAddSharedElementTransition(FragmentTransaction, GuidedStepSupportFragment)}
* will be called to perform shared element transition between GuidedStepSupportFragments.
* </li>
* <li>
* If current fragment on stack is not GuidedStepSupportFragment: assign
* {@link #UI_STYLE_ENTRANCE}
* </li>
* </ul>
* <p>
* Note: currently fragments added using this method must be created programmatically rather
* than via XML.
* @param fragmentManager The FragmentManager to be used in the transaction.
* @param fragment The GuidedStepSupportFragment to be inserted into the fragment stack.
* @param id The id of container to add GuidedStepSupportFragment, can be android.R.id.content.
* @return The ID returned by the call FragmentTransaction.commit.
*/
public static int add(
@NonNull FragmentManager fragmentManager,
@NonNull GuidedStepSupportFragment fragment,
int id
) {
GuidedStepSupportFragment current = getCurrentGuidedStepSupportFragment(fragmentManager);
boolean inGuidedStep = current != null;
if (IS_FRAMEWORK_FRAGMENT && Build.VERSION.SDK_INT >= 21 && Build.VERSION.SDK_INT < 23
&& !inGuidedStep) {
// workaround b/22631964 for framework fragment
fragmentManager.beginTransaction()
.replace(id, new DummyFragment(), TAG_LEAN_BACK_ACTIONS_FRAGMENT)
.commit();
}
FragmentTransaction ft = fragmentManager.beginTransaction();
fragment.setUiStyle(inGuidedStep ? UI_STYLE_REPLACE : UI_STYLE_ENTRANCE);
ft.addToBackStack(fragment.generateStackEntryName());
if (current != null) {
fragment.onAddSharedElementTransition(ft, current);
}
return ft.replace(id, fragment, TAG_LEAN_BACK_ACTIONS_FRAGMENT).commit();
}
/**
* Called when this fragment is added to FragmentTransaction with {@link #UI_STYLE_REPLACE} (aka
* when the GuidedStepSupportFragment replacing an existing GuidedStepSupportFragment). Default implementation
* establishes connections between action background views to morph action background bounds
* change from disappearing GuidedStepSupportFragment into this GuidedStepSupportFragment. The default
* implementation heavily relies on {@link GuidedActionsStylist}'s layout, app may override this
* method when modifying the default layout of {@link GuidedActionsStylist}.
*
* @see GuidedActionsStylist
* @see #onProvideFragmentTransitions()
* @param ft The FragmentTransaction to add shared element.
* @param disappearing The disappearing fragment.
*/
protected void onAddSharedElementTransition(
@NonNull FragmentTransaction ft,
@NonNull GuidedStepSupportFragment disappearing
) {
View fragmentView = disappearing.getView();
addNonNullSharedElementTransition(ft, fragmentView.findViewById(
R.id.action_fragment_root), "action_fragment_root");
addNonNullSharedElementTransition(ft, fragmentView.findViewById(
R.id.action_fragment_background), "action_fragment_background");
addNonNullSharedElementTransition(ft, fragmentView.findViewById(
R.id.action_fragment), "action_fragment");
addNonNullSharedElementTransition(ft, fragmentView.findViewById(
R.id.guidedactions_root), "guidedactions_root");
addNonNullSharedElementTransition(ft, fragmentView.findViewById(
R.id.guidedactions_content), "guidedactions_content");
addNonNullSharedElementTransition(ft, fragmentView.findViewById(
R.id.guidedactions_list_background), "guidedactions_list_background");
addNonNullSharedElementTransition(ft, fragmentView.findViewById(
R.id.guidedactions_root2), "guidedactions_root2");
addNonNullSharedElementTransition(ft, fragmentView.findViewById(
R.id.guidedactions_content2), "guidedactions_content2");
addNonNullSharedElementTransition(ft, fragmentView.findViewById(
R.id.guidedactions_list_background2), "guidedactions_list_background2");
}
private static void addNonNullSharedElementTransition (FragmentTransaction ft, View subView,
String transitionName)
{
if (Build.VERSION.SDK_INT >= 21) {
if (subView != null) {
ft.addSharedElement(subView, transitionName);
}
}
}
/**
* Returns BackStackEntry name for the GuidedStepSupportFragment or empty String if no entry is
* associated. Note {@link #UI_STYLE_ACTIVITY_ROOT} will return empty String. The method
* returns undefined value if the fragment is not in FragmentManager.
* @return BackStackEntry name for the GuidedStepSupportFragment or empty String if no entry is
* associated.
*/
final String generateStackEntryName() {
return generateStackEntryName(getUiStyle(), getClass());
}
/**
* Generates BackStackEntry name for GuidedStepSupportFragment class or empty String if no entry is
* associated. Note {@link #UI_STYLE_ACTIVITY_ROOT} is not allowed and returns empty String.
* @param uiStyle {@link #UI_STYLE_REPLACE} or {@link #UI_STYLE_ENTRANCE}
* @return BackStackEntry name for the GuidedStepSupportFragment or empty String if no entry is
* associated.
*/
static String generateStackEntryName(int uiStyle, Class<?> guidedStepFragmentClass) {
switch (uiStyle) {
case UI_STYLE_REPLACE:
return ENTRY_NAME_REPLACE + guidedStepFragmentClass.getName();
case UI_STYLE_ENTRANCE:
return ENTRY_NAME_ENTRANCE + guidedStepFragmentClass.getName();
case UI_STYLE_ACTIVITY_ROOT:
default:
return "";
}
}
/**
* Returns true if the backstack entry represents GuidedStepSupportFragment with
* {@link #UI_STYLE_ENTRANCE}, i.e. this is the first GuidedStepSupportFragment pushed to stack; false
* otherwise.
* @see #generateStackEntryName(int, Class)
* @param backStackEntryName Name of BackStackEntry.
* @return True if the backstack represents GuidedStepSupportFragment with {@link #UI_STYLE_ENTRANCE};
* false otherwise.
*/
static boolean isStackEntryUiStyleEntrance(String backStackEntryName) {
return backStackEntryName != null && backStackEntryName.startsWith(ENTRY_NAME_ENTRANCE);
}
/**
* Extract Class name from BackStackEntry name.
* @param backStackEntryName Name of BackStackEntry.
* @return Class name of GuidedStepSupportFragment.
*/
static String getGuidedStepSupportFragmentClassName(String backStackEntryName) {
if (backStackEntryName.startsWith(ENTRY_NAME_REPLACE)) {
return backStackEntryName.substring(ENTRY_NAME_REPLACE.length());
} else if (backStackEntryName.startsWith(ENTRY_NAME_ENTRANCE)) {
return backStackEntryName.substring(ENTRY_NAME_ENTRANCE.length());
} else {
return "";
}
}
/**
* Adds the specified GuidedStepSupportFragment as content of Activity; no backstack entry is added so
* the activity will be dismissed when BACK key is pressed. The method is typically called in
* Activity.onCreate() when savedInstanceState is null. When savedInstanceState is not null,
* the Activity is being restored, do not call addAsRoot() to duplicate the Fragment restored
* by FragmentManager.
* {@link #UI_STYLE_ACTIVITY_ROOT} is assigned.
*
* Note: currently fragments added using this method must be created programmatically rather
* than via XML.
* @param activity The Activity to be used to insert GuidedstepFragment.
* @param fragment The GuidedStepSupportFragment to be inserted into the fragment stack.
* @param id The id of container to add GuidedStepSupportFragment, can be android.R.id.content.
* @return The ID returned by the call FragmentTransaction.commit, or -1 there is already
* GuidedStepSupportFragment.
*/
public static int addAsRoot(
@NonNull FragmentActivity activity,
@NonNull GuidedStepSupportFragment fragment,
int id
) {
// Workaround b/23764120: call getDecorView() to force requestFeature of ActivityTransition.
activity.getWindow().getDecorView();
FragmentManager fragmentManager = activity.getSupportFragmentManager();
if (fragmentManager.findFragmentByTag(TAG_LEAN_BACK_ACTIONS_FRAGMENT) != null) {
Log.w(TAG, "Fragment is already exists, likely calling "
+ "addAsRoot() when savedInstanceState is not null in Activity.onCreate().");
return -1;
}
FragmentTransaction ft = fragmentManager.beginTransaction();
fragment.setUiStyle(UI_STYLE_ACTIVITY_ROOT);
return ft.replace(id, fragment, TAG_LEAN_BACK_ACTIONS_FRAGMENT).commit();
}
/**
* Returns the current GuidedStepSupportFragment on the fragment transaction stack.
* @return The current GuidedStepSupportFragment, if any, on the fragment transaction stack.
*/
@Nullable
public static GuidedStepSupportFragment getCurrentGuidedStepSupportFragment(
@NonNull FragmentManager fm
) {
Fragment f = fm.findFragmentByTag(TAG_LEAN_BACK_ACTIONS_FRAGMENT);
if (f instanceof GuidedStepSupportFragment) {
return (GuidedStepSupportFragment) f;
}
return null;
}
/**
* Returns the GuidanceStylist that displays guidance information for the user.
* @return The GuidanceStylist for this fragment.
*/
@NonNull
public GuidanceStylist getGuidanceStylist() {
return mGuidanceStylist;
}
/**
* Returns the GuidedActionsStylist that displays the actions the user may take.
* @return The GuidedActionsStylist for this fragment.
*/
@NonNull
public GuidedActionsStylist getGuidedActionsStylist() {
return mActionsStylist;
}
/**
* Returns the list of button GuidedActions that the user may take in this fragment.
* @return The list of button GuidedActions for this fragment.
*/
@NonNull
public List<GuidedAction> getButtonActions() {
return mButtonActions;
}
/**
* Find button GuidedAction by Id.
* @param id Id of the button action to search.
* @return GuidedAction object or null if not found.
*/
@Nullable
public GuidedAction findButtonActionById(long id) {
int index = findButtonActionPositionById(id);
return index >= 0 ? mButtonActions.get(index) : null;
}
/**
* Find button GuidedAction position in array by Id.
* @param id Id of the button action to search.
* @return position of GuidedAction object in array or -1 if not found.
*/
public int findButtonActionPositionById(long id) {
if (mButtonActions != null) {
for (int i = 0; i < mButtonActions.size(); i++) {
if (mButtonActions.get(i).getId() == id) {
return i;
}
}
}
return -1;
}
/**
* Returns the GuidedActionsStylist that displays the button actions the user may take.
* @return The GuidedActionsStylist for this fragment.
*/
@NonNull
public GuidedActionsStylist getGuidedButtonActionsStylist() {
return mButtonActionsStylist;
}
/**
* Sets the list of button GuidedActions that the user may take in this fragment.
* @param actions The list of button GuidedActions for this fragment.
*/
public void setButtonActions(@NonNull List<GuidedAction> actions) {
mButtonActions = actions;
if (mButtonAdapter != null) {
mButtonAdapter.setActions(mButtonActions);
}
}
/**
* Notify an button action has changed and update its UI.
* @param position Position of the button GuidedAction in array.
*/
public void notifyButtonActionChanged(int position) {
if (mButtonAdapter != null) {
mButtonAdapter.notifyItemChanged(position);
}
}
/**
* Returns the view corresponding to the button action at the indicated position in the list of
* actions for this fragment.
* @param position The integer position of the button action of interest.
* @return The View corresponding to the button action at the indicated position, or null if
* that action is not currently onscreen.
*/
@Nullable
public View getButtonActionItemView(int position) {
final RecyclerView.ViewHolder holder = mButtonActionsStylist.getActionsGridView()
.findViewHolderForPosition(position);
return holder == null ? null : holder.itemView;
}
/**
* Scrolls the action list to the position indicated, selecting that button action's view.
* @param position The integer position of the button action of interest.
*/
public void setSelectedButtonActionPosition(int position) {
mButtonActionsStylist.getActionsGridView().setSelectedPosition(position);
}
/**
* Returns the position if the currently selected button GuidedAction.
* @return position The integer position of the currently selected button action.
*/
public int getSelectedButtonActionPosition() {
return mButtonActionsStylist.getActionsGridView().getSelectedPosition();
}
/**
* Returns the list of GuidedActions that the user may take in this fragment.
* @return The list of GuidedActions for this fragment.
*/
@NonNull
public List<GuidedAction> getActions() {
return mActions;
}
/**
* Find GuidedAction by Id.
* @param id Id of the action to search.
* @return GuidedAction object or null if not found.
*/
@Nullable
public GuidedAction findActionById(long id) {
int index = findActionPositionById(id);
return index >= 0 ? mActions.get(index) : null;
}
/**
* Find GuidedAction position in array by Id.
* @param id Id of the action to search.
* @return position of GuidedAction object in array or -1 if not found.
*/
public int findActionPositionById(long id) {
if (mActions != null) {
for (int i = 0; i < mActions.size(); i++) {
if (mActions.get(i).getId() == id) {
return i;
}
}
}
return -1;
}
/**
* Sets the list of GuidedActions that the user may take in this fragment.
* Uses DiffCallback set by {@link #setActionsDiffCallback(DiffCallback)}.
*
* @param actions The list of GuidedActions for this fragment.
*/
public void setActions(@NonNull List<GuidedAction> actions) {
mActions = actions;
if (mAdapter != null) {
mAdapter.setActions(mActions);
}
}
/**
* Sets the RecyclerView DiffCallback used when {@link #setActions(List)} is called. By default
* GuidedStepSupportFragment uses
* {@link androidx.leanback.widget.GuidedActionDiffCallback}.
* Sets it to null if app wants to refresh the whole list.
*
* @param diffCallback DiffCallback used in {@link #setActions(List)}.
*/
public void setActionsDiffCallback(@Nullable DiffCallback<GuidedAction> diffCallback) {
mAdapter.setDiffCallback(diffCallback);
}
/**
* Notify an action has changed and update its UI.
* @param position Position of the GuidedAction in array.
*/
public void notifyActionChanged(int position) {
if (mAdapter != null) {
mAdapter.notifyItemChanged(position);
}
}
/**
* Returns the view corresponding to the action at the indicated position in the list of
* actions for this fragment.
* @param position The integer position of the action of interest.
* @return The View corresponding to the action at the indicated position, or null if that
* action is not currently onscreen.
*/
@Nullable
public View getActionItemView(int position) {
final RecyclerView.ViewHolder holder = mActionsStylist.getActionsGridView()
.findViewHolderForPosition(position);
return holder == null ? null : holder.itemView;
}
/**
* Scrolls the action list to the position indicated, selecting that action's view.
* @param position The integer position of the action of interest.
*/
public void setSelectedActionPosition(int position) {
mActionsStylist.getActionsGridView().setSelectedPosition(position);
}
/**
* Returns the position if the currently selected GuidedAction.
* @return position The integer position of the currently selected action.
*/
public int getSelectedActionPosition() {
return mActionsStylist.getActionsGridView().getSelectedPosition();
}
/**
* Called by Constructor to provide fragment transitions. The default implementation assigns
* transitions based on {@link #getUiStyle()}:
* <ul>
* <li> {@link #UI_STYLE_REPLACE} Slide from/to end(right) for enter transition, slide from/to
* start(left) for exit transition, shared element enter transition is set to ChangeBounds.</li>
* <li> {@link #UI_STYLE_ENTRANCE} Enter transition is set to slide from both sides, exit
* transition is same as {@link #UI_STYLE_REPLACE}, no shared element enter transition.</li>
* <li> {@link #UI_STYLE_ACTIVITY_ROOT} Enter transition is set to null and app should rely on
* activity transition, exit transition is same as {@link #UI_STYLE_REPLACE}, no shared element
* enter transition.</li>
* </ul>
* <p>
* The default implementation heavily relies on {@link GuidedActionsStylist} and
* {@link GuidanceStylist} layout, app may override this method when modifying the default
* layout of {@link GuidedActionsStylist} or {@link GuidanceStylist}.
* <p>
* TIP: because the fragment view is removed during fragment transition, in general app cannot
* use two Visibility transition together. Workaround is to create your own Visibility
* transition that controls multiple animators (e.g. slide and fade animation in one Transition
* class).
*/
protected void onProvideFragmentTransitions() {
if (Build.VERSION.SDK_INT >= 21) {
final int uiStyle = getUiStyle();
if (uiStyle == UI_STYLE_REPLACE) {
Object enterTransition = TransitionHelper.createFadeAndShortSlide(Gravity.END);
TransitionHelper.exclude(enterTransition, R.id.guidedstep_background, true);
TransitionHelper.exclude(enterTransition, R.id.guidedactions_sub_list_background,
true);
setEnterTransition(enterTransition);
Object fade = TransitionHelper.createFadeTransition(
TransitionHelper.FADE_IN | TransitionHelper.FADE_OUT);
TransitionHelper.include(fade, R.id.guidedactions_sub_list_background);
Object changeBounds = TransitionHelper.createChangeBounds(false);
Object sharedElementTransition = TransitionHelper.createTransitionSet(false);
TransitionHelper.addTransition(sharedElementTransition, fade);
TransitionHelper.addTransition(sharedElementTransition, changeBounds);
setSharedElementEnterTransition(sharedElementTransition);
} else if (uiStyle == UI_STYLE_ENTRANCE) {
if (entranceTransitionType == SLIDE_FROM_SIDE) {
Object fade = TransitionHelper.createFadeTransition(
TransitionHelper.FADE_IN | TransitionHelper.FADE_OUT);
TransitionHelper.include(fade, R.id.guidedstep_background);
Object slideFromSide = TransitionHelper.createFadeAndShortSlide(
Gravity.END | Gravity.START);
TransitionHelper.include(slideFromSide, R.id.content_fragment);
TransitionHelper.include(slideFromSide, R.id.action_fragment_root);
Object enterTransition = TransitionHelper.createTransitionSet(false);
TransitionHelper.addTransition(enterTransition, fade);
TransitionHelper.addTransition(enterTransition, slideFromSide);
setEnterTransition(enterTransition);
} else {
Object slideFromBottom = TransitionHelper.createFadeAndShortSlide(
Gravity.BOTTOM);
TransitionHelper.include(slideFromBottom, R.id.guidedstep_background_view_root);
Object enterTransition = TransitionHelper.createTransitionSet(false);
TransitionHelper.addTransition(enterTransition, slideFromBottom);
setEnterTransition(enterTransition);
}
// No shared element transition
setSharedElementEnterTransition(null);
} else if (uiStyle == UI_STYLE_ACTIVITY_ROOT) {
// for Activity root, we don't need enter transition, use activity transition
setEnterTransition(null);
// No shared element transition
setSharedElementEnterTransition(null);
}
// exitTransition is same for all style
Object exitTransition = TransitionHelper.createFadeAndShortSlide(Gravity.START);
TransitionHelper.exclude(exitTransition, R.id.guidedstep_background, true);
TransitionHelper.exclude(exitTransition, R.id.guidedactions_sub_list_background,
true);
setExitTransition(exitTransition);
}
}
/**
* Called by onCreateView to inflate background view. Default implementation loads view
* from {@link R.layout#lb_guidedstep_background} which holds a reference to
* guidedStepBackground.
* @param inflater LayoutInflater to load background view.
* @param container Parent view of background view.
* @param savedInstanceState
* @return Created background view or null if no background.
*/
@Nullable
public View onCreateBackgroundView(
@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState
) {
return inflater.inflate(R.layout.lb_guidedstep_background, container, false);
}
/**
* Set UI style to fragment arguments. Default value is {@link #UI_STYLE_ENTRANCE} when fragment
* is first initialized. UI style is used to choose different fragment transition animations and
* determine if this is the first GuidedStepSupportFragment on backstack. In most cases app does not
* directly call this method, app calls helper function
* {@link #add(FragmentManager, GuidedStepSupportFragment, int)}. However if the app creates Fragment
* transaction and controls backstack by itself, it would need call setUiStyle() to select the
* fragment transition to use.
*
* @param style {@link #UI_STYLE_ACTIVITY_ROOT} {@link #UI_STYLE_REPLACE} or
* {@link #UI_STYLE_ENTRANCE}.
*/
public void setUiStyle(int style) {
int oldStyle = getUiStyle();
Bundle arguments = getArguments();
boolean isNew = false;
if (arguments == null) {
arguments = new Bundle();
isNew = true;
}
arguments.putInt(EXTRA_UI_STYLE, style);
// call setArgument() will validate if the fragment is already added.
if (isNew) {
setArguments(arguments);
}
if (style != oldStyle) {
onProvideFragmentTransitions();
}
}
/**
* Read UI style from fragment arguments. Default value is {@link #UI_STYLE_ENTRANCE} when
* fragment is first initialized. UI style is used to choose different fragment transition
* animations and determine if this is the first GuidedStepSupportFragment on backstack.
*
* @return {@link #UI_STYLE_ACTIVITY_ROOT} {@link #UI_STYLE_REPLACE} or
* {@link #UI_STYLE_ENTRANCE}.
* @see #onProvideFragmentTransitions()
*/
public int getUiStyle() {
Bundle b = getArguments();
if (b == null) return UI_STYLE_ENTRANCE;
return b.getInt(EXTRA_UI_STYLE, UI_STYLE_ENTRANCE);
}
/**
* {@inheritDoc}
*/
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (DEBUG) Log.v(TAG, "onCreate");
// Initialize stylists
mGuidanceStylist = onCreateGuidanceStylist();
mActionsStylist = onCreateActionsStylist();
mButtonActionsStylist = onCreateButtonActionsStylist();
// Set correct transition from saved arguments.
onProvideFragmentTransitions();
ArrayList<GuidedAction> actions = new ArrayList<GuidedAction>();
onCreateActions(actions, savedInstanceState);
if (savedInstanceState != null) {
onRestoreActions(actions, savedInstanceState);
}
setActions(actions);
ArrayList<GuidedAction> buttonActions = new ArrayList<GuidedAction>();
onCreateButtonActions(buttonActions, savedInstanceState);
if (savedInstanceState != null) {
onRestoreButtonActions(buttonActions, savedInstanceState);
}
setButtonActions(buttonActions);
}
/**
* {@inheritDoc}
*/
@Override
public void onDestroyView() {
mGuidanceStylist.onDestroyView();
mActionsStylist.onDestroyView();
mButtonActionsStylist.onDestroyView();
mAdapter = null;
mSubAdapter = null;
mButtonAdapter = null;
mAdapterGroup = null;
super.onDestroyView();
}
/**
* {@inheritDoc}
*/
@Override
@Nullable
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
if (DEBUG) Log.v(TAG, "onCreateView");
resolveTheme();
inflater = getThemeInflater(inflater);
GuidedStepRootLayout root = (GuidedStepRootLayout) inflater.inflate(
R.layout.lb_guidedstep_fragment, container, false);
root.setFocusOutStart(isFocusOutStartAllowed());
root.setFocusOutEnd(isFocusOutEndAllowed());
ViewGroup guidanceContainer = (ViewGroup) root.findViewById(R.id.content_fragment);
ViewGroup actionContainer = (ViewGroup) root.findViewById(R.id.action_fragment);
((NonOverlappingLinearLayout) actionContainer).setFocusableViewAvailableFixEnabled(true);
Guidance guidance = onCreateGuidance(savedInstanceState);
View guidanceView = mGuidanceStylist.onCreateView(inflater, guidanceContainer, guidance);
guidanceContainer.addView(guidanceView);
View actionsView = mActionsStylist.onCreateView(inflater, actionContainer);
actionContainer.addView(actionsView);
View buttonActionsView = mButtonActionsStylist.onCreateView(inflater, actionContainer);
actionContainer.addView(buttonActionsView);
GuidedActionAdapter.EditListener editListener = new GuidedActionAdapter.EditListener() {
@Override
public void onImeOpen() {
runImeAnimations(true);
}
@Override
public void onImeClose() {
runImeAnimations(false);
}
@Override
public long onGuidedActionEditedAndProceed(GuidedAction action) {
return GuidedStepSupportFragment.this.onGuidedActionEditedAndProceed(action);
}
@Override
public void onGuidedActionEditCanceled(GuidedAction action) {
GuidedStepSupportFragment.this.onGuidedActionEditCanceled(action);
}
};
mAdapter = new GuidedActionAdapter(mActions, new GuidedActionAdapter.ClickListener() {
@Override
public void onGuidedActionClicked(GuidedAction action) {
GuidedStepSupportFragment.this.onGuidedActionClicked(action);
if (isExpanded()) {
collapseAction(true);
} else if (action.hasSubActions() || action.hasEditableActivatorView()) {
expandAction(action, true);
}
}
}, this, mActionsStylist, false);
mButtonAdapter =
new GuidedActionAdapter(mButtonActions, new GuidedActionAdapter.ClickListener() {
@Override
public void onGuidedActionClicked(GuidedAction action) {
GuidedStepSupportFragment.this.onGuidedActionClicked(action);
}
}, this, mButtonActionsStylist, false);
mSubAdapter = new GuidedActionAdapter(null, new GuidedActionAdapter.ClickListener() {
@Override
public void onGuidedActionClicked(GuidedAction action) {
if (mActionsStylist.isInExpandTransition()) {
return;
}
if (GuidedStepSupportFragment.this.onSubGuidedActionClicked(action)) {
collapseSubActions();
}
}
}, this, mActionsStylist, true);
mAdapterGroup = new GuidedActionAdapterGroup();
mAdapterGroup.addAdpter(mAdapter, mButtonAdapter);
mAdapterGroup.addAdpter(mSubAdapter, null);
mAdapterGroup.setEditListener(editListener);
mActionsStylist.setEditListener(editListener);
mActionsStylist.getActionsGridView().setAdapter(mAdapter);
if (mActionsStylist.getSubActionsGridView() != null) {
mActionsStylist.getSubActionsGridView().setAdapter(mSubAdapter);
}
mButtonActionsStylist.getActionsGridView().setAdapter(mButtonAdapter);
if (mButtonActions.size() == 0) {
// when there is no button actions, we don't need show the second panel, but keep
// the width zero to run ChangeBounds transition.
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
buttonActionsView.getLayoutParams();
lp.weight = 0;
buttonActionsView.setLayoutParams(lp);
} else {
// when there are two actions panel, we need adjust the weight of action to
// guidedActionContentWidthWeightTwoPanels.
Context ctx = mThemeWrapper != null ? mThemeWrapper : getContext();
TypedValue typedValue = new TypedValue();
if (ctx.getTheme().resolveAttribute(R.attr.guidedActionContentWidthWeightTwoPanels,
typedValue, true)) {
View actionsRoot = root.findViewById(R.id.action_fragment_root);
float weight = typedValue.getFloat();
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) actionsRoot
.getLayoutParams();
lp.weight = weight;
actionsRoot.setLayoutParams(lp);
}
}
// Add the background view.
View backgroundView = onCreateBackgroundView(inflater, root, savedInstanceState);
if (backgroundView != null) {
FrameLayout backgroundViewRoot = (FrameLayout)root.findViewById(
R.id.guidedstep_background_view_root);
backgroundViewRoot.addView(backgroundView, 0);
}
return root;
}
@Override
public void onResume() {
super.onResume();
getView().findViewById(R.id.action_fragment).requestFocus();
}
/**
* Get the key will be used to save GuidedAction with Fragment.
* @param action GuidedAction to get key.
* @return Key to save the GuidedAction.
*/
final String getAutoRestoreKey(GuidedAction action) {
return EXTRA_ACTION_PREFIX + action.getId();
}
/**
* Get the key will be used to save GuidedAction with Fragment.
* @param action GuidedAction to get key.
* @return Key to save the GuidedAction.
*/
final String getButtonAutoRestoreKey(GuidedAction action) {
return EXTRA_BUTTON_ACTION_PREFIX + action.getId();
}
static boolean isSaveEnabled(GuidedAction action) {
return action.isAutoSaveRestoreEnabled() && action.getId() != GuidedAction.NO_ID;
}
final void onRestoreActions(List<GuidedAction> actions, Bundle savedInstanceState) {
for (int i = 0, size = actions.size(); i < size; i++) {
GuidedAction action = actions.get(i);
if (isSaveEnabled(action)) {
action.onRestoreInstanceState(savedInstanceState, getAutoRestoreKey(action));
}
}
}
final void onRestoreButtonActions(List<GuidedAction> actions, Bundle savedInstanceState) {
for (int i = 0, size = actions.size(); i < size; i++) {
GuidedAction action = actions.get(i);
if (isSaveEnabled(action)) {
action.onRestoreInstanceState(savedInstanceState, getButtonAutoRestoreKey(action));
}
}
}
final void onSaveActions(List<GuidedAction> actions, Bundle outState) {
for (int i = 0, size = actions.size(); i < size; i++) {
GuidedAction action = actions.get(i);
if (isSaveEnabled(action)) {
action.onSaveInstanceState(outState, getAutoRestoreKey(action));
}
}
}
final void onSaveButtonActions(List<GuidedAction> actions, Bundle outState) {
for (int i = 0, size = actions.size(); i < size; i++) {
GuidedAction action = actions.get(i);
if (isSaveEnabled(action)) {
action.onSaveInstanceState(outState, getButtonAutoRestoreKey(action));
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
onSaveActions(mActions, outState);
onSaveButtonActions(mButtonActions, outState);
}
private static boolean isGuidedStepTheme(Context context) {
int resId = R.attr.guidedStepThemeFlag;
TypedValue typedValue = new TypedValue();
boolean found = context.getTheme().resolveAttribute(resId, typedValue, true);
if (DEBUG) Log.v(TAG, "Found guided step theme flag? " + found);
return found && typedValue.type == TypedValue.TYPE_INT_BOOLEAN && typedValue.data != 0;
}
/**
* Convenient method to close GuidedStepSupportFragments on top of other content or finish Activity if
* GuidedStepSupportFragments were started in a separate activity. Pops all stack entries including
* {@link #UI_STYLE_ENTRANCE}; if {@link #UI_STYLE_ENTRANCE} is not found, finish the activity.
* Note that this method must be paired with {@link #add(FragmentManager, GuidedStepSupportFragment,
* int)} which sets up the stack entry name for finding which fragment we need to pop back to.
*/
public void finishGuidedStepSupportFragments() {
final FragmentManager fragmentManager = getFragmentManager();
final int entryCount = fragmentManager.getBackStackEntryCount();
if (entryCount > 0) {
for (int i = entryCount - 1; i >= 0; i--) {
BackStackEntry entry = fragmentManager.getBackStackEntryAt(i);
if (isStackEntryUiStyleEntrance(entry.getName())) {
GuidedStepSupportFragment top = getCurrentGuidedStepSupportFragment(fragmentManager);
if (top != null) {
top.setUiStyle(UI_STYLE_ENTRANCE);
}
fragmentManager.popBackStackImmediate(entry.getId(),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
return;
}
}
}
ActivityCompat.finishAfterTransition(getActivity());
}
/**
* Convenient method to pop to fragment with Given class.
* @param guidedStepFragmentClass Name of the Class of GuidedStepSupportFragment to pop to.
* @param flags Either 0 or {@link FragmentManager#POP_BACK_STACK_INCLUSIVE}.
*/
public void popBackStackToGuidedStepSupportFragment(
@NonNull Class<?> guidedStepFragmentClass,
int flags
) {
if (!GuidedStepSupportFragment.class.isAssignableFrom(guidedStepFragmentClass)) {
return;
}
final FragmentManager fragmentManager = getFragmentManager();
final int entryCount = fragmentManager.getBackStackEntryCount();
String className = guidedStepFragmentClass.getName();
if (entryCount > 0) {
for (int i = entryCount - 1; i >= 0; i--) {
BackStackEntry entry = fragmentManager.getBackStackEntryAt(i);
String entryClassName = getGuidedStepSupportFragmentClassName(entry.getName());
if (className.equals(entryClassName)) {
fragmentManager.popBackStackImmediate(entry.getId(), flags);
return;
}
}
}
}
/**
* Returns true if allows focus out of start edge of GuidedStepSupportFragment, false otherwise.
* Default value is false, the reason is to disable FocusFinder to find focusable views
* beneath content of GuidedStepSupportFragment. Subclass may override.
* @return True if allows focus out of start edge of GuidedStepSupportFragment.
*/
public boolean isFocusOutStartAllowed() {
return false;
}
/**
* Returns true if allows focus out of end edge of GuidedStepSupportFragment, false otherwise.
* Default value is false, the reason is to disable FocusFinder to find focusable views
* beneath content of GuidedStepSupportFragment. Subclass may override.
* @return True if allows focus out of end edge of GuidedStepSupportFragment.
*/
public boolean isFocusOutEndAllowed() {
return false;
}
/**
* Sets the transition type to be used for {@link #UI_STYLE_ENTRANCE} animation.
* Currently we provide 2 different variations for animation - slide in from
* side (default) or bottom.
*
* Ideally we can retrieve the screen mode settings from the theme attribute
* {@code Theme.Leanback.GuidedStep#guidedStepHeightWeight} and use that to
* determine the transition. But the fragment context to retrieve the theme
* isn't available on platform v23 or earlier.
*
* For now clients(subclasses) can call this method inside the constructor.
*/
@RestrictTo(LIBRARY_GROUP_PREFIX)
public void setEntranceTransitionType(int transitionType) {
this.entranceTransitionType = transitionType;
}
/**
* Opens the provided action in edit mode and raises ime. This can be
* used to programmatically skip the extra click required to go into edit mode. This method
* can be invoked in {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}.
*/
public void openInEditMode(@Nullable GuidedAction action) {
mActionsStylist.openInEditMode(action);
}
private void resolveTheme() {
// Look up the guidedStepTheme in the currently specified theme. If it exists,
// replace the theme with its value.
Context context = getContext();
int theme = onProvideTheme();
if (theme == -1 && !isGuidedStepTheme(context)) {
// Look up the guidedStepTheme in the activity's currently specified theme. If it
// exists, replace the theme with its value.
int resId = R.attr.guidedStepTheme;
TypedValue typedValue = new TypedValue();
boolean found = context.getTheme().resolveAttribute(resId, typedValue, true);
if (DEBUG) Log.v(TAG, "Found guided step theme reference? " + found);
if (found) {
ContextThemeWrapper themeWrapper =
new ContextThemeWrapper(context, typedValue.resourceId);
if (isGuidedStepTheme(themeWrapper)) {
mThemeWrapper = themeWrapper;
} else {
found = false;
mThemeWrapper = null;
}
}
if (!found) {
Log.e(TAG, "GuidedStepSupportFragment does not have an appropriate theme set.");
}
} else if (theme != -1) {
mThemeWrapper = new ContextThemeWrapper(context, theme);
}
}
private LayoutInflater getThemeInflater(LayoutInflater inflater) {
if (mThemeWrapper == null) {
return inflater;
} else {
return inflater.cloneInContext(mThemeWrapper);
}
}
void runImeAnimations(boolean entering) {
ArrayList<Animator> animators = new ArrayList<Animator>();
if (entering) {
mGuidanceStylist.onImeAppearing(animators);
mActionsStylist.onImeAppearing(animators);
mButtonActionsStylist.onImeAppearing(animators);
} else {
mGuidanceStylist.onImeDisappearing(animators);
mActionsStylist.onImeDisappearing(animators);
mButtonActionsStylist.onImeDisappearing(animators);
}
AnimatorSet set = new AnimatorSet();
set.playTogether(animators);
set.start();
}
}