GuidedActionsStylist.java revision ac07e9d12b10138d4a449522f7082a40f18861e2
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_guidedStepEntryAnimation
914158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepExitAnimation
924158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepReentryAnimation
934158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepReturnAnimation
944158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeAppearingAnimation
954158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeDisappearingAnimation
96ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsEntryAnimation
97ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsSelectorShowAnimation
98ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsSelectorHideAnimation
99ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsContainerStyle
100ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsSelectorStyle
101ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsListStyle
102ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemContainerStyle
103ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemCheckmarkStyle
104ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemIconStyle
105ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemContentStyle
106ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemTitleStyle
107ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemDescriptionStyle
108ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionItemChevronStyle
109ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionCheckedAnimation
110ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionUncheckedAnimation
111ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionPressedAnimation
112ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionUnpressedAnimation
113ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionEnabledChevronAlpha
114ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionDisabledChevronAlpha
115ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionContentWidth
116ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionContentWidthNoIcon
117ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionTitleMinLines
118ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionTitleMaxLines
119ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionDescriptionMinLines
120ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionVerticalPadding
121ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @see android.support.v17.leanback.app.GuidedStepFragment
122ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing * @see GuidedAction
123ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing */
124ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesingpublic class GuidedActionsStylist implements FragmentAnimationProvider {
125ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
126ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
127ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * ViewHolder caches information about the action item layouts' subviews. Subclasses of {@link
128ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * GuidedActionsStylist} may also wish to subclass this in order to add fields.
129ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @see GuidedAction
130ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
131ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public static class ViewHolder {
132ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
133ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        public final View view;
134ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
135ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        private View mContentView;
136ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        private TextView mTitleView;
137ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        private TextView mDescriptionView;
138ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        private ImageView mIconView;
139ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        private ImageView mCheckmarkView;
140ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        private ImageView mChevronView;
141ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
142ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        /**
143ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * Constructs an ViewHolder and caches the relevant subviews.
144ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         */
145ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        public ViewHolder(View v) {
146ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            view = v;
147ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
148ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mContentView = v.findViewById(R.id.guidedactions_item_content);
149ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mTitleView = (TextView) v.findViewById(R.id.guidedactions_item_title);
150ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mDescriptionView = (TextView) v.findViewById(R.id.guidedactions_item_description);
151ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mIconView = (ImageView) v.findViewById(R.id.guidedactions_item_icon);
152ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mCheckmarkView = (ImageView) v.findViewById(R.id.guidedactions_item_checkmark);
153ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mChevronView = (ImageView) v.findViewById(R.id.guidedactions_item_chevron);
154ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
155ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
156ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        /**
157ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * Returns the content view within this view holder's view, where title and description are
158ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * shown.
159ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         */
160ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        public View getContentView() {
161ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            return mContentView;
162ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
163ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
164ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        /**
165ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * Returns the title view within this view holder's view.
166ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         */
167ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        public TextView getTitleView() {
168ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            return mTitleView;
169ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
170ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
171ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        /**
172ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing         * Convenience method to return an editable version of the title, if possible,
173ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing         * or null if the title view isn't an EditText.
174ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing         */
175ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing        public EditText getEditableTitleView() {
176ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing            return (mTitleView instanceof EditText) ? (EditText)mTitleView : null;
177ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing        }
178ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing
179ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing        /**
180ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * Returns the description view within this view holder's view.
181ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         */
182ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        public TextView getDescriptionView() {
183ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            return mDescriptionView;
184ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
185ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
186ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        /**
187ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * Returns the icon view within this view holder's view.
188ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         */
189ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        public ImageView getIconView() {
190ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            return mIconView;
191ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
192ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
193ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        /**
194ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * Returns the checkmark view within this view holder's view.
195ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         */
196ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        public ImageView getCheckmarkView() {
197ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            return mCheckmarkView;
198ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
199ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
200ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        /**
201ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * Returns the chevron view within this view holder's view.
202ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         */
203ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        public ImageView getChevronView() {
204ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            return mChevronView;
205ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
206ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
207ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
208ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
209ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private static String TAG = "GuidedActionsStylist";
210ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
211ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    protected View mMainView;
212ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    protected VerticalGridView mActionsGridView;
213ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    protected View mSelectorView;
214ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
215ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    // Cached values from resources
216ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private float mEnabledChevronAlpha;
217ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private float mDisabledChevronAlpha;
218ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private int mContentWidth;
219ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private int mContentWidthNoIcon;
220ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private int mTitleMinLines;
221ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private int mTitleMaxLines;
222ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private int mDescriptionMinLines;
223ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private int mVerticalPadding;
224ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private int mDisplayHeight;
225ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
226ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
227ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Creates a view appropriate for displaying a list of GuidedActions, using the provided
228ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * inflater and container.
229ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * <p>
230ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * <i>Note: Does not actually add the created view to the container; the caller should do
231ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * this.</i>
232ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param inflater The layout inflater to be used when constructing the view.
233ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param container The view group to be passed in the call to
234ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * <code>LayoutInflater.inflate</code>.
235ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return The view to be added to the caller's view hierarchy.
236ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
237ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public View onCreateView(LayoutInflater inflater, ViewGroup container) {
238ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mMainView = inflater.inflate(onProvideLayoutId(), container, false);
239ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mSelectorView = mMainView.findViewById(R.id.guidedactions_selector);
240ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (mMainView instanceof VerticalGridView) {
241ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mActionsGridView = (VerticalGridView) mMainView;
242ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        } else {
243ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mActionsGridView = (VerticalGridView) mMainView.findViewById(R.id.guidedactions_list);
244ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (mActionsGridView == null) {
245ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                throw new IllegalStateException("No ListView exists.");
246ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
247ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mActionsGridView.setWindowAlignmentOffset(0);
248ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mActionsGridView.setWindowAlignmentOffsetPercent(50f);
249ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mActionsGridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
250ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (mSelectorView != null) {
251ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                mActionsGridView.setOnScrollListener(new
252ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        SelectorAnimator(mSelectorView, mActionsGridView));
253ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
254ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
255ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
256ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mActionsGridView.requestFocusFromTouch();
257ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
258ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (mSelectorView != null) {
259ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            // ALlow focus to move to other views
260ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mActionsGridView.getViewTreeObserver().addOnGlobalFocusChangeListener(
261ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    new ViewTreeObserver.OnGlobalFocusChangeListener() {
262ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        private boolean mChildFocused;
263ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
264ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        @Override
265ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        public void onGlobalFocusChanged(View oldFocus, View newFocus) {
266ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                            View focusedChild = mActionsGridView.getFocusedChild();
267ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                            if (focusedChild == null) {
268ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                                mSelectorView.setVisibility(View.INVISIBLE);
269ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                                mChildFocused = false;
270ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                            } else if (!mChildFocused) {
271ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                                mChildFocused = true;
272ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                                mSelectorView.setVisibility(View.VISIBLE);
273ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                                updateSelectorView(focusedChild);
274ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                            }
275ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        }
276ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    });
277ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
278ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
279ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // Cache widths, chevron alpha values, max and min text lines, etc
280ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        Context ctx = mMainView.getContext();
281ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        TypedValue val = new TypedValue();
282ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mEnabledChevronAlpha = getFloat(ctx, val, R.attr.guidedActionEnabledChevronAlpha);
283ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mDisabledChevronAlpha = getFloat(ctx, val, R.attr.guidedActionDisabledChevronAlpha);
284ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mContentWidth = getDimension(ctx, val, R.attr.guidedActionContentWidth);
285ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mContentWidthNoIcon = getDimension(ctx, val, R.attr.guidedActionContentWidthNoIcon);
286ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mTitleMinLines = getInteger(ctx, val, R.attr.guidedActionTitleMinLines);
287ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mTitleMaxLines = getInteger(ctx, val, R.attr.guidedActionTitleMaxLines);
288ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mDescriptionMinLines = getInteger(ctx, val, R.attr.guidedActionDescriptionMinLines);
289ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mVerticalPadding = getDimension(ctx, val, R.attr.guidedActionVerticalPadding);
290ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mDisplayHeight = ((WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE))
291ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                .getDefaultDisplay().getHeight();
292ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
293ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return mMainView;
294ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
295ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
296ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
297ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Returns the VerticalGridView that displays the list of GuidedActions.
298ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return The VerticalGridView for this presenter.
299ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
300ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public VerticalGridView getActionsGridView() {
301ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return mActionsGridView;
302ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
303ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
304ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
305ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Provides the resource ID of the layout defining the host view for the list of guided actions.
306ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Subclasses may override to provide their own customized layouts. The base implementation
307ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * returns {@link android.support.v17.leanback.R.layout#lb_guidedactions}. If overridden, the
308ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * substituted layout should contain matching IDs for any views that should be managed by the
309ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * base class; this can be achieved by starting with a copy of the base layout file.
310ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return The resource ID of the layout to be inflated to define the host view for the list
311ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * of GuidedActions.
312ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
313ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public int onProvideLayoutId() {
314ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return R.layout.lb_guidedactions;
315ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
316ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
317ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
318ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Provides the resource ID of the layout defining the view for an individual guided actions.
319ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Subclasses may override to provide their own customized layouts. The base implementation
320ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * returns {@link android.support.v17.leanback.R.layout#lb_guidedactions_item}. If overridden,
321ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * the substituted layout should contain matching IDs for any views that should be managed by
322ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing     * the base class; this can be achieved by starting with a copy of the base layout file. Note
323ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing     * that in order for the item to support editing, the title view should both subclass {@link
324ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing     * android.widget.EditText} and implement {@link ImeKeyMonitor}; see {@link
325ac07e9d12b10138d4a449522f7082a40f18861e2Kris Giesing     * GuidedActionEditText}.
326ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return The resource ID of the layout to be inflated to define the view to display an
327ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * individual GuidedAction.
328ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
329ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public int onProvideItemLayoutId() {
330ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return R.layout.lb_guidedactions_item;
331ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
332ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
333ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
334ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Constructs a {@link ViewHolder} capable of representing {@link GuidedAction}s. Subclasses
335ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * may choose to return a subclass of ViewHolder.
336ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * <p>
337ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * <i>Note: Should not actually add the created view to the parent; the caller will do
338ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * this.</i>
339ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param parent The view group to be used as the parent of the new view.
340ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return The view to be added to the caller's view hierarchy.
341ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
342ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public ViewHolder onCreateViewHolder(ViewGroup parent) {
343ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
344ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        View v = inflater.inflate(onProvideItemLayoutId(), parent, false);
345ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return new ViewHolder(v);
346ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
347ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
348ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
349ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Binds a {@link ViewHolder} to a particular {@link GuidedAction}.
350ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param vh The view holder to be associated with the given action.
351ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param action The guided action to be displayed by the view holder's view.
352ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return The view to be added to the caller's view hierarchy.
353ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
354ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public void onBindViewHolder(ViewHolder vh, GuidedAction action) {
355ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
356ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (vh.mTitleView != null) {
357ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            vh.mTitleView.setText(action.getTitle());
358ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
359ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (vh.mDescriptionView != null) {
360ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            vh.mDescriptionView.setText(action.getDescription());
361ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            vh.mDescriptionView.setVisibility(TextUtils.isEmpty(action.getDescription()) ?
362ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    View.GONE : View.VISIBLE);
363ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
364ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // Clients might want the check mark view to be gone entirely, in which case, ignore it.
365ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (vh.mCheckmarkView != null && vh.mCheckmarkView.getVisibility() != View.GONE) {
366ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            vh.mCheckmarkView.setVisibility(action.isChecked() ? View.VISIBLE : View.INVISIBLE);
367ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
368ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
369ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (vh.mContentView != null) {
370ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            ViewGroup.LayoutParams contentLp = vh.mContentView.getLayoutParams();
371ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (setIcon(vh.mIconView, action)) {
372ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                contentLp.width = mContentWidth;
373ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            } else {
374ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                contentLp.width = mContentWidthNoIcon;
375ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
376ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            vh.mContentView.setLayoutParams(contentLp);
377ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
378ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
379ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (vh.mChevronView != null) {
380ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            vh.mChevronView.setVisibility(action.hasNext() ? View.VISIBLE : View.INVISIBLE);
381ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            vh.mChevronView.setAlpha(action.isEnabled() ? mEnabledChevronAlpha :
382ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    mDisabledChevronAlpha);
383ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
384ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
385ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (action.hasMultilineDescription()) {
386ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (vh.mTitleView != null) {
387ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                vh.mTitleView.setMaxLines(mTitleMaxLines);
388ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                if (vh.mDescriptionView != null) {
389ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    vh.mDescriptionView.setMaxHeight(getDescriptionMaxHeight(vh.view.getContext(),
390ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                            vh.mTitleView));
391ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                }
392ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
393ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        } else {
394ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (vh.mTitleView != null) {
395ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                vh.mTitleView.setMaxLines(mTitleMinLines);
396ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
397ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (vh.mDescriptionView != null) {
398ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                vh.mDescriptionView.setMaxLines(mDescriptionMinLines);
399ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
400ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
401ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
402ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
403ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
404ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Animates the view holder's view (or subviews thereof) when the action has had its focus
405ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * state changed.
406ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param vh The view holder associated with the relevant action.
407ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param focused True if the action has become focused, false if it has lost focus.
408ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
409ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public void onAnimateItemFocused(ViewHolder vh, boolean focused) {
410ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // No animations for this, currently, because the animation is done on
411ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // mSelectorView
412ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
413ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
414ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
415ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Animates the view holder's view (or subviews thereof) when the action has had its press
416ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * state changed.
417ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param vh The view holder associated with the relevant action.
418ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param pressed True if the action has been pressed, false if it has been unpressed.
419ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
420ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public void onAnimateItemPressed(ViewHolder vh, boolean pressed) {
421ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        int attr = pressed ? R.attr.guidedActionPressedAnimation :
422ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                R.attr.guidedActionUnpressedAnimation;
423ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        createAnimator(vh.view, attr).start();
424ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
425ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
426ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
427ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Animates the view holder's view (or subviews thereof) when the action has had its check
428ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * state changed.
429ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param vh The view holder associated with the relevant action.
430ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @param checked True if the action has become checked, false if it has become unchecked.
431ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
432ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public void onAnimateItemChecked(ViewHolder vh, boolean checked) {
433ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        final View checkView = vh.mCheckmarkView;
434ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (checkView != null) {
435ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (checked) {
436ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                checkView.setVisibility(View.VISIBLE);
437ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                createAnimator(checkView, R.attr.guidedActionCheckedAnimation).start();
438ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            } else {
439633f924bac2b143ae67f86eace4d5068f2acab5eKris Giesing                Animator animator = createAnimator(checkView,
440633f924bac2b143ae67f86eace4d5068f2acab5eKris Giesing                        R.attr.guidedActionUncheckedAnimation);
441ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                animator.addListener(new AnimatorListenerAdapter() {
442ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    @Override
443ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    public void onAnimationEnd(Animator animation) {
444ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        checkView.setVisibility(View.INVISIBLE);
445ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    }
446ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                });
447ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                animator.start();
448ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
449ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
450ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
451ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
452ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /*
453ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * ==========================================
454ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * FragmentAnimationProvider overrides
455ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * ==========================================
456ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
457ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
458ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
459ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * {@inheritDoc}
460ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
461ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    @Override
462ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public void onActivityEnter(@NonNull List<Animator> animators) {
463ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        animators.add(createAnimator(mMainView, R.attr.guidedActionsEntryAnimation));
464ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
465ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
466ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
467ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * {@inheritDoc}
468ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
469ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    @Override
470ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public void onActivityExit(@NonNull List<Animator> animators) {}
471ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
472ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
473ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * {@inheritDoc}
474ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
475ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    @Override
476ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public void onFragmentEnter(@NonNull List<Animator> animators) {
477ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        animators.add(createAnimator(mActionsGridView, R.attr.guidedStepEntryAnimation));
478ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        animators.add(createAnimator(mSelectorView, R.attr.guidedStepEntryAnimation));
479ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
480ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
481ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
482ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * {@inheritDoc}
483ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
484ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    @Override
485ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public void onFragmentExit(@NonNull List<Animator> animators) {
486ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        animators.add(createAnimator(mActionsGridView, R.attr.guidedStepExitAnimation));
487ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        animators.add(createAnimator(mSelectorView, R.attr.guidedStepExitAnimation));
488ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
489ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
490ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
491ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * {@inheritDoc}
492ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
493ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    @Override
494ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public void onFragmentReenter(@NonNull List<Animator> animators) {
495ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        animators.add(createAnimator(mActionsGridView, R.attr.guidedStepReentryAnimation));
496ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        animators.add(createAnimator(mSelectorView, R.attr.guidedStepReentryAnimation));
497ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
498ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
499ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
500ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * {@inheritDoc}
501ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
502ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    @Override
503ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    public void onFragmentReturn(@NonNull List<Animator> animators) {
504ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        animators.add(createAnimator(mActionsGridView, R.attr.guidedStepReturnAnimation));
505ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        animators.add(createAnimator(mSelectorView, R.attr.guidedStepReturnAnimation));
506ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
507ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
5084158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing    /**
5094158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing     * {@inheritDoc}
5104158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing     */
5114158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing    @Override
5124158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing    public void onImeAppearing(@NonNull List<Animator> animators) {
5134158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing        animators.add(createAnimator(mActionsGridView, R.attr.guidedStepImeAppearingAnimation));
5144158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing        animators.add(createAnimator(mSelectorView, R.attr.guidedStepImeAppearingAnimation));
5154158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing    }
5164158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing
5174158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing    /**
5184158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing     * {@inheritDoc}
5194158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing     */
5204158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing    @Override
5214158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing    public void onImeDisappearing(@NonNull List<Animator> animators) {
5224158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing        animators.add(createAnimator(mActionsGridView, R.attr.guidedStepImeDisappearingAnimation));
5234158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing        animators.add(createAnimator(mSelectorView, R.attr.guidedStepImeDisappearingAnimation));
5244158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing    }
5254158705d3f0751d419a08c47a659abeae5f6c196Kris Giesing
526ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /*
527ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * ==========================================
528ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Private methods
529ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * ==========================================
530ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
531ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
532ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private void updateSelectorView(View focusedChild) {
533ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // Display the selector view.
534ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        int height = focusedChild.getHeight();
535ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        LayoutParams lp = mSelectorView.getLayoutParams();
536ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        lp.height = height;
537ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mSelectorView.setLayoutParams(lp);
538ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        mSelectorView.setAlpha(1f);
539ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
540ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
541ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private float getFloat(Context ctx, TypedValue typedValue, int attrId) {
542ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        ctx.getTheme().resolveAttribute(attrId, typedValue, true);
543ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // Android resources don't have a native float type, so we have to use strings.
544ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return Float.valueOf(ctx.getResources().getString(typedValue.resourceId));
545ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
546ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
547ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private int getInteger(Context ctx, TypedValue typedValue, int attrId) {
548ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        ctx.getTheme().resolveAttribute(attrId, typedValue, true);
549ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return ctx.getResources().getInteger(typedValue.resourceId);
550ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
551ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
552ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private int getDimension(Context ctx, TypedValue typedValue, int attrId) {
553ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        ctx.getTheme().resolveAttribute(attrId, typedValue, true);
554ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return ctx.getResources().getDimensionPixelSize(typedValue.resourceId);
555ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
556ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
557ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private static Animator createAnimator(View v, int attrId) {
558ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        Context ctx = v.getContext();
559ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        TypedValue typedValue = new TypedValue();
560ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        ctx.getTheme().resolveAttribute(attrId, typedValue, true);
561ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        Animator animator = AnimatorInflater.loadAnimator(ctx, typedValue.resourceId);
562ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        animator.setTarget(v);
563ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return animator;
564ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
565ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
566ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private boolean setIcon(final ImageView iconView, GuidedAction action) {
567ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        Drawable icon = null;
568ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        if (iconView != null) {
569ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            Context context = iconView.getContext();
570ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            icon = action.getIcon();
571ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (icon != null) {
572162b21598d9f4fd49748b3c7e27501fe1277210dChristopher Lane                // setImageDrawable resets the drawable's level unless we set the view level first.
573162b21598d9f4fd49748b3c7e27501fe1277210dChristopher Lane                iconView.setImageLevel(icon.getLevel());
574ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                iconView.setImageDrawable(icon);
575ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                iconView.setVisibility(View.VISIBLE);
576ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            } else {
577ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                iconView.setVisibility(View.GONE);
578ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
579ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
580ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return icon != null;
581ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
582ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
583ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
584ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * @return the max height in pixels the description can be such that the
585ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     *         action nicely takes up the entire screen.
586ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
587ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private int getDescriptionMaxHeight(Context context, TextView title) {
588ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // The 2 multiplier on the title height calculation is a
589ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // conservative estimate for font padding which can not be
590ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // calculated at this stage since the view hasn't been rendered yet.
591ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        return (int)(mDisplayHeight - 2*mVerticalPadding - 2*mTitleMaxLines*title.getLineHeight());
592ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
593ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
594ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    /**
595ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * SelectorAnimator
596ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * Controls animation for selected item backgrounds
597ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     * TODO: Move into focus animation override?
598ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing     */
599ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    private static class SelectorAnimator extends RecyclerView.OnScrollListener {
600ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
601ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        private final View mSelectorView;
602ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        private final ViewGroup mParentView;
603ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        private volatile boolean mFadedOut = true;
604ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
605ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        SelectorAnimator(View selectorView, ViewGroup parentView) {
606ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mSelectorView = selectorView;
607ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            mParentView = parentView;
608ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
609ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
610ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // We want to fade in the selector if we've stopped scrolling on it. If
611ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // we're scrolling, we want to ensure to dim the selector if we haven't
612ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // already. We dim the last highlighted view so that while a user is
613ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        // scrolling, nothing is highlighted.
614ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        @Override
615ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
616ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            Animator animator = null;
617ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            boolean fadingOut = false;
618ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (newState == RecyclerView.SCROLL_STATE_IDLE) {
619ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                // The selector starts with a height of 0. In order to scale up from
620ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                // 0, we first need the set the height to 1 and scale from there.
621ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                View focusedChild = mParentView.getFocusedChild();
622ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                if (focusedChild != null) {
623ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    int selectorHeight = mSelectorView.getHeight();
624ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    float scaleY = (float) focusedChild.getHeight() / selectorHeight;
625ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    AnimatorSet animators = (AnimatorSet)createAnimator(mSelectorView,
626ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                            R.attr.guidedActionsSelectorShowAnimation);
627ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    if (mFadedOut) {
628ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        // selector is completely faded out, so we can just scale before fading in.
629ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        mSelectorView.setScaleY(scaleY);
630ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        animator = animators.getChildAnimations().get(0);
631ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    } else {
632ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        // selector is not faded out, so we must animate the scale as we fade in.
633ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        ((ObjectAnimator)animators.getChildAnimations().get(1))
634ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                                .setFloatValues(scaleY);
635ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                        animator = animators;
636ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    }
637ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                }
638ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            } else {
639ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                animator = createAnimator(mSelectorView, R.attr.guidedActionsSelectorHideAnimation);
640ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                fadingOut = true;
641ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
642ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            if (animator != null) {
643ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                animator.addListener(new Listener(fadingOut));
644ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                animator.start();
645ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
646ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
647ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
648ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        /**
649ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * Sets {@link BaseScrollAdapterFragment#mFadedOut}
650ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * {@link BaseScrollAdapterFragment#mFadedOut} is true, iff
651ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * {@link BaseScrollAdapterFragment#mSelectorView} has an alpha of 0
652ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * (faded out). If false the view either has an alpha of 1 (visible) or
653ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         * is in the process of animating.
654ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing         */
655ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        private class Listener implements Animator.AnimatorListener {
656ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            private boolean mFadingOut;
657ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            private boolean mCanceled;
658ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
659ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            public Listener(boolean fadingOut) {
660ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                mFadingOut = fadingOut;
661ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
662ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
663ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            @Override
664ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            public void onAnimationStart(Animator animation) {
665ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                if (!mFadingOut) {
666ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    mFadedOut = false;
667ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                }
668ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
669ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
670ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            @Override
671ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            public void onAnimationEnd(Animator animation) {
672ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                if (!mCanceled && mFadingOut) {
673ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                    mFadedOut = true;
674ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                }
675ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
676ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
677ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            @Override
678ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            public void onAnimationCancel(Animator animation) {
679ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing                mCanceled = true;
680ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
681ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
682ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            @Override
683ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            public void onAnimationRepeat(Animator animation) {
684ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing            }
685ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing        }
686ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing    }
687ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing
688ebd3d9078dbaebd10a9506ca086435eb63e8a2d2Kris Giesing}
689