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