Snap for 11302939 from 92a8c258275ea6bea194b02f7a12b0407b8a8920 to 24Q2-release

Change-Id: Ia6c4a6311bbdcaca9616fdc7b61bf911a6aee9df
diff --git a/Android.bp b/Android.bp
index 80a73b5..f9fc433 100644
--- a/Android.bp
+++ b/Android.bp
@@ -49,6 +49,7 @@
         "main/java/com/google/android/setupcompat/*.java",
         "main/java/com/google/android/setupcompat/internal/*.java",
         "main/java/com/google/android/setupcompat/logging/*.java",
+        "main/java/com/google/android/setupcompat/logging/*.kt",
         "main/java/com/google/android/setupcompat/logging/internal/*.java",
         "main/java/com/google/android/setupcompat/template/*.java",
         "main/java/com/google/android/setupcompat/util/*.java",
diff --git a/lint-baseline.xml b/lint-baseline.xml
index 43e81f8..bd06eb8 100644
--- a/lint-baseline.xml
+++ b/lint-baseline.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
 
     <issue
         id="NewApi"
-        message="`android:Widget.Material.Button.Colored` requires API level 23 (current min is 14)"
-        errorLine1="    &lt;style name=&quot;SucPartnerCustomizationButton.Primary&quot; parent=&quot;android:Widget.Material.Button.Colored&quot;>"
+        message="`android:Widget.Material.Button.Colored` requires API level 23 (current min is 19)"
+        errorLine1='    &lt;style name="SucPartnerCustomizationButton.Primary" parent="android:Widget.Material.Button.Colored"&gt;'
         errorLine2="                                                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="external/setupcompat/main/res/values/styles.xml"
@@ -14,30 +14,8 @@
 
     <issue
         id="NewApi"
-        message="`android:fontFamily` requires API level 16 (current min is 14)"
-        errorLine1="        &lt;item name=&quot;android:fontFamily&quot;>?attr/sucFooterBarButtonFontFamily&lt;/item>"
-        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="external/setupcompat/main/res/values/styles.xml"
-            line="48"
-            column="15"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="`android:stateListAnimator` requires API level 21 (current min is 14)"
-        errorLine1="        &lt;item name=&quot;android:stateListAnimator&quot;>@null&lt;/item>"
-        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="external/setupcompat/main/res/values/styles.xml"
-            line="54"
-            column="15"/>
-    </issue>
-
-    <issue
-        id="NewApi"
-        message="`android:Widget.Material.Button.Borderless.Colored` requires API level 21 (current min is 14)"
-        errorLine1="    &lt;style name=&quot;SucPartnerCustomizationButton.Secondary&quot; parent=&quot;android:Widget.Material.Button.Borderless.Colored&quot;>"
+        message="`android:Widget.Material.Button.Borderless.Colored` requires API level 21 (current min is 19)"
+        errorLine1='    &lt;style name="SucPartnerCustomizationButton.Secondary" parent="android:Widget.Material.Button.Borderless.Colored"&gt;'
         errorLine2="                                                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="external/setupcompat/main/res/values/styles.xml"
@@ -45,15 +23,4 @@
             column="59"/>
     </issue>
 
-    <issue
-        id="NewApi"
-        message="`android:fontFamily` requires API level 16 (current min is 14)"
-        errorLine1="        &lt;item name=&quot;android:fontFamily&quot;>?attr/sucFooterBarButtonFontFamily&lt;/item>"
-        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="external/setupcompat/main/res/values/styles.xml"
-            line="68"
-            column="15"/>
-    </issue>
-
-</issues>
+</issues>
\ No newline at end of file
diff --git a/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java b/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
index 70b28d9..26e7042 100644
--- a/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
+++ b/main/java/com/google/android/setupcompat/PartnerCustomizationLayout.java
@@ -36,6 +36,8 @@
 import com.google.android.setupcompat.internal.SetupCompatServiceInvoker;
 import com.google.android.setupcompat.internal.TemplateLayout;
 import com.google.android.setupcompat.logging.CustomEvent;
+import com.google.android.setupcompat.logging.LoggingObserver;
+import com.google.android.setupcompat.logging.LoggingObserver.SetupCompatUiEvent.LayoutInflatedEvent;
 import com.google.android.setupcompat.logging.MetricKey;
 import com.google.android.setupcompat.logging.SetupMetricsLogger;
 import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
@@ -258,8 +260,8 @@
   }
 
   /**
-   * PartnerCustomizationLayout is a template layout for different type of GlifLayout.
-   * This method allows each type of layout to report its "GlifLayoutType".
+   * PartnerCustomizationLayout is a template layout for different type of GlifLayout. This method
+   * allows each type of layout to report its "GlifLayoutType".
    */
   public void setLayoutTypeMetrics(PersistableBundle bundle) {
     this.layoutTypeBundle = bundle;
@@ -329,6 +331,18 @@
   }
 
   /**
+   * Sets a logging observer for {@link FooterBarMixin}. The logging observer is used to log
+   * impressions and clicks on the layout and footer bar buttons.
+   *
+   * @throws UnsupportedOperationException if the primary or secondary button has been set before
+   *     the logging observer is set
+   */
+  public void setLoggingObserver(LoggingObserver loggingObserver) {
+    getMixin(FooterBarMixin.class).setLoggingObserver(loggingObserver);
+    loggingObserver.log(new LayoutInflatedEvent(this));
+  }
+
+  /**
    * Invoke the method onFocusStatusChanged when onWindowFocusChangeListener receive onFocusChanged.
    */
   private void onFocusChanged(boolean hasFocus) {
@@ -339,4 +353,3 @@
                 activity, PartnerCustomizationLayout.this, hasFocus));
   }
 }
-
diff --git a/main/java/com/google/android/setupcompat/logging/LoggingObserver.kt b/main/java/com/google/android/setupcompat/logging/LoggingObserver.kt
new file mode 100644
index 0000000..4b2f092
--- /dev/null
+++ b/main/java/com/google/android/setupcompat/logging/LoggingObserver.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 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.logging
+
+import android.view.View
+
+/**
+ * An abstract class which can be attached to a Setupcompat layout and provides methods for logging
+ * impressions and interactions of its views and buttons.
+ */
+interface LoggingObserver {
+  fun log(event: SetupCompatUiEvent)
+
+  sealed class SetupCompatUiEvent {
+    data class LayoutInflatedEvent(val view: View) : SetupCompatUiEvent()
+
+    data class LayoutShownEvent(val view: View) : SetupCompatUiEvent()
+
+    data class ButtonInflatedEvent(val view: View, val buttonType: ButtonType) :
+      SetupCompatUiEvent()
+
+    data class ButtonShownEvent(val view: View, val buttonType: ButtonType) : SetupCompatUiEvent()
+
+    data class ButtonInteractionEvent(val view: View, val interactionType: InteractionType) :
+      SetupCompatUiEvent()
+  }
+
+  enum class ButtonType {
+    UNKNOWN,
+    PRIMARY,
+    SECONDARY
+  }
+
+  enum class InteractionType {
+    UNKNOWN,
+    TAP,
+    LONG_PRESS,
+    DOUBLE_TAP
+  }
+}
diff --git a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
index b417857..84cba21 100644
--- a/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
+++ b/main/java/com/google/android/setupcompat/template/FooterBarMixin.java
@@ -51,6 +51,8 @@
 import com.google.android.setupcompat.R;
 import com.google.android.setupcompat.internal.FooterButtonPartnerConfig;
 import com.google.android.setupcompat.internal.TemplateLayout;
+import com.google.android.setupcompat.logging.LoggingObserver;
+import com.google.android.setupcompat.logging.LoggingObserver.SetupCompatUiEvent.ButtonInflatedEvent;
 import com.google.android.setupcompat.logging.internal.FooterBarMixinMetrics;
 import com.google.android.setupcompat.partnerconfig.PartnerConfig;
 import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
@@ -76,6 +78,7 @@
   @VisibleForTesting public LinearLayout buttonContainer;
   private FooterButton primaryButton;
   private FooterButton secondaryButton;
+  private LoggingObserver loggingObserver;
   @IdRes private int primaryButtonId;
   @IdRes private int secondaryButtonId;
   @VisibleForTesting public FooterButtonPartnerConfig primaryButtonPartnerConfigForTesting;
@@ -222,7 +225,25 @@
       metrics.logSecondaryButtonInitialStateVisibility(
           /* isVisible= */ true, /* isUsingXml= */ true);
     }
+  }
 
+  public void setLoggingObserver(LoggingObserver observer) {
+    loggingObserver = observer;
+
+    // If primary button is already created, it's likely that {@code setPrimaryButton()} was called
+    // before an {@link LoggingObserver} is set, we need to set an observer and call the right
+    // logging method here.
+    if (primaryButtonId != 0) {
+      loggingObserver.log(
+          new ButtonInflatedEvent(getPrimaryButtonView(), LoggingObserver.ButtonType.PRIMARY));
+      getPrimaryButton().setLoggingObserver(observer);
+    }
+    // Same for secondary button.
+    if (secondaryButtonId != 0) {
+      loggingObserver.log(
+          new ButtonInflatedEvent(getSecondaryButtonView(), LoggingObserver.ButtonType.SECONDARY));
+      getSecondaryButton().setLoggingObserver(observer);
+    }
   }
 
   protected boolean isFooterButtonAlignedEnd() {
@@ -409,6 +430,11 @@
     primaryButtonPartnerConfigForTesting = footerButtonPartnerConfig;
     onFooterButtonInflated(button, footerBarPrimaryBackgroundColor);
     onFooterButtonApplyPartnerResource(button, footerButtonPartnerConfig);
+    if (loggingObserver != null) {
+      loggingObserver.log(
+          new ButtonInflatedEvent(getPrimaryButtonView(), LoggingObserver.ButtonType.PRIMARY));
+      footerButton.setLoggingObserver(loggingObserver);
+    }
 
     // Make sure the position of buttons are correctly and prevent primary button create twice or
     // more.
@@ -439,7 +465,7 @@
   /** Sets secondary button for footer. */
   @MainThread
   public void setSecondaryButton(FooterButton footerButton) {
-    setSecondaryButton(footerButton, /*usePrimaryStyle= */ false);
+    setSecondaryButton(footerButton, /* usePrimaryStyle= */ false);
   }
 
   /** Sets secondary button for footer. Allow to use the primary button style. */
@@ -495,6 +521,10 @@
 
     onFooterButtonInflated(button, footerBarSecondaryBackgroundColor);
     onFooterButtonApplyPartnerResource(button, footerButtonPartnerConfig);
+    if (loggingObserver != null) {
+      loggingObserver.log(new ButtonInflatedEvent(button, LoggingObserver.ButtonType.SECONDARY));
+      footerButton.setLoggingObserver(loggingObserver);
+    }
 
     // Make sure the position of buttons are correctly and prevent secondary button create twice or
     // more.
diff --git a/main/java/com/google/android/setupcompat/template/FooterButton.java b/main/java/com/google/android/setupcompat/template/FooterButton.java
index 90c13ec..38b81c2 100644
--- a/main/java/com/google/android/setupcompat/template/FooterButton.java
+++ b/main/java/com/google/android/setupcompat/template/FooterButton.java
@@ -33,6 +33,9 @@
 import androidx.annotation.StyleRes;
 import com.google.android.setupcompat.R;
 import com.google.android.setupcompat.logging.CustomEvent;
+import com.google.android.setupcompat.logging.LoggingObserver;
+import com.google.android.setupcompat.logging.LoggingObserver.InteractionType;
+import com.google.android.setupcompat.logging.LoggingObserver.SetupCompatUiEvent.ButtonInteractionEvent;
 import java.lang.annotation.Retention;
 import java.util.Locale;
 
@@ -53,6 +56,7 @@
   private OnClickListener onClickListener;
   private OnClickListener onClickListenerWhenDisabled;
   private OnButtonEventListener buttonListener;
+  private LoggingObserver loggingObserver;
   private int clickCount = 0;
   private Locale locale;
   private int direction;
@@ -223,9 +227,16 @@
     if (onClickListener != null) {
       clickCount++;
       onClickListener.onClick(v);
+      if (loggingObserver != null) {
+        loggingObserver.log(new ButtonInteractionEvent(v, InteractionType.TAP));
+      }
     }
   }
 
+  void setLoggingObserver(LoggingObserver loggingObserver) {
+    this.loggingObserver = loggingObserver;
+  }
+
   /** Interface definition for a callback to be invoked when footer button API has set. */
   interface OnButtonEventListener {
 
@@ -263,23 +274,31 @@
   public @interface ButtonType {
     /** A type of button that doesn't fit into any other categories. */
     int OTHER = 0;
+
     /**
      * A type of button that will set up additional elements of the ongoing setup step(s) when
      * clicked.
      */
     int ADD_ANOTHER = 1;
+
     /** A type of button that will cancel the ongoing setup step(s) and exit setup when clicked. */
     int CANCEL = 2;
+
     /** A type of button that will clear the progress when clicked. (eg: clear PIN code) */
     int CLEAR = 3;
+
     /** A type of button that will exit the setup flow when clicked. */
     int DONE = 4;
+
     /** A type of button that will go to the next screen, or next step in the flow when clicked. */
     int NEXT = 5;
+
     /** A type of button to opt-in or agree to the features described in the current screen. */
     int OPT_IN = 6;
+
     /** A type of button that will skip the current step when clicked. */
     int SKIP = 7;
+
     /** A type of button that will stop the ongoing setup step(s) and skip forward when clicked. */
     int STOP = 8;
   }
diff --git a/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java b/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
index 60b2e21..19eea8a 100644
--- a/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
+++ b/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
@@ -23,6 +23,7 @@
 import android.provider.Settings;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
+import com.google.errorprone.annotations.InlineMe;
 import java.util.Arrays;
 
 /**
@@ -32,12 +33,20 @@
  */
 public final class WizardManagerHelper {
 
-  @VisibleForTesting public static final String ACTION_NEXT = "com.android.wizard.NEXT";
+  /** Enum for notifying an Activity that what SetupWizard flow is */
+  public enum SuwLifeCycleEnum {
+    UNKNOWN,
+    INITIALIZATION,
+    PREDEFERRED,
+    DEFERRED,
+    PORTAL,
+    RESTORE_ANYTIME;
+  }
 
-  // EXTRA_SCRIPT_URI and EXTRA_ACTION_ID are used in setup wizard in versions before M and are
-  // kept for backwards compatibility.
-  @VisibleForTesting static final String EXTRA_SCRIPT_URI = "scriptUri";
-  @VisibleForTesting static final String EXTRA_ACTION_ID = "actionId";
+  /** Extra for notifying an Activity that what SetupWizard flow is. */
+  public static final String EXTRA_SUW_LIFECYCLE = "suw_lifecycle";
+
+  @VisibleForTesting public static final String ACTION_NEXT = "com.android.wizard.NEXT";
 
   @VisibleForTesting static final String EXTRA_WIZARD_BUNDLE = "wizardBundle";
   private static final String EXTRA_RESULT_CODE = "com.android.setupwizard.ResultCode";
@@ -128,12 +137,19 @@
       dstIntent.putExtra(key, srcIntent.getBooleanExtra(key, false));
     }
 
-    for (String key : Arrays.asList(EXTRA_THEME, EXTRA_SCRIPT_URI, EXTRA_ACTION_ID)) {
-      dstIntent.putExtra(key, srcIntent.getStringExtra(key));
-    }
+    // The TikTok code in Restore doesn't let us put serializable extras into intents.
+    dstIntent.putExtra(
+        EXTRA_SUW_LIFECYCLE,
+        srcIntent.getIntExtra(EXTRA_SUW_LIFECYCLE, SuwLifeCycleEnum.UNKNOWN.ordinal()));
+    dstIntent.putExtra(EXTRA_THEME, srcIntent.getStringExtra(EXTRA_THEME));
   }
 
-  /** @deprecated Use {@link isInitialSetupWizard} instead. */
+  /**
+   * @deprecated Use {@link isInitialSetupWizard} instead.
+   */
+  @InlineMe(
+      replacement = "intent.getBooleanExtra(WizardManagerHelper.EXTRA_IS_FIRST_RUN, false)",
+      imports = "com.google.android.setupcompat.util.WizardManagerHelper")
   @Deprecated
   public static boolean isSetupWizardIntent(Intent intent) {
     return intent.getBooleanExtra(EXTRA_IS_FIRST_RUN, false);
diff --git a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java
index 03e9bb5..efa3b7a 100644
--- a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java
+++ b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java
@@ -210,6 +210,10 @@
   CONFIG_ILLUSTRATION_MAX_WIDTH(
       PartnerConfigKey.KEY_ILLUSTRATION_MAX_WIDTH, ResourceType.DIMENSION),
 
+  // The max height of the illustration
+  CONFIG_ILLUSTRATION_MAX_HEIGHT(
+      PartnerConfigKey.KEY_ILLUSTRATION_MAX_HEIGHT, ResourceType.DIMENSION),
+
   // Background color of the header area
   CONFIG_HEADER_AREA_BACKGROUND_COLOR(
       PartnerConfigKey.KEY_HEADER_AREA_BACKGROUND_COLOR, ResourceType.COLOR),
diff --git a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java
index 1dac81f..fb05a40 100644
--- a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java
+++ b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java
@@ -82,6 +82,7 @@
   PartnerConfigKey.KEY_ICON_MARGIN_TOP,
   PartnerConfigKey.KEY_ICON_SIZE,
   PartnerConfigKey.KEY_ILLUSTRATION_MAX_WIDTH,
+  PartnerConfigKey.KEY_ILLUSTRATION_MAX_HEIGHT,
   PartnerConfigKey.KEY_DESCRIPTION_TEXT_SIZE,
   PartnerConfigKey.KEY_DESCRIPTION_TEXT_COLOR,
   PartnerConfigKey.KEY_DESCRIPTION_LINK_TEXT_COLOR,
@@ -308,8 +309,12 @@
   // Size of the icon
   String KEY_ICON_SIZE = "setup_design_icon_size";
 
+  // The max width for illustration
   String KEY_ILLUSTRATION_MAX_WIDTH = "setup_design_illustration_max_width";
 
+  // The max height for illustration
+  String KEY_ILLUSTRATION_MAX_HEIGHT = "setup_design_illustration_max_height";
+
   // Background color of the header area
   String KEY_HEADER_AREA_BACKGROUND_COLOR = "setup_design_header_area_background_color";