Import updated Android SetupCompat Library 369420590 am: 56ea8be381

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

Change-Id: I224940a995aad2a3040cb3b9db162f38a2d4796d
diff --git a/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java b/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
index 3f3eb9d..0f0a58e 100644
--- a/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
+++ b/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
@@ -42,6 +42,7 @@
 import com.google.android.setupcompat.template.FooterButton;
 import com.google.android.setupcompat.template.StatusBarMixin;
 import com.google.android.setupcompat.template.SystemNavBarMixin;
+import com.google.android.setupcompat.util.BuildCompatUtils;
 import com.google.android.setupcompat.util.WizardManagerHelper;
 
 /** A templatization layout with consistent style used in Setup Wizard or app itself. */
@@ -56,6 +57,18 @@
    */
   private boolean usePartnerResourceAttr;
 
+  /**
+   * Attribute indicating whether using full dynamic colors or not. This corresponds to the {@code
+   * app:sucFullDynamicColor} XML attribute.
+   */
+  private boolean useFullDynamicColorAttr;
+
+  /**
+   * Attribute indicating whether usage of dynamic is allowed. This corresponds to the existence of
+   * {@code app:sucFullDynamicColor} XML attribute.
+   */
+  private boolean useDynamicColor;
+
   private Activity activity;
 
   public PartnerCustomizationLayout(Context context) {
@@ -157,6 +170,10 @@
         isSetupFlow
             || a.getBoolean(R.styleable.SucPartnerCustomizationLayout_sucUsePartnerResource, true);
 
+    useDynamicColor = a.hasValue(R.styleable.SucPartnerCustomizationLayout_sucFullDynamicColor);
+    useFullDynamicColorAttr =
+        a.getBoolean(R.styleable.SucPartnerCustomizationLayout_sucFullDynamicColor, false);
+
     a.recycle();
 
     if (Log.isLoggable(TAG, Log.DEBUG)) {
@@ -169,7 +186,11 @@
               + " enablePartnerResourceLoading="
               + enablePartnerResourceLoading()
               + " usePartnerResourceAttr="
-              + usePartnerResourceAttr);
+              + usePartnerResourceAttr
+              + " useDynamicColor="
+              + useDynamicColor
+              + " useFullDynamicColorAttr="
+              + useFullDynamicColorAttr);
     }
   }
 
@@ -253,4 +274,29 @@
     }
     return true;
   }
+
+  /**
+   * Returns {@code true} if the current layout/activity applies dynamic color. Otherwise, returns
+   * {@code false}.
+   */
+  public boolean shouldApplyDynamicColor() {
+    if (!enablePartnerResourceLoading()) {
+      return false;
+    }
+    if (!useDynamicColor) {
+      return false;
+    }
+    if (!BuildCompatUtils.isAtLeastS()) {
+      return false;
+    }
+    if (!PartnerConfigHelper.get(getContext()).isAvailable()) {
+      return false;
+    }
+    return true;
+  }
+
+  /** Returns boolean value of the {@code app:sucFullDynamicColor}. */
+  public boolean useFullDynamicColor() {
+    return shouldApplyDynamicColor() && useFullDynamicColorAttr;
+  }
 }
diff --git a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
index 0952f0b..8584527 100644
--- a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
+++ b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
@@ -63,6 +63,7 @@
 import com.google.android.setupcompat.partnerconfig.PartnerConfig;
 import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
 import com.google.android.setupcompat.template.FooterButton.ButtonType;
+import java.util.Locale;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -77,6 +78,8 @@
   @Nullable private final ViewStub footerStub;
 
   @VisibleForTesting final boolean applyPartnerResources;
+  @VisibleForTesting final boolean applyDynamicColor;
+  final boolean useFullDynamicColor;
 
   private LinearLayout buttonContainer;
   private FooterButton primaryButton;
@@ -141,6 +144,25 @@
           }
         }
       }
+
+      @Override
+      @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
+      public void onLocaleChanged(Locale locale) {
+        if (buttonContainer != null) {
+          Button button = buttonContainer.findViewById(id);
+          if (button != null && locale != null) {
+            button.setTextLocale(locale);
+          }
+        }
+      }
+
+      @Override
+      @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
+      public void onDirectionChanged(int direction) {
+        if (buttonContainer != null && direction != -1) {
+          buttonContainer.setLayoutDirection(direction);
+        }
+      }
     };
   }
 
@@ -159,6 +181,14 @@
         layout instanceof PartnerCustomizationLayout
             && ((PartnerCustomizationLayout) layout).shouldApplyPartnerResource();
 
+    applyDynamicColor =
+        layout instanceof PartnerCustomizationLayout
+            && ((PartnerCustomizationLayout) layout).shouldApplyDynamicColor();
+
+    useFullDynamicColor =
+        layout instanceof PartnerCustomizationLayout
+            && ((PartnerCustomizationLayout) layout).useFullDynamicColor();
+
     TypedArray a =
         context.obtainStyledAttributes(attrs, R.styleable.SucFooterBarMixin, defStyleAttr, 0);
     defaultPadding =
@@ -259,6 +289,8 @@
             .getColor(context, PartnerConfig.CONFIG_FOOTER_BAR_BG_COLOR);
     buttonContainer.setBackgroundColor(color);
 
+    // TODO: Apply dynamic color on footer bar background if useFullDynamicColor is true
+
     footerBarPaddingTop =
         (int)
             PartnerConfigHelper.get(context)
@@ -313,7 +345,13 @@
                     /* defaultPartnerTheme= */ R.style.SucPartnerCustomizationButton_Primary,
                     /* buttonBackgroundColorConfig= */ PartnerConfig
                         .CONFIG_FOOTER_PRIMARY_BUTTON_BG_COLOR))
-            .setButtonBackgroundConfig(PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_BG_COLOR)
+            .setButtonBackgroundConfig(
+                (applyDynamicColor
+                        && PartnerConfigHelper.get(context)
+                            .isPartnerConfigAvailable(
+                                PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_DYNAMIC_COLOR))
+                    ? PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_BG_DYNAMIC_COLOR
+                    : PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_BG_COLOR)
             .setButtonDisableAlphaConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_DISABLED_ALPHA)
             .setButtonDisableBackgroundConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_DISABLED_BG_COLOR)
             .setButtonIconConfig(getDrawablePartnerConfig(footerButton.getButtonType()))
@@ -377,7 +415,13 @@
             .setButtonIconConfig(getDrawablePartnerConfig(footerButton.getButtonType()))
             .setButtonRadiusConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_RADIUS)
             .setButtonRippleColorAlphaConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_RIPPLE_COLOR_ALPHA)
-            .setTextColorConfig(PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_COLOR)
+            .setTextColorConfig(
+                (applyDynamicColor
+                        && PartnerConfigHelper.get(context)
+                            .isPartnerConfigAvailable(
+                                PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_DYNAMIC_COLOR))
+                    ? PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_DYNAMIC_COLOR
+                    : PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_COLOR)
             .setTextSizeConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_TEXT_SIZE)
             .setButtonMinHeight(PartnerConfig.CONFIG_FOOTER_BUTTON_MIN_HEIGHT)
             .setTextTypeFaceConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_FONT_FAMILY)
@@ -557,6 +601,20 @@
   private void onFooterButtonApplyPartnerResource(
       Button button, FooterButtonPartnerConfig footerButtonPartnerConfig) {
     if (!applyPartnerResources) {
+      // dynamic color should still work if not applying partner resource
+      if (applyDynamicColor) {
+        // override config related to dynamic color
+        updateButtonTextColorWithPartnerConfig(
+            button, footerButtonPartnerConfig.getButtonTextColorConfig());
+        updateButtonBackgroundWithPartnerConfig(
+            button,
+            footerButtonPartnerConfig.getButtonBackgroundConfig(),
+            footerButtonPartnerConfig.getButtonDisableAlphaConfig(),
+            footerButtonPartnerConfig.getButtonDisableBackgroundConfig());
+        updateButtonRippleColorWithPartnerConfig(button, footerButtonPartnerConfig);
+
+        // TODO: Apply full dynamic color if useFullDynamicColor is true
+      }
       return;
     }
     updateButtonTextColorWithPartnerConfig(
diff --git a/main/java/com/google/android/setupcompat/template/FooterButton.java b/main/java/com/google/android/setupcompat/template/FooterButton.java
index b23b1bb..90c13ec 100644
--- a/main/java/com/google/android/setupcompat/template/FooterButton.java
+++ b/main/java/com/google/android/setupcompat/template/FooterButton.java
@@ -34,6 +34,7 @@
 import com.google.android.setupcompat.R;
 import com.google.android.setupcompat.logging.CustomEvent;
 import java.lang.annotation.Retention;
+import java.util.Locale;
 
 /**
  * Definition of a footer button. Clients can use this class to customize attributes like text,
@@ -53,6 +54,8 @@
   private OnClickListener onClickListenerWhenDisabled;
   private OnButtonEventListener buttonListener;
   private int clickCount = 0;
+  private Locale locale;
+  private int direction;
 
   public FooterButton(Context context, AttributeSet attrs) {
     TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SucFooterButton);
@@ -78,11 +81,15 @@
       CharSequence text,
       @Nullable OnClickListener listener,
       @ButtonType int buttonType,
-      @StyleRes int theme) {
+      @StyleRes int theme,
+      Locale locale,
+      int direction) {
     this.text = text;
     onClickListener = listener;
     this.buttonType = buttonType;
     this.theme = theme;
+    this.locale = locale;
+    this.direction = direction;
   }
 
   /** Returns the text that this footer button is displaying. */
@@ -142,6 +149,16 @@
     return enabled;
   }
 
+  /** Returns the layout direction for this footer button. */
+  public int getLayoutDirection() {
+    return direction;
+  }
+
+  /** Returns the text locale for this footer button. */
+  public Locale getTextLocale() {
+    return locale;
+  }
+
   /**
    * Sets the visibility state of this footer button.
    *
@@ -172,6 +189,22 @@
     }
   }
 
+  /** Sets the text locale to be displayed on footer button. */
+  public void setTextLocale(Locale locale) {
+    this.locale = locale;
+    if (buttonListener != null) {
+      buttonListener.onLocaleChanged(locale);
+    }
+  }
+
+  /** Sets the layout direction to be displayed on footer button. */
+  public void setLayoutDirection(int direction) {
+    this.direction = direction;
+    if (buttonListener != null) {
+      buttonListener.onDirectionChanged(direction);
+    }
+  }
+
   /**
    * Registers a callback to be invoked when footer button API has set.
    *
@@ -201,6 +234,10 @@
     void onVisibilityChanged(int visibility);
 
     void onTextChanged(CharSequence text);
+
+    void onLocaleChanged(Locale locale);
+
+    void onDirectionChanged(int direction);
   }
 
   /** Maximum valid value of ButtonType */
@@ -308,12 +345,16 @@
    *         .setListener(primaryButton)
    *         .setButtonType(ButtonType.NEXT)
    *         .setTheme(R.style.SuwGlifButton_Primary)
+   *         .setTextLocale(Locale.CANADA)
+   *         .setLayoutDirection(View.LAYOUT_DIRECTION_LTR)
    *         .build();
    * </pre>
    */
   public static class Builder {
     private final Context context;
     private String text = "";
+    private Locale locale = null;
+    private int direction = -1;
     private OnClickListener onClickListener = null;
     @ButtonType private int buttonType = ButtonType.OTHER;
     private int theme = 0;
@@ -334,6 +375,18 @@
       return this;
     }
 
+    /** Sets the {@code locale} of FooterButton. */
+    public Builder setTextLocale(Locale locale) {
+      this.locale = locale;
+      return this;
+    }
+
+    /** Sets the {@code direction} of FooterButton. */
+    public Builder setLayoutDirection(int direction) {
+      this.direction = direction;
+      return this;
+    }
+
     /** Sets the {@code listener} of FooterButton. */
     public Builder setListener(@Nullable OnClickListener listener) {
       onClickListener = listener;
@@ -353,7 +406,7 @@
     }
 
     public FooterButton build() {
-      return new FooterButton(text, onClickListener, buttonType, theme);
+      return new FooterButton(text, onClickListener, buttonType, theme, locale, direction);
     }
   }
 }
diff --git a/main/java/com/google/android/setupcompat/util/BuildCompatUtils.java b/main/java/com/google/android/setupcompat/util/BuildCompatUtils.java
new file mode 100644
index 0000000..ea54745
--- /dev/null
+++ b/main/java/com/google/android/setupcompat/util/BuildCompatUtils.java
@@ -0,0 +1,66 @@
+/*
+ * 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.util;
+
+import android.os.Build;
+
+/**
+ * An util class to check whether the current OS version is higher or equal to sdk version of
+ * device.
+ */
+public final class BuildCompatUtils {
+
+  /**
+   * Implementation of BuildCompat.isAtLeast*() suitable for use in Setup
+   *
+   * <p>BuildCompat.isAtLeast*() can be changed by Android Release team, and once that is changed it
+   * may take weeks for that to propagate to stable/prerelease/experimental SDKs in Google3. Also it
+   * can be different in all these channels. This can cause random issues, especially with sidecars
+   * (i.e., the code running on R may not know that it runs on R).
+   *
+   * <p>This still should try using BuildCompat.isAtLeastR() as source of truth, but also checking
+   * for VERSION_SDK_INT and VERSION.CODENAME in case when BuildCompat implementation returned
+   * false. Note that both checks should be >= and not = to make sure that when Android version
+   * increases (i.e., from R to S), this does not stop working.
+   *
+   * <p>Supported configurations:
+   *
+   * <ul>
+   *   <li>For current Android release: while new API is not finalized yet (CODENAME = "S", SDK_INT
+   *       = 30|31)
+   *   <li>For current Android release: when new API is finalized (CODENAME = "REL", SDK_INT = 31)
+   *   <li>For next Android release (CODENAME = "T", SDK_INT = 30+)
+   * </ul>
+   *
+   * <p>Note that Build.VERSION_CODES.S cannot be used here until final SDK is available in all
+   * Google3 channels, because it is equal to Build.VERSION_CODES.CUR_DEVELOPMENT before API
+   * finalization.
+   *
+   * @return Whether the current OS version is higher or equal to S.
+   */
+  public static boolean isAtLeastS() {
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+      return false;
+    }
+    return (Build.VERSION.CODENAME.equals("REL") && Build.VERSION.SDK_INT >= 31)
+        || (Build.VERSION.CODENAME.length() == 1
+            && Build.VERSION.CODENAME.charAt(0) >= 'S'
+            && Build.VERSION.CODENAME.charAt(0) <= 'Z');
+  }
+
+  private BuildCompatUtils() {}
+}
diff --git a/main/res/values/attrs.xml b/main/res/values/attrs.xml
index 585a28a..07f87ed 100644
--- a/main/res/values/attrs.xml
+++ b/main/res/values/attrs.xml
@@ -32,6 +32,7 @@
              This attribute will be ignored and use partner resource when inside setup wizard flow.
              The default value is true. -->
         <attr name="sucUsePartnerResource" format="boolean" />
+        <attr name="sucFullDynamicColor" format="boolean" />
     </declare-styleable>
 
     <!-- Status bar attributes; only takes effect on M or above -->
diff --git a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java
index 5a71afe..cacbf80 100644
--- a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java
+++ b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java
@@ -120,6 +120,10 @@
   CONFIG_FOOTER_PRIMARY_BUTTON_BG_COLOR(
       PartnerConfigKey.KEY_FOOTER_PRIMARY_BUTTON_BG_COLOR, ResourceType.COLOR),
 
+  // Dynamic background color of the primary footer button
+  CONFIG_FOOTER_PRIMARY_BUTTON_BG_DYNAMIC_COLOR(
+      PartnerConfigKey.KEY_FOOTER_PRIMARY_BUTTON_BG_DYNAMIC_COLOR, ResourceType.COLOR),
+
   // Text color of the primary footer button
   CONFIG_FOOTER_PRIMARY_BUTTON_TEXT_COLOR(
       PartnerConfigKey.KEY_FOOTER_PRIMARY_BUTTON_TEXT_COLOR, ResourceType.COLOR),
@@ -132,6 +136,10 @@
   CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_COLOR(
       PartnerConfigKey.KEY_FOOTER_SECONDARY_BUTTON_TEXT_COLOR, ResourceType.COLOR),
 
+  // Dynamic text color of the secondary footer button
+  CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_DYNAMIC_COLOR(
+      PartnerConfigKey.KEY_FOOTER_SECONDARY_BUTTON_TEXT_DYNAMIC_COLOR, ResourceType.COLOR),
+
   // Background color of layout
   CONFIG_LAYOUT_BACKGROUND_COLOR(PartnerConfigKey.KEY_LAYOUT_BACKGROUND_COLOR, ResourceType.COLOR),
 
@@ -233,6 +241,34 @@
   // The padding top of the content
   CONFIG_CONTENT_PADDING_TOP(PartnerConfigKey.KEY_CONTENT_PADDING_TOP, ResourceType.DIMENSION),
 
+  // The text size of the content info.
+  CONFIG_CONTENT_INFO_TEXT_SIZE(
+      PartnerConfigKey.KEY_CONTENT_INFO_TEXT_SIZE, ResourceType.DIMENSION),
+
+  // The font family of the content info.
+  CONFIG_CONTENT_INFO_FONT_FAMILY(
+      PartnerConfigKey.KEY_CONTENT_INFO_FONT_FAMILY, ResourceType.STRING),
+
+  // The text line spacing extra of the content info.
+  CONFIG_CONTENT_INFO_LINE_SPACING_EXTRA(
+      PartnerConfigKey.KEY_CONTENT_INFO_LINE_SPACING_EXTRA, ResourceType.DIMENSION),
+
+  // The icon size of the content info.
+  CONFIG_CONTENT_INFO_ICON_SIZE(
+      PartnerConfigKey.KEY_CONTENT_INFO_ICON_SIZE, ResourceType.DIMENSION),
+
+  // The icon margin end of the content info.
+  CONFIG_CONTENT_INFO_ICON_MARGIN_END(
+      PartnerConfigKey.KEY_CONTENT_INFO_ICON_MARGIN_END, ResourceType.DIMENSION),
+
+  // The padding top of the content info.
+  CONFIG_CONTENT_INFO_PADDING_TOP(
+      PartnerConfigKey.KEY_CONTENT_INFO_PADDING_TOP, ResourceType.DIMENSION),
+
+  // The padding bottom of the content info.
+  CONFIG_CONTENT_INFO_PADDING_BOTTOM(
+      PartnerConfigKey.KEY_CONTENT_INFO_PADDING_BOTTOM, ResourceType.DIMENSION),
+
   // The title text size of list items.
   CONFIG_ITEMS_TITLE_TEXT_SIZE(PartnerConfigKey.KEY_ITEMS_TITLE_TEXT_SIZE, ResourceType.DIMENSION),
 
@@ -283,6 +319,11 @@
   CONFIG_PROGRESS_ILLUSTRATION_UPDATE(
       PartnerConfigKey.KEY_PROGRESS_ILLUSTRATION_UPDATE, ResourceType.ILLUSTRATION),
 
+  // The animation of loading screen used in those activities which is finishing setup.
+  // For example:com.google.android.setupwizard.FINAL_HOLD
+  CONFIG_PROGRESS_ILLUSTRATION_FINAL_HOLD(
+      PartnerConfigKey.KEY_PROGRESS_ILLUSTRATION_FINAL_HOLD, ResourceType.ILLUSTRATION),
+
   // The animation of loading screen to define how long showing on the pages.
   CONFIG_PROGRESS_ILLUSTRATION_DISPLAY_MINIMUM_MS(
       PartnerConfigKey.KEY_PROGRESS_ILLUSTRATION_DISPLAY_MINIMUM_MS, ResourceType.INTEGER),
@@ -307,6 +348,11 @@
   CONFIG_LOADING_LOTTIE_UPDATE(
       PartnerConfigKey.KEY_LOADING_LOTTIE_UPDATE, ResourceType.ILLUSTRATION),
 
+  // The animation for S+ devices used in those screens which is updating devices.
+  // For example:com.google.android.setupwizard.COMPAT_EARLY_UPDATE
+  CONFIG_LOADING_LOTTIE_FINAL_HOLD(
+      PartnerConfigKey.KEY_LOADING_LOTTIE_FINAL_HOLD, ResourceType.ILLUSTRATION),
+
   // The transition type to decide the transition between activities or fragments.
   CONFIG_TRANSITION_TYPE(PartnerConfigKey.KEY_TRANSITION_TYPE, ResourceType.INTEGER),
 
@@ -326,6 +372,10 @@
   CONFIG_LOTTIE_LIGHT_THEME_CUSTOMIZATION_UPDATE(
       PartnerConfigKey.KEY_LOADING_LIGHT_THEME_CUSTOMIZATION_UPDATE, ResourceType.STRING_ARRAY),
 
+  // The list of keypath and color map, applied to update animation when light theme.
+  CONFIG_LOTTIE_LIGHT_THEME_CUSTOMIZATION_FINAL_HOLD(
+      PartnerConfigKey.KEY_LOADING_LIGHT_THEME_CUSTOMIZATION_FINAL_HOLD, ResourceType.STRING_ARRAY),
+
   // The list of keypath and color map, applied to default animation when dark theme.
   CONFIG_LOTTIE_DARK_THEME_CUSTOMIZATION_DEFAULT(
       PartnerConfigKey.KEY_LOADING_DARK_THEME_CUSTOMIZATION_DEFAULT, ResourceType.STRING_ARRAY),
@@ -340,7 +390,11 @@
 
   // The list of keypath and color map, applied to update animation when dark theme.
   CONFIG_LOTTIE_DARK_THEME_CUSTOMIZATION_UPDATE(
-      PartnerConfigKey.KEY_LOADING_DARK_THEME_CUSTOMIZATION_UPDATE, ResourceType.STRING_ARRAY);
+      PartnerConfigKey.KEY_LOADING_DARK_THEME_CUSTOMIZATION_UPDATE, ResourceType.STRING_ARRAY),
+
+  // The list of keypath and color map, applied to final hold animation when dark theme.
+  CONFIG_LOTTIE_DARK_THEME_CUSTOMIZATION_FINAL_HOLD(
+      PartnerConfigKey.KEY_LOADING_DARK_THEME_CUSTOMIZATION_FINAL_HOLD, ResourceType.STRING_ARRAY);
 
   /** Resource type of the partner resources type. */
   public enum ResourceType {
diff --git a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java
index f53ee40..dd43989 100644
--- a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java
+++ b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java
@@ -66,7 +66,7 @@
 
   @VisibleForTesting public static Bundle applyExtendedPartnerConfigBundle = null;
 
-  @VisibleForTesting static Bundle applyDynamicColorBundle = null;
+  @VisibleForTesting public static Bundle applyDynamicColorBundle = null;
 
   private static PartnerConfigHelper instance = null;
 
@@ -159,6 +159,13 @@
       Resources resource = resourceEntry.getResources();
       int resId = resourceEntry.getResourceId();
 
+      // for @null
+      TypedValue outValue = new TypedValue();
+      resource.getValue(resId, outValue, true);
+      if (outValue.type == TypedValue.TYPE_REFERENCE && outValue.data == 0) {
+        return result;
+      }
+
       if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
         result = resource.getColor(resId, null);
       } else {
diff --git a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java
index 4f21ae2..aa77ee8 100644
--- a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java
+++ b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java
@@ -50,9 +50,11 @@
   PartnerConfigKey.KEY_FOOTER_BUTTON_DISABLED_ALPHA,
   PartnerConfigKey.KEY_FOOTER_BUTTON_DISABLED_BG_COLOR,
   PartnerConfigKey.KEY_FOOTER_PRIMARY_BUTTON_BG_COLOR,
+  PartnerConfigKey.KEY_FOOTER_PRIMARY_BUTTON_BG_DYNAMIC_COLOR,
   PartnerConfigKey.KEY_FOOTER_PRIMARY_BUTTON_TEXT_COLOR,
   PartnerConfigKey.KEY_FOOTER_SECONDARY_BUTTON_BG_COLOR,
   PartnerConfigKey.KEY_FOOTER_SECONDARY_BUTTON_TEXT_COLOR,
+  PartnerConfigKey.KEY_FOOTER_SECONDARY_BUTTON_TEXT_DYNAMIC_COLOR,
   PartnerConfigKey.KEY_LAYOUT_BACKGROUND_COLOR,
   PartnerConfigKey.KEY_LAYOUT_MARGIN_START,
   PartnerConfigKey.KEY_LAYOUT_MARGIN_END,
@@ -83,6 +85,13 @@
   PartnerConfigKey.KEY_CONTENT_FONT_FAMILY,
   PartnerConfigKey.KEY_CONTENT_LAYOUT_GRAVITY,
   PartnerConfigKey.KEY_CONTENT_PADDING_TOP,
+  PartnerConfigKey.KEY_CONTENT_INFO_TEXT_SIZE,
+  PartnerConfigKey.KEY_CONTENT_INFO_FONT_FAMILY,
+  PartnerConfigKey.KEY_CONTENT_INFO_LINE_SPACING_EXTRA,
+  PartnerConfigKey.KEY_CONTENT_INFO_ICON_SIZE,
+  PartnerConfigKey.KEY_CONTENT_INFO_ICON_MARGIN_END,
+  PartnerConfigKey.KEY_CONTENT_INFO_PADDING_TOP,
+  PartnerConfigKey.KEY_CONTENT_INFO_PADDING_BOTTOM,
   PartnerConfigKey.KEY_ITEMS_TITLE_TEXT_SIZE,
   PartnerConfigKey.KEY_ITEMS_SUMMARY_TEXT_SIZE,
   PartnerConfigKey.KEY_ITEMS_SUMMARY_MARGIN_TOP,
@@ -96,19 +105,23 @@
   PartnerConfigKey.KEY_PROGRESS_ILLUSTRATION_ACCOUNT,
   PartnerConfigKey.KEY_PROGRESS_ILLUSTRATION_CONNECTION,
   PartnerConfigKey.KEY_PROGRESS_ILLUSTRATION_UPDATE,
+  PartnerConfigKey.KEY_PROGRESS_ILLUSTRATION_FINAL_HOLD,
   PartnerConfigKey.KEY_PROGRESS_ILLUSTRATION_DISPLAY_MINIMUM_MS,
   PartnerConfigKey.KEY_LOADING_LOTTIE_ACCOUNT,
   PartnerConfigKey.KEY_LOADING_LOTTIE_CONNECTION,
   PartnerConfigKey.KEY_LOADING_LOTTIE_DEFAULT,
   PartnerConfigKey.KEY_LOADING_LOTTIE_UPDATE,
+  PartnerConfigKey.KEY_LOADING_LOTTIE_FINAL_HOLD,
   PartnerConfigKey.KEY_LOADING_LIGHT_THEME_CUSTOMIZATION_DEFAULT,
   PartnerConfigKey.KEY_LOADING_LIGHT_THEME_CUSTOMIZATION_ACCOUNT,
   PartnerConfigKey.KEY_LOADING_LIGHT_THEME_CUSTOMIZATION_CONNECTION,
   PartnerConfigKey.KEY_LOADING_LIGHT_THEME_CUSTOMIZATION_UPDATE,
+  PartnerConfigKey.KEY_LOADING_LIGHT_THEME_CUSTOMIZATION_FINAL_HOLD,
   PartnerConfigKey.KEY_LOADING_DARK_THEME_CUSTOMIZATION_DEFAULT,
   PartnerConfigKey.KEY_LOADING_DARK_THEME_CUSTOMIZATION_ACCOUNT,
   PartnerConfigKey.KEY_LOADING_DARK_THEME_CUSTOMIZATION_CONNECTION,
   PartnerConfigKey.KEY_LOADING_DARK_THEME_CUSTOMIZATION_UPDATE,
+  PartnerConfigKey.KEY_LOADING_DARK_THEME_CUSTOMIZATION_FINAL_HOLD,
   PartnerConfigKey.KEY_TRANSITION_TYPE,
 })
 // TODO: can be removed and always reference PartnerConfig.getResourceName()?
@@ -195,6 +208,10 @@
   // Background color of the primary footer button
   String KEY_FOOTER_PRIMARY_BUTTON_BG_COLOR = "setup_compat_footer_primary_button_bg_color";
 
+  // Dynamic background color of the primary footer button
+  String KEY_FOOTER_PRIMARY_BUTTON_BG_DYNAMIC_COLOR =
+      "setup_compat_footer_1st_button_bg_dynamic_color";
+
   // Text color of the primary footer button
   String KEY_FOOTER_PRIMARY_BUTTON_TEXT_COLOR = "setup_compat_footer_primary_button_text_color";
 
@@ -204,6 +221,10 @@
   // Text color of the secondary footer button
   String KEY_FOOTER_SECONDARY_BUTTON_TEXT_COLOR = "setup_compat_footer_secondary_button_text_color";
 
+  // Dynamic text color of the secondary footer button
+  String KEY_FOOTER_SECONDARY_BUTTON_TEXT_DYNAMIC_COLOR =
+      "setup_compat_footer_2nd_button_text_dynamic_color";
+
   // Background color of layout
   String KEY_LAYOUT_BACKGROUND_COLOR = "setup_design_layout_bg_color";
 
@@ -296,6 +317,27 @@
   // The padding top of the content
   String KEY_CONTENT_PADDING_TOP = "setup_design_content_padding_top";
 
+  // The text size of the content info.
+  String KEY_CONTENT_INFO_TEXT_SIZE = "setup_design_content_info_text_size";
+
+  // The font family of the content info.
+  String KEY_CONTENT_INFO_FONT_FAMILY = "setup_design_content_info_font_family";
+
+  // The text line spacing extra of the content info.
+  String KEY_CONTENT_INFO_LINE_SPACING_EXTRA = "setup_design_content_info_line_spacing_extra";
+
+  // The icon size of the content info.
+  String KEY_CONTENT_INFO_ICON_SIZE = "setup_design_content_info_icon_size";
+
+  // The icon margin end of the content info.
+  String KEY_CONTENT_INFO_ICON_MARGIN_END = "setup_design_content_info_icon_margin_end";
+
+  // The padding top of the content info.
+  String KEY_CONTENT_INFO_PADDING_TOP = "setup_design_content_info_padding_top";
+
+  // The padding bottom of the content info.
+  String KEY_CONTENT_INFO_PADDING_BOTTOM = "setup_design_content_info_padding_bottom";
+
   // The title text size of list items.
   String KEY_ITEMS_TITLE_TEXT_SIZE = "setup_design_items_title_text_size";
 
@@ -339,6 +381,10 @@
   // For example:com.google.android.setupwizard.COMPAT_EARLY_UPDATE
   String KEY_PROGRESS_ILLUSTRATION_UPDATE = "progress_illustration_custom_update";
 
+  // The animation of loading screen used in those activities which is updating device.
+  // For example:com.google.android.setupwizard.FINAL_HOLD
+  String KEY_PROGRESS_ILLUSTRATION_FINAL_HOLD = "final_hold_custom_illustration";
+
   // The minimum illustration display time, set to 0 may cause the illustration stuck
   String KEY_PROGRESS_ILLUSTRATION_DISPLAY_MINIMUM_MS = "progress_illustration_display_minimum_ms";
 
@@ -358,6 +404,10 @@
   // For example:com.google.android.setupwizard.COMPAT_EARLY_UPDATE
   String KEY_LOADING_LOTTIE_UPDATE = "loading_animation_custom_update";
 
+  // The animation for S+ devices used in those screens which is updating devices.
+  // For example:com.google.android.setupwizard.FINAL_HOLD
+  String KEY_LOADING_LOTTIE_FINAL_HOLD = "loading_animation_custom_final_hold";
+
   // A string-array to list all the key path and color map for default animation for light theme.
   // For example: background:#FFFFFF
   String KEY_LOADING_LIGHT_THEME_CUSTOMIZATION_DEFAULT =
@@ -377,6 +427,11 @@
   // For example: background:#FFFFFF
   String KEY_LOADING_LIGHT_THEME_CUSTOMIZATION_UPDATE = "loading_light_theme_customization_update";
 
+  // A string-array to list all the key path and color map for final hold animation for light theme.
+  // For example: background:#FFFFFF
+  String KEY_LOADING_LIGHT_THEME_CUSTOMIZATION_FINAL_HOLD =
+      "loading_light_theme_customization_final_hold";
+
   // A string-array to list all the key path and color map for default animation for dark theme.
   // For example: background:#000000
   String KEY_LOADING_DARK_THEME_CUSTOMIZATION_DEFAULT = "loading_dark_theme_customization_default";
@@ -394,6 +449,11 @@
   // For example: background:#000000
   String KEY_LOADING_DARK_THEME_CUSTOMIZATION_UPDATE = "loading_dark_theme_customization_update";
 
+  // A string-array to list all the key path and color map for final hold animation for dark theme.
+  // For example: background:#000000
+  String KEY_LOADING_DARK_THEME_CUSTOMIZATION_FINAL_HOLD =
+      "loading_dark_theme_customization_final_hold";
+
   // The transition type between activities
   String KEY_TRANSITION_TYPE = "setup_design_transition_type";
 }