1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.car.setupwizardlib;
17 
18 import android.content.Context;
19 import android.content.res.TypedArray;
20 import android.graphics.PorterDuff;
21 import android.graphics.Rect;
22 import android.graphics.Typeface;
23 import android.graphics.drawable.Drawable;
24 import android.graphics.drawable.GradientDrawable;
25 import android.graphics.drawable.InsetDrawable;
26 import android.graphics.drawable.RippleDrawable;
27 import android.text.TextUtils;
28 import android.util.AttributeSet;
29 import android.util.Log;
30 import android.util.TypedValue;
31 import android.view.LayoutInflater;
32 import android.view.TouchDelegate;
33 import android.view.View;
34 import android.view.ViewGroup;
35 import android.view.ViewStub;
36 import android.widget.Button;
37 import android.widget.ImageView;
38 import android.widget.LinearLayout;
39 import android.widget.ProgressBar;
40 import android.widget.TextView;
41 
42 import androidx.annotation.Nullable;
43 import androidx.annotation.StyleRes;
44 import androidx.annotation.VisibleForTesting;
45 
46 import com.android.car.setupwizardlib.partner.PartnerConfig;
47 import com.android.car.setupwizardlib.partner.PartnerConfigHelper;
48 
49 import java.util.Locale;
50 import java.util.Objects;
51 
52 /**
53  * Custom layout for the Car Setup Wizard. Provides accessors for modifying elements such as buttons
54  * and progress bars. Any modifications to elements built by
55  * the CarSetupWizardLayout should be done through methods provided by this class unless that is
56  * not possible so as to keep the state internally consistent.
57  *
58  * @deprecated Use {@link CarSetupWizardCompatLayout} or {@link CarSetupWizardDesignLayout}.
59  */
60 @Deprecated
61 public class CarSetupWizardLayout extends LinearLayout {
62     private static final String TAG = CarSetupWizardLayout.class.getSimpleName();
63 
64     private View mBackButton;
65     private View mTitleBar;
66     private TextView mToolbarTitle;
67     private PartnerConfigHelper mPartnerConfigHelper;
68 
69     /* <p>The Primary Toolbar Button should always be used when there is only a single action that
70      * moves the wizard to the next screen (e.g. Only need a 'Skip' button).
71      *
72      * When there are two actions that can move the wizard to the next screen (e.g. either 'Skip'
73      * or 'Let's Go' are the two options), then the Primary is used for the positive action
74      * while the Secondary is used for the negative action.</p>
75      */
76     private Button mPrimaryToolbarButton;
77 
78     /*
79      * Flag to track the primary toolbar button flat state.
80      */
81     private boolean mPrimaryToolbarButtonFlat;
82     private View.OnClickListener mPrimaryToolbarButtonOnClick;
83     private Button mSecondaryToolbarButton;
84     private ProgressBar mProgressBar;
85 
CarSetupWizardLayout(Context context)86     public CarSetupWizardLayout(Context context) {
87         this(context, null);
88     }
89 
CarSetupWizardLayout(Context context, @Nullable AttributeSet attrs)90     public CarSetupWizardLayout(Context context, @Nullable AttributeSet attrs) {
91         this(context, attrs, 0);
92     }
93 
CarSetupWizardLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr)94     public CarSetupWizardLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
95         this(context, attrs, defStyleAttr, 0);
96     }
97 
98     /**
99      * On initialization, the layout gets all of the custom attributes and initializes
100      * the custom views that can be set by the user (e.g. back button, continue button).
101      */
CarSetupWizardLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)102     public CarSetupWizardLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
103             int defStyleRes) {
104         super(context, attrs, defStyleAttr, defStyleRes);
105 
106         mPartnerConfigHelper = PartnerConfigHelper.get(context);
107         TypedArray attrArray = context.getTheme().obtainStyledAttributes(
108                 attrs,
109                 R.styleable.CarSetupWizardLayout,
110                 0, 0);
111 
112         init(attrArray);
113     }
114 
115     /**
116      * Inflates the layout and sets the custom views (e.g. back button, continue button).
117      */
init(TypedArray attrArray)118     private void init(TypedArray attrArray) {
119         boolean showBackButton;
120 
121         boolean showToolbarTitle;
122         String toolbarTitleText;
123 
124         boolean showPrimaryToolbarButton;
125         String primaryToolbarButtonText;
126         boolean primaryToolbarButtonEnabled;
127 
128         boolean showSecondaryToolbarButton;
129         String secondaryToolbarButtonText;
130         boolean secondaryToolbarButtonEnabled;
131 
132         boolean showProgressBar;
133         boolean indeterminateProgressBar;
134 
135         try {
136             showBackButton = attrArray.getBoolean(
137                     R.styleable.CarSetupWizardLayout_showBackButton, true);
138             showToolbarTitle = attrArray.getBoolean(
139                     R.styleable.CarSetupWizardLayout_showToolbarTitle, false);
140             toolbarTitleText = attrArray.getString(
141                     R.styleable.CarSetupWizardLayout_toolbarTitleText);
142             showPrimaryToolbarButton = attrArray.getBoolean(
143                     R.styleable.CarSetupWizardLayout_showPrimaryToolbarButton, true);
144             primaryToolbarButtonText = attrArray.getString(
145                     R.styleable.CarSetupWizardLayout_primaryToolbarButtonText);
146             primaryToolbarButtonEnabled = attrArray.getBoolean(
147                     R.styleable.CarSetupWizardLayout_primaryToolbarButtonEnabled, true);
148             mPrimaryToolbarButtonFlat = attrArray.getBoolean(
149                     R.styleable.CarSetupWizardLayout_primaryToolbarButtonFlat, false);
150             showSecondaryToolbarButton = attrArray.getBoolean(
151                     R.styleable.CarSetupWizardLayout_showSecondaryToolbarButton, false);
152             secondaryToolbarButtonText = attrArray.getString(
153                     R.styleable.CarSetupWizardLayout_secondaryToolbarButtonText);
154             secondaryToolbarButtonEnabled = attrArray.getBoolean(
155                     R.styleable.CarSetupWizardLayout_secondaryToolbarButtonEnabled, true);
156             showProgressBar = attrArray.getBoolean(
157                     R.styleable.CarSetupWizardLayout_showProgressBar, false);
158             indeterminateProgressBar = attrArray.getBoolean(
159                     R.styleable.CarSetupWizardLayout_indeterminateProgressBar, true);
160         } finally {
161             attrArray.recycle();
162         }
163 
164         LayoutInflater inflater = LayoutInflater.from(getContext());
165         inflater.inflate(R.layout.car_setup_wizard_layout, this);
166 
167         // Set the back button visibility based on the custom attribute.
168         setBackButton(findViewById(R.id.back_button));
169         Drawable drawable = mPartnerConfigHelper.getDrawable(
170                 getContext(), PartnerConfig.CONFIG_TOOLBAR_BUTTON_ICON_BACK);
171         if (drawable != null) {
172             ((ImageView) mBackButton).setImageDrawable(drawable);
173         }
174         setBackButtonVisible(showBackButton);
175 
176         // Se the title bar.
177         setTitleBar(findViewById(R.id.application_bar));
178         int toolbarBgColor =
179                 mPartnerConfigHelper.getColor(getContext(), PartnerConfig.CONFIG_TOOLBAR_BG_COLOR);
180         if (toolbarBgColor != 0) {
181             mTitleBar.setBackgroundColor(toolbarBgColor);
182         }
183 
184         // Set the toolbar title visibility and text based on the custom attributes.
185         setToolbarTitle(findViewById(R.id.toolbar_title));
186         if (showToolbarTitle) {
187             setToolbarTitleText(toolbarTitleText);
188         } else {
189             setToolbarTitleVisible(false);
190         }
191 
192         // Set the primary continue button visibility and text based on the custom attributes.
193         ViewStub primaryToolbarButtonStub =
194                 (ViewStub) findViewById(R.id.primary_toolbar_button_stub);
195         // Set the button layout to flat if that attribute was set.
196         if (mPrimaryToolbarButtonFlat) {
197             primaryToolbarButtonStub.setLayoutResource(R.layout.flat_button);
198         }
199         primaryToolbarButtonStub.inflate();
200         setPrimaryToolbarButton(findViewById(R.id.primary_toolbar_button));
201         if (showPrimaryToolbarButton) {
202             setPrimaryToolbarButtonText(primaryToolbarButtonText);
203             setPrimaryToolbarButtonEnabled(primaryToolbarButtonEnabled);
204 
205             setBackground(
206                     mPrimaryToolbarButton,
207                     PartnerConfig.CONFIG_TOOLBAR_PRIMARY_BUTTON_BG,
208                     PartnerConfig.CONFIG_TOOLBAR_PRIMARY_BUTTON_BG_COLOR);
209 
210             setButtonPadding(mPrimaryToolbarButton);
211             setButtonTypeFace(mPrimaryToolbarButton);
212             setButtonTextSize(mPrimaryToolbarButton);
213             setButtonTextColor(
214                     mPrimaryToolbarButton,
215                     PartnerConfig.CONFIG_TOOLBAR_PRIMARY_BUTTON_TEXT_COLOR);
216         } else {
217             setPrimaryToolbarButtonVisible(false);
218         }
219 
220         // Set the secondary continue button visibility and text based on the custom attributes.
221         ViewStub secondaryToolbarButtonStub =
222                 (ViewStub) findViewById(R.id.secondary_toolbar_button_stub);
223         if (showSecondaryToolbarButton || !TextUtils.isEmpty(secondaryToolbarButtonText)) {
224             secondaryToolbarButtonStub.inflate();
225             mSecondaryToolbarButton = findViewById(R.id.secondary_toolbar_button);
226             setSecondaryToolbarButtonText(secondaryToolbarButtonText);
227             setSecondaryToolbarButtonEnabled(secondaryToolbarButtonEnabled);
228             setSecondaryToolbarButtonVisible(showSecondaryToolbarButton);
229         }
230 
231         mProgressBar = findViewById(R.id.progress_bar);
232         setProgressBarVisible(showProgressBar);
233         setProgressBarIndeterminate(indeterminateProgressBar);
234 
235         // Set orientation programmatically since the inflated layout uses <merge>
236         setOrientation(LinearLayout.VERTICAL);
237     }
238 
239     /**
240      * Set a given view's visibility.
241      */
242     @VisibleForTesting
setViewVisible(View view, boolean visible)243     void setViewVisible(View view, boolean visible) {
244         view.setVisibility(visible ? View.VISIBLE : View.GONE);
245     }
246 
247     // Add or remove the back button touch delegate depending on whether it is visible.
248     @VisibleForTesting
updateBackButtonTouchDelegate(boolean visible)249     void updateBackButtonTouchDelegate(boolean visible) {
250         if (visible) {
251             // Post this action in the parent's message queue to make sure the parent
252             // lays out its children before getHitRect() is called
253             this.post(() -> {
254                 Rect delegateArea = new Rect();
255 
256                 mBackButton.getHitRect(delegateArea);
257 
258                 /*
259                  * Update the delegate area based on the difference between the current size and
260                  * the touch target size
261                  */
262                 float touchTargetSize = getResources().getDimension(
263                         R.dimen.car_touch_target_size);
264                 float primaryIconSize = getResources().getDimension(
265                         R.dimen.car_primary_icon_size);
266 
267                 int sizeDifference = (int) ((touchTargetSize - primaryIconSize) / 2);
268 
269                 delegateArea.right += sizeDifference;
270                 delegateArea.bottom += sizeDifference;
271                 delegateArea.left -= sizeDifference;
272                 delegateArea.top -= sizeDifference;
273 
274                 // Set the TouchDelegate on the parent view
275                 TouchDelegate touchDelegate = new TouchDelegate(delegateArea,
276                         mBackButton);
277 
278                 if (View.class.isInstance(mBackButton.getParent())) {
279                     ((View) mBackButton.getParent()).setTouchDelegate(touchDelegate);
280                 }
281             });
282         } else {
283             // Set the TouchDelegate to null if the back button is not visible.
284             if (View.class.isInstance(mBackButton.getParent())) {
285                 ((View) mBackButton.getParent()).setTouchDelegate(null);
286             }
287         }
288     }
289 
290     /**
291      * Gets the back button.
292      */
getBackButton()293     public View getBackButton() {
294         return mBackButton;
295     }
296 
297     @VisibleForTesting
setBackButton(View backButton)298     final void setBackButton(View backButton) {
299         mBackButton = backButton;
300     }
301 
302     /**
303      * Set the back button onClickListener to given listener. Can be null if the listener should
304      * be overridden so no callback is made.
305      */
setBackButtonListener(@ullable View.OnClickListener listener)306     public void setBackButtonListener(@Nullable View.OnClickListener listener) {
307         mBackButton.setOnClickListener(listener);
308     }
309 
310     /**
311      * Set the back button visibility to the given visibility.
312      */
setBackButtonVisible(boolean visible)313     public void setBackButtonVisible(boolean visible) {
314         setViewVisible(mBackButton, visible);
315         updateBackButtonTouchDelegate(visible);
316     }
317 
318     /**
319      * Gets the toolbar title.
320      */
getToolbarTitle()321     public TextView getToolbarTitle() {
322         return mToolbarTitle;
323     }
324 
325     @VisibleForTesting
setToolbarTitle(TextView toolbarTitle)326     final void setToolbarTitle(TextView toolbarTitle) {
327         mToolbarTitle = toolbarTitle;
328     }
329 
330     /**
331      * Sets the header title visibility to given value.
332      */
setToolbarTitleVisible(boolean visible)333     public void setToolbarTitleVisible(boolean visible) {
334         setViewVisible(mToolbarTitle, visible);
335     }
336 
337     /**
338      * Sets the header title text to the provided text.
339      */
setToolbarTitleText(String text)340     public void setToolbarTitleText(String text) {
341         mToolbarTitle.setText(text);
342     }
343 
344     /**
345      * Sets the style for the toolbar title.
346      */
setToolbarTitleStyle(@tyleRes int style)347     public void setToolbarTitleStyle(@StyleRes int style) {
348         mToolbarTitle.setTextAppearance(style);
349     }
350 
351     /**
352      * Gets the primary toolbar button.
353      */
getPrimaryToolbarButton()354     public Button getPrimaryToolbarButton() {
355         return mPrimaryToolbarButton;
356     }
357 
358     @VisibleForTesting
setPrimaryToolbarButton(Button primaryToolbarButton)359     final void setPrimaryToolbarButton(Button primaryToolbarButton) {
360         mPrimaryToolbarButton = primaryToolbarButton;
361     }
362 
363     /**
364      * Set the primary continue button visibility to the given visibility.
365      */
setPrimaryToolbarButtonVisible(boolean visible)366     public void setPrimaryToolbarButtonVisible(boolean visible) {
367         setViewVisible(mPrimaryToolbarButton, visible);
368     }
369 
370     /**
371      * Set whether the primary continue button is enabled.
372      */
setPrimaryToolbarButtonEnabled(boolean enabled)373     public void setPrimaryToolbarButtonEnabled(boolean enabled) {
374         mPrimaryToolbarButton.setEnabled(enabled);
375     }
376 
377     /**
378      * Set the primary continue button text to the given text.
379      */
setPrimaryToolbarButtonText(String text)380     public void setPrimaryToolbarButtonText(String text) {
381         mPrimaryToolbarButton.setText(text);
382     }
383 
384     /**
385      * Set the primary continue button onClickListener to the given listener. Can be null if the
386      * listener should be overridden so no callback is made. All changes to primary toolbar
387      * button's onClickListener should be made here so they can be stored through changes to the
388      * button.
389      */
setPrimaryToolbarButtonListener(@ullable View.OnClickListener listener)390     public void setPrimaryToolbarButtonListener(@Nullable View.OnClickListener listener) {
391         mPrimaryToolbarButtonOnClick = listener;
392         mPrimaryToolbarButton.setOnClickListener(listener);
393     }
394 
395     /**
396      * Getter for the flatness of the primary toolbar button.
397      */
getPrimaryToolbarButtonFlat()398     public boolean getPrimaryToolbarButtonFlat() {
399         return mPrimaryToolbarButtonFlat;
400     }
401 
402     /**
403      * Changes the button in the primary slot to a flat theme, maintaining the text, visibility,
404      * whether it is enabled, and id.
405      * <p>NOTE: that other attributes set manually on the primaryToolbarButton will be lost on calls
406      * to this method as the button will be replaced.</p>
407      */
setPrimaryToolbarButtonFlat(boolean isFlat)408     public void setPrimaryToolbarButtonFlat(boolean isFlat) {
409         // Do nothing if the state isn't changing.
410         if (isFlat == mPrimaryToolbarButtonFlat) {
411             return;
412         }
413         Button newPrimaryButton = createPrimaryToolbarButton(isFlat);
414 
415         ViewGroup parent = (ViewGroup) findViewById(R.id.button_container);
416         int buttonIndex = parent.indexOfChild(mPrimaryToolbarButton);
417         parent.removeViewAt(buttonIndex);
418         parent.addView(newPrimaryButton, buttonIndex);
419 
420         // Update state of layout
421         setPrimaryToolbarButton(newPrimaryButton);
422         mPrimaryToolbarButtonFlat = isFlat;
423     }
424 
425     @VisibleForTesting
createPrimaryToolbarButton(boolean isFlat)426     Button createPrimaryToolbarButton(boolean isFlat) {
427         int layoutId = isFlat ? R.layout.flat_button : R.layout.primary_button;
428         Button newPrimaryButton = (Button) inflate(getContext(), layoutId, null);
429         newPrimaryButton.setId(mPrimaryToolbarButton.getId());
430         newPrimaryButton.setVisibility(mPrimaryToolbarButton.getVisibility());
431         newPrimaryButton.setEnabled(mPrimaryToolbarButton.isEnabled());
432         newPrimaryButton.setText(mPrimaryToolbarButton.getText());
433         newPrimaryButton.setOnClickListener(mPrimaryToolbarButtonOnClick);
434         newPrimaryButton.setLayoutParams(mPrimaryToolbarButton.getLayoutParams());
435 
436         return newPrimaryButton;
437     }
438 
439     /**
440      * Gets the secondary toolbar button.
441      */
getSecondaryToolbarButton()442     public Button getSecondaryToolbarButton() {
443         return mSecondaryToolbarButton;
444     }
445 
446     /**
447      * Set the secondary continue button visibility to the given visibility.
448      */
setSecondaryToolbarButtonVisible(boolean visible)449     public void setSecondaryToolbarButtonVisible(boolean visible) {
450         // If not setting it visible and it hasn't been inflated yet then don't inflate.
451         if (!visible && mSecondaryToolbarButton == null) {
452             return;
453         }
454         maybeInflateSecondaryToolbarButton();
455         setViewVisible(mSecondaryToolbarButton, visible);
456     }
457 
458     /**
459      * Sets whether the secondary continue button is enabled.
460      */
setSecondaryToolbarButtonEnabled(boolean enabled)461     public void setSecondaryToolbarButtonEnabled(boolean enabled) {
462         maybeInflateSecondaryToolbarButton();
463         mSecondaryToolbarButton.setEnabled(enabled);
464     }
465 
466     /**
467      * Sets the secondary continue button text to the given text.
468      */
setSecondaryToolbarButtonText(String text)469     public void setSecondaryToolbarButtonText(String text) {
470         maybeInflateSecondaryToolbarButton();
471         mSecondaryToolbarButton.setText(text);
472     }
473 
474     /**
475      * Sets the secondary continue button onClickListener to the given listener. Can be null if the
476      * listener should be overridden so no callback is made.
477      */
setSecondaryToolbarButtonListener(@ullable View.OnClickListener listener)478     public void setSecondaryToolbarButtonListener(@Nullable View.OnClickListener listener) {
479         maybeInflateSecondaryToolbarButton();
480         mSecondaryToolbarButton.setOnClickListener(listener);
481     }
482 
483     /**
484      * Gets the progress bar.
485      */
getProgressBar()486     public ProgressBar getProgressBar() {
487         return mProgressBar;
488     }
489 
490     /**
491      * Sets the progress bar visibility to the given visibility.
492      */
setProgressBarVisible(boolean visible)493     public void setProgressBarVisible(boolean visible) {
494         setViewVisible(mProgressBar, visible);
495     }
496 
497     /**
498      * Sets the progress bar indeterminate/determinate state.
499      */
setProgressBarIndeterminate(boolean indeterminate)500     public void setProgressBarIndeterminate(boolean indeterminate) {
501         mProgressBar.setIndeterminate(indeterminate);
502     }
503 
504     /**
505      * Sets the progress bar's progress.
506      */
setProgressBarProgress(int progress)507     public void setProgressBarProgress(int progress) {
508         setProgressBarIndeterminate(false);
509         mProgressBar.setProgress(progress);
510     }
511 
512     /**
513      * Sets the locale to be used for rendering.
514      */
applyLocale(Locale locale)515     public void applyLocale(Locale locale) {
516         if (locale == null) {
517             return;
518         }
519         int direction = TextUtils.getLayoutDirectionFromLocale(locale);
520         setLayoutDirection(direction);
521 
522         mToolbarTitle.setTextLocale(locale);
523         mToolbarTitle.setLayoutDirection(direction);
524 
525         mPrimaryToolbarButton.setTextLocale(locale);
526         mPrimaryToolbarButton.setLayoutDirection(direction);
527 
528         mSecondaryToolbarButton.setTextLocale(locale);
529         mSecondaryToolbarButton.setLayoutDirection(direction);
530     }
531 
532     /**
533      * Sets the title bar view.
534      */
setTitleBar(View titleBar)535     private void setTitleBar(View titleBar) {
536         mTitleBar = titleBar;
537     }
538 
539     /**
540      * A method that inflates the SecondaryToolbarButton if it is has not already been
541      * inflated. If it has been inflated already this method will do nothing.
542      */
maybeInflateSecondaryToolbarButton()543     private void maybeInflateSecondaryToolbarButton() {
544         ViewStub secondaryToolbarButtonStub = findViewById(R.id.secondary_toolbar_button_stub);
545         // If the secondaryToolbarButtonStub is null then the stub has been inflated so there is
546         // nothing to do.
547         if (secondaryToolbarButtonStub != null) {
548             secondaryToolbarButtonStub.inflate();
549             mSecondaryToolbarButton = findViewById(R.id.secondary_toolbar_button);
550             setSecondaryToolbarButtonVisible(false);
551 
552             setBackground(
553                     mSecondaryToolbarButton,
554                     PartnerConfig.CONFIG_TOOLBAR_SECONDARY_BUTTON_BG,
555                     PartnerConfig.CONFIG_TOOLBAR_SECONDARY_BUTTON_BG_COLOR);
556 
557             setButtonPadding(mSecondaryToolbarButton);
558             setButtonTypeFace(mSecondaryToolbarButton);
559             setButtonTextSize(mSecondaryToolbarButton);
560             setButtonTextColor(
561                     mSecondaryToolbarButton,
562                     PartnerConfig.CONFIG_TOOLBAR_SECONDARY_BUTTON_TEXT_COLOR);
563 
564             // Set button spacing
565             float marginEnd = PartnerConfigHelper.get(getContext()).getDimension(
566                     getContext(),
567                     PartnerConfig.CONFIG_TOOLBAR_BUTTON_SPACING);
568 
569             MarginLayoutParams layoutParams =
570                     (MarginLayoutParams) mSecondaryToolbarButton.getLayoutParams();
571             layoutParams.setMarginEnd(Math.round(marginEnd));
572         }
573     }
574 
575     /** Sets button text color using partner overlay if exists */
576     @VisibleForTesting
setButtonTextColor(TextView button, PartnerConfig config)577     void setButtonTextColor(TextView button, PartnerConfig config) {
578         int color = mPartnerConfigHelper.getColor(getContext(), config);
579         if (color != 0) {
580             button.setTextColor(color);
581         }
582     }
583 
584     /**
585      * Sets background using partner overlay if exists. Background color and radius are only
586      * applied if background resource doesn't exist. Otherwise default background color and radius
587      * may override what's set in the background.
588      */
589     @VisibleForTesting
setBackground(View view, PartnerConfig bgConfig, PartnerConfig bgColorConfig)590     void setBackground(View view, PartnerConfig bgConfig, PartnerConfig bgColorConfig) {
591         Drawable background = mPartnerConfigHelper.getDrawable(getContext(), bgConfig);
592         if (background == null) {
593             if (view instanceof Button) {
594                 setButtonRadius((Button) view);
595             }
596             setBackgroundColor(view, bgColorConfig);
597         } else {
598             view.setBackground(background);
599         }
600     }
601 
602     /** Sets button background color using partner overlay if exists */
603     @VisibleForTesting
setBackgroundColor(View button, PartnerConfig config)604     void setBackgroundColor(View button, PartnerConfig config) {
605         int color = mPartnerConfigHelper.getColor(getContext(), config);
606         if (color != 0) {
607             Drawable background = button.getBackground();
608             if (background != null) {
609                 background.mutate().setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
610             }
611         }
612     }
613 
614     /** Sets button text size using partner overlay if exists */
615     @VisibleForTesting
setButtonTextSize(TextView button)616     void setButtonTextSize(TextView button) {
617         float dimension = mPartnerConfigHelper.getDimension(
618                 getContext(),
619                 PartnerConfig.CONFIG_TOOLBAR_BUTTON_TEXT_SIZE);
620         if (dimension != 0) {
621             button.setTextSize(TypedValue.COMPLEX_UNIT_PX, dimension);
622         }
623     }
624 
625     /** Sets button type face with partner overlay if exists */
setButtonTypeFace(TextView button)626     private void setButtonTypeFace(TextView button) {
627         String fontFamily = mPartnerConfigHelper.getString(
628                 getContext(),
629                 PartnerConfig.CONFIG_TOOLBAR_BUTTON_FONT_FAMILY);
630         if (TextUtils.isEmpty(fontFamily)) {
631             return;
632         }
633 
634         Typeface typeface = Typeface.create(fontFamily, Typeface.NORMAL);
635         if (Objects.equals(typeface, Typeface.DEFAULT)) {
636             Log.w(TAG, String.format(
637                     "Couldn't find font: %s. Setting default font.",
638                     fontFamily));
639         }
640         button.setTypeface(typeface);
641     }
642 
643     /** Sets button radius using partner overlay if exists */
setButtonRadius(Button button)644     private void setButtonRadius(Button button) {
645         float radius = mPartnerConfigHelper.getDimension(
646                 getContext(),
647                 PartnerConfig.CONFIG_TOOLBAR_BUTTON_RADIUS);
648 
649         GradientDrawable gradientDrawable = getGradientDrawable(button);
650         if (gradientDrawable != null) {
651             gradientDrawable.setCornerRadius(radius);
652         }
653     }
654 
setButtonPadding(Button button)655     private void setButtonPadding(Button button) {
656         int hPadding = Math.round(
657                 PartnerConfigHelper.get(getContext()).getDimension(
658                         getContext(),
659                         PartnerConfig.CONFIG_TOOLBAR_BUTTON_PADDING_HORIZONTAL)
660         );
661         int vPadding = Math.round(
662                 PartnerConfigHelper.get(getContext()).getDimension(
663                         getContext(),
664                         PartnerConfig.CONFIG_TOOLBAR_BUTTON_PADDING_VERTICAL)
665         );
666         button.setPadding(hPadding, vPadding, hPadding, vPadding);
667     }
668 
getGradientDrawable(Button button)669     private GradientDrawable getGradientDrawable(Button button) {
670         Drawable drawable = button.getBackground();
671         if (drawable instanceof InsetDrawable) {
672             return getGradientDrawableFromInsetDrawable((InsetDrawable) drawable);
673         }
674 
675         if (drawable instanceof RippleDrawable) {
676             drawable = ((RippleDrawable) drawable).getDrawable(0);
677             if (drawable instanceof InsetDrawable) {
678                 return getGradientDrawableFromInsetDrawable((InsetDrawable) drawable);
679             }
680             return (GradientDrawable) drawable;
681         }
682 
683         return null;
684     }
685 
getGradientDrawableFromInsetDrawable(InsetDrawable insetDrawable)686     private GradientDrawable getGradientDrawableFromInsetDrawable(InsetDrawable insetDrawable) {
687         return (GradientDrawable) insetDrawable.getDrawable();
688     }
689 }
690