GuidanceStylist.java revision ebd3d9078dbaebd10a9506ca086435eb63e8a2d2
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.util.TypedValue;
24import android.view.LayoutInflater;
25import android.view.View;
26import android.view.ViewGroup;
27import android.widget.ImageView;
28import android.widget.TextView;
29
30import java.util.List;
31
32/**
33 * GuidanceStylist is used within a {@link android.support.v17.leanback.app.GuidedStepFragment}
34 * to display contextual information for the decision(s) required at that step.
35 * <p>
36 * Many aspects of the base GuidanceStylist can be customized through theming; see the theme
37 * attributes below. Note that these attributes are not set on individual elements in layout
38 * XML, but instead would be set in a custom theme. See
39 * <a href="http://developer.android.com/guide/topics/ui/themes.html">Styles and Themes</a>
40 * for more information.
41 * <p>
42 * If these hooks are insufficient, this class may also be subclassed. Subclasses
43 * may wish to override the {@link #onProvideLayoutId} method to change the layout file used to
44 * display the guidance; more complex layouts may be supported by also providing a subclass of
45 * {@link GuidanceStylist.Guidance} with extra fields.
46 * <p>
47 * Note: If an alternate layout is provided, the following view IDs should be used to refer to base
48 * elements:
49 * <ul>
50 * <li>{@link android.support.v17.leanback.R.id#guidance_title}</li>
51 * <li>{@link android.support.v17.leanback.R.id#guidance_description}</li>
52 * <li>{@link android.support.v17.leanback.R.id#guidance_breadcrumb}</li>
53 * <li>{@link android.support.v17.leanback.R.id#guidance_icon}</li>
54 * </ul><p>
55 * View IDs are allowed to be missing, in which case the corresponding views will be null.
56 *
57 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceEntryAnimation
58 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepEntryAnimation
59 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepExitAnimation
60 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepReentryAnimation
61 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepReturnAnimation
62 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceContainerStyle
63 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceTitleStyle
64 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceDescriptionStyle
65 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceBreadcrumbStyle
66 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceIconStyle
67 * @see android.support.v17.leanback.app.GuidedStepFragment
68 * @see GuidanceStylist.Guidance
69 */
70public class GuidanceStylist implements FragmentAnimationProvider {
71
72    /**
73     * A data class representing contextual information for a {@link
74     * android.support.v17.leanback.app.GuidedStepFragment}. Guidance consists of a short title,
75     * a longer description, a breadcrumb to help with global navigation (often indicating where
76     * the back button will lead), and an optional icon.  All this information is intended to
77     * provide users with the appropriate context to make the decision(s) required by the current
78     * step.
79     * <p>
80     * Clients may provide a subclass of this if they wish to remember auxiliary data for use in
81     * a customized GuidanceStylist.
82     */
83    public static class Guidance {
84        private final String mTitle;
85        private final String mDescription;
86        private final String mBreadcrumb;
87        private final Drawable mIconDrawable;
88
89        /**
90         * Constructs a Guidance object with the specified title, description, breadcrumb, and
91         * icon drawable.
92         * @param title The title for the current guided step.
93         * @param description The description for the current guided step.
94         * @param breadcrumb The breadcrumb for the current guided step.
95         * @param icon The icon drawable representing the current guided step.
96         */
97        public Guidance(String title, String description, String breadcrumb, Drawable icon) {
98            mBreadcrumb = breadcrumb;
99            mTitle = title;
100            mDescription = description;
101            mIconDrawable = icon;
102        }
103
104        /**
105         * Returns the title specified when this Guidance was constructed.
106         * @return The title for this Guidance.
107         */
108        public String getTitle() {
109            return mTitle;
110        }
111
112        /**
113         * Returns the description specified when this Guidance was constructed.
114         * @return The description for this Guidance.
115         */
116        public String getDescription() {
117            return mDescription;
118        }
119
120        /**
121         * Returns the breadcrumb specified when this Guidance was constructed.
122         * @return The breadcrumb for this Guidance.
123         */
124        public String getBreadcrumb() {
125            return mBreadcrumb;
126        }
127
128        /**
129         * Returns the icon drawable specified when this Guidance was constructed.
130         * @return The icon for this Guidance.
131         */
132        public Drawable getIconDrawable() {
133            return mIconDrawable;
134        }
135    }
136
137    private TextView mTitleView;
138    private TextView mDescriptionView;
139    private TextView mBreadcrumbView;
140    private ImageView mIconView;
141
142    /**
143     * Creates an appropriately configured view for the given Guidance, using the provided
144     * inflater and container.
145     * <p>
146     * <i>Note: Does not actually add the created view to the container; the caller should do
147     * this.</i>
148     * @param inflater The layout inflater to be used when constructing the view.
149     * @param container The view group to be passed in the call to
150     * <code>LayoutInflater.inflate</code>.
151     * @param guidance The guidance data for the view.
152     * @return The view to be added to the caller's view hierarchy.
153     */
154    public View onCreateView(LayoutInflater inflater, ViewGroup container, Guidance guidance) {
155        View guidanceView = inflater.inflate(onProvideLayoutId(), container, false);
156        mTitleView = (TextView) guidanceView.findViewById(R.id.guidance_title);
157        mBreadcrumbView = (TextView) guidanceView.findViewById(R.id.guidance_breadcrumb);
158        mDescriptionView = (TextView) guidanceView.findViewById(R.id.guidance_description);
159        mIconView = (ImageView) guidanceView.findViewById(R.id.guidance_icon);
160
161        // We allow any of the cached subviews to be null, so that subclasses can choose not to
162        // display a particular piece of information.
163        if (mTitleView != null) {
164            mTitleView.setText(guidance.getTitle());
165        }
166        if (mBreadcrumbView != null) {
167            mBreadcrumbView.setText(guidance.getBreadcrumb());
168        }
169        if (mDescriptionView != null) {
170            mDescriptionView.setText(guidance.getDescription());
171        }
172        if (mIconView != null) {
173            mIconView.setImageDrawable(guidance.getIconDrawable());
174        }
175        return guidanceView;
176    }
177
178    /**
179     * Provides the resource ID of the layout defining the guidance view. Subclasses may override
180     * to provide their own customized layouts. The base implementation returns
181     * {@link android.support.v17.leanback.R.layout#lb_guidance}. If overridden, the substituted
182     * layout should contain matching IDs for any views that should be managed by the base class;
183     * this can be achieved by starting with a copy of the base layout file.
184     * @return The resource ID of the layout to be inflated to define the guidance view.
185     */
186    public int onProvideLayoutId() {
187        return R.layout.lb_guidance;
188    }
189
190    /**
191     * Returns the view displaying the title of the guidance.
192     * @return The text view object for the title.
193     */
194    public TextView getTitleView() {
195        return mTitleView;
196    }
197
198    /**
199     * Returns the view displaying the description of the guidance.
200     * @return The text view object for the description.
201     */
202    public TextView getDescriptionView() {
203        return mDescriptionView;
204    }
205
206    /**
207     * Returns the view displaying the breadcrumb of the guidance.
208     * @return The text view object for the breadcrumb.
209     */
210    public TextView getBreadcrumbView() {
211        return mBreadcrumbView;
212    }
213
214    /**
215     * Returns the view displaying the icon of the guidance.
216     * @return The image view object for the icon.
217     */
218    public ImageView getIconView() {
219        return mIconView;
220    }
221
222    /**
223     * {@inheritDoc}
224     */
225    @Override
226    public void onActivityEnter(@NonNull List<Animator> animators) {
227        addAnimator(animators, mTitleView, R.attr.guidanceEntryAnimation);
228        addAnimator(animators, mBreadcrumbView, R.attr.guidanceEntryAnimation);
229        addAnimator(animators, mDescriptionView, R.attr.guidanceEntryAnimation);
230        addAnimator(animators, mIconView, R.attr.guidanceEntryAnimation);
231    }
232
233    /**
234     * {@inheritDoc}
235     */
236    @Override
237    public void onActivityExit(@NonNull List<Animator> animators) {}
238
239    /**
240     * {@inheritDoc}
241     */
242    @Override
243    public void onFragmentEnter(@NonNull List<Animator> animators) {
244        addAnimator(animators, mTitleView, R.attr.guidedStepEntryAnimation);
245        addAnimator(animators, mBreadcrumbView, R.attr.guidedStepEntryAnimation);
246        addAnimator(animators, mDescriptionView, R.attr.guidedStepEntryAnimation);
247        addAnimator(animators, mIconView, R.attr.guidedStepEntryAnimation);
248    }
249
250    /**
251     * {@inheritDoc}
252     */
253    @Override
254    public void onFragmentExit(@NonNull List<Animator> animators) {
255        addAnimator(animators, mTitleView, R.attr.guidedStepExitAnimation);
256        addAnimator(animators, mBreadcrumbView, R.attr.guidedStepExitAnimation);
257        addAnimator(animators, mDescriptionView, R.attr.guidedStepExitAnimation);
258        addAnimator(animators, mIconView, R.attr.guidedStepExitAnimation);
259    }
260
261    /**
262     * {@inheritDoc}
263     */
264    @Override
265    public void onFragmentReenter(@NonNull List<Animator> animators) {
266        addAnimator(animators, mTitleView, R.attr.guidedStepReentryAnimation);
267        addAnimator(animators, mBreadcrumbView, R.attr.guidedStepReentryAnimation);
268        addAnimator(animators, mDescriptionView, R.attr.guidedStepReentryAnimation);
269        addAnimator(animators, mIconView, R.attr.guidedStepReentryAnimation);
270    }
271
272    /**
273     * {@inheritDoc}
274     */
275    @Override
276    public void onFragmentReturn(@NonNull List<Animator> animators) {
277        addAnimator(animators, mTitleView, R.attr.guidedStepReturnAnimation);
278        addAnimator(animators, mBreadcrumbView, R.attr.guidedStepReturnAnimation);
279        addAnimator(animators, mDescriptionView, R.attr.guidedStepReturnAnimation);
280        addAnimator(animators, mIconView, R.attr.guidedStepReturnAnimation);
281    }
282
283    private void addAnimator(List<Animator> animators, View v, int attrId) {
284        if (v != null) {
285            Context ctx = v.getContext();
286            TypedValue typedValue = new TypedValue();
287            ctx.getTheme().resolveAttribute(attrId, typedValue, true);
288            Animator animator = AnimatorInflater.loadAnimator(ctx, typedValue.resourceId);
289            animator.setTarget(v);
290            animators.add(animator);
291        }
292    }
293
294}
295