Using a runtime generated layout for tests instead of defining xml

This allows support for easily setting up default layouts

Bug: 277345535
Test: Presubmit
Flag: N/A
Change-Id: I1c089d60ac3f8add8d7e1060d343e04d30afe094
diff --git a/tests/Android.bp b/tests/Android.bp
index 81853d1..da8d844 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -45,6 +45,7 @@
       "src/com/android/launcher3/ui/AbstractLauncherUiTest.java",
       "src/com/android/launcher3/ui/PortraitLandscapeRunner.java",
       "src/com/android/launcher3/ui/TaplTestsLauncher3.java",
+      "src/com/android/launcher3/util/LauncherLayoutBuilder.java",
       "src/com/android/launcher3/util/TestUtil.java",
       "src/com/android/launcher3/util/Wait.java",
       "src/com/android/launcher3/util/WidgetUtils.java",
diff --git a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index a37c3cd..dc835e2 100644
--- a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -106,11 +106,6 @@
     public static final String REQUEST_GET_HAD_NONTEST_EVENTS = "get-had-nontest-events";
     public static final String REQUEST_STOP_EVENT_LOGGING = "stop-event-logging";
     public static final String REQUEST_CLEAR_DATA = "clear-data";
-    public static final String REQUEST_USE_TEST_WORKSPACE_LAYOUT = "use-test-workspace-layout";
-    public static final String REQUEST_USE_TEST2_WORKSPACE_LAYOUT = "use-test2-workspace-layout";
-    public static final String REQUEST_USE_TAPL_WORKSPACE_LAYOUT = "use-tapl-workspace-layout";
-    public static final String REQUEST_USE_DEFAULT_WORKSPACE_LAYOUT =
-            "use-default-workspace-layout";
     public static final String REQUEST_HOTSEAT_ICON_NAMES = "get-hotseat-icon-names";
     public static final String REQUEST_IS_TABLET = "is-tablet";
     public static final String REQUEST_IS_TWO_PANELS = "is-two-panel";
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 217bec3..81f1525 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -54,12 +54,14 @@
 import com.android.launcher3.tapl.HomeAppIconMenuItem;
 import com.android.launcher3.tapl.Widgets;
 import com.android.launcher3.tapl.Workspace;
+import com.android.launcher3.util.LauncherLayoutBuilder;
 import com.android.launcher3.util.TestUtil;
 import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 import com.android.launcher3.util.rule.TISBindRule;
 import com.android.launcher3.widget.picker.WidgetsFullSheet;
 import com.android.launcher3.widget.picker.WidgetsRecyclerView;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Rule;
@@ -83,6 +85,8 @@
     @Rule
     public TISBindRule mTISBindRule = new TISBindRule();
 
+    private AutoCloseable mLauncherLayout;
+
     @Before
     public void setUp() throws Exception {
         super.setUp();
@@ -101,6 +105,13 @@
         AbstractLauncherUiTest.checkDetectedLeaks(test.mLauncher);
     }
 
+    @After
+    public void tearDown() throws Exception {
+        if (mLauncherLayout != null) {
+            mLauncherLayout.close();
+        }
+    }
+
     // Please don't add negative test cases for methods that fail only after a long wait.
     public static void expectFail(String message, Runnable action) {
         boolean failed = false;
@@ -230,8 +241,10 @@
     @Test
     @ScreenRecord // b/202433017
     public void testWorkspace() throws Exception {
-        // Make sure there is an instance of chrome on the hotseat
-        mLauncher.useTaplWorkspaceLayoutOnReload();
+        // Set workspace  that includes the chrome Activity app icon on the hotseat.
+        LauncherLayoutBuilder builder = new LauncherLayoutBuilder()
+                .atHotseat(0).putApp("com.android.chrome", "com.google.android.apps.chrome.Main");
+        mLauncherLayout = TestUtil.setLauncherDefaultLayout(mTargetContext, builder);
         clearLauncherData();
 
         final Workspace workspace = mLauncher.getWorkspace();
diff --git a/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java b/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java
index 3abafdf..c4b6d43 100644
--- a/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java
+++ b/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java
@@ -30,6 +30,8 @@
 import com.android.launcher3.tapl.Workspace;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.ui.TaplTestsLauncher3;
+import com.android.launcher3.util.LauncherLayoutBuilder;
+import com.android.launcher3.util.TestUtil;
 
 import org.junit.After;
 import org.junit.Before;
@@ -49,12 +51,24 @@
 @RunWith(AndroidJUnit4.class)
 public class TwoPanelWorkspaceTest extends AbstractLauncherUiTest {
 
+    private AutoCloseable mLauncherLayout;
+
     @Before
     public void setUp() throws Exception {
         super.setUp();
-        mLauncher.useTest2WorkspaceLayoutOnReload();
-        TaplTestsLauncher3.initialize(this);
 
+        // Set layout that includes Maps/Play on workspace, and Messaging/Chrome on hotseat.
+        LauncherLayoutBuilder builder = new LauncherLayoutBuilder()
+                .atHotseat(0).putApp(
+                        "com.google.android.apps.messaging",
+                        "com.google.android.apps.messaging.ui.ConversationListActivity")
+                .atHotseat(1).putApp("com.android.chrome", "com.google.android.apps.chrome.Main")
+                .atWorkspace(0, -1, 0).putApp(
+                        "com.google.android.apps.maps", "com.google.android.maps.MapsActivity")
+                .atWorkspace(3, -1, 0).putApp(
+                        "com.android.vending", "com.android.vending.AssetBrowserActivity");
+        mLauncherLayout = TestUtil.setLauncherDefaultLayout(mTargetContext, builder);
+        TaplTestsLauncher3.initialize(this);
         assumeTrue(mLauncher.isTwoPanels());
 
         // Pre verifying the screens
@@ -67,9 +81,11 @@
     }
 
     @After
-    public void tearDown() {
+    public void tearDown() throws Exception {
         executeOnLauncher(launcher -> launcher.enableHotseatEdu(true));
-        mLauncher.useDefaultWorkspaceLayoutOnReload();
+        if (mLauncherLayout != null) {
+            mLauncherLayout.close();
+        }
     }
 
     @Test
diff --git a/tests/src/com/android/launcher3/util/TestUtil.java b/tests/src/com/android/launcher3/util/TestUtil.java
index 433fd31..4981795 100644
--- a/tests/src/com/android/launcher3/util/TestUtil.java
+++ b/tests/src/com/android/launcher3/util/TestUtil.java
@@ -15,15 +15,29 @@
  */
 package com.android.launcher3.util;
 
+import static android.util.Base64.NO_PADDING;
+import static android.util.Base64.NO_WRAP;
+
 import static androidx.test.InstrumentationRegistry.getContext;
 import static androidx.test.InstrumentationRegistry.getInstrumentation;
 import static androidx.test.InstrumentationRegistry.getTargetContext;
 
+import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_KEY;
+import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_LABEL;
+import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_TAG;
+
+import android.app.blob.BlobHandle;
+import android.app.blob.BlobStoreManager;
+import android.content.Context;
 import android.content.pm.LauncherApps;
 import android.content.res.Resources;
+import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
 import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Base64;
 
 import androidx.test.uiautomator.UiDevice;
 
@@ -36,6 +50,8 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.MessageDigest;
 import java.util.concurrent.CountDownLatch;
 import java.util.function.Predicate;
 import java.util.function.ToIntFunction;
@@ -109,6 +125,35 @@
                 "pm uninstall " + DUMMY_PACKAGE);
     }
 
+    /**
+     * Sets the default layout for Launcher and returns an object which can be used to clear
+     * the data
+     */
+    public static AutoCloseable setLauncherDefaultLayout(
+            Context context, LauncherLayoutBuilder layoutBuilder) throws Exception {
+        byte[] data = layoutBuilder.build().getBytes();
+        byte[] digest = MessageDigest.getInstance("SHA-256").digest(data);
+
+        BlobHandle handle = BlobHandle.createWithSha256(
+                digest, LAYOUT_DIGEST_LABEL, 0, LAYOUT_DIGEST_TAG);
+        BlobStoreManager blobManager = context.getSystemService(BlobStoreManager.class);
+        final long sessionId = blobManager.createSession(handle);
+        CountDownLatch wait = new CountDownLatch(1);
+        try (BlobStoreManager.Session session = blobManager.openSession(sessionId)) {
+            try (OutputStream out = new AutoCloseOutputStream(session.openWrite(0, -1))) {
+                out.write(data);
+            }
+            session.allowPublicAccess();
+            session.commit(AsyncTask.THREAD_POOL_EXECUTOR, i -> wait.countDown());
+        }
+
+        String key = Base64.encodeToString(digest, NO_WRAP | NO_PADDING);
+        Settings.Secure.putString(context.getContentResolver(), LAYOUT_DIGEST_KEY, key);
+        wait.await();
+        return () ->
+            Settings.Secure.putString(context.getContentResolver(), LAYOUT_DIGEST_KEY, null);
+    }
+
     private static class PackageInstallCheck extends LauncherApps.Callback
             implements AutoCloseable {
 
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 9905603..ba8f070 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -1851,36 +1851,6 @@
         getTestInfo(TestProtocol.REQUEST_CLEAR_DATA);
     }
 
-    /**
-     * Reloads the workspace with a test layout that includes the Test Activity app icon on the
-     * hotseat.
-     */
-    public void useTestWorkspaceLayoutOnReload() {
-        getTestInfo(TestProtocol.REQUEST_USE_TEST_WORKSPACE_LAYOUT);
-    }
-
-    /**
-     * Reloads the workspace with a test layout that includes Maps/Play on workspace, and
-     * Dialer/Messaging/Chrome/Camera on hotseat.
-     */
-    public void useTest2WorkspaceLayoutOnReload() {
-        getTestInfo(TestProtocol.REQUEST_USE_TEST2_WORKSPACE_LAYOUT);
-    }
-
-
-    /**
-     * Reloads the workspace with a test layout that includes the chrome Activity app icon on the
-     * hotseat.
-     */
-    public void useTaplWorkspaceLayoutOnReload() {
-        getTestInfo(TestProtocol.REQUEST_USE_TAPL_WORKSPACE_LAYOUT);
-    }
-
-    /** Reloads the workspace with the default layout defined by the user's grid size selection. */
-    public void useDefaultWorkspaceLayoutOnReload() {
-        getTestInfo(TestProtocol.REQUEST_USE_DEFAULT_WORKSPACE_LAYOUT);
-    }
-
     /** Shows the taskbar if it is hidden, otherwise does nothing. */
     public void showTaskbarIfHidden() {
         getTestInfo(TestProtocol.REQUEST_UNSTASH_TASKBAR_IF_STASHED);