GuidanceStylist.java revision db14ff55f779f2a0c6e2c4c8009bb67458beb267
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
12 * the License.
13 */
14package android.support.v17.leanback.widget;
15
16import android.animation.Animator;
17import android.animation.AnimatorInflater;
18import android.content.Context;
19import android.content.res.TypedArray;
20import android.graphics.drawable.Drawable;
21import android.support.annotation.NonNull;
22import android.support.v17.leanback.R;
23import android.text.TextUtils;
24import android.util.TypedValue;
25import android.view.LayoutInflater;
26import android.view.View;
27import android.view.ViewGroup;
28import android.widget.ImageView;
29import android.widget.TextView;
30
31import java.util.List;
32
33/**
34 * GuidanceStylist is used within a {@link android.support.v17.leanback.app.GuidedStepFragment}
35 * to display contextual information for the decision(s) required at that step.
36 * <p>
37 * Many aspects of the base GuidanceStylist can be customized through theming; see the theme
38 * attributes below. Note that these attributes are not set on individual elements in layout
39 * XML, but instead would be set in a custom theme. See
40 * <a href="http://developer.android.com/guide/topics/ui/themes.html">Styles and Themes</a>
41 * for more information.
42 * <p>
43 * If these hooks are insufficient, this class may also be subclassed. Subclasses
44 * may wish to override the {@link #onProvideLayoutId} method to change the layout file used to
45 * display the guidance; more complex layouts may be supported by also providing a subclass of
46 * {@link GuidanceStylist.Guidance} with extra fields.
47 * <p>
48 * Note: If an alternate layout is provided, the following view IDs should be used to refer to base
49 * elements:
50 * <ul>
51 * <li>{@link android.support.v17.leanback.R.id#guidance_title}</li>
52 * <li>{@link android.support.v17.leanback.R.id#guidance_description}</li>
53 * <li>{@link android.support.v17.leanback.R.id#guidance_breadcrumb}</li>
54 * <li>{@link android.support.v17.leanback.R.id#guidance_icon}</li>
55 * </ul><p>
56 * View IDs are allowed to be missing, in which case the corresponding views will be null.
57 *
58 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeAppearingAnimation
59 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeDisappearingAnimation
60 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceContainerStyle
61 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceTitleStyle
62 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceDescriptionStyle
63 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceBreadcrumbStyle
64 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceIconStyle
65 * @see android.support.v17.leanback.app.GuidedStepFragment
66 * @see GuidanceStylist.Guidance
67 */
68public class GuidanceStylist implements FragmentAnimationProvider {
69
70    /**
71     * A data class representing contextual information for a {@link
72     * android.support.v17.leanback.app.GuidedStepFragment}. Guidance consists of a short title,
73     * a longer description, a breadcrumb to help with global navigation (often indicating where
74     * the back button will lead), and an optional icon.  All this information is intended to
75     * provide users with the appropriate context to make the decision(s) required by the current
76     * step.
77     * <p>
78     * Clients may provide a subclass of this if they wish to remember auxiliary data for use in
79     * a customized GuidanceStylist.
80     */
81    public static class Guidance {
82        private final String mTitle;
83        private final String mDescription;
84        private final String mBreadcrumb;
85        private final Drawable mIconDrawable;
86
87        /**
88         * Constructs a Guidance object with the specified title, description, breadcrumb, and
89         * icon drawable.
90         * @param title The title for the current guided step.
91         * @param description The description for the current guided step.
92         * @param breadcrumb The breadcrumb for the current guided step.
93         * @param icon The icon drawable representing the current guided step.
94         */
95        public Guidance(String title, String description, String breadcrumb, Drawable icon) {
96            mBreadcrumb = breadcrumb;
97            mTitle = title;
98            mDescription = description;
99            mIconDrawable = icon;
100        }
101
102        /**
103         * Returns the title specified when this Guidance was constructed.
104         * @return The title for this Guidance.
105         */
106        public String getTitle() {
107            return mTitle;
108        }
109
110        /**
111         * Returns the description specified when this Guidance was constructed.
112         * @return The description for this Guidance.
113         */
114        public String getDescription() {
115            return mDescription;
116        }
117
118        /**
119         * Returns the breadcrumb specified when this Guidance was constructed.
120         * @return The breadcrumb for this Guidance.
121         */
122        public String getBreadcrumb() {
123            return mBreadcrumb;
124        }
125
126        /**
127         * Returns the icon drawable specified when this Guidance was constructed.
128         * @return The icon for this Guidance.
129         */
130        public Drawable getIconDrawable() {
131            return mIconDrawable;
132        }
133    }
134
135    private TextView mTitleView;
136    private TextView mDescriptionView;
137    private TextView mBreadcrumbView;
138    private ImageView mIconView;
139
140    /**
141     * Creates an appropriately configured view for the given Guidance, using the provided
142     * inflater and container.
143     * <p>
144     * <i>Note: Does not actually add the created view to the container; the caller should do
145     * this.</i>
146     * @param inflater The layout inflater to be used when constructing the view.
147     * @param container The view group to be passed in the call to
148     * <code>LayoutInflater.inflate</code>.
149     * @param guidance The guidance data for the view.
150     * @return The view to be added to the caller's view hierarchy.
151     */
152    public View onCreateView(LayoutInflater inflater, ViewGroup container, Guidance guidance) {
153        View guidanceView = inflater.inflate(onProvideLayoutId(), container, false);
154        mTitleView = (TextView) guidanceView.findViewById(R.id.guidance_title);
155        mBreadcrumbView = (TextView) guidanceView.findViewById(R.id.guidance_breadcrumb);
156        mDescriptionView = (TextView) guidanceView.findViewById(R.id.guidance_description);
157        mIconView = (ImageView) guidanceView.findViewById(R.id.guidance_icon);
158        View guidanceContainer = guidanceView.findViewById(R.id.guidance_container);
159
160        // We allow any of the cached subviews to be null, so that subclasses can choose not to
161        // display a particular piece of information.
162        if (mTitleView != null) {
163            mTitleView.setText(guidance.getTitle());
164        }
165        if (mBreadcrumbView != null) {
166            mBreadcrumbView.setText(guidance.getBreadcrumb());
167        }
168        if (mDescriptionView != null) {
169            mDescriptionView.setText(guidance.getDescription());
170        }
171        if (mIconView != null) {
172            mIconView.setImageDrawable(guidance.getIconDrawable());
173        }
174        if (guidanceContainer != null) {
175            CharSequence contentDescription = guidanceContainer.getContentDescription();
176            if (TextUtils.isEmpty(contentDescription)) {
177                guidanceContainer.setContentDescription(new StringBuilder()
178                        .append(guidance.getBreadcrumb()).append('\n')
179                        .append(guidance.getTitle()).append('\n')
180                        .append(guidance.getDescription())
181                        .toString());
182            }
183        }
184        return guidanceView;
185    }
186
187    /**
188     * Called when destroy the View created by GuidanceStylist.
189     */
190    public void onDestroyView() {
191        mBreadcrumbView = null;
192        mDescriptionView = null;
193        mIconView = null;
194        mTitleView = null;
195    }
196
197    /**
198     * Provides the resource ID of the layout defining the guidance view. Subclasses may override
199     * to provide their own customized layouts. The base implementation returns
200     * {@link android.support.v17.leanback.R.layout#lb_guidance}. If overridden, the substituted
201     * layout should contain matching IDs for any views that should be managed by the base class;
202     * this can be achieved by starting with a copy of the base layout file.
203     * @return The resource ID of the layout to be inflated to define the guidance view.
204     */
205    public int onProvideLayoutId() {
206        return R.layout.lb_guidance;
207    }
208
209    /**
210     * Returns the view displaying the title of the guidance.
211     * @return The text view object for the title.
212     */
213    public TextView getTitleView() {
214        return mTitleView;
215    }
216
217    /**
218     * Returns the view displaying the description of the guidance.
219     * @return The text view object for the description.
220     */
221    public TextView getDescriptionView() {
222        return mDescriptionView;
223    }
224
225    /**
226     * Returns the view displaying the breadcrumb of the guidance.
227     * @return The text view object for the breadcrumb.
228     */
229    public TextView getBreadcrumbView() {
230        return mBreadcrumbView;
231    }
232
233    /**
234     * Returns the view displaying the icon of the guidance.
235     * @return The image view object for the icon.
236     */
237    public ImageView getIconView() {
238        return mIconView;
239    }
240
241    /**
242     * {@inheritDoc}
243     */
244    @Override
245    public void onImeAppearing(@NonNull List<Animator> animators) {
246        addAnimator(animators, mTitleView, R.attr.guidedStepImeAppearingAnimation);
247        addAnimator(animators, mBreadcrumbView, R.attr.guidedStepImeAppearingAnimation);
248        addAnimator(animators, mDescriptionView, R.attr.guidedStepImeAppearingAnimation);
249        addAnimator(animators, mIconView, R.attr.guidedStepImeAppearingAnimation);
250    }
251
252    /**
253     * {@inheritDoc}
254     */
255    @Override
256    public void onImeDisappearing(@NonNull List<Animator> animators) {
257        addAnimator(animators, mTitleView, R.attr.guidedStepImeDisappearingAnimation);
258        addAnimator(animators, mBreadcrumbView, R.attr.guidedStepImeDisappearingAnimation);
259        addAnimator(animators, mDescriptionView, R.attr.guidedStepImeDisappearingAnimation);
260        addAnimator(animators, mIconView, R.attr.guidedStepImeDisappearingAnimation);
261    }
262
263    private void addAnimator(List<Animator> animators, View v, int attrId) {
264        if (v != null) {
265            Context ctx = v.getContext();
266            TypedValue typedValue = new TypedValue();
267            ctx.getTheme().resolveAttribute(attrId, typedValue, true);
268            Animator animator = AnimatorInflater.loadAnimator(ctx, typedValue.resourceId);
269            animator.setTarget(v);
270            animators.add(animator);
271        }
272    }
273
274}
275