Add new e2e test case: seeker_show_halfsheet_test.

This test verify half sheet UI pop up for the FP simulator.
This is also the first step for initial pairing e2e tests.

Test: atest -v CtsNearbyMultiDevicesTestSuite
Demo: http://recall/-/cycSROwIosTzHgYDSIqPXl/cfN94OCK5mdobvjdxRD2gp
Bug: 214015364
Change-Id: Ife1279ca34ece513080b4a9bfe8dd03d045d4d94
diff --git a/nearby/tests/multidevices/clients/Android.bp b/nearby/tests/multidevices/clients/Android.bp
index 9c1f3f5..579baa5 100644
--- a/nearby/tests/multidevices/clients/Android.bp
+++ b/nearby/tests/multidevices/clients/Android.bp
@@ -25,8 +25,11 @@
         "NearbyFastPairProviderLib",
         "NearbyFastPairSeekerSharedLib",
         "androidx.test.core",
+        "androidx.test.ext.junit",
+        "androidx.test.uiautomator_uiautomator",
         "kotlin-stdlib",
         "mobly-snippet-lib",
+        "truth-prebuilt",
     ],
 }
 
diff --git a/nearby/tests/multidevices/clients/AndroidManifest.xml b/nearby/tests/multidevices/clients/AndroidManifest.xml
index 39140af..86c10b2 100644
--- a/nearby/tests/multidevices/clients/AndroidManifest.xml
+++ b/nearby/tests/multidevices/clients/AndroidManifest.xml
@@ -28,6 +28,7 @@
     <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
 
     <application>
         <meta-data
@@ -40,6 +41,11 @@
     </application>
 
     <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:label="Nearby Mainline Module Instrumentation Test"
+        android:targetPackage="android.nearby.multidevices" />
+
+    <instrumentation
         android:name="com.google.android.mobly.snippet.SnippetRunner"
         android:label="Nearby Mainline Module Mobly Snippet"
         android:targetPackage="android.nearby.multidevices" />
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairSeekerSnippet.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairSeekerSnippet.kt
index 54d4c67..617eac1 100644
--- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairSeekerSnippet.kt
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairSeekerSnippet.kt
@@ -17,10 +17,14 @@
 package android.nearby.multidevices.fastpair.seeker
 
 import android.content.Context
+import android.nearby.FastPairDeviceMetadata
 import android.nearby.NearbyManager
 import android.nearby.ScanCallback
 import android.nearby.ScanRequest
+import android.nearby.fastpair.seeker.FAKE_TEST_ACCOUNT_NAME
 import android.nearby.multidevices.fastpair.seeker.data.FastPairTestDataManager
+import android.nearby.multidevices.fastpair.seeker.ui.CheckNearbyHalfSheetUiTest
+import android.nearby.multidevices.fastpair.seeker.ui.DismissNearbyHalfSheetUiTest
 import androidx.test.core.app.ApplicationProvider
 import com.google.android.mobly.snippet.Snippet
 import com.google.android.mobly.snippet.rpc.AsyncRpc
@@ -60,6 +64,34 @@
         nearbyManager.stopScan(scanCallback)
     }
 
+    /** Waits and asserts the HalfSheet showed for Fast Pair pairing.
+     *
+     * @param modelId the expected model id to be associated with the HalfSheet.
+     * @param timeout the number of seconds to wait before giving up.
+     */
+    @Rpc(description = "Waits the HalfSheet showed for Fast Pair pairing.")
+    fun waitAndAssertHalfSheetShowed(modelId: String, timeout: Int) {
+        Log.i("Waits and asserts the HalfSheet showed for Fast Pair model $modelId.")
+
+        val deviceMetadata: FastPairDeviceMetadata =
+            fastPairTestDataManager.testDataCache.getFastPairDeviceMetadata(modelId)
+                ?: throw IllegalArgumentException(
+                    "Can't find $modelId-FastPairAntispoofKeyDeviceMetadata pair in " +
+                            "FastPairTestDataCache."
+                )
+        val deviceName = deviceMetadata.name!!
+        val initialPairingDescriptionTemplateText = deviceMetadata.initialPairingDescription!!
+
+        CheckNearbyHalfSheetUiTest(
+            waitHalfSheetPopupTimeoutSeconds = timeout,
+            halfSheetTitleText = deviceName,
+            halfSheetSubtitleText = initialPairingDescriptionTemplateText.format(
+                deviceName,
+                FAKE_TEST_ACCOUNT_NAME
+            )
+        ).checkNearbyHalfSheetUi()
+    }
+
     /** Puts a model id to FastPairAntispoofKeyDeviceMetadata pair into test data cache.
      *
      * @param modelId a string of model id to be associated with.
@@ -88,6 +120,24 @@
         return fastPairTestDataManager.testDataCache.dumpAccountKeyDeviceMetadataListAsJson()
     }
 
+    /** Writes into {@link Settings} whether Fast Pair scan is enabled.
+     *
+     * @param enable whether the Fast Pair scan should be enabled.
+     */
+    @Rpc(description = "Writes into Settings whether Fast Pair scan is enabled.")
+    fun setFastPairScanEnabled(enable: Boolean) {
+        Log.i("Writes into Settings whether Fast Pair scan is enabled.")
+        NearbyManager.setFastPairScanEnabled(appContext, enable)
+    }
+
+    /** Dismisses the half sheet UI if showed. */
+    @Rpc(description = "Dismisses the half sheet UI if showed.")
+    fun dismissHalfSheet() {
+        Log.i("Dismisses the half sheet UI if showed.")
+
+        DismissNearbyHalfSheetUiTest().dismissHalfSheet()
+    }
+
     /** Invokes when the snippet runner shutting down. */
     override fun shutdown() {
         super.shutdown()
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/ui/CheckNearbyHalfSheetUiTest.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/ui/CheckNearbyHalfSheetUiTest.kt
new file mode 100644
index 0000000..84b5e89
--- /dev/null
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/ui/CheckNearbyHalfSheetUiTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 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 android.nearby.multidevices.fastpair.seeker.ui
+
+import android.os.Bundle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** An instrumented test to check Nearby half sheet UI showed correctly.
+ *
+ * To run this test directly:
+ * am instrument -w -r \
+ * -e class android.nearby.multidevices.fastpair.seeker.ui.CheckNearbyHalfSheetUiTest \
+ * android.nearby.multidevices/androidx.test.runner.AndroidJUnitRunner
+ */
+@RunWith(AndroidJUnit4::class)
+class CheckNearbyHalfSheetUiTest {
+    private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+    private val waitHalfSheetPopupTimeoutMs: Long
+    private val halfSheetTitleText: String
+    private val halfSheetSubtitleText: String
+
+    constructor() {
+        val arguments: Bundle = InstrumentationRegistry.getArguments()
+        waitHalfSheetPopupTimeoutMs = arguments.getLong(
+            WAIT_HALF_SHEET_POPUP_TIMEOUT_KEY,
+            DEFAULT_WAIT_HALF_SHEET_POPUP_TIMEOUT_MS
+        )
+        halfSheetTitleText =
+            arguments.getString(HALF_SHEET_TITLE_KEY, DEFAULT_HALF_SHEET_TITLE_TEXT)
+        halfSheetSubtitleText =
+            arguments.getString(HALF_SHEET_SUBTITLE_KEY, DEFAULT_HALF_SHEET_SUBTITLE_TEXT)
+    }
+
+    constructor(
+        waitHalfSheetPopupTimeoutSeconds: Int,
+        halfSheetTitleText: String,
+        halfSheetSubtitleText: String
+    ) {
+        this.waitHalfSheetPopupTimeoutMs = waitHalfSheetPopupTimeoutSeconds * 1000L
+        this.halfSheetTitleText = halfSheetTitleText
+        this.halfSheetSubtitleText = halfSheetSubtitleText
+    }
+
+    @Test
+    fun checkNearbyHalfSheetUi() {
+        // Check Nearby half sheet showed by checking button "Connect" on the DevicePairingFragment.
+        val isConnectButtonShowed = device.wait(
+            Until.hasObject(NearbyHalfSheetUiMap.DevicePairingFragment.connectButton),
+            waitHalfSheetPopupTimeoutMs
+        )
+        assertWithMessage("Nearby half sheet didn't show within $waitHalfSheetPopupTimeoutMs ms.")
+            .that(isConnectButtonShowed).isTrue()
+
+        val halfSheetTitle =
+            device.findObject(NearbyHalfSheetUiMap.DevicePairingFragment.halfSheetTitle)
+        assertThat(halfSheetTitle).isNotNull()
+        assertThat(halfSheetTitle.text).isEqualTo(halfSheetTitleText)
+
+        val halfSheetSubtitle =
+            device.findObject(NearbyHalfSheetUiMap.DevicePairingFragment.halfSheetSubtitle)
+        assertThat(halfSheetSubtitle).isNotNull()
+        assertThat(halfSheetSubtitle.text).isEqualTo(halfSheetSubtitleText)
+
+        val deviceImage = device.findObject(NearbyHalfSheetUiMap.DevicePairingFragment.deviceImage)
+        assertThat(deviceImage).isNotNull()
+
+        val infoButton = device.findObject(NearbyHalfSheetUiMap.DevicePairingFragment.infoButton)
+        assertThat(infoButton).isNotNull()
+    }
+
+    companion object {
+        private const val DEFAULT_WAIT_HALF_SHEET_POPUP_TIMEOUT_MS = 1000L
+        private const val DEFAULT_HALF_SHEET_TITLE_TEXT = "Fast Pair Provider Simulator"
+        private const val DEFAULT_HALF_SHEET_SUBTITLE_TEXT = "Fast Pair Provider Simulator will " +
+                "appear on devices linked with nearby-mainline-fpseeker@google.com"
+        private const val WAIT_HALF_SHEET_POPUP_TIMEOUT_KEY = "WAIT_HALF_SHEET_POPUP_TIMEOUT_MS"
+        private const val HALF_SHEET_TITLE_KEY = "HALF_SHEET_TITLE"
+        private const val HALF_SHEET_SUBTITLE_KEY = "HALF_SHEET_SUBTITLE"
+    }
+}
\ No newline at end of file
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/ui/DismissNearbyHalfSheetUiTest.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/ui/DismissNearbyHalfSheetUiTest.kt
new file mode 100644
index 0000000..1d99d26
--- /dev/null
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/ui/DismissNearbyHalfSheetUiTest.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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 android.nearby.multidevices.fastpair.seeker.ui
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** An instrumented test to dismiss Nearby half sheet UI.
+ *
+ * To run this test directly:
+ * am instrument -w -r \
+ * -e class android.nearby.multidevices.fastpair.seeker.ui.DismissNearbyHalfSheetUiTest \
+ * android.nearby.multidevices/androidx.test.runner.AndroidJUnitRunner
+ */
+@RunWith(AndroidJUnit4::class)
+class DismissNearbyHalfSheetUiTest {
+    private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+
+    @Test
+    fun dismissHalfSheet() {
+        device.pressHome()
+        device.waitForIdle()
+
+        assertWithMessage("Fail to dismiss Nearby half sheet.").that(
+            device.findObject(NearbyHalfSheetUiMap.DevicePairingFragment.connectButton)
+        ).isNull()
+    }
+}
\ No newline at end of file
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/ui/NearbyHalfSheetUiMap.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/ui/NearbyHalfSheetUiMap.kt
new file mode 100644
index 0000000..c94ff01
--- /dev/null
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/ui/NearbyHalfSheetUiMap.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 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 android.nearby.multidevices.fastpair.seeker.ui
+
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.BySelector
+
+/** UiMap for Nearby Mainline Half Sheet. */
+object NearbyHalfSheetUiMap {
+    private const val PACKAGE_NAME = "com.google.android.nearby.halfsheet"
+    private const val ANDROID_WIDGET_BUTTON = "android.widget.Button"
+    private const val ANDROID_WIDGET_IMAGE_VIEW = "android.widget.ImageView"
+    private const val ANDROID_WIDGET_TEXT_VIEW = "android.widget.TextView"
+
+    object DevicePairingFragment {
+        val halfSheetTitle: BySelector =
+            By.res(PACKAGE_NAME, "toolbar_title").clazz(ANDROID_WIDGET_TEXT_VIEW)
+        val halfSheetSubtitle: BySelector =
+            By.res(PACKAGE_NAME, "header_subtitle").clazz(ANDROID_WIDGET_TEXT_VIEW)
+        val deviceImage: BySelector =
+            By.res(PACKAGE_NAME, "pairing_pic").clazz(ANDROID_WIDGET_IMAGE_VIEW)
+        val connectButton: BySelector =
+            By.res(PACKAGE_NAME, "connect_btn").clazz(ANDROID_WIDGET_BUTTON).text("Connect")
+        val infoButton: BySelector =
+            By.res(PACKAGE_NAME, "info_icon").clazz(ANDROID_WIDGET_IMAGE_VIEW)
+    }
+}
\ No newline at end of file