Snap for 11526283 from 131aa921e4c30dc0ef04e36adc92004c89866ba5 to 24Q2-release

Change-Id: I4875a460870c17a0aa639ea26ce16377b905a947
diff --git a/main/java/com/google/android/setupcompat/util/ForceTwoPaneHelper.java b/main/java/com/google/android/setupcompat/util/ForceTwoPaneHelper.java
new file mode 100644
index 0000000..e69b890
--- /dev/null
+++ b/main/java/com/google/android/setupcompat/util/ForceTwoPaneHelper.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2024 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.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+import androidx.annotation.LayoutRes;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
+
+/**
+ * A helper class to support force two pane feature on portrait orientation. This will inflate the
+ * layout from xml resource which concatenates with _two_pane suffix.
+ */
+public final class ForceTwoPaneHelper {
+
+  // Refer Support different screen sizes as guideline that any device that the width >= 600 will
+  // consider as large screen
+  // https://developer.android.com/guide/topics/large-screens/support-different-screen-sizes
+  private static final int DEFAULT_ADAPT_WINDOW_WIDTH = 600;
+
+  private static final Logger LOG = new Logger("ForceTwoPaneHelper");
+
+  /** A string to be a suffix of resource name which is associating to force two pane feature. */
+  public static final String FORCE_TWO_PANE_SUFFIX = "_two_pane";
+
+  /**
+   * Returns true to indicate the forced two pane feature is enabled, otherwise, returns false. This
+   * feature is supported from Sdk U while the feature enabled from SUW side.
+   */
+  public static boolean isForceTwoPaneEnable(Context context) {
+    return Build.VERSION.SDK_INT >= VERSION_CODES.UPSIDE_DOWN_CAKE
+        && PartnerConfigHelper.isForceTwoPaneEnabled(context);
+  }
+
+  /**
+   * Returns true if satisfied 1) enable force two-pane feature, 2) portrait mode, 3) width >=
+   * setup_compat_two_pane_adapt_window_width, forced to show in two-pane style, otherwise, returns
+   * false.
+   */
+  public static boolean shouldForceTwoPane(Context context) {
+    if (!isForceTwoPaneEnable(context)) {
+      return false;
+    }
+
+    if (context == null) {
+      return false;
+    }
+
+    WindowManager windowManager = context.getSystemService(WindowManager.class);
+    if (windowManager != null) {
+      WindowMetrics windowMetrics = windowManager.getCurrentWindowMetrics();
+      if (windowMetrics.getBounds().width() > windowMetrics.getBounds().height()) {
+        // Return false for portrait mode
+        return false;
+      }
+
+      int widthInDp = (int) (windowMetrics.getBounds().width() / windowMetrics.getDensity());
+      int adaptWindowWidth =
+          PartnerConfigHelper.get(context)
+              .getInteger(
+                  context,
+                  PartnerConfig.CONFIG_TWO_PANE_ADAPT_WINDOW_WIDTH,
+                  DEFAULT_ADAPT_WINDOW_WIDTH);
+      return widthInDp >= adaptWindowWidth;
+    }
+
+    return false;
+  }
+
+  /**
+   * Returns a layout which is picking up from the layout resources with _two_pane suffix. Fallback
+   * to origin resource id if the layout resource not available. For example, pass an
+   * glif_sud_template resource id and it will return glif_sud_template_two_pane resource id if it
+   * available.
+   */
+  @LayoutRes
+  @SuppressLint("DiscouragedApi")
+  public static int getForceTwoPaneStyleLayout(Context context, int template) {
+    if (!isForceTwoPaneEnable(context)) {
+      return template;
+    }
+
+    if (template == Resources.ID_NULL) {
+      return template;
+    }
+
+    try {
+      String layoutResName = context.getResources().getResourceEntryName(template);
+      int twoPaneLayoutId =
+          context
+              .getResources()
+              .getIdentifier(
+                  layoutResName + FORCE_TWO_PANE_SUFFIX, "layout", context.getPackageName());
+      if (twoPaneLayoutId != Resources.ID_NULL) {
+        return twoPaneLayoutId;
+      }
+    } catch (NotFoundException ignore) {
+      LOG.w("Resource id 0x" + Integer.toHexString(template) + " is not found");
+    }
+
+    return template;
+  }
+
+  private ForceTwoPaneHelper() {}
+}
diff --git a/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java b/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
index 19eea8a..1378c06 100644
--- a/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
+++ b/main/java/com/google/android/setupcompat/util/WizardManagerHelper.java
@@ -35,12 +35,18 @@
 
   /** Enum for notifying an Activity that what SetupWizard flow is */
   public enum SuwLifeCycleEnum {
-    UNKNOWN,
-    INITIALIZATION,
-    PREDEFERRED,
-    DEFERRED,
-    PORTAL,
-    RESTORE_ANYTIME;
+    UNKNOWN(0),
+    INITIALIZATION(1),
+    PREDEFERRED(2),
+    DEFERRED(3),
+    PORTAL(4),
+    RESTORE_ANYTIME(5);
+
+    public final int value;
+
+    SuwLifeCycleEnum(int value) {
+      this.value = value;
+    }
   }
 
   /** Extra for notifying an Activity that what SetupWizard flow is. */
@@ -140,7 +146,7 @@
     // 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()));
+        srcIntent.getIntExtra(EXTRA_SUW_LIFECYCLE, SuwLifeCycleEnum.UNKNOWN.value));
     dstIntent.putExtra(EXTRA_THEME, srcIntent.getStringExtra(EXTRA_THEME));
   }
 
diff --git a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java
index efa3b7a..7ab946d 100644
--- a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java
+++ b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfig.java
@@ -275,7 +275,8 @@
       PartnerConfigKey.KEY_ACCOUNT_NAME_TEXT_SIZE, ResourceType.DIMENSION),
 
   // Font family of the account name
-  CONFIG_ACCOUNT_NAME_FONT_FAMILY(PartnerConfigKey.KEY_ACCOUNT_NAME_FONT_FAMILY, ResourceType.STRING),
+  CONFIG_ACCOUNT_NAME_FONT_FAMILY(
+      PartnerConfigKey.KEY_ACCOUNT_NAME_FONT_FAMILY, ResourceType.STRING),
 
   // Margin end of the account avatar
   CONFIG_ACCOUNT_AVATAR_MARGIN_END(
@@ -499,7 +500,11 @@
 
   // The margin bottom of progress bar.
   CONFIG_PROGRESS_BAR_MARGIN_BOTTOM(
-      PartnerConfigKey.KEY_PROGRESS_BAR_MARGIN_BOTTOM, ResourceType.DIMENSION);
+      PartnerConfigKey.KEY_PROGRESS_BAR_MARGIN_BOTTOM, ResourceType.DIMENSION),
+
+  // The adapt window width to be part of determining two pane style condition
+  CONFIG_TWO_PANE_ADAPT_WINDOW_WIDTH(
+      PartnerConfigKey.KEY_TWO_PANE_ADAPT_WINDOW_WIDTH, ResourceType.INTEGER);
 
   /** 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 e87d998..f027011 100644
--- a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java
+++ b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigHelper.java
@@ -85,6 +85,9 @@
       "isEmbeddedActivityOnePaneEnabled";
 
   @VisibleForTesting
+  public static final String IS_FORCE_TWO_PANE_ENABLED_METHOD = "isForceTwoPaneEnabled";
+
+  @VisibleForTesting
   public static final String GET_SUW_DEFAULT_THEME_STRING_METHOD = "suwDefaultThemeString";
 
   @VisibleForTesting public static final String SUW_PACKAGE_NAME = "com.google.android.setupwizard";
@@ -125,6 +128,10 @@
 
   @VisibleForTesting static Bundle applyTransitionBundle = null;
 
+  @SuppressWarnings("NonFinalStaticField")
+  @VisibleForTesting
+  public static Bundle applyForceTwoPaneBundle = null;
+
   @VisibleForTesting public static int savedOrientation = Configuration.ORIENTATION_PORTRAIT;
 
   /** The method name to get if transition settings is set from client. */
@@ -139,6 +146,9 @@
 
   @VisibleForTesting public static int savedScreenWidth = Configuration.SCREEN_WIDTH_DP_UNDEFINED;
 
+  /** A string to be a suffix of resource name which is associating to force two pane feature. */
+  @VisibleForTesting static final String FORCE_TWO_PANE_SUFFIX = "_two_pane";
+
   public static synchronized PartnerConfigHelper get(@NonNull Context context) {
     if (!isValidInstance(context)) {
       instance = new PartnerConfigHelper(context);
@@ -593,6 +603,8 @@
 
     if (BuildCompatUtils.isAtLeastU() && isActivityEmbedded(context)) {
       resourceEntry = adjustEmbeddedActivityResourceEntryDefaultValue(context, resourceEntry);
+    } else if (BuildCompatUtils.isAtLeastU() && isForceTwoPaneEnabled(context)) {
+      resourceEntry = adjustForceTwoPaneResourceEntryDefaultValue(context, resourceEntry);
     } else if (BuildCompatUtils.isAtLeastT() && shouldApplyMaterialYouStyle(context)) {
       resourceEntry = adjustMaterialYouResourceEntryDefaultValue(context, resourceEntry);
     }
@@ -730,6 +742,49 @@
     return inputResourceEntry;
   }
 
+  // Retrieve {@code resourceEntry} with _two_pane suffix resource from the partner resource,
+  // otherwise fallback to origin partner resource if two pane resource not available.
+  ResourceEntry adjustForceTwoPaneResourceEntryDefaultValue(
+      Context context, ResourceEntry resourceEntry) {
+    if (context == null) {
+      return resourceEntry;
+    }
+
+    try {
+      String resourceTypeName =
+          resourceEntry.getResources().getResourceTypeName(resourceEntry.getResourceId());
+      String forceTwoPaneResourceName =
+          resourceEntry.getResourceName().concat(FORCE_TWO_PANE_SUFFIX);
+      int twoPaneResourceId =
+          resourceEntry
+              .getResources()
+              .getIdentifier(
+                  forceTwoPaneResourceName, resourceTypeName, resourceEntry.getPackageName());
+      if (twoPaneResourceId != Resources.ID_NULL) {
+        Log.i(TAG, "two pane resource=" + forceTwoPaneResourceName);
+        return new ResourceEntry(
+            resourceEntry.getPackageName(),
+            forceTwoPaneResourceName,
+            twoPaneResourceId,
+            resourceEntry.getResources());
+      } else {
+        // If resource id is not available from the Overlay package, try to get it from setup wizard
+        // package.
+        PackageManager packageManager = context.getPackageManager();
+        Resources resources = packageManager.getResourcesForApplication(SUW_PACKAGE_NAME);
+        twoPaneResourceId =
+            resources.getIdentifier(forceTwoPaneResourceName, resourceTypeName, SUW_PACKAGE_NAME);
+        if (twoPaneResourceId != 0) {
+          return new ResourceEntry(
+              SUW_PACKAGE_NAME, forceTwoPaneResourceName, twoPaneResourceId, resources);
+        }
+      }
+    } catch (NameNotFoundException | NotFoundException ignore) {
+      // fall through
+    }
+    return resourceEntry;
+  }
+
   @VisibleForTesting
   public static synchronized void resetInstance() {
     instance = null;
@@ -742,6 +797,7 @@
     applyEmbeddedActivityOnePaneBundle = null;
     suwDefaultThemeBundle = null;
     applyTransitionBundle = null;
+    applyForceTwoPaneBundle = null;
   }
 
   /**
@@ -982,8 +1038,7 @@
    * than the client.
    */
   public static boolean isGlifThemeControlledTransitionApplied(@NonNull Context context) {
-    if (applyTransitionBundle == null
-        || applyTransitionBundle.isEmpty()) {
+    if (applyTransitionBundle == null || applyTransitionBundle.isEmpty()) {
       try {
         applyTransitionBundle =
             context
@@ -1000,14 +1055,34 @@
                 + " as default value");
       }
     }
-    if (applyTransitionBundle != null
-        && !applyTransitionBundle.isEmpty()) {
-      return applyTransitionBundle.getBoolean(
-          APPLY_GLIF_THEME_CONTROLLED_TRANSITION_METHOD, true);
+    if (applyTransitionBundle != null && !applyTransitionBundle.isEmpty()) {
+      return applyTransitionBundle.getBoolean(APPLY_GLIF_THEME_CONTROLLED_TRANSITION_METHOD, true);
     }
     return true;
   }
 
+  /** Returns a boolean indicate whether the force two pane feature enable or not. */
+  public static boolean isForceTwoPaneEnabled(@NonNull Context context) {
+    if (applyForceTwoPaneBundle == null || applyForceTwoPaneBundle.isEmpty()) {
+      try {
+        applyForceTwoPaneBundle =
+            context
+                .getContentResolver()
+                .call(
+                    getContentUri(),
+                    IS_FORCE_TWO_PANE_ENABLED_METHOD,
+                    /* arg= */ null,
+                    /* extras= */ null);
+      } catch (IllegalArgumentException | SecurityException exception) {
+        Log.w(TAG, "isForceTwoPaneEnabled status is unknown; return as false.");
+      }
+    }
+    if (applyForceTwoPaneBundle != null && !applyForceTwoPaneBundle.isEmpty()) {
+      return applyForceTwoPaneBundle.getBoolean(IS_FORCE_TWO_PANE_ENABLED_METHOD, false);
+    }
+    return false;
+  }
+
   @VisibleForTesting
   static Uri getContentUri() {
     return new Uri.Builder()
diff --git a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java
index fb05a40..220054c 100644
--- a/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java
+++ b/partnerconfig/java/com/google/android/setupcompat/partnerconfig/PartnerConfigKey.java
@@ -150,6 +150,7 @@
   PartnerConfigKey.KEY_LOADING_LAYOUT_WAIT_FOR_ANIMATION_FINISHED,
   PartnerConfigKey.KEY_PROGRESS_BAR_MARGIN_TOP,
   PartnerConfigKey.KEY_PROGRESS_BAR_MARGIN_BOTTOM,
+  PartnerConfigKey.KEY_TWO_PANE_ADAPT_WINDOW_WIDTH,
 })
 // TODO: can be removed and always reference PartnerConfig.getResourceName()?
 @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
@@ -566,4 +567,7 @@
 
   // A margin bottom of the content frame of progress bar.
   String KEY_PROGRESS_BAR_MARGIN_BOTTOM = "setup_design_progress_bar_margin_bottom";
+
+  // A adapt window width to determine how large to show two panel.
+  String KEY_TWO_PANE_ADAPT_WINDOW_WIDTH = "setup_compat_two_pane_adapt_window_width";
 }