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