Import updated Android SetupCompat Library 373282927 am: 6a89234f6c

Original change: https://googleplex-android-review.googlesource.com/c/platform/external/setupcompat/+/14513727

Change-Id: Ia98f85e7820a79cb7b2ed0b2997430f1a472d815
diff --git a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
index 9fd8cef..8f807e6 100644
--- a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
+++ b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
@@ -24,19 +24,10 @@
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.graphics.Color;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.InsetDrawable;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.RippleDrawable;
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
 import android.os.PersistableBundle;
 import android.util.AttributeSet;
-import android.util.StateSet;
-import android.util.TypedValue;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -57,7 +48,6 @@
 import com.google.android.setupcompat.PartnerCustomizationLayout;
 import com.google.android.setupcompat.R;
 import com.google.android.setupcompat.internal.FooterButtonPartnerConfig;
-import com.google.android.setupcompat.internal.Preconditions;
 import com.google.android.setupcompat.internal.TemplateLayout;
 import com.google.android.setupcompat.logging.internal.FooterBarMixinMetrics;
 import com.google.android.setupcompat.partnerconfig.PartnerConfig;
@@ -79,6 +69,7 @@
 
   @VisibleForTesting final boolean applyPartnerResources;
   @VisibleForTesting final boolean applyDynamicColor;
+  @VisibleForTesting final boolean useFullDynamicColor;
 
   private LinearLayout buttonContainer;
   private FooterButton primaryButton;
@@ -98,7 +89,6 @@
   private boolean removeFooterBarWhenEmpty = true;
   private boolean isSecondaryButtonInPrimaryStyle = false;
 
-  private static final float DEFAULT_DISABLED_ALPHA = 0.26f;
   private static final AtomicInteger nextGeneratedId = new AtomicInteger(1);
 
   @VisibleForTesting public final FooterBarMixinMetrics metrics = new FooterBarMixinMetrics();
@@ -185,6 +175,10 @@
         layout instanceof PartnerCustomizationLayout
             && ((PartnerCustomizationLayout) layout).shouldApplyDynamicColor();
 
+    useFullDynamicColor =
+        layout instanceof PartnerCustomizationLayout
+            && ((PartnerCustomizationLayout) layout).useFullDynamicColor();
+
     TypedArray a =
         context.obtainStyledAttributes(attrs, R.styleable.SucFooterBarMixin, defStyleAttr, 0);
     defaultPadding =
@@ -279,11 +273,14 @@
       return;
     }
 
-    @ColorInt
-    int color =
-        PartnerConfigHelper.get(context)
-            .getColor(context, PartnerConfig.CONFIG_FOOTER_BAR_BG_COLOR);
-    buttonContainer.setBackgroundColor(color);
+    // skip apply partner resources on footerbar background if dynamic color enabled
+    if (!useFullDynamicColor) {
+      @ColorInt
+      int color =
+          PartnerConfigHelper.get(context)
+              .getColor(context, PartnerConfig.CONFIG_FOOTER_BAR_BG_COLOR);
+      buttonContainer.setBackgroundColor(color);
+    }
 
     footerBarPaddingTop =
         (int)
@@ -478,7 +475,7 @@
   protected void onFooterButtonInflated(Button button, @ColorInt int defaultButtonBackgroundColor) {
     // Try to set default background
     if (defaultButtonBackgroundColor != 0) {
-      updateButtonBackground(button, defaultButtonBackgroundColor);
+      FooterButtonStyleUtils.updateButtonBackground(button, defaultButtonBackgroundColor);
     } else {
       // TODO: get button background color from activity theme
     }
@@ -617,208 +614,45 @@
     if (!applyDynamicColor) {
       updateButtonTextColorWithPartnerConfig(
           button, footerButtonPartnerConfig.getButtonTextColorConfig());
-      updateButtonBackgroundWithPartnerConfig(
+      FooterButtonStyleUtils.updateButtonBackgroundWithPartnerConfig(
+          context,
           button,
           footerButtonPartnerConfig.getButtonBackgroundConfig(),
           footerButtonPartnerConfig.getButtonDisableAlphaConfig(),
           footerButtonPartnerConfig.getButtonDisableBackgroundConfig());
-      updateButtonRippleColorWithPartnerConfig(button, footerButtonPartnerConfig);
+      FooterButtonStyleUtils.updateButtonRippleColorWithPartnerConfig(
+          context, button, footerButtonPartnerConfig);
     }
 
-    updateButtonTextSizeWithPartnerConfig(
-        button, footerButtonPartnerConfig.getButtonTextSizeConfig());
-    updateButtonMinHeightWithPartnerConfig(
-        button, footerButtonPartnerConfig.getButtonMinHeightConfig());
-    updateButtonTypeFaceWithPartnerConfig(
+    FooterButtonStyleUtils.updateButtonTextSizeWithPartnerConfig(
+        context, button, footerButtonPartnerConfig.getButtonTextSizeConfig());
+    FooterButtonStyleUtils.updateButtonMinHeightWithPartnerConfig(
+        context, button, footerButtonPartnerConfig.getButtonMinHeightConfig());
+    FooterButtonStyleUtils.updateButtonTypeFaceWithPartnerConfig(
+        context,
         button,
         footerButtonPartnerConfig.getButtonTextTypeFaceConfig(),
         footerButtonPartnerConfig.getButtonTextStyleConfig());
-    updateButtonRadiusWithPartnerConfig(button, footerButtonPartnerConfig.getButtonRadiusConfig());
-    updateButtonIconWithPartnerConfig(button, footerButtonPartnerConfig.getButtonIconConfig());
+    FooterButtonStyleUtils.updateButtonRadiusWithPartnerConfig(
+        context, button, footerButtonPartnerConfig.getButtonRadiusConfig());
+    FooterButtonStyleUtils.updateButtonIconWithPartnerConfig(
+        context,
+        button,
+        footerButtonPartnerConfig.getButtonIconConfig(),
+        primaryButtonId == button.getId());
   }
 
   private void updateButtonTextColorWithPartnerConfig(
       Button button, PartnerConfig buttonTextColorConfig) {
     if (button.isEnabled()) {
-      @ColorInt
-      int color = PartnerConfigHelper.get(context).getColor(context, buttonTextColorConfig);
-      if (color != Color.TRANSPARENT) {
-        button.setTextColor(ColorStateList.valueOf(color));
-      }
+      FooterButtonStyleUtils.updateButtonTextEnabledColorWithPartnerConfig(
+          context, button, buttonTextColorConfig);
     } else {
-      // disable state will use the default disable state color
-      button.setTextColor(
-          button.getId() == primaryButtonId ? primaryDefaultTextColor : secondaryDefaultTextColor);
-    }
-  }
-
-  private void updateButtonTextSizeWithPartnerConfig(
-      Button button, PartnerConfig buttonTextSizeConfig) {
-    float size = PartnerConfigHelper.get(context).getDimension(context, buttonTextSizeConfig);
-    if (size > 0) {
-      button.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
-    }
-  }
-
-  private void updateButtonMinHeightWithPartnerConfig(
-      Button button, PartnerConfig buttonMinHeightConfig) {
-    if (PartnerConfigHelper.get(context).isPartnerConfigAvailable(buttonMinHeightConfig)) {
-      float size = PartnerConfigHelper.get(context).getDimension(context, buttonMinHeightConfig);
-      if (size > 0) {
-        button.setMinHeight((int) size);
-      }
-    }
-  }
-
-  private void updateButtonTypeFaceWithPartnerConfig(
-      Button button, PartnerConfig buttonTextTypeFaceConfig, PartnerConfig buttonTextStyleConfig) {
-    String fontFamilyName =
-        PartnerConfigHelper.get(context).getString(context, buttonTextTypeFaceConfig);
-
-    int textStyleValue = Typeface.NORMAL;
-    if (PartnerConfigHelper.get(context).isPartnerConfigAvailable(buttonTextStyleConfig)) {
-      textStyleValue =
-          PartnerConfigHelper.get(context)
-              .getInteger(context, buttonTextStyleConfig, Typeface.NORMAL);
-    }
-    Typeface font = Typeface.create(fontFamilyName, textStyleValue);
-    if (font != null) {
-      button.setTypeface(font);
-    }
-  }
-
-  @TargetApi(VERSION_CODES.Q)
-  private void updateButtonBackgroundWithPartnerConfig(
-      Button button,
-      PartnerConfig buttonBackgroundConfig,
-      PartnerConfig buttonDisableAlphaConfig,
-      PartnerConfig buttonDisableBackgroundConfig) {
-    Preconditions.checkArgument(
-        Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q,
-        "Update button background only support on sdk Q or higher");
-    @ColorInt int color;
-    @ColorInt int disabledColor;
-    float disabledAlpha;
-    int[] DISABLED_STATE_SET = {-android.R.attr.state_enabled};
-    int[] ENABLED_STATE_SET = {};
-    color = PartnerConfigHelper.get(context).getColor(context, buttonBackgroundConfig);
-    disabledAlpha =
-        PartnerConfigHelper.get(context).getFraction(context, buttonDisableAlphaConfig, 0f);
-    disabledColor =
-        PartnerConfigHelper.get(context).getColor(context, buttonDisableBackgroundConfig);
-
-    if (color != Color.TRANSPARENT) {
-      if (disabledAlpha <= 0f) {
-        // if no partner resource, fallback to theme disable alpha
-        float alpha;
-        TypedArray a = context.obtainStyledAttributes(new int[] {android.R.attr.disabledAlpha});
-        alpha = a.getFloat(0, DEFAULT_DISABLED_ALPHA);
-        a.recycle();
-        disabledAlpha = alpha;
-      }
-      if (disabledColor == Color.TRANSPARENT) {
-        // if no partner resource, fallback to button background color
-        disabledColor = color;
-      }
-
-      // Set text color for ripple.
-      ColorStateList colorStateList =
-          new ColorStateList(
-              new int[][] {DISABLED_STATE_SET, ENABLED_STATE_SET},
-              new int[] {convertRgbToArgb(disabledColor, disabledAlpha), color});
-
-      // b/129482013: When a LayerDrawable is mutated, a new clone of its children drawables are
-      // created, but without copying the state from the parent drawable. So even though the
-      // parent is getting the correct drawable state from the view, the children won't get those
-      // states until a state change happens.
-      // As a workaround, we mutate the drawable and forcibly set the state to empty, and then
-      // refresh the state so the children will have the updated states.
-      button.getBackground().mutate().setState(new int[0]);
-      button.refreshDrawableState();
-      button.setBackgroundTintList(colorStateList);
-    }
-  }
-
-  private void updateButtonBackground(Button button, @ColorInt int color) {
-    button.getBackground().mutate().setColorFilter(color, Mode.SRC_ATOP);
-  }
-
-  private void updateButtonRadiusWithPartnerConfig(
-      Button button, PartnerConfig buttonRadiusConfig) {
-    if (Build.VERSION.SDK_INT >= VERSION_CODES.N) {
-      float radius = PartnerConfigHelper.get(context).getDimension(context, buttonRadiusConfig);
-      GradientDrawable gradientDrawable = getGradientDrawable(button);
-      if (gradientDrawable != null) {
-        gradientDrawable.setCornerRadius(radius);
-      }
-    }
-  }
-
-  private void updateButtonRippleColorWithPartnerConfig(
-      Button button, FooterButtonPartnerConfig footerButtonPartnerConfig) {
-    // RippleDrawable is available after sdk 21. And because on lower sdk the RippleDrawable is
-    // unavailable. Since Stencil customization provider only works on Q+, there is no need to
-    // perform any customization for versions 21.
-    if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
-      RippleDrawable rippleDrawable = getRippleDrawable(button);
-      if (rippleDrawable == null) {
-        return;
-      }
-
-      int[] pressedState = {android.R.attr.state_pressed};
-      @ColorInt int color;
-      // Get partner text color.
-      color =
-          PartnerConfigHelper.get(context)
-              .getColor(context, footerButtonPartnerConfig.getButtonTextColorConfig());
-
-      float alpha =
-          PartnerConfigHelper.get(context)
-              .getFraction(context, footerButtonPartnerConfig.getButtonRippleColorAlphaConfig());
-
-      // Set text color for ripple.
-      ColorStateList colorStateList =
-          new ColorStateList(
-              new int[][] {pressedState, StateSet.NOTHING},
-              new int[] {convertRgbToArgb(color, alpha), Color.TRANSPARENT});
-      rippleDrawable.setColor(colorStateList);
-    }
-  }
-
-  private void updateButtonIconWithPartnerConfig(Button button, PartnerConfig buttonIconConfig) {
-    if (button == null) {
-      return;
-    }
-    Drawable icon = null;
-    if (buttonIconConfig != null) {
-      icon = PartnerConfigHelper.get(context).getDrawable(context, buttonIconConfig);
-    }
-    setButtonIcon(button, icon);
-  }
-
-  private void setButtonIcon(Button button, Drawable icon) {
-    if (button == null) {
-      return;
-    }
-
-    if (icon != null) {
-      // TODO: restrict the icons to a reasonable size
-      int h = icon.getIntrinsicHeight();
-      int w = icon.getIntrinsicWidth();
-      icon.setBounds(0, 0, w, h);
-    }
-
-    Drawable iconStart = null;
-    Drawable iconEnd = null;
-    if (button.getId() == primaryButtonId) {
-      iconEnd = icon;
-    } else if (button.getId() == secondaryButtonId) {
-      iconStart = icon;
-    }
-    if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
-      button.setCompoundDrawablesRelative(iconStart, null, iconEnd, null);
-    } else {
-      button.setCompoundDrawables(iconStart, null, iconEnd, null);
+      FooterButtonStyleUtils.updateButtonTextDisableColor(
+          button,
+          /* is Primary= */ (primaryButtonId == button.getId() || isSecondaryButtonInPrimaryStyle)
+              ? primaryDefaultTextColor
+              : secondaryDefaultTextColor);
     }
   }
 
@@ -857,43 +691,6 @@
     return result;
   }
 
-  GradientDrawable getGradientDrawable(Button button) {
-    // RippleDrawable is available after sdk 21, InsetDrawable#getDrawable is available after
-    // sdk 19. So check the sdk is higher than sdk 21 and since Stencil customization provider only
-    // works on Q+, there is no need to perform any customization for versions 21.
-    if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
-      Drawable drawable = button.getBackground();
-      if (drawable instanceof InsetDrawable) {
-        LayerDrawable layerDrawable = (LayerDrawable) ((InsetDrawable) drawable).getDrawable();
-        return (GradientDrawable) layerDrawable.getDrawable(0);
-      } else if (drawable instanceof RippleDrawable) {
-        InsetDrawable insetDrawable = (InsetDrawable) ((RippleDrawable) drawable).getDrawable(0);
-        return (GradientDrawable) insetDrawable.getDrawable();
-      }
-    }
-    return null;
-  }
-
-  RippleDrawable getRippleDrawable(Button button) {
-    // RippleDrawable is available after sdk 21. And because on lower sdk the RippleDrawable is
-    // unavailable. Since Stencil customization provider only works on Q+, there is no need to
-    // perform any customization for versions 21.
-    if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
-      Drawable drawable = button.getBackground();
-      if (drawable instanceof InsetDrawable) {
-        return (RippleDrawable) ((InsetDrawable) drawable).getDrawable();
-      } else if (drawable instanceof RippleDrawable) {
-        return (RippleDrawable) drawable;
-      }
-    }
-    return null;
-  }
-
-  @ColorInt
-  private static int convertRgbToArgb(@ColorInt int color, float alpha) {
-    return Color.argb((int) (alpha * 255), Color.red(color), Color.green(color), Color.blue(color));
-  }
-
   protected View inflateFooter(@LayoutRes int footer) {
     if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
       LayoutInflater inflater =
diff --git a/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java b/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java
new file mode 100644
index 0000000..98a1426
--- /dev/null
+++ b/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.setupcompat.template;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.InsetDrawable;
+import android.graphics.drawable.LayerDrawable;
+import android.graphics.drawable.RippleDrawable;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
+import android.util.StateSet;
+import android.util.TypedValue;
+import android.widget.Button;
+import androidx.annotation.ColorInt;
+import androidx.annotation.VisibleForTesting;
+import com.google.android.setupcompat.internal.FooterButtonPartnerConfig;
+import com.google.android.setupcompat.internal.Preconditions;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
+
+/** Utils for updating the button style. */
+public class FooterButtonStyleUtils {
+  private static final float DEFAULT_DISABLED_ALPHA = 0.26f;
+
+  static void updateButtonTextEnabledColorWithPartnerConfig(
+      Context context, Button button, PartnerConfig buttonEnableTextColorConfig) {
+    @ColorInt
+    int color = PartnerConfigHelper.get(context).getColor(context, buttonEnableTextColorConfig);
+    if (color != Color.TRANSPARENT) {
+      button.setTextColor(ColorStateList.valueOf(color));
+    }
+  }
+
+  static void updateButtonTextDisableColor(Button button, ColorStateList defaultTextColor) {
+    // TODO : add disable footer button text color partner config
+
+    // disable state will use the default disable state color
+    button.setTextColor(defaultTextColor);
+  }
+
+  @TargetApi(VERSION_CODES.Q)
+  static void updateButtonBackgroundWithPartnerConfig(
+      Context context,
+      Button button,
+      PartnerConfig buttonBackgroundConfig,
+      PartnerConfig buttonDisableAlphaConfig,
+      PartnerConfig buttonDisableBackgroundConfig) {
+    Preconditions.checkArgument(
+        Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q,
+        "Update button background only support on sdk Q or higher");
+    @ColorInt int disabledColor;
+    float disabledAlpha;
+    int[] DISABLED_STATE_SET = {-android.R.attr.state_enabled};
+    int[] ENABLED_STATE_SET = {};
+    @ColorInt
+    int color = PartnerConfigHelper.get(context).getColor(context, buttonBackgroundConfig);
+    disabledAlpha =
+        PartnerConfigHelper.get(context).getFraction(context, buttonDisableAlphaConfig, 0f);
+    disabledColor =
+        PartnerConfigHelper.get(context).getColor(context, buttonDisableBackgroundConfig);
+
+    if (color != Color.TRANSPARENT) {
+      if (disabledAlpha <= 0f) {
+        // if no partner resource, fallback to theme disable alpha
+        TypedArray a = context.obtainStyledAttributes(new int[] {android.R.attr.disabledAlpha});
+        float alpha = a.getFloat(0, DEFAULT_DISABLED_ALPHA);
+        a.recycle();
+        disabledAlpha = alpha;
+      }
+      if (disabledColor == Color.TRANSPARENT) {
+        // if no partner resource, fallback to button background color
+        disabledColor = color;
+      }
+
+      // Set text color for ripple.
+      ColorStateList colorStateList =
+          new ColorStateList(
+              new int[][] {DISABLED_STATE_SET, ENABLED_STATE_SET},
+              new int[] {convertRgbToArgb(disabledColor, disabledAlpha), color});
+
+      // b/129482013: When a LayerDrawable is mutated, a new clone of its children drawables are
+      // created, but without copying the state from the parent drawable. So even though the
+      // parent is getting the correct drawable state from the view, the children won't get those
+      // states until a state change happens.
+      // As a workaround, we mutate the drawable and forcibly set the state to empty, and then
+      // refresh the state so the children will have the updated states.
+      button.getBackground().mutate().setState(new int[0]);
+      button.refreshDrawableState();
+      button.setBackgroundTintList(colorStateList);
+    }
+  }
+
+  static void updateButtonRippleColorWithPartnerConfig(
+      Context context, Button button, FooterButtonPartnerConfig footerButtonPartnerConfig) {
+    // RippleDrawable is available after sdk 21. And because on lower sdk the RippleDrawable is
+    // unavailable. Since Stencil customization provider only works on Q+, there is no need to
+    // perform any customization for versions 21.
+    if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+      RippleDrawable rippleDrawable = getRippleDrawable(button);
+      if (rippleDrawable == null) {
+        return;
+      }
+
+      int[] pressedState = {android.R.attr.state_pressed};
+      // Get partner text color.
+      @ColorInt
+      int color =
+          PartnerConfigHelper.get(context)
+              .getColor(context, footerButtonPartnerConfig.getButtonTextColorConfig());
+
+      float alpha =
+          PartnerConfigHelper.get(context)
+              .getFraction(context, footerButtonPartnerConfig.getButtonRippleColorAlphaConfig());
+
+      // Set text color for ripple.
+      ColorStateList colorStateList =
+          new ColorStateList(
+              new int[][] {pressedState, StateSet.NOTHING},
+              new int[] {convertRgbToArgb(color, alpha), Color.TRANSPARENT});
+      rippleDrawable.setColor(colorStateList);
+    }
+  }
+
+  static void updateButtonTextSizeWithPartnerConfig(
+      Context context, Button button, PartnerConfig buttonTextSizeConfig) {
+    float size = PartnerConfigHelper.get(context).getDimension(context, buttonTextSizeConfig);
+    if (size > 0) {
+      button.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
+    }
+  }
+
+  static void updateButtonMinHeightWithPartnerConfig(
+      Context context, Button button, PartnerConfig buttonMinHeightConfig) {
+    if (PartnerConfigHelper.get(context).isPartnerConfigAvailable(buttonMinHeightConfig)) {
+      float size = PartnerConfigHelper.get(context).getDimension(context, buttonMinHeightConfig);
+      if (size > 0) {
+        button.setMinHeight((int) size);
+      }
+    }
+  }
+
+  static void updateButtonTypeFaceWithPartnerConfig(
+      Context context,
+      Button button,
+      PartnerConfig buttonTextTypeFaceConfig,
+      PartnerConfig buttonTextStyleConfig) {
+    String fontFamilyName =
+        PartnerConfigHelper.get(context).getString(context, buttonTextTypeFaceConfig);
+
+    int textStyleValue = Typeface.NORMAL;
+    if (PartnerConfigHelper.get(context).isPartnerConfigAvailable(buttonTextStyleConfig)) {
+      textStyleValue =
+          PartnerConfigHelper.get(context)
+              .getInteger(context, buttonTextStyleConfig, Typeface.NORMAL);
+    }
+    Typeface font = Typeface.create(fontFamilyName, textStyleValue);
+    if (font != null) {
+      button.setTypeface(font);
+    }
+  }
+
+  static void updateButtonRadiusWithPartnerConfig(
+      Context context, Button button, PartnerConfig buttonRadiusConfig) {
+    if (Build.VERSION.SDK_INT >= VERSION_CODES.N) {
+      float radius = PartnerConfigHelper.get(context).getDimension(context, buttonRadiusConfig);
+      GradientDrawable gradientDrawable = getGradientDrawable(button);
+      if (gradientDrawable != null) {
+        gradientDrawable.setCornerRadius(radius);
+      }
+    }
+  }
+
+  static void updateButtonIconWithPartnerConfig(
+      Context context, Button button, PartnerConfig buttonIconConfig, boolean isPrimary) {
+    if (button == null) {
+      return;
+    }
+    Drawable icon = null;
+    if (buttonIconConfig != null) {
+      icon = PartnerConfigHelper.get(context).getDrawable(context, buttonIconConfig);
+    }
+    setButtonIcon(button, icon, isPrimary);
+  }
+
+  private static void setButtonIcon(Button button, Drawable icon, boolean isPrimary) {
+    if (button == null) {
+      return;
+    }
+
+    if (icon != null) {
+      // TODO: restrict the icons to a reasonable size
+      int h = icon.getIntrinsicHeight();
+      int w = icon.getIntrinsicWidth();
+      icon.setBounds(0, 0, w, h);
+    }
+
+    Drawable iconStart = null;
+    Drawable iconEnd = null;
+    if (isPrimary) {
+      iconEnd = icon;
+    } else {
+      iconStart = icon;
+    }
+    if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
+      button.setCompoundDrawablesRelative(iconStart, null, iconEnd, null);
+    } else {
+      button.setCompoundDrawables(iconStart, null, iconEnd, null);
+    }
+  }
+
+  static void updateButtonBackground(Button button, @ColorInt int color) {
+    button.getBackground().mutate().setColorFilter(color, Mode.SRC_ATOP);
+  }
+
+  @VisibleForTesting
+  public static GradientDrawable getGradientDrawable(Button button) {
+    // RippleDrawable is available after sdk 21, InsetDrawable#getDrawable is available after
+    // sdk 19. So check the sdk is higher than sdk 21 and since Stencil customization provider only
+    // works on Q+, there is no need to perform any customization for versions 21.
+    if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+      Drawable drawable = button.getBackground();
+      if (drawable instanceof InsetDrawable) {
+        LayerDrawable layerDrawable = (LayerDrawable) ((InsetDrawable) drawable).getDrawable();
+        return (GradientDrawable) layerDrawable.getDrawable(0);
+      } else if (drawable instanceof RippleDrawable) {
+        InsetDrawable insetDrawable = (InsetDrawable) ((RippleDrawable) drawable).getDrawable(0);
+        return (GradientDrawable) insetDrawable.getDrawable();
+      }
+    }
+    return null;
+  }
+
+  static RippleDrawable getRippleDrawable(Button button) {
+    // RippleDrawable is available after sdk 21. And because on lower sdk the RippleDrawable is
+    // unavailable. Since Stencil customization provider only works on Q+, there is no need to
+    // perform any customization for versions 21.
+    if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+      Drawable drawable = button.getBackground();
+      if (drawable instanceof InsetDrawable) {
+        return (RippleDrawable) ((InsetDrawable) drawable).getDrawable();
+      } else if (drawable instanceof RippleDrawable) {
+        return (RippleDrawable) drawable;
+      }
+    }
+    return null;
+  }
+
+  @ColorInt
+  private static int convertRgbToArgb(@ColorInt int color, float alpha) {
+    return Color.argb((int) (alpha * 255), Color.red(color), Color.green(color), Color.blue(color));
+  }
+
+  private FooterButtonStyleUtils() {}
+}
diff --git a/main/java/com/google/android/setupcompat/template/StatusBarMixin.java b/main/java/com/google/android/setupcompat/template/StatusBarMixin.java
index a818793..c0f1c45 100644
--- a/main/java/com/google/android/setupcompat/template/StatusBarMixin.java
+++ b/main/java/com/google/android/setupcompat/template/StatusBarMixin.java
@@ -112,10 +112,14 @@
    */
   public void setStatusBarBackground(Drawable background) {
     if (partnerCustomizationLayout.shouldApplyPartnerResource()) {
+      // If full dynamic color enabled which means this activity is running outside of setup
+      // flow, the colors should refer to R.style.SudFullDynamicColorThemeGlifV3.
+      if (!partnerCustomizationLayout.useFullDynamicColor()) {
       Context context = partnerCustomizationLayout.getContext();
       background =
           PartnerConfigHelper.get(context)
               .getDrawable(context, PartnerConfig.CONFIG_STATUS_BAR_BACKGROUND);
+      }
     }
 
     if (statusBarLayout == null) {
diff --git a/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java b/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java
index 8050406..32c708c 100644
--- a/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java
+++ b/main/java/com/google/android/setupcompat/template/SystemNavBarMixin.java
@@ -47,6 +47,7 @@
   private final TemplateLayout templateLayout;
   @Nullable private final Window windowOfActivity;
   @VisibleForTesting final boolean applyPartnerResources;
+  @VisibleForTesting final boolean useFullDynamicColor;
   private int sucSystemNavBarBackgroundColor = 0;
 
   /**
@@ -61,6 +62,10 @@
     this.applyPartnerResources =
         layout instanceof PartnerCustomizationLayout
             && ((PartnerCustomizationLayout) layout).shouldApplyPartnerResource();
+
+    this.useFullDynamicColor =
+        layout instanceof PartnerCustomizationLayout
+            && ((PartnerCustomizationLayout) layout).useFullDynamicColor();
   }
 
   /**
@@ -109,10 +114,14 @@
   public void setSystemNavBarBackground(int color) {
     if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP && windowOfActivity != null) {
       if (applyPartnerResources) {
-        Context context = templateLayout.getContext();
-        color =
-            PartnerConfigHelper.get(context)
-                .getColor(context, PartnerConfig.CONFIG_NAVIGATION_BAR_BG_COLOR);
+        // If full dynamic color enabled which means this activity is running outside of setup
+        // flow, the colors should refer to R.style.SudFullDynamicColorThemeGlifV3.
+        if (!useFullDynamicColor) {
+          Context context = templateLayout.getContext();
+          color =
+              PartnerConfigHelper.get(context)
+                  .getColor(context, PartnerConfig.CONFIG_NAVIGATION_BAR_BG_COLOR);
+        }
       }
       windowOfActivity.setNavigationBarColor(color);
     }