java.lang.Object
↳androidx.leanback.widget.GuidedActionsStylist
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.widget.GuidedActionsStylist android.support.v17.leanback.widget.GuidedActionsStylist
Overview
GuidedActionsStylist is used within a GuidedStepFragment
to supply the right-side panel where users can take actions. It consists of a container for the
list of actions, and a stationary selector view that indicates visually the location of focus.
GuidedActionsStylist has two different layouts: default is for normal actions including text,
radio, checkbox, DatePicker, etc, the other when GuidedActionsStylist.setAsButtonActions() is called is
recommended for button actions such as "yes", "no".
Many aspects of the base GuidedActionsStylist can be customized through theming; see the
theme attributes below. Note that these attributes are not set on individual elements in layout
XML, but instead would be set in a custom theme. See
Styles and Themes
for more information.
If these hooks are insufficient, this class may also be subclassed. Subclasses may wish to
override the GuidedActionsStylist.onProvideLayoutId() method to change the layout used to display the
list container and selector; override GuidedActionsStylist.onProvideItemLayoutId(int) and
GuidedActionsStylist.getItemViewType(GuidedAction) method to change the layout used to display each action.
To support a "click to activate" view similar to DatePicker, app needs:
Note: If an alternate list layout is provided, the following view IDs must be supplied:
These view IDs must be present in order for the stylist to function. The list ID must correspond
to a VerticalGridView or subclass.
If an alternate item layout is provided, the following view IDs should be used to refer to base
elements:
These view IDs are allowed to be missing, in which case the corresponding views in GuidedActionsStylist.ViewHolder will be null.
In order to support editable actions, the view associated with guidedactions_item_title should
be a subclass of , and should satisfy the ImeKeyMonitor interface and GuidedActionAutofillSupport interface.
Summary
Fields |
---|
public static final int | VIEW_TYPE_DATE_PICKER ViewType for DatePicker. |
public static final int | VIEW_TYPE_DEFAULT Default viewType that associated with default layout Id for the action item. |
Methods |
---|
public void | collapseAction(boolean withTransition)
Collapse expanded action. |
public void | expandAction(GuidedAction action, boolean withTransition)
Expand an action. |
public VerticalGridView | getActionsGridView()
Returns the VerticalGridView that displays the list of GuidedActions. |
public GuidedAction | getExpandedAction()
|
public int | getItemViewType(GuidedAction action)
Return view type of action, each different type can have differently associated layout Id. |
public VerticalGridView | getSubActionsGridView()
Returns the VerticalGridView that displays the sub actions list of an expanded action. |
public final boolean | isBackKeyToCollapseActivatorView()
|
public final boolean | isBackKeyToCollapseSubActions()
|
public boolean | isButtonActions()
Returns true if it is button actions list, false for normal actions list. |
public boolean | isExpanded()
|
public boolean | isExpandTransitionSupported()
Returns if expand/collapse animation is supported. |
public boolean | isInExpandTransition()
Returns true if it is running an expanding or collapsing transition, false otherwise. |
public boolean | isSubActionsExpanded()
|
public void | onAnimateItemChecked(GuidedActionsStylist.ViewHolder vh, boolean checked)
Animates the view holder's view (or subviews thereof) when the action has had its check state
changed. |
public void | onAnimateItemFocused(GuidedActionsStylist.ViewHolder vh, boolean focused)
Animates the view holder's view (or subviews thereof) when the action has had its focus
state changed. |
public void | onAnimateItemPressed(GuidedActionsStylist.ViewHolder vh, boolean pressed)
Animates the view holder's view (or subviews thereof) when the action has had its press
state changed. |
public void | onAnimateItemPressedCancelled(GuidedActionsStylist.ViewHolder vh)
Resets the view holder's view to unpressed state. |
public void | onBindActivatorView(GuidedActionsStylist.ViewHolder vh, GuidedAction action)
Performs binding activator view value to action. |
public void | onBindCheckMarkView(GuidedActionsStylist.ViewHolder vh, GuidedAction action)
Sets states of check mark view, called by GuidedActionsStylist.onBindViewHolder(GuidedActionsStylist.ViewHolder, GuidedAction)
when action's checkset Id is other than GuidedAction.NO_CHECK_SET. |
public void | onBindChevronView(GuidedActionsStylist.ViewHolder vh, GuidedAction action)
Sets states of chevron view, called by GuidedActionsStylist.onBindViewHolder(GuidedActionsStylist.ViewHolder, GuidedAction). |
public void | onBindViewHolder(GuidedActionsStylist.ViewHolder vh, GuidedAction action)
Binds a GuidedActionsStylist.ViewHolder to a particular GuidedAction. |
public View | onCreateView(LayoutInflater inflater, ViewGroup container)
Creates a view appropriate for displaying a list of GuidedActions, using the provided
inflater and container. |
public GuidedActionsStylist.ViewHolder | onCreateViewHolder(ViewGroup parent)
Constructs a GuidedActionsStylist.ViewHolder capable of representing GuidedActions. |
public GuidedActionsStylist.ViewHolder | onCreateViewHolder(ViewGroup parent, int viewType)
Constructs a GuidedActionsStylist.ViewHolder capable of representing GuidedActions. |
public void | onDestroyView()
Called when destroy the View created by GuidedActionsStylist. |
protected void | onEditingModeChange(GuidedActionsStylist.ViewHolder vh, boolean editing, boolean withTransition)
Called when editing mode of an ViewHolder is changed. |
protected void | onEditingModeChange(GuidedActionsStylist.ViewHolder vh, GuidedAction action, boolean editing)
|
public void | onImeAppearing(java.util.List<Animator> animators)
|
public void | onImeDisappearing(java.util.List<Animator> animators)
|
public int | onProvideItemLayoutId()
Provides the resource ID of the layout defining the view for an individual guided actions. |
public int | onProvideItemLayoutId(int viewType)
Provides the resource ID of the layout defining the view for an individual guided actions. |
public int | onProvideLayoutId()
Provides the resource ID of the layout defining the host view for the list of guided actions. |
public boolean | onUpdateActivatorView(GuidedActionsStylist.ViewHolder vh, GuidedAction action)
Performs updating GuidedAction from activator view. |
public void | onUpdateExpandedViewHolder(GuidedActionsStylist.ViewHolder avh)
Expand or collapse GuidedActionStylist. |
public void | openInEditMode(GuidedAction action)
Switches action to edit mode and pops up the keyboard. |
public void | setAsButtonActions()
Choose the layout resource for button actions in GuidedActionsStylist.onProvideLayoutId(). |
public final void | setBackKeyToCollapseActivatorView(boolean backToCollapse)
Enable or disable using BACK key to collapse GuidedAction with editable activator
view. |
public final void | setBackKeyToCollapseSubActions(boolean backToCollapse)
Enable or disable using BACK key to collapse sub actions list. |
public void | setEditingMode(GuidedActionsStylist.ViewHolder vh, GuidedAction action, boolean editing)
|
public void | setEditListener(GuidedActionAdapter.EditListener listener)
Sets listener for reporting view being edited. |
public void | setExpandedViewHolder(GuidedActionsStylist.ViewHolder avh)
Expands or collapse the sub actions list view with transition animation |
protected void | setupImeOptions(GuidedActionsStylist.ViewHolder vh, GuidedAction action)
Called by GuidedActionsStylist.onBindViewHolder(GuidedActionsStylist.ViewHolder, GuidedAction) to setup IME options. |
public void | startExpandedTransition(GuidedActionsStylist.ViewHolder avh)
Start transition to expand or collapse GuidedActionStylist. |
from java.lang.Object | clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Fields
public static final int
VIEW_TYPE_DEFAULTDefault viewType that associated with default layout Id for the action item.
See also: GuidedActionsStylist.getItemViewType(GuidedAction), GuidedActionsStylist.onProvideItemLayoutId(int), GuidedActionsStylist.onCreateViewHolder(ViewGroup, int)
public static final int
VIEW_TYPE_DATE_PICKERViewType for DatePicker.
Constructors
public
GuidedActionsStylist()
Methods
public View
onCreateView(LayoutInflater inflater, ViewGroup container)
Creates a view appropriate for displaying a list of GuidedActions, using the provided
inflater and container.
Note: Does not actually add the created view to the container; the caller should do
this.
Parameters:
inflater: The layout inflater to be used when constructing the view.
container: The view group to be passed in the call to
LayoutInflater.inflate
.
Returns:
The view to be added to the caller's view hierarchy.
public void
setAsButtonActions()
Choose the layout resource for button actions in GuidedActionsStylist.onProvideLayoutId().
public boolean
isButtonActions()
Returns true if it is button actions list, false for normal actions list.
Returns:
True if it is button actions list, false for normal actions list.
public void
onDestroyView()
Called when destroy the View created by GuidedActionsStylist.
Returns the VerticalGridView that displays the list of GuidedActions.
Returns:
The VerticalGridView for this presenter.
Returns the VerticalGridView that displays the sub actions list of an expanded action.
Returns:
The VerticalGridView that displays the sub actions list of an expanded action.
public int
onProvideLayoutId()
Provides the resource ID of the layout defining the host view for the list of guided actions.
Subclasses may override to provide their own customized layouts. The base implementation
returns or
if
GuidedActionsStylist.isButtonActions() is true. If overridden, the substituted layout should contain
matching IDs for any views that should be managed by the base class; this can be achieved by
starting with a copy of the base layout file.
Returns:
The resource ID of the layout to be inflated to define the host view for the list of
GuidedActions.
Return view type of action, each different type can have differently associated layout Id.
Default implementation returns GuidedActionsStylist.VIEW_TYPE_DEFAULT.
Parameters:
action: The action object.
Returns:
View type that used in GuidedActionsStylist.onProvideItemLayoutId(int).
public int
onProvideItemLayoutId()
Provides the resource ID of the layout defining the view for an individual guided actions.
Subclasses may override to provide their own customized layouts. The base implementation
returns . If overridden,
the substituted layout should contain matching IDs for any views that should be managed by
the base class; this can be achieved by starting with a copy of the base layout file. Note
that in order for the item to support editing, the title view should both subclass and implement ImeKeyMonitor,
GuidedActionAutofillSupport; see GuidedActionEditText. To support different types of Layouts, override GuidedActionsStylist.onProvideItemLayoutId(int).
Returns:
The resource ID of the layout to be inflated to define the view to display an
individual GuidedAction.
public int
onProvideItemLayoutId(int viewType)
Provides the resource ID of the layout defining the view for an individual guided actions.
Subclasses may override to provide their own customized layouts. The base implementation
supports:
If overridden, the substituted layout should contain matching IDs for any views that should
be managed by the base class; this can be achieved by starting with a copy of the base layout
file. Note that in order for the item to support editing, the title view should both subclass
and implement
ImeKeyMonitor; see
GuidedActionEditText.
Parameters:
viewType: View type returned by GuidedActionsStylist.getItemViewType(GuidedAction)
Returns:
The resource ID of the layout to be inflated to define the view to display an
individual GuidedAction.
Constructs a GuidedActionsStylist.ViewHolder capable of representing GuidedActions. Subclasses
may choose to return a subclass of ViewHolder. To support different view types, override
GuidedActionsStylist.onCreateViewHolder(ViewGroup, int)
Note: Should not actually add the created view to the parent; the caller will do
this.
Parameters:
parent: The view group to be used as the parent of the new view.
Returns:
The view to be added to the caller's view hierarchy.
Constructs a GuidedActionsStylist.ViewHolder capable of representing GuidedActions. Subclasses
may choose to return a subclass of ViewHolder.
Note: Should not actually add the created view to the parent; the caller will do
this.
Parameters:
parent: The view group to be used as the parent of the new view.
viewType: The viewType returned by GuidedActionsStylist.getItemViewType(GuidedAction)
Returns:
The view to be added to the caller's view hierarchy.
Binds a GuidedActionsStylist.ViewHolder to a particular GuidedAction.
Parameters:
vh: The view holder to be associated with the given action.
action: The guided action to be displayed by the view holder's view.
Returns:
The view to be added to the caller's view hierarchy.
Switches action to edit mode and pops up the keyboard.
Called by GuidedActionsStylist.onBindViewHolder(GuidedActionsStylist.ViewHolder, GuidedAction) to setup IME options. Default
implementation assigns . Subclass may override.
Parameters:
vh: The view holder to be associated with the given action.
action: The guided action to be displayed by the view holder's view.
Deprecated: This method is for internal library use only and should not
be called directly.
Deprecated: Use GuidedActionsStylist.onEditingModeChange(GuidedActionsStylist.ViewHolder, boolean, boolean).
Called when editing mode of an ViewHolder is changed. Subclass must call
super.onEditingModeChange(vh,editing,withTransition)
.
Parameters:
vh: ViewHolder to change editing mode.
editing: True to enable editing, false to stop editing
withTransition: True to run expand transiiton, false otherwise.
Animates the view holder's view (or subviews thereof) when the action has had its focus
state changed.
Parameters:
vh: The view holder associated with the relevant action.
focused: True if the action has become focused, false if it has lost focus.
Animates the view holder's view (or subviews thereof) when the action has had its press
state changed.
Parameters:
vh: The view holder associated with the relevant action.
pressed: True if the action has been pressed, false if it has been unpressed.
Resets the view holder's view to unpressed state.
Parameters:
vh: The view holder associated with the relevant action.
Animates the view holder's view (or subviews thereof) when the action has had its check state
changed. Default implementation calls setChecked() if GuidedActionsStylist.ViewHolder.getCheckmarkView()
is instance of .
Parameters:
vh: The view holder associated with the relevant action.
checked: True if the action has become checked, false if it has become unchecked.
See also: GuidedActionsStylist.onBindCheckMarkView(GuidedActionsStylist.ViewHolder, GuidedAction)
Sets states of check mark view, called by GuidedActionsStylist.onBindViewHolder(GuidedActionsStylist.ViewHolder, GuidedAction)
when action's checkset Id is other than GuidedAction.NO_CHECK_SET. Default
implementation assigns drawable loaded from theme attribute
for checkbox or
for radio button. Subclass rarely needs
override the method, instead app can provide its own drawable that supports transition
animations, change theme attributes and
in {androidx.leanback.R.
styleable#LeanbackGuidedStepTheme}.
Parameters:
vh: The view holder associated with the relevant action.
action: The GuidedAction object to bind to.
See also: GuidedActionsStylist.onAnimateItemChecked(GuidedActionsStylist.ViewHolder, boolean)
Performs binding activator view value to action. Default implementation supports
GuidedDatePickerAction, subclass may override to add support of other views.
Parameters:
vh: ViewHolder of activator view.
action: GuidedAction to bind.
Performs updating GuidedAction from activator view. Default implementation supports
GuidedDatePickerAction, subclass may override to add support of other views.
Parameters:
vh: ViewHolder of activator view.
action: GuidedAction to update.
Returns:
True if value has been updated, false otherwise.
Sets listener for reporting view being edited.
Sets states of chevron view, called by GuidedActionsStylist.onBindViewHolder(GuidedActionsStylist.ViewHolder, GuidedAction).
Subclass may override.
Parameters:
vh: The view holder associated with the relevant action.
action: The GuidedAction object to bind to.
Deprecated: use GuidedActionsStylist.expandAction(GuidedAction, boolean) and
GuidedActionsStylist.collapseAction(boolean)
Expands or collapse the sub actions list view with transition animation
Parameters:
avh: When not null, fill sub actions list of this ViewHolder into sub actions list and
hide the other items in main list. When null, collapse the sub actions list.
public boolean
isInExpandTransition()
Returns true if it is running an expanding or collapsing transition, false otherwise.
Returns:
True if it is running an expanding or collapsing transition, false otherwise.
public boolean
isExpandTransitionSupported()
Returns if expand/collapse animation is supported. When this method returns true,
GuidedActionsStylist.startExpandedTransition(GuidedActionsStylist.ViewHolder) will be used. When this method returns false,
GuidedActionsStylist.onUpdateExpandedViewHolder(GuidedActionsStylist.ViewHolder) will be called.
Returns:
True if it is running an expanding or collapsing transition, false otherwise.
Deprecated: use GuidedActionsStylist.expandAction(GuidedAction, boolean) and
GuidedActionsStylist.collapseAction(boolean)
Start transition to expand or collapse GuidedActionStylist.
Parameters:
avh: When not null, the GuidedActionStylist expands the sub actions of avh. When null
the GuidedActionStylist will collapse sub actions.
public final void
setBackKeyToCollapseSubActions(boolean backToCollapse)
Enable or disable using BACK key to collapse sub actions list. Default is enabled.
Parameters:
backToCollapse: True to enable using BACK key to collapse sub actions list, false
to disable.
See also: GuidedAction.hasSubActions(), GuidedAction.getSubActions()
public final boolean
isBackKeyToCollapseSubActions()
Returns:
True if using BACK key to collapse sub actions list, false otherwise. Default value
is true.
See also: GuidedAction.hasSubActions(), GuidedAction.getSubActions()
public final void
setBackKeyToCollapseActivatorView(boolean backToCollapse)
Enable or disable using BACK key to collapse GuidedAction with editable activator
view. Default is enabled.
Parameters:
backToCollapse: True to enable using BACK key to collapse GuidedAction with
editable activator view.
See also: GuidedAction.hasEditableActivatorView()
public final boolean
isBackKeyToCollapseActivatorView()
Returns:
True if using BACK key to collapse GuidedAction with editable activator
view, false otherwise. Default value is true.
See also: GuidedAction.hasEditableActivatorView()
public void
expandAction(
GuidedAction action, boolean withTransition)
Expand an action. Do nothing if it is in animation or there is action expanded.
Parameters:
action: Action to expand.
withTransition: True to run transition animation, false otherwsie.
public void
collapseAction(boolean withTransition)
Collapse expanded action. Do nothing if it is in animation or there is no action expanded.
Parameters:
withTransition: True to run transition animation, false otherwsie.
public boolean
isSubActionsExpanded()
Returns:
True if sub actions list is expanded.
public boolean
isExpanded()
Returns:
True if there is GuidedActionsStylist.getExpandedAction() is not null, false otherwise.
Returns:
Current expanded GuidedAction or null if not expanded.
Expand or collapse GuidedActionStylist.
Parameters:
avh: When not null, the GuidedActionStylist expands the sub actions of avh. When null
the GuidedActionStylist will collapse sub actions.
public void
onImeAppearing(java.util.List<Animator> animators)
public void
onImeDisappearing(java.util.List<Animator> animators)
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.widget;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
import static androidx.leanback.widget.GuidedAction.EDITING_ACTIVATOR_VIEW;
import static androidx.leanback.widget.GuidedAction.EDITING_DESCRIPTION;
import static androidx.leanback.widget.GuidedAction.EDITING_NONE;
import static androidx.leanback.widget.GuidedAction.EDITING_TITLE;
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Build.VERSION;
import android.text.InputType;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.AccessibilityDelegate;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.inputmethod.EditorInfo;
import android.widget.Checkable;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.content.ContextCompat;
import androidx.leanback.R;
import androidx.leanback.transition.TransitionEpicenterCallback;
import androidx.leanback.transition.TransitionHelper;
import androidx.leanback.transition.TransitionListener;
import androidx.leanback.widget.GuidedActionAdapter.EditListener;
import androidx.leanback.widget.picker.DatePicker;
import androidx.recyclerview.widget.RecyclerView;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
/**
* GuidedActionsStylist is used within a {@link androidx.leanback.app.GuidedStepFragment}
* to supply the right-side panel where users can take actions. It consists of a container for the
* list of actions, and a stationary selector view that indicates visually the location of focus.
* GuidedActionsStylist has two different layouts: default is for normal actions including text,
* radio, checkbox, DatePicker, etc, the other when {@link #setAsButtonActions()} is called is
* recommended for button actions such as "yes", "no".
* <p>
* Many aspects of the base GuidedActionsStylist can be customized through theming; see the
* theme attributes below. Note that these attributes are not set on individual elements in layout
* XML, but instead would be set in a custom theme. See
* <a href="http://developer.android.com/guide/topics/ui/themes.html">Styles and Themes</a>
* for more information.
* <p>
* If these hooks are insufficient, this class may also be subclassed. Subclasses may wish to
* override the {@link #onProvideLayoutId} method to change the layout used to display the
* list container and selector; override {@link #onProvideItemLayoutId(int)} and
* {@link #getItemViewType(GuidedAction)} method to change the layout used to display each action.
* <p>
* To support a "click to activate" view similar to DatePicker, app needs:
* <ul>
* <li> Override {@link #onProvideItemLayoutId(int)} and {@link #getItemViewType(GuidedAction)},
* provides a layout id for the action.</li>
* <li> The layout must include a widget with id "guidedactions_activator_item", the widget is
* toggled edit mode by {@link View#setActivated(boolean)}.</li>
* <li>
* Override {@link #onBindActivatorView(ViewHolder, GuidedAction)} to populate values into View.
* </li>
* <li>
* Override {@link #onUpdateActivatorView(ViewHolder, GuidedAction)} to update action.
* </li>
* </ul>
* <p>
* Note: If an alternate list layout is provided, the following view IDs must be supplied:
* <ul>
* <li>{@link androidx.leanback.R.id#guidedactions_list}</li>
* </ul><p>
* These view IDs must be present in order for the stylist to function. The list ID must correspond
* to a {@link VerticalGridView} or subclass.
* <p>
* If an alternate item layout is provided, the following view IDs should be used to refer to base
* elements:
* <ul>
* <li>{@link androidx.leanback.R.id#guidedactions_item_content}</li>
* <li>{@link androidx.leanback.R.id#guidedactions_item_title}</li>
* <li>{@link androidx.leanback.R.id#guidedactions_item_description}</li>
* <li>{@link androidx.leanback.R.id#guidedactions_item_icon}</li>
* <li>{@link androidx.leanback.R.id#guidedactions_item_checkmark}</li>
* <li>{@link androidx.leanback.R.id#guidedactions_item_chevron}</li>
* </ul><p>
* These view IDs are allowed to be missing, in which case the corresponding views in {@link
* GuidedActionsStylist.ViewHolder} will be null.
* <p>
* In order to support editable actions, the view associated with guidedactions_item_title should
* be a subclass of {@link android.widget.EditText}, and should satisfy the {@link
* ImeKeyMonitor} interface and {@link GuidedActionAutofillSupport} interface.
*
* {@link androidx.leanback.R.attr#guidedStepImeAppearingAnimation}
* {@link androidx.leanback.R.attr#guidedStepImeDisappearingAnimation}
* {@link androidx.leanback.R.attr#guidedActionsSelectorDrawable}
* {@link androidx.leanback.R.attr#guidedActionsListStyle}
* {@link androidx.leanback.R.attr#guidedSubActionsListStyle}
* {@link androidx.leanback.R.attr#guidedButtonActionsListStyle}
* {@link androidx.leanback.R.attr#guidedActionItemContainerStyle}
* {@link androidx.leanback.R.attr#guidedActionItemCheckmarkStyle}
* {@link androidx.leanback.R.attr#guidedActionItemIconStyle}
* {@link androidx.leanback.R.attr#guidedActionItemContentStyle}
* {@link androidx.leanback.R.attr#guidedActionItemTitleStyle}
* {@link androidx.leanback.R.attr#guidedActionItemDescriptionStyle}
* {@link androidx.leanback.R.attr#guidedActionItemChevronStyle}
* {@link androidx.leanback.R.attr#guidedActionPressedAnimation}
* {@link androidx.leanback.R.attr#guidedActionUnpressedAnimation}
* {@link androidx.leanback.R.attr#guidedActionEnabledChevronAlpha}
* {@link androidx.leanback.R.attr#guidedActionDisabledChevronAlpha}
* {@link androidx.leanback.R.attr#guidedActionTitleMinLines}
* {@link androidx.leanback.R.attr#guidedActionTitleMaxLines}
* {@link androidx.leanback.R.attr#guidedActionDescriptionMinLines}
* {@link androidx.leanback.R.attr#guidedActionVerticalPadding}
* @see android.R.attr#listChoiceIndicatorSingle
* @see android.R.attr#listChoiceIndicatorMultiple
* @see androidx.leanback.app.GuidedStepFragment
* @see GuidedAction
*/
public class GuidedActionsStylist implements FragmentAnimationProvider {
/**
* Default viewType that associated with default layout Id for the action item.
* @see #getItemViewType(GuidedAction)
* @see #onProvideItemLayoutId(int)
* @see #onCreateViewHolder(ViewGroup, int)
*/
public static final int VIEW_TYPE_DEFAULT = 0;
/**
* ViewType for DatePicker.
*/
public static final int VIEW_TYPE_DATE_PICKER = 1;
final static ItemAlignmentFacet sGuidedActionItemAlignFacet;
static {
sGuidedActionItemAlignFacet = new ItemAlignmentFacet();
ItemAlignmentFacet.ItemAlignmentDef alignedDef = new ItemAlignmentFacet.ItemAlignmentDef();
alignedDef.setItemAlignmentViewId(R.id.guidedactions_item_title);
alignedDef.setAlignedToTextViewBaseline(true);
alignedDef.setItemAlignmentOffset(0);
alignedDef.setItemAlignmentOffsetWithPadding(true);
alignedDef.setItemAlignmentOffsetPercent(0);
sGuidedActionItemAlignFacet.setAlignmentDefs(new ItemAlignmentFacet.ItemAlignmentDef[]{alignedDef});
}
/**
* ViewHolder caches information about the action item layouts' subviews. Subclasses of {@link
* GuidedActionsStylist} may also wish to subclass this in order to add fields.
* @see GuidedAction
*/
public static class ViewHolder extends RecyclerView.ViewHolder implements FacetProvider {
GuidedAction mAction;
private View mContentView;
TextView mTitleView;
TextView mDescriptionView;
View mActivatorView;
ImageView mIconView;
ImageView mCheckmarkView;
ImageView mChevronView;
int mEditingMode = EDITING_NONE;
private final boolean mIsSubAction;
Animator mPressAnimator;
final AccessibilityDelegate mDelegate = new AccessibilityDelegate() {
@Override
public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(host, event);
event.setChecked(mAction != null && mAction.isChecked());
}
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(host, info);
info.setCheckable(
mAction != null && mAction.getCheckSetId() != GuidedAction.NO_CHECK_SET);
info.setChecked(mAction != null && mAction.isChecked());
}
};
/**
* Constructs an ViewHolder and caches the relevant subviews.
*/
public ViewHolder(@NonNull View v) {
this(v, false);
}
/**
* Constructs an ViewHolder for sub action and caches the relevant subviews.
*/
public ViewHolder(@NonNull View v, boolean isSubAction) {
super(v);
mContentView = v.findViewById(R.id.guidedactions_item_content);
mTitleView = (TextView) v.findViewById(R.id.guidedactions_item_title);
mActivatorView = v.findViewById(R.id.guidedactions_activator_item);
mDescriptionView = (TextView) v.findViewById(R.id.guidedactions_item_description);
mIconView = (ImageView) v.findViewById(R.id.guidedactions_item_icon);
mCheckmarkView = (ImageView) v.findViewById(R.id.guidedactions_item_checkmark);
mChevronView = (ImageView) v.findViewById(R.id.guidedactions_item_chevron);
mIsSubAction = isSubAction;
v.setAccessibilityDelegate(mDelegate);
}
/**
* Returns the content view within this view holder's view, where title and description are
* shown.
*/
@Nullable
public View getContentView() {
return mContentView;
}
/**
* Returns the title view within this view holder's view.
*/
@Nullable
public TextView getTitleView() {
return mTitleView;
}
/**
* Convenience method to return an editable version of the title, if possible,
* or null if the title view isn't an EditText.
*/
@Nullable
public EditText getEditableTitleView() {
return (mTitleView instanceof EditText) ? (EditText)mTitleView : null;
}
/**
* Returns the description view within this view holder's view.
*/
@Nullable
public TextView getDescriptionView() {
return mDescriptionView;
}
/**
* Convenience method to return an editable version of the description, if possible,
* or null if the description view isn't an EditText.
*/
@Nullable
public EditText getEditableDescriptionView() {
return (mDescriptionView instanceof EditText) ? (EditText)mDescriptionView : null;
}
/**
* Returns the icon view within this view holder's view.
*/
@Nullable
public ImageView getIconView() {
return mIconView;
}
/**
* Returns the checkmark view within this view holder's view.
*/
@Nullable
public ImageView getCheckmarkView() {
return mCheckmarkView;
}
/**
* Returns the chevron view within this view holder's view.
*/
@Nullable
public ImageView getChevronView() {
return mChevronView;
}
/**
* Returns true if in editing title, description, or activator View, false otherwise.
*/
public boolean isInEditing() {
return mEditingMode != EDITING_NONE;
}
/**
* Returns true if in editing title, description, so IME would be open.
* @return True if in editing title, description, so IME would be open, false otherwise.
*/
public boolean isInEditingText() {
return mEditingMode == EDITING_TITLE || mEditingMode == EDITING_DESCRIPTION;
}
/**
* Returns true if the TextView is in editing title, false otherwise.
*/
public boolean isInEditingTitle() {
return mEditingMode == EDITING_TITLE;
}
/**
* Returns true if the TextView is in editing description, false otherwise.
*/
public boolean isInEditingDescription() {
return mEditingMode == EDITING_DESCRIPTION;
}
/**
* Returns true if is in editing activator view with id guidedactions_activator_item, false
* otherwise.
*/
public boolean isInEditingActivatorView() {
return mEditingMode == EDITING_ACTIVATOR_VIEW;
}
/**
* @return Current editing title view or description view or activator view or null if not
* in editing.
*/
@Nullable
public View getEditingView() {
switch(mEditingMode) {
case EDITING_TITLE:
return mTitleView;
case EDITING_DESCRIPTION:
return mDescriptionView;
case EDITING_ACTIVATOR_VIEW:
return mActivatorView;
case EDITING_NONE:
default:
return null;
}
}
/**
* @return True if bound action is inside {@link GuidedAction#getSubActions()}, false
* otherwise.
*/
public boolean isSubAction() {
return mIsSubAction;
}
/**
* @return Currently bound action.
*/
@Nullable
public GuidedAction getAction() {
return mAction;
}
void setActivated(boolean activated) {
mActivatorView.setActivated(activated);
if (itemView instanceof GuidedActionItemContainer) {
((GuidedActionItemContainer) itemView).setFocusOutAllowed(!activated);
}
}
@Nullable
@Override
public Object getFacet(@NonNull Class<?> facetClass) {
if (facetClass == ItemAlignmentFacet.class) {
return sGuidedActionItemAlignFacet;
}
return null;
}
void press(boolean pressed) {
if (mPressAnimator != null) {
mPressAnimator.cancel();
mPressAnimator = null;
}
final int themeAttrId = pressed ? R.attr.guidedActionPressedAnimation :
R.attr.guidedActionUnpressedAnimation;
Context ctx = itemView.getContext();
TypedValue typedValue = new TypedValue();
if (ctx.getTheme().resolveAttribute(themeAttrId, typedValue, true)) {
mPressAnimator = AnimatorInflater.loadAnimator(ctx, typedValue.resourceId);
mPressAnimator.setTarget(itemView);
mPressAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mPressAnimator = null;
}
});
mPressAnimator.start();
}
}
}
private static final String TAG = "GuidedActionsStylist";
ViewGroup mMainView;
private VerticalGridView mActionsGridView;
VerticalGridView mSubActionsGridView;
private View mSubActionsBackground;
private View mContentView;
private boolean mButtonActions;
// Cached values from resources
private float mEnabledTextAlpha;
private float mDisabledTextAlpha;
private float mEnabledDescriptionAlpha;
private float mDisabledDescriptionAlpha;
private float mEnabledChevronAlpha;
private float mDisabledChevronAlpha;
private int mTitleMinLines;
private int mTitleMaxLines;
private int mDescriptionMinLines;
private int mVerticalPadding;
private int mDisplayHeight;
private EditListener mEditListener;
@SuppressWarnings("WeakerAccess") /* synthetic access */
GuidedAction mExpandedAction = null;
Object mExpandTransition;
private boolean mBackToCollapseSubActions = true;
private boolean mBackToCollapseActivatorView = true;
private float mKeyLinePercent;
/**
* Creates a view appropriate for displaying a list of GuidedActions, using the provided
* inflater and container.
* <p>
* <i>Note: Does not actually add the created view to the container; the caller should do
* this.</i>
* @param inflater The layout inflater to be used when constructing the view.
* @param container The view group to be passed in the call to
* <code>LayoutInflater.inflate</code>.
* @return The view to be added to the caller's view hierarchy.
*/
@NonNull
@SuppressWarnings("deprecation") /* defaultDisplay */
public View onCreateView(@NonNull LayoutInflater inflater, final @NonNull ViewGroup container) {
TypedArray ta = inflater.getContext().getTheme().obtainStyledAttributes(
R.styleable.LeanbackGuidedStepTheme);
float keylinePercent = ta.getFloat(R.styleable.LeanbackGuidedStepTheme_guidedStepKeyline,
40);
mMainView = (ViewGroup) inflater.inflate(onProvideLayoutId(), container, false);
mContentView = mMainView.findViewById(mButtonActions ? R.id.guidedactions_content2 :
R.id.guidedactions_content);
if (mMainView instanceof VerticalGridView) {
mActionsGridView = (VerticalGridView) mMainView;
} else {
mActionsGridView = (VerticalGridView) mMainView.findViewById(mButtonActions
? R.id.guidedactions_list2 : R.id.guidedactions_list);
if (mActionsGridView == null) {
throw new IllegalStateException("No ListView exists.");
}
mActionsGridView.setWindowAlignmentOffsetPercent(keylinePercent);
mActionsGridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
if (!mButtonActions) {
mSubActionsGridView = (VerticalGridView) mMainView.findViewById(
R.id.guidedactions_sub_list);
mSubActionsBackground = mMainView.findViewById(
R.id.guidedactions_sub_list_background);
}
}
mActionsGridView.setFocusable(false);
mActionsGridView.setFocusableInTouchMode(false);
// Cache widths, chevron alpha values, max and min text lines, etc
Context ctx = mMainView.getContext();
TypedValue val = new TypedValue();
mEnabledChevronAlpha = getFloat(ctx, val, R.attr.guidedActionEnabledChevronAlpha);
mDisabledChevronAlpha = getFloat(ctx, val, R.attr.guidedActionDisabledChevronAlpha);
mTitleMinLines = getInteger(ctx, val, R.attr.guidedActionTitleMinLines);
mTitleMaxLines = getInteger(ctx, val, R.attr.guidedActionTitleMaxLines);
mDescriptionMinLines = getInteger(ctx, val, R.attr.guidedActionDescriptionMinLines);
mVerticalPadding = getDimension(ctx, val, R.attr.guidedActionVerticalPadding);
mDisplayHeight = ((WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay().getHeight();
mEnabledTextAlpha = getFloatValue(ctx.getResources(), val, R.dimen
.lb_guidedactions_item_unselected_text_alpha);
mDisabledTextAlpha = getFloatValue(ctx.getResources(), val, R.dimen
.lb_guidedactions_item_disabled_text_alpha);
mEnabledDescriptionAlpha = getFloatValue(ctx.getResources(), val, R.dimen
.lb_guidedactions_item_unselected_description_text_alpha);
mDisabledDescriptionAlpha = getFloatValue(ctx.getResources(), val, R.dimen
.lb_guidedactions_item_disabled_description_text_alpha);
mKeyLinePercent = GuidanceStylingRelativeLayout.getKeyLinePercent(ctx);
if (mContentView instanceof GuidedActionsRelativeLayout) {
((GuidedActionsRelativeLayout) mContentView).setInterceptKeyEventListener(
new GuidedActionsRelativeLayout.InterceptKeyEventListener() {
@Override
public boolean onInterceptKeyEvent(KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK
&& event.getAction() == KeyEvent.ACTION_UP
&& mExpandedAction != null) {
if ((mExpandedAction.hasSubActions()
&& isBackKeyToCollapseSubActions())
|| (mExpandedAction.hasEditableActivatorView()
&& isBackKeyToCollapseActivatorView())) {
collapseAction(true);
return true;
}
}
return false;
}
}
);
}
return mMainView;
}
/**
* Choose the layout resource for button actions in {@link #onProvideLayoutId()}.
*/
public void setAsButtonActions() {
if (mMainView != null) {
throw new IllegalStateException("setAsButtonActions() must be called before creating "
+ "views");
}
mButtonActions = true;
}
/**
* Returns true if it is button actions list, false for normal actions list.
* @return True if it is button actions list, false for normal actions list.
*/
public boolean isButtonActions() {
return mButtonActions;
}
/**
* Called when destroy the View created by GuidedActionsStylist.
*/
public void onDestroyView() {
mExpandedAction = null;
mExpandTransition = null;
mActionsGridView = null;
mSubActionsGridView = null;
mSubActionsBackground = null;
mContentView = null;
mMainView = null;
}
/**
* Returns the VerticalGridView that displays the list of GuidedActions.
* @return The VerticalGridView for this presenter.
*/
@Nullable
public VerticalGridView getActionsGridView() {
return mActionsGridView;
}
/**
* Returns the VerticalGridView that displays the sub actions list of an expanded action.
* @return The VerticalGridView that displays the sub actions list of an expanded action.
*/
@Nullable
public VerticalGridView getSubActionsGridView() {
return mSubActionsGridView;
}
/**
* Provides the resource ID of the layout defining the host view for the list of guided actions.
* Subclasses may override to provide their own customized layouts. The base implementation
* returns {@link androidx.leanback.R.layout#lb_guidedactions} or
* {@link androidx.leanback.R.layout#lb_guidedbuttonactions} if
* {@link #isButtonActions()} is true. If overridden, the substituted layout should contain
* matching IDs for any views that should be managed by the base class; this can be achieved by
* starting with a copy of the base layout file.
*
* @return The resource ID of the layout to be inflated to define the host view for the list of
* GuidedActions.
*/
public int onProvideLayoutId() {
return mButtonActions ? R.layout.lb_guidedbuttonactions : R.layout.lb_guidedactions;
}
/**
* Return view type of action, each different type can have differently associated layout Id.
* Default implementation returns {@link #VIEW_TYPE_DEFAULT}.
* @param action The action object.
* @return View type that used in {@link #onProvideItemLayoutId(int)}.
*/
public int getItemViewType(@NonNull GuidedAction action) {
if (action instanceof GuidedDatePickerAction) {
return VIEW_TYPE_DATE_PICKER;
}
return VIEW_TYPE_DEFAULT;
}
/**
* Provides the resource ID of the layout defining the view for an individual guided actions.
* Subclasses may override to provide their own customized layouts. The base implementation
* returns {@link androidx.leanback.R.layout#lb_guidedactions_item}. If overridden,
* the substituted layout should contain matching IDs for any views that should be managed by
* the base class; this can be achieved by starting with a copy of the base layout file. Note
* that in order for the item to support editing, the title view should both subclass {@link
* android.widget.EditText} and implement {@link ImeKeyMonitor},
* {@link GuidedActionAutofillSupport}; see {@link
* GuidedActionEditText}. To support different types of Layouts, override {@link
* #onProvideItemLayoutId(int)}.
* @return The resource ID of the layout to be inflated to define the view to display an
* individual GuidedAction.
*/
public int onProvideItemLayoutId() {
return R.layout.lb_guidedactions_item;
}
/**
* Provides the resource ID of the layout defining the view for an individual guided actions.
* Subclasses may override to provide their own customized layouts. The base implementation
* supports:
* <ul>
* <li>{@link androidx.leanback.R.layout#lb_guidedactions_item}</li>
* <li>{{@link androidx.leanback.R.layout#lb_guidedactions_datepicker_item}.</li>
* </ul>
* If overridden, the substituted layout should contain matching IDs for any views that should
* be managed by the base class; this can be achieved by starting with a copy of the base layout
* file. Note that in order for the item to support editing, the title view should both subclass
* {@link android.widget.EditText} and implement {@link ImeKeyMonitor}; see
* {@link GuidedActionEditText}.
*
* @param viewType View type returned by {@link #getItemViewType(GuidedAction)}
* @return The resource ID of the layout to be inflated to define the view to display an
* individual GuidedAction.
*/
public int onProvideItemLayoutId(int viewType) {
if (viewType == VIEW_TYPE_DEFAULT) {
return onProvideItemLayoutId();
} else if (viewType == VIEW_TYPE_DATE_PICKER) {
return R.layout.lb_guidedactions_datepicker_item;
} else {
throw new RuntimeException("ViewType " + viewType
+ " not supported in GuidedActionsStylist");
}
}
/**
* Constructs a {@link ViewHolder} capable of representing {@link GuidedAction}s. Subclasses
* may choose to return a subclass of ViewHolder. To support different view types, override
* {@link #onCreateViewHolder(ViewGroup, int)}
* <p>
* <i>Note: Should not actually add the created view to the parent; the caller will do
* this.</i>
* @param parent The view group to be used as the parent of the new view.
* @return The view to be added to the caller's view hierarchy.
*/
@NonNull
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View v = inflater.inflate(onProvideItemLayoutId(), parent, false);
return new ViewHolder(v, parent == mSubActionsGridView);
}
/**
* Constructs a {@link ViewHolder} capable of representing {@link GuidedAction}s. Subclasses
* may choose to return a subclass of ViewHolder.
* <p>
* <i>Note: Should not actually add the created view to the parent; the caller will do
* this.</i>
* @param parent The view group to be used as the parent of the new view.
* @param viewType The viewType returned by {@link #getItemViewType(GuidedAction)}
* @return The view to be added to the caller's view hierarchy.
*/
@NonNull
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_DEFAULT) {
return onCreateViewHolder(parent);
}
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View v = inflater.inflate(onProvideItemLayoutId(viewType), parent, false);
return new ViewHolder(v, parent == mSubActionsGridView);
}
/**
* Binds a {@link ViewHolder} to a particular {@link GuidedAction}.
* @param vh The view holder to be associated with the given action.
* @param action The guided action to be displayed by the view holder's view.
* @return The view to be added to the caller's view hierarchy.
*/
public void onBindViewHolder(@NonNull ViewHolder vh, @NonNull GuidedAction action) {
vh.mAction = action;
if (vh.mTitleView != null) {
vh.mTitleView.setInputType(action.getInputType());
vh.mTitleView.setText(action.getTitle());
vh.mTitleView.setAlpha(action.isEnabled() ? mEnabledTextAlpha : mDisabledTextAlpha);
vh.mTitleView.setFocusable(false);
vh.mTitleView.setClickable(false);
vh.mTitleView.setLongClickable(false);
if (Build.VERSION.SDK_INT >= 28) {
if (action.isEditable()) {
Api26Impl.setAutofillHints(vh.mTitleView, action.getAutofillHints());
} else {
Api26Impl.setAutofillHints(vh.mTitleView, (String[]) null);
}
} else if (VERSION.SDK_INT >= 26) {
// disable autofill below P as dpad/keyboard is not supported
Api26Impl.setImportantForAutofill(vh.mTitleView, View.IMPORTANT_FOR_AUTOFILL_NO);
}
}
if (vh.mDescriptionView != null) {
vh.mDescriptionView.setInputType(action.getDescriptionInputType());
vh.mDescriptionView.setText(action.getDescription());
vh.mDescriptionView.setVisibility(TextUtils.isEmpty(action.getDescription())
? View.GONE : View.VISIBLE);
vh.mDescriptionView.setAlpha(action.isEnabled() ? mEnabledDescriptionAlpha :
mDisabledDescriptionAlpha);
vh.mDescriptionView.setFocusable(false);
vh.mDescriptionView.setClickable(false);
vh.mDescriptionView.setLongClickable(false);
if (Build.VERSION.SDK_INT >= 28) {
if (action.isDescriptionEditable()) {
Api26Impl.setAutofillHints(vh.mDescriptionView, action.getAutofillHints());
} else {
Api26Impl.setAutofillHints(vh.mDescriptionView, (String[]) null);
}
} else if (VERSION.SDK_INT >= 26) {
// disable autofill below P as dpad/keyboard is not supported
Api26Impl.setImportantForAutofill(vh.mTitleView, View.IMPORTANT_FOR_AUTOFILL_NO);
}
}
// Clients might want the check mark view to be gone entirely, in which case, ignore it.
if (vh.mCheckmarkView != null) {
onBindCheckMarkView(vh, action);
}
setIcon(vh.mIconView, action);
if (action.hasMultilineDescription()) {
if (vh.mTitleView != null) {
setMaxLines(vh.mTitleView, mTitleMaxLines);
vh.mTitleView.setInputType(
vh.mTitleView.getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
if (vh.mDescriptionView != null) {
vh.mDescriptionView.setInputType(vh.mDescriptionView.getInputType()
| InputType.TYPE_TEXT_FLAG_MULTI_LINE);
vh.mDescriptionView.setMaxHeight(getDescriptionMaxHeight(vh.mTitleView));
}
}
} else {
if (vh.mTitleView != null) {
setMaxLines(vh.mTitleView, mTitleMinLines);
}
if (vh.mDescriptionView != null) {
setMaxLines(vh.mDescriptionView, mDescriptionMinLines);
}
}
if (vh.mActivatorView != null) {
onBindActivatorView(vh, action);
}
setEditingMode(vh, false /*editing*/, false /*withTransition*/);
if (action.isFocusable()) {
vh.itemView.setFocusable(true);
((ViewGroup) vh.itemView).setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
} else {
vh.itemView.setFocusable(false);
((ViewGroup) vh.itemView).setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
}
setupImeOptions(vh, action);
updateChevronAndVisibility(vh);
}
/**
* Switches action to edit mode and pops up the keyboard.
*/
public void openInEditMode(@NonNull GuidedAction action) {
final GuidedActionAdapter guidedActionAdapter =
(GuidedActionAdapter) getActionsGridView().getAdapter();
int actionIndex = guidedActionAdapter.getActions().indexOf(action);
if (actionIndex < 0 || !action.isEditable()) {
return;
}
getActionsGridView().setSelectedPosition(actionIndex, new ViewHolderTask() {
@Override
public void run(@NonNull RecyclerView.ViewHolder viewHolder) {
ViewHolder vh = (ViewHolder) viewHolder;
guidedActionAdapter.mGroup.openIme(guidedActionAdapter, vh);
}
});
}
private static void setMaxLines(TextView view, int maxLines) {
// setSingleLine must be called before setMaxLines because it resets maximum to
// Integer.MAX_VALUE.
if (maxLines == 1) {
view.setSingleLine(true);
} else {
view.setSingleLine(false);
view.setMaxLines(maxLines);
}
}
/**
* Called by {@link #onBindViewHolder(ViewHolder, GuidedAction)} to setup IME options. Default
* implementation assigns {@link EditorInfo#IME_ACTION_DONE}. Subclass may override.
* @param vh The view holder to be associated with the given action.
* @param action The guided action to be displayed by the view holder's view.
*/
protected void setupImeOptions(@NonNull ViewHolder vh, @NonNull GuidedAction action) {
setupNextImeOptions(vh.getEditableTitleView());
setupNextImeOptions(vh.getEditableDescriptionView());
}
private void setupNextImeOptions(EditText edit) {
if (edit != null) {
edit.setImeOptions(EditorInfo.IME_ACTION_NEXT);
}
}
/**
* @deprecated This method is for internal library use only and should not
* be called directly.
*/
@Deprecated
public void setEditingMode(ViewHolder vh, GuidedAction action, boolean editing) {
if (editing != vh.isInEditing() && isInExpandTransition()) {
onEditingModeChange(vh, action, editing);
}
}
void setEditingMode(ViewHolder vh, boolean editing) {
setEditingMode(vh, editing, true /*withTransition*/);
}
void setEditingMode(ViewHolder vh, boolean editing, boolean withTransition) {
if (editing != vh.isInEditing() && !isInExpandTransition()) {
onEditingModeChange(vh, editing, withTransition);
}
}
/**
* @deprecated Use {@link #onEditingModeChange(ViewHolder, boolean, boolean)}.
*/
@Deprecated
protected void onEditingModeChange(ViewHolder vh, GuidedAction action, boolean editing) {
}
/**
* Called when editing mode of an ViewHolder is changed. Subclass must call
* <code>super.onEditingModeChange(vh,editing,withTransition)</code>.
*
* @param vh ViewHolder to change editing mode.
* @param editing True to enable editing, false to stop editing
* @param withTransition True to run expand transiiton, false otherwise.
*/
@CallSuper
protected void onEditingModeChange(
@NonNull ViewHolder vh,
boolean editing,
boolean withTransition) {
GuidedAction action = vh.getAction();
TextView titleView = vh.getTitleView();
TextView descriptionView = vh.getDescriptionView();
if (editing) {
CharSequence editTitle = action.getEditTitle();
if (titleView != null && editTitle != null) {
titleView.setText(editTitle);
}
CharSequence editDescription = action.getEditDescription();
if (descriptionView != null && editDescription != null) {
descriptionView.setText(editDescription);
}
if (action.isDescriptionEditable()) {
if (descriptionView != null) {
descriptionView.setVisibility(View.VISIBLE);
descriptionView.setInputType(action.getDescriptionEditInputType());
descriptionView.requestFocusFromTouch();
}
vh.mEditingMode = EDITING_DESCRIPTION;
} else if (action.isEditable()){
if (titleView != null) {
titleView.setInputType(action.getEditInputType());
titleView.requestFocusFromTouch();
}
vh.mEditingMode = EDITING_TITLE;
} else if (vh.mActivatorView != null) {
onEditActivatorView(vh, editing, withTransition);
vh.mEditingMode = EDITING_ACTIVATOR_VIEW;
}
} else {
if (titleView != null) {
titleView.setText(action.getTitle());
}
if (descriptionView != null) {
descriptionView.setText(action.getDescription());
}
if (vh.mEditingMode == EDITING_DESCRIPTION) {
if (descriptionView != null) {
descriptionView.setVisibility(TextUtils.isEmpty(action.getDescription())
? View.GONE : View.VISIBLE);
descriptionView.setInputType(action.getDescriptionInputType());
}
} else if (vh.mEditingMode == EDITING_TITLE) {
if (titleView != null) {
titleView.setInputType(action.getInputType());
}
} else if (vh.mEditingMode == EDITING_ACTIVATOR_VIEW) {
if (vh.mActivatorView != null) {
onEditActivatorView(vh, editing, withTransition);
}
}
vh.mEditingMode = EDITING_NONE;
}
// call deprecated method for backward compatible
onEditingModeChange(vh, action, editing);
}
/**
* Animates the view holder's view (or subviews thereof) when the action has had its focus
* state changed.
* @param vh The view holder associated with the relevant action.
* @param focused True if the action has become focused, false if it has lost focus.
*/
public void onAnimateItemFocused(@NonNull ViewHolder vh, boolean focused) {
// No animations for this, currently, because the animation is done on
// mSelectorView
}
/**
* Animates the view holder's view (or subviews thereof) when the action has had its press
* state changed.
* @param vh The view holder associated with the relevant action.
* @param pressed True if the action has been pressed, false if it has been unpressed.
*/
public void onAnimateItemPressed(@NonNull ViewHolder vh, boolean pressed) {
vh.press(pressed);
}
/**
* Resets the view holder's view to unpressed state.
* @param vh The view holder associated with the relevant action.
*/
public void onAnimateItemPressedCancelled(@NonNull ViewHolder vh) {
vh.press(false);
}
/**
* Animates the view holder's view (or subviews thereof) when the action has had its check state
* changed. Default implementation calls setChecked() if {@link ViewHolder#getCheckmarkView()}
* is instance of {@link Checkable}.
*
* @param vh The view holder associated with the relevant action.
* @param checked True if the action has become checked, false if it has become unchecked.
* @see #onBindCheckMarkView(ViewHolder, GuidedAction)
*/
public void onAnimateItemChecked(@NonNull ViewHolder vh, boolean checked) {
if (vh.mCheckmarkView instanceof Checkable) {
((Checkable) vh.mCheckmarkView).setChecked(checked);
}
}
/**
* Sets states of check mark view, called by {@link #onBindViewHolder(ViewHolder, GuidedAction)}
* when action's checkset Id is other than {@link GuidedAction#NO_CHECK_SET}. Default
* implementation assigns drawable loaded from theme attribute
* {@link android.R.attr#listChoiceIndicatorMultiple} for checkbox or
* {@link android.R.attr#listChoiceIndicatorSingle} for radio button. Subclass rarely needs
* override the method, instead app can provide its own drawable that supports transition
* animations, change theme attributes {@link android.R.attr#listChoiceIndicatorMultiple} and
* {@link android.R.attr#listChoiceIndicatorSingle} in {androidx.leanback.R.
* styleable#LeanbackGuidedStepTheme}.
*
* @param vh The view holder associated with the relevant action.
* @param action The GuidedAction object to bind to.
* @see #onAnimateItemChecked(ViewHolder, boolean)
*/
public void onBindCheckMarkView(@NonNull ViewHolder vh, @NonNull GuidedAction action) {
if (action.getCheckSetId() != GuidedAction.NO_CHECK_SET) {
vh.mCheckmarkView.setVisibility(View.VISIBLE);
int attrId = action.getCheckSetId() == GuidedAction.CHECKBOX_CHECK_SET_ID
? android.R.attr.listChoiceIndicatorMultiple
: android.R.attr.listChoiceIndicatorSingle;
final Context context = vh.mCheckmarkView.getContext();
Drawable drawable = null;
TypedValue typedValue = new TypedValue();
if (context.getTheme().resolveAttribute(attrId, typedValue, true)) {
drawable = ContextCompat.getDrawable(context, typedValue.resourceId);
}
vh.mCheckmarkView.setImageDrawable(drawable);
if (vh.mCheckmarkView instanceof Checkable) {
((Checkable) vh.mCheckmarkView).setChecked(action.isChecked());
}
} else {
vh.mCheckmarkView.setVisibility(View.GONE);
}
}
/**
* Performs binding activator view value to action. Default implementation supports
* GuidedDatePickerAction, subclass may override to add support of other views.
* @param vh ViewHolder of activator view.
* @param action GuidedAction to bind.
*/
public void onBindActivatorView(@NonNull ViewHolder vh, @NonNull GuidedAction action) {
if (action instanceof GuidedDatePickerAction) {
GuidedDatePickerAction dateAction = (GuidedDatePickerAction) action;
DatePicker dateView = (DatePicker) vh.mActivatorView;
dateView.setDatePickerFormat(dateAction.getDatePickerFormat());
if (dateAction.getMinDate() != Long.MIN_VALUE) {
dateView.setMinDate(dateAction.getMinDate());
}
if (dateAction.getMaxDate() != Long.MAX_VALUE) {
dateView.setMaxDate(dateAction.getMaxDate());
}
Calendar c = Calendar.getInstance();
c.setTimeInMillis(dateAction.getDate());
dateView.setDate(c.get(Calendar.YEAR), c.get(Calendar.MONTH),
c.get(Calendar.DAY_OF_MONTH), false);
}
}
/**
* Performs updating GuidedAction from activator view. Default implementation supports
* GuidedDatePickerAction, subclass may override to add support of other views.
* @param vh ViewHolder of activator view.
* @param action GuidedAction to update.
* @return True if value has been updated, false otherwise.
*/
public boolean onUpdateActivatorView(@NonNull ViewHolder vh, @NonNull GuidedAction action) {
if (action instanceof GuidedDatePickerAction) {
GuidedDatePickerAction dateAction = (GuidedDatePickerAction) action;
DatePicker dateView = (DatePicker) vh.mActivatorView;
if (dateAction.getDate() != dateView.getDate()) {
dateAction.setDate(dateView.getDate());
return true;
}
}
return false;
}
/**
* Sets listener for reporting view being edited.
*/
@RestrictTo(LIBRARY_GROUP_PREFIX)
public void setEditListener(@NonNull EditListener listener) {
mEditListener = listener;
}
void onEditActivatorView(final ViewHolder vh, boolean editing, final boolean withTransition) {
if (editing) {
startExpanded(vh, withTransition);
vh.itemView.setFocusable(false);
vh.mActivatorView.requestFocus();
vh.mActivatorView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!isInExpandTransition()) {
((GuidedActionAdapter) getActionsGridView().getAdapter())
.performOnActionClick(vh);
}
}
});
} else {
if (onUpdateActivatorView(vh, vh.getAction())) {
if (mEditListener != null) {
mEditListener.onGuidedActionEditedAndProceed(vh.getAction());
}
}
vh.itemView.setFocusable(true);
vh.itemView.requestFocus();
startExpanded(null, withTransition);
vh.mActivatorView.setOnClickListener(null);
vh.mActivatorView.setClickable(false);
}
}
/**
* Sets states of chevron view, called by {@link #onBindViewHolder(ViewHolder, GuidedAction)}.
* Subclass may override.
*
* @param vh The view holder associated with the relevant action.
* @param action The GuidedAction object to bind to.
*/
public void onBindChevronView(@NonNull ViewHolder vh, @NonNull GuidedAction action) {
final boolean hasNext = action.hasNext();
final boolean hasSubActions = action.hasSubActions();
if (hasNext || hasSubActions) {
vh.mChevronView.setVisibility(View.VISIBLE);
vh.mChevronView.setAlpha(action.isEnabled() ? mEnabledChevronAlpha :
mDisabledChevronAlpha);
if (hasNext) {
float r = mMainView != null
&& mMainView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? 180f : 0f;
vh.mChevronView.setRotation(r);
} else if (action == mExpandedAction) {
vh.mChevronView.setRotation(270);
} else {
vh.mChevronView.setRotation(90);
}
} else {
vh.mChevronView.setVisibility(View.GONE);
}
}
/**
* Expands or collapse the sub actions list view with transition animation
* @param avh When not null, fill sub actions list of this ViewHolder into sub actions list and
* hide the other items in main list. When null, collapse the sub actions list.
* @deprecated use {@link #expandAction(GuidedAction, boolean)} and
* {@link #collapseAction(boolean)}
*/
@Deprecated
public void setExpandedViewHolder(ViewHolder avh) {
expandAction(avh == null ? null : avh.getAction(), isExpandTransitionSupported());
}
/**
* Returns true if it is running an expanding or collapsing transition, false otherwise.
* @return True if it is running an expanding or collapsing transition, false otherwise.
*/
public boolean isInExpandTransition() {
return mExpandTransition != null;
}
/**
* Returns if expand/collapse animation is supported. When this method returns true,
* {@link #startExpandedTransition(ViewHolder)} will be used. When this method returns false,
* {@link #onUpdateExpandedViewHolder(ViewHolder)} will be called.
* @return True if it is running an expanding or collapsing transition, false otherwise.
*/
public boolean isExpandTransitionSupported() {
return VERSION.SDK_INT >= 21;
}
/**
* Start transition to expand or collapse GuidedActionStylist.
* @param avh When not null, the GuidedActionStylist expands the sub actions of avh. When null
* the GuidedActionStylist will collapse sub actions.
* @deprecated use {@link #expandAction(GuidedAction, boolean)} and
* {@link #collapseAction(boolean)}
*/
@Deprecated
public void startExpandedTransition(ViewHolder avh) {
expandAction(avh == null ? null : avh.getAction(), isExpandTransitionSupported());
}
/**
* Enable or disable using BACK key to collapse sub actions list. Default is enabled.
*
* @param backToCollapse True to enable using BACK key to collapse sub actions list, false
* to disable.
* @see GuidedAction#hasSubActions
* @see GuidedAction#getSubActions
*/
public final void setBackKeyToCollapseSubActions(boolean backToCollapse) {
mBackToCollapseSubActions = backToCollapse;
}
/**
* @return True if using BACK key to collapse sub actions list, false otherwise. Default value
* is true.
*
* @see GuidedAction#hasSubActions
* @see GuidedAction#getSubActions
*/
public final boolean isBackKeyToCollapseSubActions() {
return mBackToCollapseSubActions;
}
/**
* Enable or disable using BACK key to collapse {@link GuidedAction} with editable activator
* view. Default is enabled.
*
* @param backToCollapse True to enable using BACK key to collapse {@link GuidedAction} with
* editable activator view.
* @see GuidedAction#hasEditableActivatorView
*/
public final void setBackKeyToCollapseActivatorView(boolean backToCollapse) {
mBackToCollapseActivatorView = backToCollapse;
}
/**
* @return True if using BACK key to collapse {@link GuidedAction} with editable activator
* view, false otherwise. Default value is true.
*
* @see GuidedAction#hasEditableActivatorView
*/
public final boolean isBackKeyToCollapseActivatorView() {
return mBackToCollapseActivatorView;
}
/**
* Expand an action. Do nothing if it is in animation or there is action expanded.
*
* @param action Action to expand.
* @param withTransition True to run transition animation, false otherwsie.
*/
public void expandAction(@NonNull GuidedAction action, final boolean withTransition) {
if (isInExpandTransition() || mExpandedAction != null) {
return;
}
int actionPosition =
((GuidedActionAdapter) getActionsGridView().getAdapter()).indexOf(action);
if (actionPosition < 0) {
return;
}
boolean runTransition = isExpandTransitionSupported() && withTransition;
if (!runTransition) {
getActionsGridView().setSelectedPosition(actionPosition,
new ViewHolderTask() {
@Override
public void run(RecyclerView.ViewHolder vh) {
GuidedActionsStylist.ViewHolder avh =
(GuidedActionsStylist.ViewHolder)vh;
if (avh.getAction().hasEditableActivatorView()) {
setEditingMode(avh, true /*editing*/, false /*withTransition*/);
} else {
onUpdateExpandedViewHolder(avh);
}
}
});
if (action.hasSubActions()) {
onUpdateSubActionsGridView(action, true);
}
} else {
getActionsGridView().setSelectedPosition(actionPosition,
new ViewHolderTask() {
@Override
public void run(RecyclerView.ViewHolder vh) {
GuidedActionsStylist.ViewHolder avh =
(GuidedActionsStylist.ViewHolder)vh;
if (avh.getAction().hasEditableActivatorView()) {
setEditingMode(avh, true /*editing*/, true /*withTransition*/);
} else {
startExpanded(avh, true);
}
}
});
}
}
/**
* Collapse expanded action. Do nothing if it is in animation or there is no action expanded.
*
* @param withTransition True to run transition animation, false otherwsie.
*/
public void collapseAction(boolean withTransition) {
if (isInExpandTransition() || mExpandedAction == null) {
return;
}
boolean runTransition = isExpandTransitionSupported() && withTransition;
int actionPosition =
((GuidedActionAdapter) getActionsGridView().getAdapter()).indexOf(mExpandedAction);
if (actionPosition < 0) {
return;
}
if (mExpandedAction.hasEditableActivatorView()) {
setEditingMode(
((ViewHolder) getActionsGridView().findViewHolderForPosition(actionPosition)),
false /*editing*/,
runTransition);
} else {
startExpanded(null, runTransition);
}
}
int getKeyLine() {
return (int) (mKeyLinePercent * mActionsGridView.getHeight() / 100);
}
/**
* Internal method with assumption we already scroll to the new ViewHolder or is currently
* expanded.
*/
void startExpanded(ViewHolder avh, final boolean withTransition) {
ViewHolder focusAvh = null; // expand / collapse view holder
final int count = mActionsGridView.getChildCount();
for (int i = 0; i < count; i++) {
ViewHolder vh = (ViewHolder) mActionsGridView
.getChildViewHolder(mActionsGridView.getChildAt(i));
if (avh == null && vh.itemView.getVisibility() == View.VISIBLE) {
// going to collapse this one.
focusAvh = vh;
break;
} else if (avh != null && vh.getAction() == avh.getAction()) {
// going to expand this one.
focusAvh = vh;
break;
}
}
if (focusAvh == null) {
// huh?
return;
}
boolean isExpand = avh != null;
boolean isSubActionTransition = focusAvh.getAction().hasSubActions();
if (withTransition) {
Object set = TransitionHelper.createTransitionSet(false);
float slideDistance = isSubActionTransition ? focusAvh.itemView.getHeight()
: focusAvh.itemView.getHeight() * 0.5f;
Object slideAndFade = TransitionHelper.createFadeAndShortSlide(
Gravity.TOP | Gravity.BOTTOM,
slideDistance);
TransitionHelper.setEpicenterCallback(slideAndFade, new TransitionEpicenterCallback() {
Rect mRect = new Rect();
@Override
public Rect onGetEpicenter(Object transition) {
int centerY = getKeyLine();
int centerX = 0;
mRect.set(centerX, centerY, centerX, centerY);
return mRect;
}
});
Object changeFocusItemTransform = TransitionHelper.createChangeTransform();
Object changeFocusItemBounds = TransitionHelper.createChangeBounds(false);
Object fade = TransitionHelper.createFadeTransition(TransitionHelper.FADE_IN
| TransitionHelper.FADE_OUT);
Object changeGridBounds = TransitionHelper.createChangeBounds(false);
if (avh == null) {
TransitionHelper.setStartDelay(slideAndFade, 150);
TransitionHelper.setStartDelay(changeFocusItemTransform, 100);
TransitionHelper.setStartDelay(changeFocusItemBounds, 100);
TransitionHelper.setStartDelay(changeGridBounds, 100);
} else {
TransitionHelper.setStartDelay(fade, 100);
TransitionHelper.setStartDelay(changeGridBounds, 50);
TransitionHelper.setStartDelay(changeFocusItemTransform, 50);
TransitionHelper.setStartDelay(changeFocusItemBounds, 50);
}
for (int i = 0; i < count; i++) {
ViewHolder vh = (ViewHolder) mActionsGridView
.getChildViewHolder(mActionsGridView.getChildAt(i));
if (vh == focusAvh) {
// going to expand/collapse this one.
if (isSubActionTransition) {
TransitionHelper.include(changeFocusItemTransform, vh.itemView);
TransitionHelper.include(changeFocusItemBounds, vh.itemView);
}
} else {
// going to slide this item to top / bottom.
TransitionHelper.include(slideAndFade, vh.itemView);
TransitionHelper.exclude(fade, vh.itemView, true);
}
}
TransitionHelper.include(changeGridBounds, mSubActionsGridView);
TransitionHelper.include(changeGridBounds, mSubActionsBackground);
TransitionHelper.addTransition(set, slideAndFade);
// note that we don't run ChangeBounds for activating view due to the rounding problem
// of multiple level views ChangeBounds animation causing vertical jittering.
if (isSubActionTransition) {
TransitionHelper.addTransition(set, changeFocusItemTransform);
TransitionHelper.addTransition(set, changeFocusItemBounds);
}
TransitionHelper.addTransition(set, fade);
TransitionHelper.addTransition(set, changeGridBounds);
mExpandTransition = set;
TransitionHelper.addTransitionListener(mExpandTransition, new TransitionListener() {
@Override
public void onTransitionEnd(Object transition) {
mExpandTransition = null;
}
});
if (isExpand && isSubActionTransition) {
// To expand sub actions, move original position of sub actions to bottom of item
int startY = avh.itemView.getBottom();
mSubActionsGridView.offsetTopAndBottom(startY - mSubActionsGridView.getTop());
mSubActionsBackground.offsetTopAndBottom(startY - mSubActionsBackground.getTop());
}
TransitionHelper.beginDelayedTransition(mMainView, mExpandTransition);
}
onUpdateExpandedViewHolder(avh);
if (isSubActionTransition) {
onUpdateSubActionsGridView(focusAvh.getAction(), isExpand);
}
}
/**
* @return True if sub actions list is expanded.
*/
public boolean isSubActionsExpanded() {
return mExpandedAction != null && mExpandedAction.hasSubActions();
}
/**
* @return True if there is {@link #getExpandedAction()} is not null, false otherwise.
*/
public boolean isExpanded() {
return mExpandedAction != null;
}
/**
* @return Current expanded GuidedAction or null if not expanded.
*/
@Nullable
public GuidedAction getExpandedAction() {
return mExpandedAction;
}
/**
* Expand or collapse GuidedActionStylist.
* @param avh When not null, the GuidedActionStylist expands the sub actions of avh. When null
* the GuidedActionStylist will collapse sub actions.
*/
public void onUpdateExpandedViewHolder(@Nullable ViewHolder avh) {
// Note about setting the prune child flag back & forth here: without this, the actions that
// go off the screen from the top or bottom become invisible forever. This is because once
// an action is expanded, it takes more space which in turn kicks out some other actions
// off of the screen. Once, this action is collapsed (after the second click) and the
// visibility flag is set back to true for all existing actions,
// the off-the-screen actions are pruned from the view, thus
// could not be accessed, had we not disabled pruning prior to this.
if (avh == null) {
mExpandedAction = null;
mActionsGridView.setPruneChild(true);
} else if (avh.getAction() != mExpandedAction) {
mExpandedAction = avh.getAction();
mActionsGridView.setPruneChild(false);
}
// In expanding mode, notifyItemChange on expanded item will reset the translationY by
// the default ItemAnimator. So disable ItemAnimation in expanding mode.
mActionsGridView.setAnimateChildLayout(false);
final int count = mActionsGridView.getChildCount();
for (int i = 0; i < count; i++) {
ViewHolder vh = (ViewHolder) mActionsGridView
.getChildViewHolder(mActionsGridView.getChildAt(i));
updateChevronAndVisibility(vh);
}
}
void onUpdateSubActionsGridView(GuidedAction action, boolean expand) {
if (mSubActionsGridView != null) {
ViewGroup.MarginLayoutParams lp =
(ViewGroup.MarginLayoutParams) mSubActionsGridView.getLayoutParams();
GuidedActionAdapter adapter = (GuidedActionAdapter) mSubActionsGridView.getAdapter();
if (expand) {
// set to negative value so GuidedActionRelativeLayout will override with
// keyLine percentage.
lp.topMargin = -2;
lp.height = ViewGroup.MarginLayoutParams.MATCH_PARENT;
mSubActionsGridView.setLayoutParams(lp);
mSubActionsGridView.setVisibility(View.VISIBLE);
mSubActionsBackground.setVisibility(View.VISIBLE);
mSubActionsGridView.requestFocus();
adapter.setActions(action.getSubActions());
} else {
// set to explicit value, which will disable the keyLine percentage calculation
// in GuidedRelativeLayout.
int actionPosition = ((GuidedActionAdapter) mActionsGridView.getAdapter())
.indexOf(action);
lp.topMargin = mActionsGridView.getLayoutManager()
.findViewByPosition(actionPosition).getBottom();
lp.height = 0;
mSubActionsGridView.setVisibility(View.INVISIBLE);
mSubActionsBackground.setVisibility(View.INVISIBLE);
mSubActionsGridView.setLayoutParams(lp);
adapter.setActions(Collections.<GuidedAction>emptyList());
mActionsGridView.requestFocus();
}
}
}
private void updateChevronAndVisibility(ViewHolder vh) {
if (!vh.isSubAction()) {
if (mExpandedAction == null) {
vh.itemView.setVisibility(View.VISIBLE);
vh.itemView.setTranslationY(0);
if (vh.mActivatorView != null) {
vh.setActivated(false);
}
} else if (vh.getAction() == mExpandedAction) {
vh.itemView.setVisibility(View.VISIBLE);
if (vh.getAction().hasSubActions()) {
vh.itemView.setTranslationY(getKeyLine() - vh.itemView.getBottom());
} else if (vh.mActivatorView != null) {
vh.itemView.setTranslationY(0);
vh.setActivated(true);
}
} else {
vh.itemView.setVisibility(View.INVISIBLE);
vh.itemView.setTranslationY(0);
}
}
if (vh.mChevronView != null) {
onBindChevronView(vh, vh.getAction());
}
}
/*
* ==========================================
* FragmentAnimationProvider overrides
* ==========================================
*/
/**
* {@inheritDoc}
*/
@Override
public void onImeAppearing(@NonNull List<Animator> animators) {
}
/**
* {@inheritDoc}
*/
@Override
public void onImeDisappearing(@NonNull List<Animator> animators) {
}
/*
* ==========================================
* Private methods
* ==========================================
*/
private static float getFloat(Context ctx, TypedValue typedValue, int attrId) {
ctx.getTheme().resolveAttribute(attrId, typedValue, true);
return typedValue.getFloat();
}
private static float getFloatValue(Resources resources, TypedValue typedValue, int resId) {
resources.getValue(resId, typedValue, true);
return typedValue.getFloat();
}
private static int getInteger(Context ctx, TypedValue typedValue, int attrId) {
ctx.getTheme().resolveAttribute(attrId, typedValue, true);
return ctx.getResources().getInteger(typedValue.resourceId);
}
private static int getDimension(Context ctx, TypedValue typedValue, int attrId) {
ctx.getTheme().resolveAttribute(attrId, typedValue, true);
return ctx.getResources().getDimensionPixelSize(typedValue.resourceId);
}
private boolean setIcon(final ImageView iconView, GuidedAction action) {
Drawable icon = null;
if (iconView != null) {
icon = action.getIcon();
if (icon != null) {
// setImageDrawable resets the drawable's level unless we set the view level first.
iconView.setImageLevel(icon.getLevel());
iconView.setImageDrawable(icon);
iconView.setVisibility(View.VISIBLE);
} else {
iconView.setVisibility(View.GONE);
}
}
return icon != null;
}
/**
* @return the max height in pixels the description can be such that the
* action nicely takes up the entire screen.
*/
private int getDescriptionMaxHeight(TextView title) {
// The 2 multiplier on the title height calculation is a
// conservative estimate for font padding which can not be
// calculated at this stage since the view hasn't been rendered yet.
return (int)(mDisplayHeight - 2*mVerticalPadding - 2*mTitleMaxLines*title.getLineHeight());
}
@RequiresApi(26)
static class Api26Impl {
private Api26Impl() {
// This class is not instantiable.
}
@androidx.annotation.DoNotInline
static void setAutofillHints(View view, String... autofillHints) {
view.setAutofillHints(autofillHints);
}
@androidx.annotation.DoNotInline
static void setImportantForAutofill(
View view,
@SuppressWarnings("SameParameterValue") int mode
) {
view.setImportantForAutofill(mode);
}
}
}