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.graphics.drawable.Drawable;
20import android.support.annotation.NonNull;
21import android.support.v17.leanback.R;
22import android.text.TextUtils;
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_guidedStepImeAppearingAnimation
58 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeDisappearingAnimation
59 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceContainerStyle
60 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceTitleStyle
61 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceDescriptionStyle
62 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceBreadcrumbStyle
63 * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidanceIconStyle
64 * @see android.support.v17.leanback.app.GuidedStepFragment
65 * @see GuidanceStylist.Guidance
66 */
67public class GuidanceStylist implements FragmentAnimationProvider {
68
69    /**
70     * A data class representing contextual information for a {@link
71     * android.support.v17.leanback.app.GuidedStepFragment}. Guidance consists of a short title,
72     * a longer description, a breadcrumb to help with global navigation (often indicating where
73     * the back button will lead), and an optional icon.  All this information is intended to
74     * provide users with the appropriate context to make the decision(s) required by the current
75     * step.
76     * <p>
77     * Clients may provide a subclass of this if they wish to remember auxiliary data for use in
78     * a customized GuidanceStylist.
79     */
80    public static class Guidance {
81        private final String mTitle;
82        private final String mDescription;
83        private final String mBreadcrumb;
84        private final Drawable mIconDrawable;
85
86        /**
87         * Constructs a Guidance object with the specified title, description, breadcrumb, and
88         * icon drawable.
89         * @param title The title for the current guided step.
90         * @param description The description for the current guided step.
91         * @param breadcrumb The breadcrumb for the current guided step.
92         * @param icon The icon drawable representing the current guided step.
93         */
94        public Guidance(String title, String description, String breadcrumb, Drawable icon) {
95            mBreadcrumb = breadcrumb;
96            mTitle = title;
97            mDescription = description;
98            mIconDrawable = icon;
99        }
100
101        /**
102         * Returns the title specified when this Guidance was constructed.
103         * @return The title for this Guidance.
104         */
105        public String getTitle() {
106            return mTitle;
107        }
108
109        /**
110         * Returns the description specified when this Guidance was constructed.
111         * @return The description for this Guidance.
112         */
113        public String getDescription() {
114            return mDescription;
115        }
116
117        /**
118         * Returns the breadcrumb specified when this Guidance was constructed.
119         * @return The breadcrumb for this Guidance.
120         */
121        public String getBreadcrumb() {
122            return mBreadcrumb;
123        }
124
125        /**
126         * Returns the icon drawable specified when this Guidance was constructed.
127         * @return The icon for this Guidance.
128         */
129        public Drawable getIconDrawable() {
130            return mIconDrawable;
131        }
132    }
133
134    private TextView mTitleView;
135    private TextView mDescriptionView;
136    private TextView mBreadcrumbView;
137    private ImageView mIconView;
138    private View mGuidanceContainer;
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(
153            final LayoutInflater inflater, ViewGroup container, Guidance guidance) {
154
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        mGuidanceContainer = guidanceView.findViewById(R.id.guidance_container);
161
162        // We allow any of the cached subviews to be null, so that subclasses can choose not to
163        // display a particular piece of information.
164        if (mTitleView != null) {
165            mTitleView.setText(guidance.getTitle());
166        }
167
168        if (mBreadcrumbView != null) {
169            mBreadcrumbView.setText(guidance.getBreadcrumb());
170        }
171
172        if (mDescriptionView != null) {
173            mDescriptionView.setText(guidance.getDescription());
174        }
175
176        if (mIconView != null) {
177            if (guidance.getIconDrawable() != null) {
178                mIconView.setImageDrawable(guidance.getIconDrawable());
179            } else {
180                mIconView.setVisibility(View.GONE);
181            }
182        }
183
184        if (mGuidanceContainer != null) {
185            CharSequence contentDescription = mGuidanceContainer.getContentDescription();
186            if (TextUtils.isEmpty(contentDescription)) {
187                StringBuilder builder = new StringBuilder();
188                if (!TextUtils.isEmpty(guidance.getBreadcrumb())) {
189                    builder.append(guidance.getBreadcrumb()).append('\n');
190                }
191                if (!TextUtils.isEmpty(guidance.getTitle())) {
192                    builder.append(guidance.getTitle()).append('\n');
193                }
194                if (!TextUtils.isEmpty(guidance.getDescription())) {
195                    builder.append(guidance.getDescription()).append('\n');
196                }
197                mGuidanceContainer.setContentDescription(builder);
198            }
199        }
200
201        return guidanceView;
202    }
203
204    /**
205     * Called when destroy the View created by GuidanceStylist.
206     */
207    public void onDestroyView() {
208        mBreadcrumbView = null;
209        mDescriptionView = null;
210        mIconView = null;
211        mTitleView = null;
212    }
213
214    /**
215     * Provides the resource ID of the layout defining the guidance view. Subclasses may override
216     * to provide their own customized layouts. The base implementation returns
217     * {@link android.support.v17.leanback.R.layout#lb_guidance}. If overridden, the substituted
218     * layout should contain matching IDs for any views that should be managed by the base class;
219     * this can be achieved by starting with a copy of the base layout file.
220     * @return The resource ID of the layout to be inflated to define the guidance view.
221     */
222    public int onProvideLayoutId() {
223        return R.layout.lb_guidance;
224    }
225
226    /**
227     * Returns the view displaying the title of the guidance.
228     * @return The text view object for the title.
229     */
230    public TextView getTitleView() {
231        return mTitleView;
232    }
233
234    /**
235     * Returns the view displaying the description of the guidance.
236     * @return The text view object for the description.
237     */
238    public TextView getDescriptionView() {
239        return mDescriptionView;
240    }
241
242    /**
243     * Returns the view displaying the breadcrumb of the guidance.
244     * @return The text view object for the breadcrumb.
245     */
246    public TextView getBreadcrumbView() {
247        return mBreadcrumbView;
248    }
249
250    /**
251     * Returns the view displaying the icon of the guidance.
252     * @return The image view object for the icon.
253     */
254    public ImageView getIconView() {
255        return mIconView;
256    }
257
258    /**
259     * {@inheritDoc}
260     */
261    @Override
262    public void onImeAppearing(@NonNull List<Animator> animators) {
263    }
264
265    /**
266     * {@inheritDoc}
267     */
268    @Override
269    public void onImeDisappearing(@NonNull List<Animator> animators) {
270    }
271
272}
273