GuidedActionsStylist.java revision 9562425bf9bc15281ac27df817141854769c1042
1ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing/*
2ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * Copyright (C) 2015 The Android Open Source Project
3ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing *
4ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * in compliance with the License. You may obtain a copy of the License at
6ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing *
7ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * http://www.apache.org/licenses/LICENSE-2.0
8ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing *
9ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * Unless required by applicable law or agreed to in writing, software distributed under the License
10ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * or implied. See the License for the specific language governing permissions and limitations under
12ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * the License.
13ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing */
14ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingpackage android.support.v17.leanback.widget;
15ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
16ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.animation.Animator;
17ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.animation.AnimatorInflater;
18ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.animation.AnimatorListenerAdapter;
19ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.animation.AnimatorSet;
20ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.animation.ObjectAnimator;
21ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.content.Context;
22ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.content.pm.PackageManager;
23ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.content.res.Resources;
24ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.content.res.TypedArray;
25ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.graphics.drawable.Drawable;
26ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.net.Uri;
27ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.support.annotation.NonNull;
28ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.support.v17.leanback.R;
29ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.support.v17.leanback.widget.VerticalGridView;
30ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.support.v7.widget.RecyclerView;
31ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.support.v7.widget.RecyclerView.ViewHolder;
32ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.text.TextUtils;
33ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.util.Log;
34ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.util.TypedValue;
35ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.view.animation.DecelerateInterpolator;
36ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.view.LayoutInflater;
37ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.view.View;
38ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.view.ViewGroup;
39ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.view.ViewGroup.LayoutParams;
40ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.view.ViewPropertyAnimator;
41ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.view.ViewTreeObserver;
42ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.view.WindowManager;
43ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesingimport android.widget.EditText;
44ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.widget.ImageView;
45ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport android.widget.TextView;
46ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
47ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingimport java.util.List;
48ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
49ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing/**
50a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout * GuidedActionsStylist is used within a {@link android.support.v17.leanback.app.GuidedStepFragment}
51a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout * to supply the right-side panel where users can take actions. It consists of a container for the
52a00bada00bff4a58436a39472ab14ccb7a8f619dCraig Stout * list of actions, and a stationary selector view that indicates visually the location of focus.
53ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <p>
54ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * Many aspects of the base GuidedActionsStylist can be customized through theming; see the
55ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * theme attributes below. Note that these attributes are not set on individual elements in layout
56ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * XML, but instead would be set in a custom theme. See
57ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <a href="http://developer.android.com/guide/topics/ui/themes.html">Styles and Themes</a>
58ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * for more information.
59ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <p>
60ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * If these hooks are insufficient, this class may also be subclassed. Subclasses may wish to
61ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * override the {@link #onProvideLayoutId} method to change the layout used to display the
62ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * list container and selector, or the {@link #onProvideItemLayoutId} method to change the layout
63ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * used to display each action.
64ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <p>
65ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * Note: If an alternate list layout is provided, the following view IDs must be supplied:
66ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <ul>
67ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <li>{@link android.support.v17.leanback.R.id#guidedactions_selector}</li>
68ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <li>{@link android.support.v17.leanback.R.id#guidedactions_list}</li>
69ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * </ul><p>
70ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * These view IDs must be present in order for the stylist to function. The list ID must correspond
71ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * to a {@link VerticalGridView} or subclass.
72ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <p>
73ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * If an alternate item layout is provided, the following view IDs should be used to refer to base
74ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * elements:
75ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <ul>
76ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <li>{@link android.support.v17.leanback.R.id#guidedactions_item_content}</li>
77ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <li>{@link android.support.v17.leanback.R.id#guidedactions_item_title}</li>
78ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <li>{@link android.support.v17.leanback.R.id#guidedactions_item_description}</li>
79ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <li>{@link android.support.v17.leanback.R.id#guidedactions_item_icon}</li>
80ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <li>{@link android.support.v17.leanback.R.id#guidedactions_item_checkmark}</li>
81ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * <li>{@link android.support.v17.leanback.R.id#guidedactions_item_chevron}</li>
82ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * </ul><p>
83ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * These view IDs are allowed to be missing, in which case the corresponding views in {@link
84ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * GuidedActionsStylist.ViewHolder} will be null.
854158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing * <p>
864158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing * In order to support editable actions, the view associated with guidedactions_item_title should
874158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing * be a subclass of {@link android.widget.EditText}, and should satisfy the {@link
884158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing * ImeKeyMonitor} interface.
89ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing *
904158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeAppearingAnimation
914158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeDisappearingAnimation
92ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsSelectorShowAnimation
93ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsSelectorHideAnimation
94ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsContainerStyle
95ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsSelectorStyle
96ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsListStyle
97ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemContainerStyle
98ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemCheckmarkStyle
99ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemIconStyle
100ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemContentStyle
101ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemTitleStyle
102ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemDescriptionStyle
103ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemChevronStyle
104ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionCheckedAnimation
105ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionUncheckedAnimation
106ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionPressedAnimation
107ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionUnpressedAnimation
108ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionEnabledChevronAlpha
109ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionDisabledChevronAlpha
110ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionContentWidth
111ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionContentWidthNoIcon
112ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionTitleMinLines
113ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionTitleMaxLines
114ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionDescriptionMinLines
115ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionVerticalPadding
116ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @see android.support.v17.leanback.app.GuidedStepFragment
117ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @see GuidedAction
118ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing */
119ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingpublic class GuidedActionsStylist implements FragmentAnimationProvider {
120ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
121ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
122ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * ViewHolder caches information about the action item layouts' subviews. Subclasses of {@link
123ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * GuidedActionsStylist} may also wish to subclass this in order to add fields.
124ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @see GuidedAction
125ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
126ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public static class ViewHolder {
127ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
128ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        public final View view;
129ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
130ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        private View mContentView;
131ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        private TextView mTitleView;
132ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        private TextView mDescriptionView;
133ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        private ImageView mIconView;
134ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        private ImageView mCheckmarkView;
135ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        private ImageView mChevronView;
136c1741246af607f6be2389056da0182c40f938348Dake Gu        private boolean mInEditing;
137c1741246af607f6be2389056da0182c40f938348Dake Gu        private boolean mInEditingDescription;
138ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
139ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        /**
140ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * Constructs an ViewHolder and caches the relevant subviews.
141ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         */
142ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        public ViewHolder(View v) {
143ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            view = v;
144ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
145ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mContentView = v.findViewById(R.id.guidedactions_item_content);
146ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mTitleView = (TextView) v.findViewById(R.id.guidedactions_item_title);
147ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mDescriptionView = (TextView) v.findViewById(R.id.guidedactions_item_description);
148ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mIconView = (ImageView) v.findViewById(R.id.guidedactions_item_icon);
149ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mCheckmarkView = (ImageView) v.findViewById(R.id.guidedactions_item_checkmark);
150ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mChevronView = (ImageView) v.findViewById(R.id.guidedactions_item_chevron);
151ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
152ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
153ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        /**
154ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * Returns the content view within this view holder's view, where title and description are
155ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * shown.
156ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         */
157ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        public View getContentView() {
158ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            return mContentView;
159ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
160ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
161ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        /**
162ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * Returns the title view within this view holder's view.
163ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         */
164ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        public TextView getTitleView() {
165ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            return mTitleView;
166ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
167ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
168ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        /**
169ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing         * Convenience method to return an editable version of the title, if possible,
170ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing         * or null if the title view isn't an EditText.
171ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing         */
172ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing        public EditText getEditableTitleView() {
173ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing            return (mTitleView instanceof EditText) ? (EditText)mTitleView : null;
174ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing        }
175ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing
176ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing        /**
177ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * Returns the description view within this view holder's view.
178ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         */
179ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        public TextView getDescriptionView() {
180ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            return mDescriptionView;
181ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
182ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
183ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        /**
184c1741246af607f6be2389056da0182c40f938348Dake Gu         * Convenience method to return an editable version of the description, if possible,
185c1741246af607f6be2389056da0182c40f938348Dake Gu         * or null if the description view isn't an EditText.
186c1741246af607f6be2389056da0182c40f938348Dake Gu         */
187c1741246af607f6be2389056da0182c40f938348Dake Gu        public EditText getEditableDescriptionView() {
188c1741246af607f6be2389056da0182c40f938348Dake Gu            return (mDescriptionView instanceof EditText) ? (EditText)mDescriptionView : null;
189c1741246af607f6be2389056da0182c40f938348Dake Gu        }
190c1741246af607f6be2389056da0182c40f938348Dake Gu
191c1741246af607f6be2389056da0182c40f938348Dake Gu        /**
192ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * Returns the icon view within this view holder's view.
193ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         */
194ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        public ImageView getIconView() {
195ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            return mIconView;
196ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
197ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
198ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        /**
199ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * Returns the checkmark view within this view holder's view.
200ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         */
201ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        public ImageView getCheckmarkView() {
202ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            return mCheckmarkView;
203ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
204ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
205ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        /**
206ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * Returns the chevron view within this view holder's view.
207ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         */
208ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        public ImageView getChevronView() {
209ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            return mChevronView;
210ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
211ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
212c1741246af607f6be2389056da0182c40f938348Dake Gu        /**
213c1741246af607f6be2389056da0182c40f938348Dake Gu         * Returns true if the TextView is in editing title or description, false otherwise.
214c1741246af607f6be2389056da0182c40f938348Dake Gu         */
215c1741246af607f6be2389056da0182c40f938348Dake Gu        public boolean isInEditing() {
216c1741246af607f6be2389056da0182c40f938348Dake Gu            return mInEditing;
217c1741246af607f6be2389056da0182c40f938348Dake Gu        }
218c1741246af607f6be2389056da0182c40f938348Dake Gu
219c1741246af607f6be2389056da0182c40f938348Dake Gu        /**
220c1741246af607f6be2389056da0182c40f938348Dake Gu         * Returns true if the TextView is in editing description, false otherwise.
221c1741246af607f6be2389056da0182c40f938348Dake Gu         */
222c1741246af607f6be2389056da0182c40f938348Dake Gu        public boolean isInEditingDescription() {
223c1741246af607f6be2389056da0182c40f938348Dake Gu            return mInEditingDescription;
224c1741246af607f6be2389056da0182c40f938348Dake Gu        }
225c1741246af607f6be2389056da0182c40f938348Dake Gu
226c1741246af607f6be2389056da0182c40f938348Dake Gu        public View getEditingView() {
227c1741246af607f6be2389056da0182c40f938348Dake Gu            if (mInEditing) {
228c1741246af607f6be2389056da0182c40f938348Dake Gu                return mInEditingDescription ?  mDescriptionView : mTitleView;
229c1741246af607f6be2389056da0182c40f938348Dake Gu            } else {
230c1741246af607f6be2389056da0182c40f938348Dake Gu                return null;
231c1741246af607f6be2389056da0182c40f938348Dake Gu            }
232c1741246af607f6be2389056da0182c40f938348Dake Gu        }
233ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
234ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
235ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private static String TAG = "GuidedActionsStylist";
236ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
237ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    protected View mMainView;
238ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    protected VerticalGridView mActionsGridView;
239ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    protected View mSelectorView;
240ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
241ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    // Cached values from resources
242c1741246af607f6be2389056da0182c40f938348Dake Gu    private float mEnabledTextAlpha;
243c1741246af607f6be2389056da0182c40f938348Dake Gu    private float mDisabledTextAlpha;
244c1741246af607f6be2389056da0182c40f938348Dake Gu    private float mEnabledDescriptionAlpha;
245c1741246af607f6be2389056da0182c40f938348Dake Gu    private float mDisabledDescriptionAlpha;
246ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private float mEnabledChevronAlpha;
247ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private float mDisabledChevronAlpha;
248ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private int mContentWidth;
249ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private int mContentWidthNoIcon;
250ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private int mTitleMinLines;
251ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private int mTitleMaxLines;
252ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private int mDescriptionMinLines;
253ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private int mVerticalPadding;
254ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private int mDisplayHeight;
255ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
256ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
257ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Creates a view appropriate for displaying a list of GuidedActions, using the provided
258ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * inflater and container.
259ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * <p>
260ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * <i>Note: Does not actually add the created view to the container; the caller should do
261ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * this.</i>
262ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param inflater The layout inflater to be used when constructing the view.
263ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param container The view group to be passed in the call to
264ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * <code>LayoutInflater.inflate</code>.
265ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return The view to be added to the caller's view hierarchy.
266ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
267ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public View onCreateView(LayoutInflater inflater, ViewGroup container) {
268ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mMainView = inflater.inflate(onProvideLayoutId(), container, false);
269ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mSelectorView = mMainView.findViewById(R.id.guidedactions_selector);
270ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (mMainView instanceof VerticalGridView) {
271ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mActionsGridView = (VerticalGridView) mMainView;
272ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        } else {
273ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mActionsGridView = (VerticalGridView) mMainView.findViewById(R.id.guidedactions_list);
274ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (mActionsGridView == null) {
275ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                throw new IllegalStateException("No ListView exists.");
276ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
277ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mActionsGridView.setWindowAlignmentOffset(0);
278ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mActionsGridView.setWindowAlignmentOffsetPercent(50f);
279ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mActionsGridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
280ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (mSelectorView != null) {
281ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                mActionsGridView.setOnScrollListener(new
282ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        SelectorAnimator(mSelectorView, mActionsGridView));
283ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
284ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
285ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
286ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mActionsGridView.requestFocusFromTouch();
287ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
288ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (mSelectorView != null) {
289ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            // ALlow focus to move to other views
290ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mActionsGridView.getViewTreeObserver().addOnGlobalFocusChangeListener(
291ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    new ViewTreeObserver.OnGlobalFocusChangeListener() {
292ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        private boolean mChildFocused;
293ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
294ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        @Override
295ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        public void onGlobalFocusChanged(View oldFocus, View newFocus) {
296ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                            View focusedChild = mActionsGridView.getFocusedChild();
297ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                            if (focusedChild == null) {
298ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                                mSelectorView.setVisibility(View.INVISIBLE);
299ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                                mChildFocused = false;
300ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                            } else if (!mChildFocused) {
301ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                                mChildFocused = true;
302ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                                mSelectorView.setVisibility(View.VISIBLE);
303ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                                updateSelectorView(focusedChild);
304ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                            }
305ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        }
306ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    });
307ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
308ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
309ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // Cache widths, chevron alpha values, max and min text lines, etc
310ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        Context ctx = mMainView.getContext();
311ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        TypedValue val = new TypedValue();
312ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mEnabledChevronAlpha = getFloat(ctx, val, R.attr.guidedActionEnabledChevronAlpha);
313ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mDisabledChevronAlpha = getFloat(ctx, val, R.attr.guidedActionDisabledChevronAlpha);
314ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mContentWidth = getDimension(ctx, val, R.attr.guidedActionContentWidth);
315ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mContentWidthNoIcon = getDimension(ctx, val, R.attr.guidedActionContentWidthNoIcon);
316ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mTitleMinLines = getInteger(ctx, val, R.attr.guidedActionTitleMinLines);
317ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mTitleMaxLines = getInteger(ctx, val, R.attr.guidedActionTitleMaxLines);
318ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mDescriptionMinLines = getInteger(ctx, val, R.attr.guidedActionDescriptionMinLines);
319ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mVerticalPadding = getDimension(ctx, val, R.attr.guidedActionVerticalPadding);
320ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mDisplayHeight = ((WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE))
321ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                .getDefaultDisplay().getHeight();
322ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
323c1741246af607f6be2389056da0182c40f938348Dake Gu        mEnabledTextAlpha = Float.valueOf(ctx.getResources().getString(R.string
324c1741246af607f6be2389056da0182c40f938348Dake Gu                .lb_guidedactions_item_unselected_text_alpha));
325c1741246af607f6be2389056da0182c40f938348Dake Gu        mDisabledTextAlpha = Float.valueOf(ctx.getResources().getString(R.string
326c1741246af607f6be2389056da0182c40f938348Dake Gu                .lb_guidedactions_item_disabled_text_alpha));
327c1741246af607f6be2389056da0182c40f938348Dake Gu        mEnabledDescriptionAlpha = Float.valueOf(ctx.getResources().getString(R.string
328c1741246af607f6be2389056da0182c40f938348Dake Gu                .lb_guidedactions_item_unselected_description_text_alpha));
329c1741246af607f6be2389056da0182c40f938348Dake Gu        mDisabledDescriptionAlpha = Float.valueOf(ctx.getResources().getString(R.string
330c1741246af607f6be2389056da0182c40f938348Dake Gu                .lb_guidedactions_item_disabled_description_text_alpha));
331ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return mMainView;
332ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
333ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
334ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
335ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Returns the VerticalGridView that displays the list of GuidedActions.
336ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return The VerticalGridView for this presenter.
337ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
338ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public VerticalGridView getActionsGridView() {
339ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return mActionsGridView;
340ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
341ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
342ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
343ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Provides the resource ID of the layout defining the host view for the list of guided actions.
344ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Subclasses may override to provide their own customized layouts. The base implementation
345ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * returns {@link android.support.v17.leanback.R.layout#lb_guidedactions}. If overridden, the
346ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * substituted layout should contain matching IDs for any views that should be managed by the
347ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * base class; this can be achieved by starting with a copy of the base layout file.
348ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return The resource ID of the layout to be inflated to define the host view for the list
349ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * of GuidedActions.
350ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
351ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public int onProvideLayoutId() {
352ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return R.layout.lb_guidedactions;
353ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
354ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
355ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
356ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Provides the resource ID of the layout defining the view for an individual guided actions.
357ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Subclasses may override to provide their own customized layouts. The base implementation
358ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * returns {@link android.support.v17.leanback.R.layout#lb_guidedactions_item}. If overridden,
359ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * the substituted layout should contain matching IDs for any views that should be managed by
360ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing     * the base class; this can be achieved by starting with a copy of the base layout file. Note
361ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing     * that in order for the item to support editing, the title view should both subclass {@link
362ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing     * android.widget.EditText} and implement {@link ImeKeyMonitor}; see {@link
363ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing     * GuidedActionEditText}.
364ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return The resource ID of the layout to be inflated to define the view to display an
365ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * individual GuidedAction.
366ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
367ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public int onProvideItemLayoutId() {
368ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return R.layout.lb_guidedactions_item;
369ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
370ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
371ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
372ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Constructs a {@link ViewHolder} capable of representing {@link GuidedAction}s. Subclasses
373ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * may choose to return a subclass of ViewHolder.
374ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * <p>
375ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * <i>Note: Should not actually add the created view to the parent; the caller will do
376ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * this.</i>
377ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param parent The view group to be used as the parent of the new view.
378ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return The view to be added to the caller's view hierarchy.
379ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
380ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public ViewHolder onCreateViewHolder(ViewGroup parent) {
381ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
382ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        View v = inflater.inflate(onProvideItemLayoutId(), parent, false);
383ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return new ViewHolder(v);
384ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
385ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
386ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
387ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Binds a {@link ViewHolder} to a particular {@link GuidedAction}.
388ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param vh The view holder to be associated with the given action.
389ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param action The guided action to be displayed by the view holder's view.
390ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return The view to be added to the caller's view hierarchy.
391ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
392ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public void onBindViewHolder(ViewHolder vh, GuidedAction action) {
393ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
394ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (vh.mTitleView != null) {
395ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            vh.mTitleView.setText(action.getTitle());
396c1741246af607f6be2389056da0182c40f938348Dake Gu            vh.mTitleView.setAlpha(action.isEnabled() ? mEnabledTextAlpha : mDisabledTextAlpha);
397ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
398ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (vh.mDescriptionView != null) {
399ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            vh.mDescriptionView.setText(action.getDescription());
400ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            vh.mDescriptionView.setVisibility(TextUtils.isEmpty(action.getDescription()) ?
401ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    View.GONE : View.VISIBLE);
402c1741246af607f6be2389056da0182c40f938348Dake Gu            vh.mDescriptionView.setAlpha(action.isEnabled() ? mEnabledDescriptionAlpha :
403c1741246af607f6be2389056da0182c40f938348Dake Gu                mDisabledDescriptionAlpha);
404ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
405ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // Clients might want the check mark view to be gone entirely, in which case, ignore it.
406ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (vh.mCheckmarkView != null && vh.mCheckmarkView.getVisibility() != View.GONE) {
407ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            vh.mCheckmarkView.setVisibility(action.isChecked() ? View.VISIBLE : View.INVISIBLE);
408ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
409ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
410ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (vh.mContentView != null) {
411ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            ViewGroup.LayoutParams contentLp = vh.mContentView.getLayoutParams();
412ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (setIcon(vh.mIconView, action)) {
413ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                contentLp.width = mContentWidth;
414ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            } else {
415ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                contentLp.width = mContentWidthNoIcon;
416ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
417ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            vh.mContentView.setLayoutParams(contentLp);
418ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
419ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
420ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (vh.mChevronView != null) {
421ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            vh.mChevronView.setVisibility(action.hasNext() ? View.VISIBLE : View.INVISIBLE);
422ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            vh.mChevronView.setAlpha(action.isEnabled() ? mEnabledChevronAlpha :
423ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    mDisabledChevronAlpha);
424ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
425ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
426ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (action.hasMultilineDescription()) {
427ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (vh.mTitleView != null) {
428ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                vh.mTitleView.setMaxLines(mTitleMaxLines);
429ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                if (vh.mDescriptionView != null) {
430ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    vh.mDescriptionView.setMaxHeight(getDescriptionMaxHeight(vh.view.getContext(),
431ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                            vh.mTitleView));
432ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                }
433ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
434ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        } else {
435ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (vh.mTitleView != null) {
436ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                vh.mTitleView.setMaxLines(mTitleMinLines);
437ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
438ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (vh.mDescriptionView != null) {
439ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                vh.mDescriptionView.setMaxLines(mDescriptionMinLines);
440ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
441ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
442c1741246af607f6be2389056da0182c40f938348Dake Gu        setEditingMode(vh, action, false);
443c1741246af607f6be2389056da0182c40f938348Dake Gu    }
444c1741246af607f6be2389056da0182c40f938348Dake Gu
445c1741246af607f6be2389056da0182c40f938348Dake Gu    public void setEditingMode(ViewHolder vh, GuidedAction action, boolean editing) {
446c1741246af607f6be2389056da0182c40f938348Dake Gu        if (editing != vh.mInEditing) {
447c1741246af607f6be2389056da0182c40f938348Dake Gu            vh.mInEditing = editing;
448c1741246af607f6be2389056da0182c40f938348Dake Gu            onEditingModeChange(vh, action, editing);
449c1741246af607f6be2389056da0182c40f938348Dake Gu        }
450c1741246af607f6be2389056da0182c40f938348Dake Gu    }
451c1741246af607f6be2389056da0182c40f938348Dake Gu
452c1741246af607f6be2389056da0182c40f938348Dake Gu    protected void onEditingModeChange(ViewHolder vh, GuidedAction action, boolean editing) {
453c1741246af607f6be2389056da0182c40f938348Dake Gu        TextView titleView = vh.getTitleView();
454c1741246af607f6be2389056da0182c40f938348Dake Gu        TextView descriptionView = vh.getDescriptionView();
455c1741246af607f6be2389056da0182c40f938348Dake Gu        if (editing) {
456c1741246af607f6be2389056da0182c40f938348Dake Gu            CharSequence editTitle = action.getEditTitle();
457c1741246af607f6be2389056da0182c40f938348Dake Gu            if (titleView != null && editTitle != null) {
458c1741246af607f6be2389056da0182c40f938348Dake Gu                titleView.setText(editTitle);
459c1741246af607f6be2389056da0182c40f938348Dake Gu            }
460c1741246af607f6be2389056da0182c40f938348Dake Gu            CharSequence editDescription = action.getEditDescription();
461c1741246af607f6be2389056da0182c40f938348Dake Gu            if (descriptionView != null && editDescription != null) {
462c1741246af607f6be2389056da0182c40f938348Dake Gu                descriptionView.setText(editDescription);
463c1741246af607f6be2389056da0182c40f938348Dake Gu            }
464c1741246af607f6be2389056da0182c40f938348Dake Gu            if (action.isDescriptionEditable()) {
465c1741246af607f6be2389056da0182c40f938348Dake Gu                if (descriptionView != null) {
466c1741246af607f6be2389056da0182c40f938348Dake Gu                    descriptionView.setVisibility(View.VISIBLE);
4679562425bf9bc15281ac27df817141854769c1042Dake Gu                    descriptionView.setInputType(action.getDescriptionEditInputType());
468c1741246af607f6be2389056da0182c40f938348Dake Gu                }
469c1741246af607f6be2389056da0182c40f938348Dake Gu                vh.mInEditingDescription = true;
470c1741246af607f6be2389056da0182c40f938348Dake Gu            } else {
471c1741246af607f6be2389056da0182c40f938348Dake Gu                vh.mInEditingDescription = false;
4729562425bf9bc15281ac27df817141854769c1042Dake Gu                if (titleView != null) {
4739562425bf9bc15281ac27df817141854769c1042Dake Gu                    titleView.setInputType(action.getEditInputType());
4749562425bf9bc15281ac27df817141854769c1042Dake Gu                }
475c1741246af607f6be2389056da0182c40f938348Dake Gu            }
476c1741246af607f6be2389056da0182c40f938348Dake Gu        } else {
477c1741246af607f6be2389056da0182c40f938348Dake Gu            if (titleView != null) {
478c1741246af607f6be2389056da0182c40f938348Dake Gu                titleView.setText(action.getTitle());
479c1741246af607f6be2389056da0182c40f938348Dake Gu            }
480c1741246af607f6be2389056da0182c40f938348Dake Gu            if (descriptionView != null) {
481c1741246af607f6be2389056da0182c40f938348Dake Gu                descriptionView.setText(action.getDescription());
482c1741246af607f6be2389056da0182c40f938348Dake Gu            }
483c1741246af607f6be2389056da0182c40f938348Dake Gu            if (vh.mInEditingDescription) {
484c1741246af607f6be2389056da0182c40f938348Dake Gu                if (descriptionView != null) {
485c1741246af607f6be2389056da0182c40f938348Dake Gu                    descriptionView.setVisibility(TextUtils.isEmpty(action.getDescription()) ?
486c1741246af607f6be2389056da0182c40f938348Dake Gu                            View.GONE : View.VISIBLE);
4879562425bf9bc15281ac27df817141854769c1042Dake Gu                    descriptionView.setInputType(action.getDescriptionInputType());
488c1741246af607f6be2389056da0182c40f938348Dake Gu                }
489c1741246af607f6be2389056da0182c40f938348Dake Gu                vh.mInEditingDescription = false;
4909562425bf9bc15281ac27df817141854769c1042Dake Gu            } else {
4919562425bf9bc15281ac27df817141854769c1042Dake Gu                if (titleView != null) {
4929562425bf9bc15281ac27df817141854769c1042Dake Gu                    titleView.setInputType(action.getInputType());
4939562425bf9bc15281ac27df817141854769c1042Dake Gu                }
494c1741246af607f6be2389056da0182c40f938348Dake Gu            }
495c1741246af607f6be2389056da0182c40f938348Dake Gu        }
496ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
497ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
498ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
499ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Animates the view holder's view (or subviews thereof) when the action has had its focus
500ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * state changed.
501ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param vh The view holder associated with the relevant action.
502ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param focused True if the action has become focused, false if it has lost focus.
503ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
504ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public void onAnimateItemFocused(ViewHolder vh, boolean focused) {
505ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // No animations for this, currently, because the animation is done on
506ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // mSelectorView
507ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
508ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
509ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
510ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Animates the view holder's view (or subviews thereof) when the action has had its press
511ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * state changed.
512ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param vh The view holder associated with the relevant action.
513ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param pressed True if the action has been pressed, false if it has been unpressed.
514ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
515ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public void onAnimateItemPressed(ViewHolder vh, boolean pressed) {
516ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        int attr = pressed ? R.attr.guidedActionPressedAnimation :
517ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                R.attr.guidedActionUnpressedAnimation;
518ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        createAnimator(vh.view, attr).start();
519ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
520ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
521ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
522ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Animates the view holder's view (or subviews thereof) when the action has had its check
523ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * state changed.
524ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param vh The view holder associated with the relevant action.
525ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param checked True if the action has become checked, false if it has become unchecked.
526ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
527ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public void onAnimateItemChecked(ViewHolder vh, boolean checked) {
528ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        final View checkView = vh.mCheckmarkView;
529ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (checkView != null) {
530ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (checked) {
531ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                checkView.setVisibility(View.VISIBLE);
532ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                createAnimator(checkView, R.attr.guidedActionCheckedAnimation).start();
533ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            } else {
534633f924bac2b143ae67f86eace4d5068f2acab5eKris Giesing                Animator animator = createAnimator(checkView,
535633f924bac2b143ae67f86eace4d5068f2acab5eKris Giesing                        R.attr.guidedActionUncheckedAnimation);
536ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                animator.addListener(new AnimatorListenerAdapter() {
537ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    @Override
538ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    public void onAnimationEnd(Animator animation) {
539ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        checkView.setVisibility(View.INVISIBLE);
540ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    }
541ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                });
542ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                animator.start();
543ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
544ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
545ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
546ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
547ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /*
548ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * ==========================================
549ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * FragmentAnimationProvider overrides
550ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * ==========================================
551ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
552ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
553ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
554ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * {@inheritDoc}
555ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
556ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    @Override
5574158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing    public void onImeAppearing(@NonNull List<Animator> animators) {
5584158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing        animators.add(createAnimator(mActionsGridView, R.attr.guidedStepImeAppearingAnimation));
5594158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing        animators.add(createAnimator(mSelectorView, R.attr.guidedStepImeAppearingAnimation));
5604158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing    }
5614158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing
5624158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing    /**
5634158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing     * {@inheritDoc}
5644158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing     */
5654158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing    @Override
5664158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing    public void onImeDisappearing(@NonNull List<Animator> animators) {
5674158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing        animators.add(createAnimator(mActionsGridView, R.attr.guidedStepImeDisappearingAnimation));
5684158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing        animators.add(createAnimator(mSelectorView, R.attr.guidedStepImeDisappearingAnimation));
5694158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing    }
5704158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing
571ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /*
572ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * ==========================================
573ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Private methods
574ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * ==========================================
575ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
576ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
577ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private void updateSelectorView(View focusedChild) {
578ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // Display the selector view.
579ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        int height = focusedChild.getHeight();
580ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        LayoutParams lp = mSelectorView.getLayoutParams();
581ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        lp.height = height;
582ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mSelectorView.setLayoutParams(lp);
583ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mSelectorView.setAlpha(1f);
584ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
585ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
586ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private float getFloat(Context ctx, TypedValue typedValue, int attrId) {
587ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        ctx.getTheme().resolveAttribute(attrId, typedValue, true);
588ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // Android resources don't have a native float type, so we have to use strings.
589ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return Float.valueOf(ctx.getResources().getString(typedValue.resourceId));
590ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
591ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
592ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private int getInteger(Context ctx, TypedValue typedValue, int attrId) {
593ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        ctx.getTheme().resolveAttribute(attrId, typedValue, true);
594ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return ctx.getResources().getInteger(typedValue.resourceId);
595ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
596ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
597ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private int getDimension(Context ctx, TypedValue typedValue, int attrId) {
598ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        ctx.getTheme().resolveAttribute(attrId, typedValue, true);
599ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return ctx.getResources().getDimensionPixelSize(typedValue.resourceId);
600ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
601ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
602ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private static Animator createAnimator(View v, int attrId) {
603ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        Context ctx = v.getContext();
604ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        TypedValue typedValue = new TypedValue();
605ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        ctx.getTheme().resolveAttribute(attrId, typedValue, true);
606ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        Animator animator = AnimatorInflater.loadAnimator(ctx, typedValue.resourceId);
607ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        animator.setTarget(v);
608ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return animator;
609ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
610ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
611ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private boolean setIcon(final ImageView iconView, GuidedAction action) {
612ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        Drawable icon = null;
613ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (iconView != null) {
614ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            Context context = iconView.getContext();
615ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            icon = action.getIcon();
616ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (icon != null) {
617162b21598d9f4fd49748b3c7e27501fe1277210dChristopher Lane                // setImageDrawable resets the drawable's level unless we set the view level first.
618162b21598d9f4fd49748b3c7e27501fe1277210dChristopher Lane                iconView.setImageLevel(icon.getLevel());
619ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                iconView.setImageDrawable(icon);
620ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                iconView.setVisibility(View.VISIBLE);
621ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            } else {
622ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                iconView.setVisibility(View.GONE);
623ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
624ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
625ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return icon != null;
626ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
627ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
628ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
629ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return the max height in pixels the description can be such that the
630ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     *         action nicely takes up the entire screen.
631ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
632ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private int getDescriptionMaxHeight(Context context, TextView title) {
633ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // The 2 multiplier on the title height calculation is a
634ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // conservative estimate for font padding which can not be
635ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // calculated at this stage since the view hasn't been rendered yet.
636ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return (int)(mDisplayHeight - 2*mVerticalPadding - 2*mTitleMaxLines*title.getLineHeight());
637ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
638ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
639ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
640ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * SelectorAnimator
641ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Controls animation for selected item backgrounds
642ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * TODO: Move into focus animation override?
643ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
644ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private static class SelectorAnimator extends RecyclerView.OnScrollListener {
645ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
646ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        private final View mSelectorView;
647ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        private final ViewGroup mParentView;
648ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        private volatile boolean mFadedOut = true;
649ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
650ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        SelectorAnimator(View selectorView, ViewGroup parentView) {
651ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mSelectorView = selectorView;
652ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mParentView = parentView;
653ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
654ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
655ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // We want to fade in the selector if we've stopped scrolling on it. If
656ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // we're scrolling, we want to ensure to dim the selector if we haven't
657ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // already. We dim the last highlighted view so that while a user is
658ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // scrolling, nothing is highlighted.
659ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        @Override
660ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
661ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            Animator animator = null;
662ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            boolean fadingOut = false;
663ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (newState == RecyclerView.SCROLL_STATE_IDLE) {
664ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                // The selector starts with a height of 0. In order to scale up from
665ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                // 0, we first need the set the height to 1 and scale from there.
666ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                View focusedChild = mParentView.getFocusedChild();
667ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                if (focusedChild != null) {
668ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    int selectorHeight = mSelectorView.getHeight();
669ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    float scaleY = (float) focusedChild.getHeight() / selectorHeight;
670ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    AnimatorSet animators = (AnimatorSet)createAnimator(mSelectorView,
671ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                            R.attr.guidedActionsSelectorShowAnimation);
672ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    if (mFadedOut) {
673ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        // selector is completely faded out, so we can just scale before fading in.
674ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        mSelectorView.setScaleY(scaleY);
675ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        animator = animators.getChildAnimations().get(0);
676ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    } else {
677ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        // selector is not faded out, so we must animate the scale as we fade in.
678ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        ((ObjectAnimator)animators.getChildAnimations().get(1))
679ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                                .setFloatValues(scaleY);
680ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        animator = animators;
681ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    }
682ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                }
683ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            } else {
684ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                animator = createAnimator(mSelectorView, R.attr.guidedActionsSelectorHideAnimation);
685ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                fadingOut = true;
686ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
687ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (animator != null) {
688ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                animator.addListener(new Listener(fadingOut));
689ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                animator.start();
690ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
691ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
692ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
693ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        /**
694ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * Sets {@link BaseScrollAdapterFragment#mFadedOut}
695ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * {@link BaseScrollAdapterFragment#mFadedOut} is true, iff
696ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * {@link BaseScrollAdapterFragment#mSelectorView} has an alpha of 0
697ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * (faded out). If false the view either has an alpha of 1 (visible) or
698ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * is in the process of animating.
699ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         */
700ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        private class Listener implements Animator.AnimatorListener {
701ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            private boolean mFadingOut;
702ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            private boolean mCanceled;
703ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
704ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            public Listener(boolean fadingOut) {
705ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                mFadingOut = fadingOut;
706ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
707ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
708ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            @Override
709ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            public void onAnimationStart(Animator animation) {
710ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                if (!mFadingOut) {
711ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    mFadedOut = false;
712ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                }
713ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
714ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
715ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            @Override
716ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            public void onAnimationEnd(Animator animation) {
717ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                if (!mCanceled && mFadingOut) {
718ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    mFadedOut = true;
719ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                }
720ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
721ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
722ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            @Override
723ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            public void onAnimationCancel(Animator animation) {
724ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                mCanceled = true;
725ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
726ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
727ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            @Override
728ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            public void onAnimationRepeat(Animator animation) {
729ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
730ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
731ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
732ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
733ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing}
734