[automerger skipped] Merge Android 12 am: 961085a273 -s ours am: 3b59093a0a -s ours am: d843abb770 -s ours am: a429fac762 -s ours

am skip reason: Merged-In I40ce7b1692e93617aec9de7a0d3a8b154fdf7a02 with SHA-1 27fa29e45b is already in history

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

Change-Id: I7079704ae8e35aee16679338d36e803fc6419237
diff --git a/main/java/com/google/android/setupcompat/internal/FooterButtonPartnerConfig.java b/main/java/com/google/android/setupcompat/internal/FooterButtonPartnerConfig.java
index 5f8bf67..fad5cbf 100644
--- a/main/java/com/google/android/setupcompat/internal/FooterButtonPartnerConfig.java
+++ b/main/java/com/google/android/setupcompat/internal/FooterButtonPartnerConfig.java
@@ -26,6 +26,7 @@
   private final PartnerConfig buttonDisableBackgroundConfig;
   private final PartnerConfig buttonIconConfig;
   private final PartnerConfig buttonTextColorConfig;
+  private final PartnerConfig buttonMarginStartConfig;
   private final PartnerConfig buttonTextSizeConfig;
   private final PartnerConfig buttonMinHeightConfig;
   private final PartnerConfig buttonTextTypeFaceConfig;
@@ -41,6 +42,7 @@
       PartnerConfig buttonDisableBackgroundConfig,
       PartnerConfig buttonIconConfig,
       PartnerConfig buttonTextColorConfig,
+      PartnerConfig buttonMarginStartConfig,
       PartnerConfig buttonTextSizeConfig,
       PartnerConfig buttonMinHeightConfig,
       PartnerConfig buttonTextTypeFaceConfig,
@@ -50,6 +52,7 @@
     this.partnerTheme = partnerTheme;
 
     this.buttonTextColorConfig = buttonTextColorConfig;
+    this.buttonMarginStartConfig = buttonMarginStartConfig;
     this.buttonTextSizeConfig = buttonTextSizeConfig;
     this.buttonMinHeightConfig = buttonMinHeightConfig;
     this.buttonTextTypeFaceConfig = buttonTextTypeFaceConfig;
@@ -86,6 +89,10 @@
     return buttonTextColorConfig;
   }
 
+  public PartnerConfig getButtonMarginStartConfig() {
+    return buttonMarginStartConfig;
+  }
+
   public PartnerConfig getButtonMinHeightConfig() {
     return buttonMinHeightConfig;
   }
@@ -118,6 +125,7 @@
     private PartnerConfig buttonDisableBackgroundConfig = null;
     private PartnerConfig buttonIconConfig = null;
     private PartnerConfig buttonTextColorConfig = null;
+    private PartnerConfig buttonMarginStartConfig = null;
     private PartnerConfig buttonTextSizeConfig = null;
     private PartnerConfig buttonMinHeight = null;
     private PartnerConfig buttonTextTypeFaceConfig = null;
@@ -154,6 +162,11 @@
       return this;
     }
 
+    public Builder setMarginStartConfig(PartnerConfig buttonMarginStartConfig) {
+      this.buttonMarginStartConfig = buttonMarginStartConfig;
+      return this;
+    }
+
     public Builder setTextColorConfig(PartnerConfig buttonTextColorConfig) {
       this.buttonTextColorConfig = buttonTextColorConfig;
       return this;
@@ -202,6 +215,7 @@
           buttonDisableBackgroundConfig,
           buttonIconConfig,
           buttonTextColorConfig,
+          buttonMarginStartConfig,
           buttonTextSizeConfig,
           buttonMinHeight,
           buttonTextTypeFaceConfig,
diff --git a/main/java/com/google/android/setupcompat/template/FooterActionButton.java b/main/java/com/google/android/setupcompat/template/FooterActionButton.java
index 86a06d9..d9726f9 100644
--- a/main/java/com/google/android/setupcompat/template/FooterActionButton.java
+++ b/main/java/com/google/android/setupcompat/template/FooterActionButton.java
@@ -28,6 +28,7 @@
 public class FooterActionButton extends Button {
 
   @Nullable private FooterButton footerButton;
+  private boolean isPrimaryButtonStyle = false;
 
   public FooterActionButton(Context context, AttributeSet attrs) {
     super(context, attrs);
@@ -54,4 +55,18 @@
     }
     return super.onTouchEvent(event);
   }
+
+  /**
+   * Sets this footer button is primary button style.
+   *
+   * @param isPrimaryButtonStyle True if this button is primary button style.
+   */
+  void setPrimaryButtonStyle(boolean isPrimaryButtonStyle) {
+    this.isPrimaryButtonStyle = isPrimaryButtonStyle;
+  }
+
+  /** Returns true when the footer button is primary button style. */
+  public boolean isPrimaryButtonStyle() {
+    return isPrimaryButtonStyle;
+  }
 }
diff --git a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
index b75d972..52e3dc9 100644
--- a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
+++ b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
@@ -22,6 +22,7 @@
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.ColorStateList;
+import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.os.Build;
@@ -29,8 +30,10 @@
 import android.os.PersistableBundle;
 import android.util.AttributeSet;
 import android.view.ContextThemeWrapper;
+import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.widget.Button;
 import android.widget.LinearLayout;
@@ -71,7 +74,7 @@
   @VisibleForTesting final boolean applyDynamicColor;
   @VisibleForTesting final boolean useFullDynamicColor;
 
-  private LinearLayout buttonContainer;
+  @VisibleForTesting LinearLayout buttonContainer;
   private FooterButton primaryButton;
   private FooterButton secondaryButton;
   @IdRes private int primaryButtonId;
@@ -83,6 +86,8 @@
 
   private int footerBarPaddingTop;
   private int footerBarPaddingBottom;
+  @VisibleForTesting int footerBarPaddingStart;
+  @VisibleForTesting int footerBarPaddingEnd;
   @VisibleForTesting int defaultPadding;
   @ColorInt private final int footerBarPrimaryBackgroundColor;
   @ColorInt private final int footerBarSecondaryBackgroundColor;
@@ -189,6 +194,10 @@
     footerBarPaddingBottom =
         a.getDimensionPixelSize(
             R.styleable.SucFooterBarMixin_sucFooterBarPaddingBottom, defaultPadding);
+    footerBarPaddingStart =
+        a.getDimensionPixelSize(R.styleable.SucFooterBarMixin_sucFooterBarPaddingStart, 0);
+    footerBarPaddingEnd =
+        a.getDimensionPixelSize(R.styleable.SucFooterBarMixin_sucFooterBarPaddingEnd, 0);
     footerBarPrimaryBackgroundColor =
         a.getColor(R.styleable.SucFooterBarMixin_sucFooterBarPrimaryFooterBackground, 0);
     footerBarSecondaryBackgroundColor =
@@ -214,9 +223,28 @@
     }
   }
 
+  private boolean isFooterButtonAlignedEnd() {
+    if (PartnerConfigHelper.get(context)
+        .isPartnerConfigAvailable(PartnerConfig.CONFIG_FOOTER_BUTTON_ALIGNED_END)) {
+      return PartnerConfigHelper.get(context)
+          .getBoolean(context, PartnerConfig.CONFIG_FOOTER_BUTTON_ALIGNED_END, false);
+    } else {
+      return false;
+    }
+  }
+
+  protected boolean isFooterButtonsEvenlyWeighted() {
+    if (!isSecondaryButtonInPrimaryStyle) {
+      return false;
+    }
+    // TODO: Support neutral button style in glif layout for phone and tablet
+    return context.getResources().getConfiguration().smallestScreenWidthDp >= 600
+        && PartnerConfigHelper.shouldApplyExtendedPartnerConfig(context);
+  }
+
   private View addSpace() {
     LinearLayout buttonContainer = ensureFooterInflated();
-    View space = new View(buttonContainer.getContext());
+    View space = new View(context);
     space.setLayoutParams(new LayoutParams(0, 0, 1.0f));
     space.setVisibility(View.INVISIBLE);
     buttonContainer.addView(space);
@@ -253,10 +281,13 @@
     }
     updateFooterBarPadding(
         buttonContainer,
-        buttonContainer.getPaddingLeft(),
+        footerBarPaddingStart,
         footerBarPaddingTop,
-        buttonContainer.getPaddingRight(),
+        footerBarPaddingEnd,
         footerBarPaddingBottom);
+    if (isFooterButtonAlignedEnd()) {
+      buttonContainer.setGravity(Gravity.END | Gravity.CENTER_VERTICAL);
+    }
   }
 
   /**
@@ -282,19 +313,39 @@
       buttonContainer.setBackgroundColor(color);
     }
 
-    footerBarPaddingTop =
-        (int)
-            PartnerConfigHelper.get(context)
-                .getDimension(context, PartnerConfig.CONFIG_FOOTER_BUTTON_PADDING_TOP);
-    footerBarPaddingBottom =
-        (int)
-            PartnerConfigHelper.get(context)
-                .getDimension(context, PartnerConfig.CONFIG_FOOTER_BUTTON_PADDING_BOTTOM);
+    if (PartnerConfigHelper.get(context)
+        .isPartnerConfigAvailable(PartnerConfig.CONFIG_FOOTER_BUTTON_PADDING_TOP)) {
+      footerBarPaddingTop =
+          (int)
+              PartnerConfigHelper.get(context)
+                  .getDimension(context, PartnerConfig.CONFIG_FOOTER_BUTTON_PADDING_TOP);
+    }
+    if (PartnerConfigHelper.get(context)
+        .isPartnerConfigAvailable(PartnerConfig.CONFIG_FOOTER_BUTTON_PADDING_BOTTOM)) {
+      footerBarPaddingBottom =
+          (int)
+              PartnerConfigHelper.get(context)
+                  .getDimension(context, PartnerConfig.CONFIG_FOOTER_BUTTON_PADDING_BOTTOM);
+    }
+    if (PartnerConfigHelper.get(context)
+        .isPartnerConfigAvailable(PartnerConfig.CONFIG_FOOTER_BAR_PADDING_START)) {
+      footerBarPaddingStart =
+          (int)
+              PartnerConfigHelper.get(context)
+                  .getDimension(context, PartnerConfig.CONFIG_FOOTER_BAR_PADDING_START);
+    }
+    if (PartnerConfigHelper.get(context)
+        .isPartnerConfigAvailable(PartnerConfig.CONFIG_FOOTER_BAR_PADDING_END)) {
+      footerBarPaddingEnd =
+          (int)
+              PartnerConfigHelper.get(context)
+                  .getDimension(context, PartnerConfig.CONFIG_FOOTER_BAR_PADDING_END);
+    }
     updateFooterBarPadding(
         buttonContainer,
-        buttonContainer.getPaddingLeft(),
+        footerBarPaddingStart,
         footerBarPaddingTop,
-        buttonContainer.getPaddingRight(),
+        footerBarPaddingEnd,
         footerBarPaddingBottom);
 
     if (PartnerConfigHelper.get(context)
@@ -343,6 +394,7 @@
             .setButtonRadiusConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_RADIUS)
             .setButtonRippleColorAlphaConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_RIPPLE_COLOR_ALPHA)
             .setTextColorConfig(PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_TEXT_COLOR)
+            .setMarginStartConfig(PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_MARGIN_START)
             .setTextSizeConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_TEXT_SIZE)
             .setButtonMinHeight(PartnerConfig.CONFIG_FOOTER_BUTTON_MIN_HEIGHT)
             .setTextTypeFaceConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_FONT_FAMILY)
@@ -353,6 +405,7 @@
     // update information for primary button. Need to update as long as the button inflated.
     primaryButtonId = button.getId();
     primaryDefaultTextColor = button.getTextColors();
+    button.setPrimaryButtonStyle(/* isPrimaryButtonStyle= */ true);
     primaryButton = footerButton;
     primaryButtonPartnerConfigForTesting = footerButtonPartnerConfig;
 
@@ -417,6 +470,7 @@
                 usePrimaryStyle
                     ? PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_TEXT_COLOR
                     : PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_COLOR)
+            .setMarginStartConfig(PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_MARGIN_START)
             .setTextSizeConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_TEXT_SIZE)
             .setButtonMinHeight(PartnerConfig.CONFIG_FOOTER_BUTTON_MIN_HEIGHT)
             .setTextTypeFaceConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_FONT_FAMILY)
@@ -427,6 +481,7 @@
     // update information for secondary button. Need to update as long as the button inflated.
     secondaryButtonId = button.getId();
     secondaryDefaultTextColor = button.getTextColors();
+    button.setPrimaryButtonStyle(usePrimaryStyle);
     secondaryButton = footerButton;
     secondaryButtonPartnerConfigForTesting = footerButtonPartnerConfig;
 
@@ -448,6 +503,14 @@
     Button tempSecondaryButton = getSecondaryButtonView();
     buttonContainer.removeAllViews();
 
+    boolean isEvenlyWeightedButtons = isFooterButtonsEvenlyWeighted();
+    boolean isLandscape =
+        context.getResources().getConfiguration().orientation
+            == Configuration.ORIENTATION_LANDSCAPE;
+    if (isLandscape && isEvenlyWeightedButtons) {
+      addSpace();
+    }
+
     if (tempSecondaryButton != null) {
       if (isSecondaryButtonInPrimaryStyle) {
         // Since the secondary button has the same style (with background) as the primary button,
@@ -461,10 +524,54 @@
       }
       buttonContainer.addView(tempSecondaryButton);
     }
-    addSpace();
+    if (!isFooterButtonAlignedEnd() && !isEvenlyWeightedButtons) {
+      addSpace();
+    }
     if (tempPrimaryButton != null) {
       buttonContainer.addView(tempPrimaryButton);
     }
+
+    setEvenlyWeightedButtons(tempPrimaryButton, tempSecondaryButton, isEvenlyWeightedButtons);
+  }
+
+  private void setEvenlyWeightedButtons(
+      Button primaryButton, Button secondaryButton, boolean isEvenlyWeighted) {
+    if (primaryButton != null && secondaryButton != null && isEvenlyWeighted) {
+      LinearLayout.LayoutParams primaryLayoutParams =
+          (LinearLayout.LayoutParams) primaryButton.getLayoutParams();
+      if (null != primaryLayoutParams) {
+        primaryLayoutParams.width = 0;
+        primaryLayoutParams.weight = 1.0f;
+        primaryButton.setLayoutParams(primaryLayoutParams);
+      }
+
+      LinearLayout.LayoutParams secondaryLayoutParams =
+          (LinearLayout.LayoutParams) secondaryButton.getLayoutParams();
+      if (null != secondaryLayoutParams) {
+        secondaryLayoutParams.width = 0;
+        secondaryLayoutParams.weight = 1.0f;
+        secondaryButton.setLayoutParams(secondaryLayoutParams);
+      }
+    } else {
+      if (primaryButton != null) {
+        LinearLayout.LayoutParams primaryLayoutParams =
+            (LinearLayout.LayoutParams) primaryButton.getLayoutParams();
+        if (null != primaryLayoutParams) {
+          primaryLayoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
+          primaryLayoutParams.weight = 0;
+          primaryButton.setLayoutParams(primaryLayoutParams);
+        }
+      }
+      if (secondaryButton != null) {
+        LinearLayout.LayoutParams secondaryLayoutParams =
+            (LinearLayout.LayoutParams) secondaryButton.getLayoutParams();
+        if (null != secondaryLayoutParams) {
+          secondaryLayoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
+          secondaryLayoutParams.weight = 0;
+          secondaryButton.setLayoutParams(secondaryLayoutParams);
+        }
+      }
+    }
   }
 
   /**
diff --git a/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java b/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java
index ef45b5c..cc445a6 100644
--- a/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java
+++ b/main/java/com/google/android/setupcompat/template/FooterButtonStyleUtils.java
@@ -32,6 +32,7 @@
 import android.os.Build.VERSION_CODES;
 import android.util.StateSet;
 import android.util.TypedValue;
+import android.view.ViewGroup;
 import android.widget.Button;
 import androidx.annotation.ColorInt;
 import androidx.annotation.VisibleForTesting;
@@ -58,6 +59,7 @@
             .setButtonRadiusConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_RADIUS)
             .setButtonRippleColorAlphaConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_RIPPLE_COLOR_ALPHA)
             .setTextColorConfig(PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_TEXT_COLOR)
+            .setMarginStartConfig(PartnerConfig.CONFIG_FOOTER_PRIMARY_BUTTON_MARGIN_START)
             .setTextSizeConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_TEXT_SIZE)
             .setButtonMinHeight(PartnerConfig.CONFIG_FOOTER_BUTTON_MIN_HEIGHT)
             .setTextTypeFaceConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_FONT_FAMILY)
@@ -92,6 +94,7 @@
             .setButtonRadiusConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_RADIUS)
             .setButtonRippleColorAlphaConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_RIPPLE_COLOR_ALPHA)
             .setTextColorConfig(PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_COLOR)
+            .setMarginStartConfig(PartnerConfig.CONFIG_FOOTER_SECONDARY_BUTTON_MARGIN_START)
             .setTextSizeConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_TEXT_SIZE)
             .setButtonMinHeight(PartnerConfig.CONFIG_FOOTER_BUTTON_MIN_HEIGHT)
             .setTextTypeFaceConfig(PartnerConfig.CONFIG_FOOTER_BUTTON_FONT_FAMILY)
@@ -133,6 +136,8 @@
         applyDynamicColor,
         footerButtonPartnerConfig.getButtonTextColorConfig(),
         footerButtonPartnerConfig.getButtonRippleColorAlphaConfig());
+    FooterButtonStyleUtils.updateButtonMarginStartWithPartnerConfig(
+        context, button, footerButtonPartnerConfig.getButtonMarginStartConfig());
     FooterButtonStyleUtils.updateButtonTextSizeWithPartnerConfig(
         context, button, footerButtonPartnerConfig.getButtonTextSizeConfig());
     FooterButtonStyleUtils.updateButtonMinHeightWithPartnerConfig(
@@ -276,6 +281,19 @@
     }
   }
 
+  static void updateButtonMarginStartWithPartnerConfig(
+      Context context, Button button, PartnerConfig buttonMarginStartConfig) {
+    ViewGroup.LayoutParams lp = button.getLayoutParams();
+    boolean partnerConfigAvailable =
+        PartnerConfigHelper.get(context).isPartnerConfigAvailable(buttonMarginStartConfig);
+    if (partnerConfigAvailable && lp instanceof ViewGroup.MarginLayoutParams) {
+      final ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp;
+      int startMargin =
+          (int) PartnerConfigHelper.get(context).getDimension(context, buttonMarginStartConfig);
+      mlp.setMargins(startMargin, mlp.topMargin, mlp.rightMargin, mlp.bottomMargin);
+    }
+  }
+
   static void updateButtonTextSizeWithPartnerConfig(
       Context context, Button button, PartnerConfig buttonTextSizeConfig) {
     float size = PartnerConfigHelper.get(context).getDimension(context, buttonTextSizeConfig);
diff --git a/main/java/com/google/android/setupcompat/util/BuildCompatUtils.java b/main/java/com/google/android/setupcompat/util/BuildCompatUtils.java
index ea54745..7fac760 100644
--- a/main/java/com/google/android/setupcompat/util/BuildCompatUtils.java
+++ b/main/java/com/google/android/setupcompat/util/BuildCompatUtils.java
@@ -17,6 +17,7 @@
 package com.google.android.setupcompat.util;
 
 import android.os.Build;
+import androidx.annotation.ChecksSdkIntAtLeast;
 
 /**
  * An util class to check whether the current OS version is higher or equal to sdk version of
@@ -25,6 +26,25 @@
 public final class BuildCompatUtils {
 
   /**
+   * Implementation of BuildCompat.isAtLeastR() suitable for use in Setup
+   *
+   * @return Whether the current OS version is higher or equal to R.
+   */
+  @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.R)
+  public static boolean isAtLeastR() {
+    return Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
+  }
+  /**
+   * Implementation of BuildCompat.isAtLeastS() suitable for use in Setup
+   *
+   * @return Whether the current OS version is higher or equal to S.
+   */
+  @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S)
+  public static boolean isAtLeastS() {
+    return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
+  }
+
+  /**
    * 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
@@ -40,10 +60,10 @@
    * <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+)
+   *   <li>For current Android release: while new API is not finalized yet (CODENAME = "T", SDK_INT
+   *       = 31|32)
+   *   <li>For current Android release: when new API is finalized (CODENAME = "REL", SDK_INT = 32)
+   *   <li>For next Android release (CODENAME = "U", SDK_INT = 33+)
    * </ul>
    *
    * <p>Note that Build.VERSION_CODES.S cannot be used here until final SDK is available in all
@@ -52,13 +72,13 @@
    *
    * @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) {
+  public static boolean isAtLeastT() {
+    if (!isAtLeastS()) {
       return false;
     }
-    return (Build.VERSION.CODENAME.equals("REL") && Build.VERSION.SDK_INT >= 31)
+    return (Build.VERSION.CODENAME.equals("REL") && Build.VERSION.SDK_INT >= 32)
         || (Build.VERSION.CODENAME.length() == 1
-            && Build.VERSION.CODENAME.charAt(0) >= 'S'
+            && Build.VERSION.CODENAME.charAt(0) >= 'T'
             && Build.VERSION.CODENAME.charAt(0) <= 'Z');
   }
 
diff --git a/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java b/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
index 79976bc..84bd68b 100644
--- a/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
+++ b/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
@@ -65,6 +65,9 @@
    */
   public static final String EXTRA_IS_SETUP_FLOW = "isSetupFlow";
 
+  /** Extra for notifying an activity that was called from suggested action activity. */
+  public static final String EXTRA_IS_SUW_SUGGESTED_ACTION_FLOW = "isSuwSuggestedActionFlow";
+
   public static final String EXTRA_THEME = "theme";
   public static final String EXTRA_USE_IMMERSIVE_MODE = "useImmersiveMode";
 
@@ -122,7 +125,8 @@
             EXTRA_IS_DEFERRED_SETUP,
             EXTRA_IS_PRE_DEFERRED_SETUP,
             EXTRA_IS_PORTAL_SETUP,
-            EXTRA_IS_SETUP_FLOW)) {
+            EXTRA_IS_SETUP_FLOW,
+            EXTRA_IS_SUW_SUGGESTED_ACTION_FLOW)) {
       dstIntent.putExtra(key, srcIntent.getBooleanExtra(key, false));
     }
 
diff --git a/main/java/com/google/android/setupcompat/view/ButtonBarLayout.java b/main/java/com/google/android/setupcompat/view/ButtonBarLayout.java
index da1ab34..1157fae 100644
--- a/main/java/com/google/android/setupcompat/view/ButtonBarLayout.java
+++ b/main/java/com/google/android/setupcompat/view/ButtonBarLayout.java
@@ -18,9 +18,12 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.view.Gravity;
 import android.view.View;
 import android.widget.LinearLayout;
 import com.google.android.setupcompat.R;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
+import com.google.android.setupcompat.template.FooterActionButton;
 
 /**
  * An extension of LinearLayout that automatically switches to vertical orientation when it can't
@@ -62,7 +65,7 @@
 
     super.onMeasure(initialWidthMeasureSpec, heightMeasureSpec);
 
-    if (getMeasuredWidth() > widthSize) {
+    if (!isFooterButtonsEventlyWeighted(getContext()) && (getMeasuredWidth() > widthSize)) {
       setStacked(true);
 
       // Measure again in the new orientation.
@@ -86,6 +89,7 @@
       if (stacked) {
         child.setTag(R.id.suc_customization_original_weight, childParams.weight);
         childParams.weight = 0;
+        childParams.leftMargin = 0;
       } else {
         Float weight = (Float) child.getTag(R.id.suc_customization_original_weight);
         if (weight != null) {
@@ -103,6 +107,8 @@
     }
 
     if (stacked) {
+      // When stacked, the buttons need to be kept in the center of the button bar.
+      setHorizontalGravity(Gravity.CENTER);
       // HACK: In the default button bar style, the left and right paddings are not
       // balanced to compensate for different alignment for borderless (left) button and
       // the raised (right) button. When it's stacked, we want the buttons to be centered,
@@ -115,4 +121,28 @@
       setPadding(originalPaddingLeft, getPaddingTop(), originalPaddingRight, getPaddingBottom());
     }
   }
+
+  private boolean isFooterButtonsEventlyWeighted(Context context) {
+    int childCount = getChildCount();
+    int primayButtonCount = 0;
+    for (int i = 0; i < childCount; i++) {
+      View child = getChildAt(i);
+      if (child instanceof FooterActionButton) {
+        if (((FooterActionButton) child).isPrimaryButtonStyle()) {
+          primayButtonCount += 1;
+        }
+      }
+    }
+    if (primayButtonCount != 2) {
+      return false;
+    }
+
+    // TODO: Support neutral button style in glif layout for phone and tablet
+    if (context.getResources().getConfiguration().smallestScreenWidthDp >= 600
+        && PartnerConfigHelper.shouldApplyExtendedPartnerConfig(context)) {
+      return true;
+    } else {
+      return false;
+    }
+  }
 }
diff --git a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java
index 280ab81..54a2ef8 100644
--- a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java
+++ b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java
@@ -40,6 +40,14 @@
   // The min height of the footer buttons
   CONFIG_FOOTER_BAR_MIN_HEIGHT(PartnerConfigKey.KEY_FOOTER_BAR_MIN_HEIGHT, ResourceType.DIMENSION),
 
+  // The padding start of the footer bar
+  CONFIG_FOOTER_BAR_PADDING_START(
+      PartnerConfigKey.KEY_FOOTER_BAR_PADDING_START, ResourceType.DIMENSION),
+
+  // The padding end of the footer bar
+  CONFIG_FOOTER_BAR_PADDING_END(
+      PartnerConfigKey.KEY_FOOTER_BAR_PADDING_END, ResourceType.DIMENSION),
+
   // The same as "windowLightNavigationBar". If set true, the navigation bar icons will be drawn
   // such that it is compatible with a light navigation bar background.
   CONFIG_LIGHT_NAVIGATION_BAR(PartnerConfigKey.KEY_LIGHT_NAVIGATION_BAR, ResourceType.BOOL),
@@ -108,6 +116,10 @@
   CONFIG_FOOTER_BUTTON_MIN_HEIGHT(
       PartnerConfigKey.KEY_FOOTER_BUTTON_MIN_HEIGHT, ResourceType.DIMENSION),
 
+  // Make the footer buttons all aligned the end
+  CONFIG_FOOTER_BUTTON_ALIGNED_END(
+      PartnerConfigKey.KEY_FOOTER_BUTTON_ALIGNED_END, ResourceType.BOOL),
+
   // Disabled background alpha of the footer buttons
   CONFIG_FOOTER_BUTTON_DISABLED_ALPHA(
       PartnerConfigKey.KEY_FOOTER_BUTTON_DISABLED_ALPHA, ResourceType.FRACTION),
@@ -124,6 +136,10 @@
   CONFIG_FOOTER_PRIMARY_BUTTON_TEXT_COLOR(
       PartnerConfigKey.KEY_FOOTER_PRIMARY_BUTTON_TEXT_COLOR, ResourceType.COLOR),
 
+  // Margin start of the primary footer button
+  CONFIG_FOOTER_PRIMARY_BUTTON_MARGIN_START(
+      PartnerConfigKey.KEY_FOOTER_PRIMARY_BUTTON_MARGIN_START, ResourceType.DIMENSION),
+
   // Background color of the secondary footer button
   CONFIG_FOOTER_SECONDARY_BUTTON_BG_COLOR(
       PartnerConfigKey.KEY_FOOTER_SECONDARY_BUTTON_BG_COLOR, ResourceType.COLOR),
@@ -132,6 +148,10 @@
   CONFIG_FOOTER_SECONDARY_BUTTON_TEXT_COLOR(
       PartnerConfigKey.KEY_FOOTER_SECONDARY_BUTTON_TEXT_COLOR, ResourceType.COLOR),
 
+  // Margin start of the secondary footer button
+  CONFIG_FOOTER_SECONDARY_BUTTON_MARGIN_START(
+      PartnerConfigKey.KEY_FOOTER_SECONDARY_BUTTON_MARGIN_START, ResourceType.DIMENSION),
+
   // Background color of layout
   CONFIG_LAYOUT_BACKGROUND_COLOR(PartnerConfigKey.KEY_LAYOUT_BACKGROUND_COLOR, ResourceType.COLOR),
 
@@ -207,6 +227,10 @@
   // Font family of the description
   CONFIG_DESCRIPTION_FONT_FAMILY(PartnerConfigKey.KEY_DESCRIPTION_FONT_FAMILY, ResourceType.STRING),
 
+  // Font family of the link text
+  CONFIG_DESCRIPTION_LINK_FONT_FAMILY(
+      PartnerConfigKey.KEY_DESCRIPTION_LINK_FONT_FAMILY, ResourceType.STRING),
+
   // Margin top of the description text
   CONFIG_DESCRIPTION_TEXT_MARGIN_TOP(
       PartnerConfigKey.KEY_DESCRIPTION_TEXT_MARGIN_TOP, ResourceType.DIMENSION),
@@ -414,7 +438,15 @@
 
   // The height of the header of the loading layout.
   CONFIG_LOADING_LAYOUT_HEADER_HEIGHT(
-      PartnerConfigKey.KEY_LOADING_LAYOUT_HEADER_HEIGHT, ResourceType.DIMENSION);
+      PartnerConfigKey.KEY_LOADING_LAYOUT_HEADER_HEIGHT, ResourceType.DIMENSION),
+
+  // The margin top of progress bar.
+  CONFIG_PROGRESS_BAR_MARGIN_TOP(
+      PartnerConfigKey.KEY_PROGRESS_BAR_MARGIN_TOP, ResourceType.DIMENSION),
+
+  // The margin bottom of progress bar.
+  CONFIG_PROGRESS_BAR_MARGIN_BOTTOM(
+      PartnerConfigKey.KEY_PROGRESS_BAR_MARGIN_BOTTOM, ResourceType.DIMENSION);
 
   /** 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 2ca8876..c0be011 100644
--- a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java
+++ b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java
@@ -81,6 +81,14 @@
 
   private static int savedOrientation = Configuration.ORIENTATION_PORTRAIT;
 
+  /**
+   * When testing related to fake PartnerConfigHelper instance, should sync the following saved
+   * config with testing environment.
+   */
+  @VisibleForTesting public static int savedScreenHeight = Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
+
+  @VisibleForTesting public static int savedScreenWidth = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
+
   public static synchronized PartnerConfigHelper get(@NonNull Context context) {
     if (!isValidInstance(context)) {
       instance = new PartnerConfigHelper(context);
@@ -93,15 +101,21 @@
     if (instance == null) {
       savedConfigUiMode = currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
       savedOrientation = currentConfig.orientation;
+      savedScreenWidth = currentConfig.screenWidthDp;
+      savedScreenHeight = currentConfig.screenHeightDp;
       return false;
     } else {
-      if (isSetupWizardDayNightEnabled(context)
-          && (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK) != savedConfigUiMode) {
+      boolean uiModeChanged =
+          isSetupWizardDayNightEnabled(context)
+              && (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK) != savedConfigUiMode;
+      if (uiModeChanged
+          || currentConfig.orientation != savedOrientation
+          || currentConfig.screenWidthDp != savedScreenWidth
+          || currentConfig.screenHeightDp != savedScreenHeight) {
         savedConfigUiMode = currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
-        resetInstance();
-        return false;
-      } else if (currentConfig.orientation != savedOrientation) {
         savedOrientation = currentConfig.orientation;
+        savedScreenHeight = currentConfig.screenHeightDp;
+        savedScreenWidth = currentConfig.screenWidthDp;
         resetInstance();
         return false;
       }
@@ -315,7 +329,7 @@
 
       result = resource.getBoolean(resId);
       partnerResourceCache.put(resourceConfig, result);
-    } catch (NullPointerException exception) {
+    } catch (NullPointerException | NotFoundException exception) {
       // fall through
     }
     return result;
@@ -364,7 +378,7 @@
       result =
           getDimensionFromTypedValue(
               context, (TypedValue) partnerResourceCache.get(resourceConfig));
-    } catch (NullPointerException exception) {
+    } catch (NullPointerException | NotFoundException exception) {
       // fall through
     }
     return result;
@@ -408,7 +422,7 @@
 
       result = resource.getFraction(resId, 1, 1);
       partnerResourceCache.put(resourceConfig, result);
-    } catch (NullPointerException exception) {
+    } catch (NullPointerException | NotFoundException exception) {
       // fall through
     }
     return result;
@@ -441,7 +455,7 @@
 
       result = resource.getInteger(resId);
       partnerResourceCache.put(resourceConfig, result);
-    } catch (NullPointerException exception) {
+    } catch (NullPointerException | NotFoundException exception) {
       // fall through
     }
     return result;
diff --git a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java
index c7444a5..bbededa 100644
--- a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java
+++ b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java
@@ -31,6 +31,8 @@
   PartnerConfigKey.KEY_NAVIGATION_BAR_DIVIDER_COLOR,
   PartnerConfigKey.KEY_FOOTER_BAR_BG_COLOR,
   PartnerConfigKey.KEY_FOOTER_BAR_MIN_HEIGHT,
+  PartnerConfigKey.KEY_FOOTER_BAR_PADDING_START,
+  PartnerConfigKey.KEY_FOOTER_BAR_PADDING_END,
   PartnerConfigKey.KEY_FOOTER_BUTTON_FONT_FAMILY,
   PartnerConfigKey.KEY_FOOTER_BUTTON_ICON_ADD_ANOTHER,
   PartnerConfigKey.KEY_FOOTER_BUTTON_ICON_CANCEL,
@@ -47,12 +49,15 @@
   PartnerConfigKey.KEY_FOOTER_BUTTON_TEXT_SIZE,
   PartnerConfigKey.KEY_FOOTER_BUTTON_TEXT_STYLE,
   PartnerConfigKey.KEY_FOOTER_BUTTON_MIN_HEIGHT,
+  PartnerConfigKey.KEY_FOOTER_BUTTON_ALIGNED_END,
   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_TEXT_COLOR,
+  PartnerConfigKey.KEY_FOOTER_PRIMARY_BUTTON_MARGIN_START,
   PartnerConfigKey.KEY_FOOTER_SECONDARY_BUTTON_BG_COLOR,
   PartnerConfigKey.KEY_FOOTER_SECONDARY_BUTTON_TEXT_COLOR,
+  PartnerConfigKey.KEY_FOOTER_SECONDARY_BUTTON_MARGIN_START,
   PartnerConfigKey.KEY_LAYOUT_BACKGROUND_COLOR,
   PartnerConfigKey.KEY_LAYOUT_MARGIN_START,
   PartnerConfigKey.KEY_LAYOUT_MARGIN_END,
@@ -75,6 +80,7 @@
   PartnerConfigKey.KEY_DESCRIPTION_TEXT_COLOR,
   PartnerConfigKey.KEY_DESCRIPTION_LINK_TEXT_COLOR,
   PartnerConfigKey.KEY_DESCRIPTION_FONT_FAMILY,
+  PartnerConfigKey.KEY_DESCRIPTION_LINK_FONT_FAMILY,
   PartnerConfigKey.KEY_DESCRIPTION_TEXT_MARGIN_TOP,
   PartnerConfigKey.KEY_DESCRIPTION_TEXT_MARGIN_BOTTOM,
   PartnerConfigKey.KEY_CONTENT_TEXT_SIZE,
@@ -128,6 +134,8 @@
   PartnerConfigKey.KEY_LOADING_LAYOUT_CONTENT_PADDING_END,
   PartnerConfigKey.KEY_LOADING_LAYOUT_CONTENT_PADDING_BOTTOM,
   PartnerConfigKey.KEY_LOADING_LAYOUT_HEADER_HEIGHT,
+  PartnerConfigKey.KEY_PROGRESS_BAR_MARGIN_TOP,
+  PartnerConfigKey.KEY_PROGRESS_BAR_MARGIN_BOTTOM,
 })
 // TODO: can be removed and always reference PartnerConfig.getResourceName()?
 @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
@@ -155,6 +163,12 @@
   // The min height of the footer bar
   String KEY_FOOTER_BAR_MIN_HEIGHT = "setup_compat_footer_bar_min_height";
 
+  // The padding start of the footer bar
+  String KEY_FOOTER_BAR_PADDING_START = "setup_compat_footer_bar_padding_start";
+
+  // The padding end of the footer bar
+  String KEY_FOOTER_BAR_PADDING_END = "setup_compat_footer_bar_padding_end";
+
   // The font face used in footer buttons. This must be a string reference to a font that is
   // available in the system. Font references (@font or @xml) are not allowed.
   String KEY_FOOTER_BUTTON_FONT_FAMILY = "setup_compat_footer_button_font_family";
@@ -204,6 +218,9 @@
   // The min height of the footer buttons
   String KEY_FOOTER_BUTTON_MIN_HEIGHT = "setup_compat_footer_button_min_height";
 
+  // Make the footer buttons all aligned the end
+  String KEY_FOOTER_BUTTON_ALIGNED_END = "setup_compat_footer_button_aligned_end";
+
   // Disabled background alpha of the footer buttons
   String KEY_FOOTER_BUTTON_DISABLED_ALPHA = "setup_compat_footer_button_disabled_alpha";
 
@@ -216,12 +233,19 @@
   // Text color of the primary footer button
   String KEY_FOOTER_PRIMARY_BUTTON_TEXT_COLOR = "setup_compat_footer_primary_button_text_color";
 
+  // Margin start of the primary footer button
+  String KEY_FOOTER_PRIMARY_BUTTON_MARGIN_START = "setup_compat_footer_primary_button_margin_start";
+
   // Background color of the secondary footer button
   String KEY_FOOTER_SECONDARY_BUTTON_BG_COLOR = "setup_compat_footer_secondary_button_bg_color";
 
   // Text color of the secondary footer button
   String KEY_FOOTER_SECONDARY_BUTTON_TEXT_COLOR = "setup_compat_footer_secondary_button_text_color";
 
+  // Margin start of the secondary footer button
+  String KEY_FOOTER_SECONDARY_BUTTON_MARGIN_START =
+      "setup_compat_footer_secondary_button_margin_start";
+
   // Background color of layout
   String KEY_LAYOUT_BACKGROUND_COLOR = "setup_design_layout_bg_color";
 
@@ -290,6 +314,9 @@
   // Font family of the description
   String KEY_DESCRIPTION_FONT_FAMILY = "setup_design_description_font_family";
 
+  // Font family of the link text
+  String KEY_DESCRIPTION_LINK_FONT_FAMILY = "setup_design_description_link_font_family";
+
   // Margin top of the header text
   String KEY_DESCRIPTION_TEXT_MARGIN_TOP = "setup_design_description_text_margin_top";
 
@@ -474,4 +501,10 @@
 
   // A height of the header of loading layout.
   String KEY_LOADING_LAYOUT_HEADER_HEIGHT = "loading_layout_header_height";
+
+  // A margin top of the content frame of progress bar.
+  String KEY_PROGRESS_BAR_MARGIN_TOP = "setup_design_progress_bar_margin_top";
+
+  // A margin bottom of the content frame of progress bar.
+  String KEY_PROGRESS_BAR_MARGIN_BOTTOM = "setup_design_progress_bar_margin_bottom";
 }