GuidedStep: support checkbox and use standard android drawable

Introduce a special checkset id for checkbox.

Current implementation uses non-standard View and animation resource for
radio. Switch to use standard android checkbox/radio defined in the
theme.

Bug 25631132

Change-Id: I0c9356a624dfd9b3b2dfc38be2b1dcb3eea125b7
diff --git a/v17/leanback/api/current.txt b/v17/leanback/api/current.txt
index 550e838..b586833 100644
--- a/v17/leanback/api/current.txt
+++ b/v17/leanback/api/current.txt
@@ -1040,6 +1040,7 @@
     field public static final long ACTION_ID_NO = -9L; // 0xfffffffffffffff7L
     field public static final long ACTION_ID_OK = -4L; // 0xfffffffffffffffcL
     field public static final long ACTION_ID_YES = -8L; // 0xfffffffffffffff8L
+    field public static final int CHECKBOX_CHECK_SET_ID = -1; // 0xffffffff
     field public static final int DEFAULT_CHECK_SET_ID = 1; // 0x1
     field public static final int NO_CHECK_SET = 0; // 0x0
   }
@@ -1093,6 +1094,8 @@
     method public void onAnimateItemFocused(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
     method public void onAnimateItemPressed(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
     method public void onAnimateItemPressedCancelled(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
+    method public void onBindCheckMarkView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+    method public void onBindChevronView(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
     method public void onBindViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
     method public android.view.View onCreateView(android.view.LayoutInflater, android.view.ViewGroup);
     method public android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder onCreateViewHolder(android.view.ViewGroup);
diff --git a/v17/leanback/res/animator/lb_guidedactions_item_checked.xml b/v17/leanback/res/animator/lb_guidedactions_item_checked.xml
deleted file mode 100644
index 463b9f7..0000000
--- a/v17/leanback/res/animator/lb_guidedactions_item_checked.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:duration="@integer/lb_guidedactions_item_animation_duration"
-    android:propertyName="alpha"
-    android:valueFrom="0.0"
-    android:valueTo="1.0"
-    android:valueType="floatType" />
diff --git a/v17/leanback/res/animator/lb_guidedactions_item_unchecked.xml b/v17/leanback/res/animator/lb_guidedactions_item_unchecked.xml
deleted file mode 100644
index 86525c8..0000000
--- a/v17/leanback/res/animator/lb_guidedactions_item_unchecked.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:duration="@integer/lb_guidedactions_item_animation_duration"
-    android:propertyName="alpha"
-    android:valueFrom="1.0"
-    android:valueTo="0.0"
-    android:valueType="floatType" />
diff --git a/v17/leanback/res/drawable/lb_guidedactions_item_checkmark.xml b/v17/leanback/res/drawable/lb_guidedactions_item_checkmark.xml
deleted file mode 100644
index ec7903b..0000000
--- a/v17/leanback/res/drawable/lb_guidedactions_item_checkmark.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval" >
-
-    <size
-        android:height="@dimen/lb_guidedactions_item_checkmark_diameter"
-        android:width="@dimen/lb_guidedactions_item_checkmark_diameter" />
-
-    <solid android:color="@color/lb_tv_white" />
-
-</shape>
diff --git a/v17/leanback/res/layout/lb_guidedactions_item.xml b/v17/leanback/res/layout/lb_guidedactions_item.xml
index 8f5fcb7..831c355 100644
--- a/v17/leanback/res/layout/lb_guidedactions_item.xml
+++ b/v17/leanback/res/layout/lb_guidedactions_item.xml
@@ -20,7 +20,7 @@
     xmlns:tools="http://schemas.android.com/tools"
     style="?attr/guidedActionItemContainerStyle" >
 
-    <ImageView
+    <android.support.v17.leanback.widget.CheckableImageView
         android:id="@+id/guidedactions_item_checkmark"
         style="?attr/guidedActionItemCheckmarkStyle"
         tools:ignore="ContentDescription" />
diff --git a/v17/leanback/res/values/attrs.xml b/v17/leanback/res/values/attrs.xml
index 9ce1094..cae72e5 100644
--- a/v17/leanback/res/values/attrs.xml
+++ b/v17/leanback/res/values/attrs.xml
@@ -398,14 +398,6 @@
         <attr name="guidedActionItemChevronStyle" format="reference" />
 
         <!-- Theme attribute for the animation used in a GuidedActionsPresenter when an action
-             is checked. Default is {@link
-             android.support.v17.leanback.R.animator#lb_guidedactions_item_checked}. -->
-        <attr name="guidedActionCheckedAnimation" format="reference" />
-        <!-- Theme attribute for the animation used in a GuidedActionsPresenter when an action
-             is unchecked. Default is {@link
-             android.support.v17.leanback.R.animator#lb_guidedactions_item_unchecked}. -->
-        <attr name="guidedActionUncheckedAnimation" format="reference" />
-        <!-- Theme attribute for the animation used in a GuidedActionsPresenter when an action
              is pressed. Default is {@link
              android.support.v17.leanback.R.animator#lb_guidedactions_item_pressed}. -->
         <attr name="guidedActionPressedAnimation" format="reference" />
diff --git a/v17/leanback/res/values/dimens.xml b/v17/leanback/res/values/dimens.xml
index f5f42f2..053c7e0 100644
--- a/v17/leanback/res/values/dimens.xml
+++ b/v17/leanback/res/values/dimens.xml
@@ -248,10 +248,10 @@
     <dimen name="lb_guidedactions_item_text_width">248dp</dimen>
     <dimen name="lb_guidedactions_item_text_width_no_icon">284dp</dimen>
     <dimen name="lb_guidedactions_item_min_height">64dp</dimen>
-    <dimen name="lb_guidedactions_item_start_padding">20dp</dimen>
+    <dimen name="lb_guidedactions_item_start_padding">28dp</dimen>
     <dimen name="lb_guidedactions_item_end_padding">28dp</dimen>
     <dimen name="lb_guidedactions_item_delimiter_padding">4dp</dimen>
-    <dimen name="lb_guidedactions_item_checkmark_diameter">8dp</dimen>
+    <dimen name="lb_guidedactions_item_checkmark_diameter">16dp</dimen>
     <dimen name="lb_guidedactions_item_icon_width">32dp</dimen>
     <dimen name="lb_guidedactions_item_icon_height">32dp</dimen>
     <dimen name="lb_guidedactions_item_title_font_size">18sp</dimen>
diff --git a/v17/leanback/res/values/styles.xml b/v17/leanback/res/values/styles.xml
index 0e5b0b6..094732d 100644
--- a/v17/leanback/res/values/styles.xml
+++ b/v17/leanback/res/values/styles.xml
@@ -456,9 +456,8 @@
         <item name="android:layout_height">@dimen/lb_guidedactions_item_checkmark_diameter</item>
         <item name="android:layout_gravity">center</item>
         <item name="android:layout_marginEnd">@dimen/lb_guidedactions_item_delimiter_padding</item>
-        <item name="android:scaleType">center</item>
-        <item name="android:src">@drawable/lb_guidedactions_item_checkmark</item>
-        <item name="android:visibility">invisible</item>
+        <item name="android:scaleType">centerInside</item>
+        <item name="android:visibility">gone</item>
     </style>
 
     <!-- Style for an action's icon in a GuidedActionsStylist's default item layout. -->
diff --git a/v17/leanback/res/values/themes.xml b/v17/leanback/res/values/themes.xml
index d00bc36..f3e2eee 100644
--- a/v17/leanback/res/values/themes.xml
+++ b/v17/leanback/res/values/themes.xml
@@ -145,8 +145,6 @@
         <item name="guidedActionItemDescriptionStyle">@style/Widget.Leanback.GuidedActionItemDescriptionStyle</item>
         <item name="guidedActionItemChevronStyle">@style/Widget.Leanback.GuidedActionItemChevronStyle</item>
 
-        <item name="guidedActionCheckedAnimation">@animator/lb_guidedactions_item_checked</item>
-        <item name="guidedActionUncheckedAnimation">@animator/lb_guidedactions_item_unchecked</item>
         <item name="guidedActionPressedAnimation">@animator/lb_guidedactions_item_pressed</item>
         <item name="guidedActionUnpressedAnimation">@animator/lb_guidedactions_item_unpressed</item>
         <item name="guidedActionEnabledChevronAlpha">@string/lb_guidedactions_item_enabled_chevron_alpha</item>
diff --git a/v17/leanback/src/android/support/v17/leanback/app/GuidedActionAdapter.java b/v17/leanback/src/android/support/v17/leanback/app/GuidedActionAdapter.java
index 6a89e30..a0fcd90 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/GuidedActionAdapter.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/GuidedActionAdapter.java
@@ -383,15 +383,17 @@
         if (getRecyclerView() != null && actionCheckSetId != GuidedAction.NO_CHECK_SET) {
             // Find any actions that are checked and are in the same group
             // as the selected action. Fade their checkmarks out.
-            for (int i = 0, size = mActions.size(); i < size; i++) {
-                GuidedAction a = mActions.get(i);
-                if (a != action && a.getCheckSetId() == actionCheckSetId && a.isChecked()) {
-                    a.setChecked(false);
-                    ViewHolder vh = getRecyclerView().findViewHolderForPosition(i);
-                    if (vh != null) {
-                        GuidedActionsStylist.ViewHolder subViewHolder =
-                                ((ActionViewHolder)vh).mStylistViewHolder;
-                        mStylist.onAnimateItemChecked(subViewHolder, false);
+            if (actionCheckSetId != GuidedAction.CHECKBOX_CHECK_SET_ID) {
+                for (int i = 0, size = mActions.size(); i < size; i++) {
+                    GuidedAction a = mActions.get(i);
+                    if (a != action && a.getCheckSetId() == actionCheckSetId && a.isChecked()) {
+                        a.setChecked(false);
+                        ViewHolder vh = getRecyclerView().findViewHolderForPosition(i);
+                        if (vh != null) {
+                            GuidedActionsStylist.ViewHolder subViewHolder =
+                                    ((ActionViewHolder)vh).mStylistViewHolder;
+                            mStylist.onAnimateItemChecked(subViewHolder, false);
+                        }
                     }
                 }
             }
@@ -400,6 +402,11 @@
             if (!action.isChecked()) {
                 action.setChecked(true);
                 mStylist.onAnimateItemChecked(avh.mStylistViewHolder, true);
+            } else {
+                if (actionCheckSetId == GuidedAction.CHECKBOX_CHECK_SET_ID) {
+                    action.setChecked(false);
+                    mStylist.onAnimateItemChecked(avh.mStylistViewHolder, false);
+                }
             }
         }
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/CheckableImageView.java b/v17/leanback/src/android/support/v17/leanback/widget/CheckableImageView.java
new file mode 100644
index 0000000..627bbd4
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/CheckableImageView.java
@@ -0,0 +1,69 @@
+/*
+ * 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 android.support.v17.leanback.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.Checkable;
+import android.widget.ImageView;
+
+/**
+ * ImageView that supports Checkable states.
+ */
+class CheckableImageView extends ImageView implements Checkable {
+
+    private boolean mChecked;
+
+    private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked };
+
+    public CheckableImageView(Context context) {
+        this(context, null);
+    }
+
+    public CheckableImageView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public CheckableImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    public int[] onCreateDrawableState(final int extraSpace) {
+        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
+        if (isChecked()) {
+            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
+        }
+        return drawableState;
+    }
+
+    @Override
+    public void toggle() {
+        setChecked(!mChecked);
+    }
+
+    @Override
+    public boolean isChecked() {
+        return mChecked;
+    }
+
+    @Override
+    public void setChecked(final boolean checked) {
+        if (mChecked != checked) {
+            mChecked = checked;
+            refreshDrawableState();
+        }
+    }
+
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidedAction.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidedAction.java
index 4031677..21986d5 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidedAction.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidedAction.java
@@ -37,8 +37,18 @@
 
     private static final String TAG = "GuidedAction";
 
+    /**
+     * Special check set Id that is neither checkbox nor radio.
+     */
     public static final int NO_CHECK_SET = 0;
+    /**
+     * Default checkset Id for radio.
+     */
     public static final int DEFAULT_CHECK_SET_ID = 1;
+    /**
+     * Checkset Id for checkbox.
+     */
+    public static final int CHECKBOX_CHECK_SET_ID = -1;
 
     /**
      * When finishing editing, goes to next action.
@@ -371,10 +381,11 @@
         }
 
         /**
-         * Indicates whether this action is part of a single-select group similar to radio buttons.
-         * When one item in a check set is checked, all others with the same check set ID will be
-         * unchecked automatically.
-         * @param checkSetId The check set ID, or {@link #NO_CHECK_SET) to indicate no check set.
+         * Indicates whether this action is part of a single-select group similar to radio buttons
+         * or this action is a checkbox. When one item in a check set is checked, all others with
+         * the same check set ID will be nchecked automatically.
+         * @param checkSetId The check set ID, or {@link GuidedAction#NO_CHECK_SET} to indicate not
+         * radio or checkbox, or {@link GuidedAction#CHECKBOX_CHECK_SET_ID} to indicate a checkbox.
          */
         public Builder checkSetId(int checkSetId) {
             mCheckSetId = checkSetId;
@@ -604,13 +615,13 @@
     }
 
     /**
-     * Returns the check set id this action is a part of. All actions in the
-     * same list with the same check set id are considered linked. When one
-     * of the actions within that set is selected, that action becomes
-     * checked, while all the other actions become unchecked.
+     * Returns the check set id this action is a part of. All actions in the same list with the same
+     * check set id are considered linked. When one of the actions within that set is selected, that
+     * action becomes checked, while all the other actions become unchecked.
      *
      * @return an integer representing the check set this action is a part of, or
-     *         {@link #NO_CHECK_SET} if this action isn't a part of a check set.
+     *         {@link #CHECKBOX_CHECK_SET_ID} if this is a checkbox, or {@link #NO_CHECK_SET} if
+     *         this action is not a checkbox or radiobutton.
      */
     public int getCheckSetId() {
         return mCheckSetId;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java
index 89ccc1e..49ec6f8 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java
@@ -27,6 +27,7 @@
 import android.support.annotation.NonNull;
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.widget.VerticalGridView;
+import android.support.v4.content.ContextCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.ViewHolder;
@@ -41,6 +42,7 @@
 import android.view.ViewPropertyAnimator;
 import android.view.ViewTreeObserver;
 import android.view.WindowManager;
+import android.widget.Checkable;
 import android.widget.EditText;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -101,8 +103,6 @@
  * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemTitleStyle
  * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemDescriptionStyle
  * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemChevronStyle
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionCheckedAnimation
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionUncheckedAnimation
  * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionPressedAnimation
  * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionUnpressedAnimation
  * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionEnabledChevronAlpha
@@ -111,6 +111,8 @@
  * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionTitleMaxLines
  * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionDescriptionMinLines
  * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionVerticalPadding
+ * @see android.R.styleable#Theme_listChoiceIndicatorSingle
+ * @see android.R.styleable#Theme_listChoiceIndicatorMultiple
  * @see android.support.v17.leanback.app.GuidedStepFragment
  * @see GuidedAction
  */
@@ -519,14 +521,12 @@
             vh.mDescriptionView.setFocusable(action.isDescriptionEditable());
         }
         // Clients might want the check mark view to be gone entirely, in which case, ignore it.
-        if (vh.mCheckmarkView != null && vh.mCheckmarkView.getVisibility() != View.GONE) {
-            vh.mCheckmarkView.setVisibility(action.isChecked() ? View.VISIBLE : View.INVISIBLE);
+        if (vh.mCheckmarkView != null) {
+            onBindCheckMarkView(vh, action);
         }
 
         if (vh.mChevronView != null) {
-            vh.mChevronView.setVisibility(action.hasNext() ? View.VISIBLE : View.GONE);
-            vh.mChevronView.setAlpha(action.isEnabled() ? mEnabledChevronAlpha :
-                    mDisabledChevronAlpha);
+            onBindChevronView(vh, action);
         }
 
         if (action.hasMultilineDescription()) {
@@ -644,31 +644,69 @@
     }
 
     /**
-     * Animates the view holder's view (or subviews thereof) when the action has had its check
-     * state changed.
+     * 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(ViewHolder vh, boolean checked) {
-        final View checkView = vh.mCheckmarkView;
-        if (checkView != null) {
-            if (checked) {
-                checkView.setVisibility(View.VISIBLE);
-                createAnimator(checkView, R.attr.guidedActionCheckedAnimation).start();
-            } else {
-                Animator animator = createAnimator(checkView,
-                        R.attr.guidedActionUncheckedAnimation);
-                animator.addListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        checkView.setVisibility(View.INVISIBLE);
-                    }
-                });
-                animator.start();
-            }
+        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 {android.support.v17.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(ViewHolder vh, 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);
+        }
+    }
+
+    /**
+     * 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(ViewHolder vh, GuidedAction action) {
+        vh.mChevronView.setVisibility(action.hasNext() ? View.VISIBLE : View.GONE);
+        vh.mChevronView.setAlpha(action.isEnabled() ? mEnabledChevronAlpha :
+                mDisabledChevronAlpha);
+    }
+
     /*
      * ==========================================
      * FragmentAnimationProvider overrides