Implement MVP for Single Provider - Single Password flow for get credential request.

https://screencast.googleplex.com/cast/NjUwOTM5ODQ4MTQzNjY3MnwyNTQ3Nzk4NS03Mg

Bug: 301206470
Test: N/A - implementing MVP code

Change-Id: If2b6880656f620c0859138551144b830e3233c46
diff --git a/packages/CredentialManager/shared/Android.bp b/packages/CredentialManager/shared/Android.bp
index 38d98a9..0d4af2a 100644
--- a/packages/CredentialManager/shared/Android.bp
+++ b/packages/CredentialManager/shared/Android.bp
@@ -12,6 +12,7 @@
     manifest: "AndroidManifest.xml",
     srcs: ["src/**/*.kt"],
     static_libs: [
+        "androidx.activity_activity-compose",
         "androidx.core_core-ktx",
         "androidx.credentials_credentials",
         "guava",
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ApiConstants.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ApiConstants.kt
new file mode 100644
index 0000000..6498ff7
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ApiConstants.kt
@@ -0,0 +1,19 @@
+/*
+ * 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.0N
+ *
+ * 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.android.credentialmanager
+
+const val IS_AUTO_SELECTED_KEY = "IS_AUTO_SELECTED"
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
index defba8d..8986e52 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
@@ -17,14 +17,25 @@
 package com.android.credentialmanager
 
 import android.content.Intent
+import android.content.pm.PackageManager
 import android.credentials.ui.RequestInfo
 import com.android.credentialmanager.ktx.requestInfo
 import com.android.credentialmanager.mapper.toGet
 import com.android.credentialmanager.mapper.toRequestCancel
+import com.android.credentialmanager.mapper.toRequestClose
 import com.android.credentialmanager.model.Request
 
-fun Intent.parse(): Request {
-    this.toRequestCancel()?.let { return it }
+fun Intent.parse(
+    packageManager: PackageManager,
+    previousIntent: Intent? = null,
+): Request {
+    this.toRequestClose(packageManager, previousIntent)?.let { closeRequest ->
+        return closeRequest
+    }
+
+    this.toRequestCancel(packageManager)?.let { cancelRequest ->
+        return cancelRequest
+    }
 
     return when (requestInfo?.type) {
         RequestInfo.TYPE_CREATE -> {
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/activity/StartBalIntentSenderForResultContract.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/activity/StartBalIntentSenderForResultContract.kt
new file mode 100644
index 0000000..ef083fd
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/activity/StartBalIntentSenderForResultContract.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.android.credentialmanager.activity
+
+import android.app.ActivityOptions
+import android.content.Context
+import android.content.Intent
+import androidx.activity.result.ActivityResult
+import androidx.activity.result.IntentSenderRequest
+import androidx.activity.result.contract.ActivityResultContract
+import androidx.activity.result.contract.ActivityResultContracts
+
+/**
+ * A custom StartIntentSenderForResult contract implementation that attaches an [ActivityOptions]
+ * that opts in for background activity launch.
+ */
+class StartBalIntentSenderForResultContract :
+    ActivityResultContract<IntentSenderRequest, ActivityResult>() {
+    override fun createIntent(context: Context, input: IntentSenderRequest): Intent {
+        val activityOptionBundle =
+            ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode(
+                ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+            ).toBundle()
+        return Intent(
+            ActivityResultContracts.StartIntentSenderForResult.ACTION_INTENT_SENDER_REQUEST
+        ).putExtra(
+            ActivityResultContracts.StartActivityForResult.EXTRA_ACTIVITY_OPTIONS_BUNDLE,
+            activityOptionBundle
+        ).putExtra(
+            ActivityResultContracts.StartIntentSenderForResult.EXTRA_INTENT_SENDER_REQUEST,
+            input
+        )
+    }
+
+    override fun parseResult(
+        resultCode: Int,
+        intent: Intent?
+    ): ActivityResult = ActivityResult(resultCode, intent)
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt
index a4c20bf..4533db6 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt
@@ -18,10 +18,12 @@
 
 import android.content.Intent
 import android.credentials.ui.CancelUiRequest
+import android.credentials.ui.Constants
 import android.credentials.ui.CreateCredentialProviderData
 import android.credentials.ui.GetCredentialProviderData
 import android.credentials.ui.ProviderData
 import android.credentials.ui.RequestInfo
+import android.os.ResultReceiver
 
 val Intent.cancelUiRequest: CancelUiRequest?
     get() = this.extras?.getParcelable(
@@ -46,3 +48,9 @@
         ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST,
         CreateCredentialProviderData::class.java
     ) ?: emptyList()
+
+val Intent.resultReceiver: ResultReceiver?
+    get() = this.getParcelableExtra(
+        Constants.EXTRA_RESULT_RECEIVER,
+        ResultReceiver::class.java
+    )
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt
index 86a6d23..555a86f 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt
@@ -17,13 +17,23 @@
 package com.android.credentialmanager.mapper
 
 import android.content.Intent
+import android.content.pm.PackageManager
+import android.util.Log
+import com.android.credentialmanager.TAG
+import com.android.credentialmanager.ktx.appLabel
 import com.android.credentialmanager.ktx.cancelUiRequest
 import com.android.credentialmanager.model.Request
 
-fun Intent.toRequestCancel(): Request.Cancel? =
+fun Intent.toRequestCancel(packageManager: PackageManager): Request.Cancel? =
     this.cancelUiRequest?.let { cancelUiRequest ->
-        Request.Cancel(
-            showCancellationUi = cancelUiRequest.shouldShowCancellationUi(),
-            appPackageName = cancelUiRequest.appPackageName
-        )
+        val appLabel = packageManager.appLabel(cancelUiRequest.appPackageName)
+        if (appLabel == null) {
+            Log.d(TAG, "Received UI cancel request with an invalid package name.")
+            null
+        } else {
+            Request.Cancel(
+                showCancellationUi = cancelUiRequest.shouldShowCancellationUi(),
+                appName = appLabel
+            )
+        }
     }
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCloseMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCloseMapper.kt
new file mode 100644
index 0000000..6de3e7d
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCloseMapper.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.0N
+ *
+ * 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.android.credentialmanager.mapper
+
+import android.content.Intent
+import android.content.pm.PackageManager
+import com.android.credentialmanager.ktx.requestInfo
+import com.android.credentialmanager.model.Request
+
+fun Intent.toRequestClose(
+    packageManager: PackageManager,
+    previousIntent: Intent? = null,
+): Request.Close? {
+    // Close request comes as "Cancel" request from Credential Manager API
+    val currentRequest = toRequestCancel(packageManager = packageManager) ?: return null
+
+    if (currentRequest.showCancellationUi) {
+        // Current request is to Cancel and not to Close
+        return null
+    }
+
+    previousIntent?.let {
+        val previousToken = previousIntent.requestInfo?.token
+        val currentToken = this.requestInfo?.token
+
+        if (previousToken != currentToken) {
+            // Current cancellation is for a different request, don't close the current flow.
+            return null
+        }
+    }
+
+    return Request.Close
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
index ed9d563..ee45fbb 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt
@@ -1,22 +1,66 @@
+/*
+ * 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.0N
+ *
+ * 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.android.credentialmanager.mapper
 
 import android.content.Intent
+import android.credentials.ui.Entry
 import android.credentials.ui.GetCredentialProviderData
+import androidx.credentials.provider.PasswordCredentialEntry
+import com.android.credentialmanager.factory.fromSlice
 import com.android.credentialmanager.ktx.getCredentialProviderDataList
+import com.android.credentialmanager.ktx.requestInfo
+import com.android.credentialmanager.ktx.resultReceiver
+import com.android.credentialmanager.model.Password
 import com.android.credentialmanager.model.Request
 import com.google.common.collect.ImmutableList
 import com.google.common.collect.ImmutableMap
 
-fun Intent.toGet() = Request.Get(
-    providers = ImmutableMap.copyOf(
-        getCredentialProviderDataList.associateBy { it.providerFlattenedComponentName }
-    ),
-    entries = ImmutableList.copyOf(
-        getCredentialProviderDataList.map { providerData ->
-            check(providerData is GetCredentialProviderData) {
-                "Invalid provider data type for GetCredentialRequest"
+fun Intent.toGet(): Request.Get {
+    val credentialEntries = mutableListOf<Pair<String, Entry>>()
+    for (providerData in getCredentialProviderDataList) {
+        if (providerData is GetCredentialProviderData) {
+            for (credentialEntry in providerData.credentialEntries) {
+                credentialEntries.add(
+                    Pair(providerData.providerFlattenedComponentName, credentialEntry)
+                )
             }
-            providerData
-        }.flatMap { it.credentialEntries }
+        }
+    }
+
+    val passwordEntries = mutableListOf<Password>()
+    for ((providerId, entry) in credentialEntries) {
+        val slice = fromSlice(entry.slice)
+        if (slice is PasswordCredentialEntry) {
+            passwordEntries.add(
+                Password(
+                    providerId = providerId,
+                    entry = entry,
+                    passwordCredentialEntry = slice
+                )
+            )
+        }
+    }
+
+    return Request.Get(
+        token = requestInfo?.token,
+        resultReceiver = this.resultReceiver,
+        providers = ImmutableMap.copyOf(
+            getCredentialProviderDataList.associateBy { it.providerFlattenedComponentName }
+        ),
+        passwordEntries = ImmutableList.copyOf(passwordEntries)
     )
-)
\ No newline at end of file
+}
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt
new file mode 100644
index 0000000..2fe4fd5
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Password.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.0N
+ *
+ * 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.android.credentialmanager.model
+
+import android.credentials.ui.Entry
+import androidx.credentials.provider.PasswordCredentialEntry
+
+data class Password(
+    val providerId: String,
+    val entry: Entry,
+    val passwordCredentialEntry: PasswordCredentialEntry,
+)
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
index bc07310..6011a1c 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
@@ -16,8 +16,9 @@
 
 package com.android.credentialmanager.model
 
-import android.credentials.ui.Entry
 import android.credentials.ui.ProviderData
+import android.os.IBinder
+import android.os.ResultReceiver
 import com.google.common.collect.ImmutableList
 import com.google.common.collect.ImmutableMap
 
@@ -25,15 +26,33 @@
  * Represents the request made by the CredentialManager API.
  */
 sealed class Request {
+
+    /**
+     * Request to close the app without displaying a message to the user and without reporting
+     * anything back to the Credential Manager service.
+     */
+    data object Close : Request()
+
+    /**
+     * Request to close the app, displaying a message to the user.
+     */
     data class Cancel(
         val showCancellationUi: Boolean,
-        val appPackageName: String?
+        val appName: String
     ) : Request()
 
+    /**
+     * Request to start the get credentials flow.
+     */
     data class Get(
+        val token: IBinder?,
+        val resultReceiver: ResultReceiver?,
         val providers: ImmutableMap<String, ProviderData>,
-        val entries: ImmutableList<Entry>,
+        val passwordEntries: ImmutableList<Password>,
     ) : Request()
 
+    /**
+     * Request to start the create credentials flow.
+     */
     data object Create : Request()
 }
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt
new file mode 100644
index 0000000..5ab5ab9
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/RequestRepository.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.0N
+ *
+ * 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.android.credentialmanager.repository
+
+import android.app.Application
+import android.content.Intent
+import android.util.Log
+import com.android.credentialmanager.TAG
+import com.android.credentialmanager.model.Request
+import com.android.credentialmanager.parse
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+
+class RequestRepository(
+    private val application: Application,
+) {
+
+    private val _requests = MutableStateFlow<Request?>(null)
+    val requests: StateFlow<Request?> = _requests
+
+    suspend fun processRequest(intent: Intent, previousIntent: Intent? = null) {
+        val request = intent.parse(
+            packageManager = application.packageManager,
+            previousIntent = previousIntent
+        )
+
+        Log.d(TAG, "Request parsed: $request")
+
+        _requests.value = request
+    }
+}
diff --git a/packages/CredentialManager/wear/Android.bp b/packages/CredentialManager/wear/Android.bp
index c0dff16..e5f5cc2 100644
--- a/packages/CredentialManager/wear/Android.bp
+++ b/packages/CredentialManager/wear/Android.bp
@@ -37,6 +37,7 @@
         "androidx.lifecycle_lifecycle-extensions",
         "androidx.lifecycle_lifecycle-livedata",
         "androidx.lifecycle_lifecycle-runtime-ktx",
+        "androidx.lifecycle_lifecycle-runtime-compose",
         "androidx.lifecycle_lifecycle-viewmodel-compose",
         "androidx.wear.compose_compose-foundation",
         "androidx.wear.compose_compose-material",
diff --git a/packages/CredentialManager/wear/AndroidManifest.xml b/packages/CredentialManager/wear/AndroidManifest.xml
index 90248734..b480ac3 100644
--- a/packages/CredentialManager/wear/AndroidManifest.xml
+++ b/packages/CredentialManager/wear/AndroidManifest.xml
@@ -1,4 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
+
 <!--
 /*
  * Copyright (c) 2023 Google Inc.
@@ -21,25 +22,27 @@
 
     <uses-feature android:name="android.hardware.type.watch" />
 
-    <uses-permission android:name="android.permission.LAUNCH_CREDENTIAL_SELECTOR"/>
-    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
-    <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"/>
+    <uses-permission android:name="android.permission.LAUNCH_CREDENTIAL_SELECTOR" />
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+    <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS" />
 
     <application
-      android:allowBackup="true"
-      android:dataExtractionRules="@xml/data_extraction_rules"
-      android:fullBackupContent="@xml/backup_rules"
-      android:label="@string/app_name"
-      android:supportsRtl="true">
+        android:name=".CredentialSelectorApp"
+        android:allowBackup="true"
+        android:dataExtractionRules="@xml/data_extraction_rules"
+        android:fullBackupContent="@xml/backup_rules"
+        android:label="@string/app_name"
+        android:supportsRtl="true">
 
+        <!-- Activity called by GMS has to be exactly:
+        com.android.credentialmanager.CredentialSelectorActivity -->
         <activity
-            android:name=".ui.CredentialSelectorActivity"
+            android:name=".CredentialSelectorActivity"
+            android:excludeFromRecents="true"
             android:exported="true"
-            android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR"
-            android:launchMode="singleTop"
             android:label="@string/app_name"
-            android:excludeFromRecents="true">
-        </activity>
-  </application>
+            android:launchMode="singleTop"
+            android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR" />
+    </application>
 
 </manifest>
diff --git a/packages/CredentialManager/wear/res/values/themes.xml b/packages/CredentialManager/wear/res/values/themes.xml
deleted file mode 100644
index 22329e9f..0000000
--- a/packages/CredentialManager/wear/res/values/themes.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ 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.
-  -->
-<resources>
-  <style name="Theme.CredentialSelector" parent="@*android:style/ThemeOverlay.DeviceDefault.Accent.DayNight">
-    <item name="android:windowContentOverlay">@null</item>
-    <item name="android:windowNoTitle">true</item>
-    <item name="android:windowBackground">@android:color/transparent</item>
-    <item name="android:windowIsTranslucent">true</item>
-  </style>
-</resources>
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
new file mode 100644
index 0000000..273d0b1
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -0,0 +1,100 @@
+/*
+ * 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.0N
+ *
+ * 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.android.credentialmanager
+
+import android.content.Intent
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.viewModels
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.wear.compose.material.MaterialTheme
+import com.android.credentialmanager.ui.WearApp
+import com.android.credentialmanager.ui.screens.single.password.SinglePasswordScreen
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.layout.belowTimeTextPreview
+import kotlinx.coroutines.launch
+
+class CredentialSelectorActivity : ComponentActivity() {
+
+    private val viewModel: CredentialSelectorViewModel by viewModels {
+        CredentialSelectorViewModel.Factory
+    }
+
+    @OptIn(ExperimentalHorologistApi::class)
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        setTheme(android.R.style.Theme_DeviceDefault)
+
+        // TODO: b/301027810 due to this issue with compose in Main platform, we are implementing a
+        // workaround. Once the issue is fixed, remove the "else" bracket and leave only the
+        // contents of the "if" bracket.
+        if (false) {
+            setContent {
+                MaterialTheme {
+                    WearApp(
+                        viewModel = viewModel,
+                        onCloseApp = ::finish,
+                    )
+                }
+            }
+        } else {
+            // TODO: b/301027810 Remove the content of this "else" bracket fully once issue is fixed
+            lifecycleScope.launch {
+                repeatOnLifecycle(Lifecycle.State.STARTED) {
+                    viewModel.uiState.collect { uiState ->
+                        when (uiState) {
+                            CredentialSelectorUiState.Idle -> {
+                                // Don't display anything, assuming that there should be minimal latency
+                                // to parse the Credential Manager intent and define the state of the
+                                // app. If latency is big, then a "loading" screen should be displayed
+                                // to the user.
+                            }
+
+                            is CredentialSelectorUiState.Get -> {
+                                setContent {
+                                    MaterialTheme {
+                                        SinglePasswordScreen(
+                                            columnState = belowTimeTextPreview(),
+                                            onCloseApp = ::finish,
+                                        )
+                                    }
+                                }
+                            }
+
+                            else -> finish()
+                        }
+                    }
+                }
+            }
+        }
+
+        viewModel.onNewIntent(intent)
+    }
+
+    override fun onNewIntent(intent: Intent) {
+        super.onNewIntent(intent)
+
+        val previousIntent = getIntent()
+        setIntent(intent)
+
+        viewModel.onNewIntent(intent, previousIntent)
+    }
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
new file mode 100644
index 0000000..7c81fd0
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.0N
+ *
+ * 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.android.credentialmanager
+
+import android.app.Application
+import com.android.credentialmanager.di.inject
+import com.android.credentialmanager.repository.RequestRepository
+
+class CredentialSelectorApp : Application() {
+
+    lateinit var requestRepository: RequestRepository
+
+    override fun onCreate() {
+        super.onCreate()
+
+        inject()
+    }
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
new file mode 100644
index 0000000..d557dc0
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.0N
+ *
+ * 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.android.credentialmanager
+
+import android.content.Intent
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
+import androidx.lifecycle.viewModelScope
+import androidx.lifecycle.viewmodel.CreationExtras
+import com.android.credentialmanager.model.Request
+import com.android.credentialmanager.repository.RequestRepository
+import com.android.credentialmanager.ui.mappers.toGet
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+class CredentialSelectorViewModel(
+    private val requestRepository: RequestRepository,
+) : ViewModel() {
+
+    val uiState: StateFlow<CredentialSelectorUiState> = requestRepository.requests
+        .map { request ->
+            when (request) {
+                null -> CredentialSelectorUiState.Idle
+                is Request.Cancel -> CredentialSelectorUiState.Cancel(request.appName)
+                Request.Close -> CredentialSelectorUiState.Close
+                Request.Create -> CredentialSelectorUiState.Create
+                is Request.Get -> request.toGet()
+            }
+        }
+        .stateIn(
+            viewModelScope,
+            started = SharingStarted.WhileSubscribed(5000),
+            initialValue = CredentialSelectorUiState.Idle,
+        )
+
+    fun onNewIntent(intent: Intent, previousIntent: Intent? = null) {
+        viewModelScope.launch {
+            requestRepository.processRequest(intent = intent, previousIntent = previousIntent)
+        }
+    }
+
+    companion object {
+        val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
+            @Suppress("UNCHECKED_CAST")
+            override fun <T : ViewModel> create(
+                modelClass: Class<T>,
+                extras: CreationExtras
+            ): T {
+                val application = checkNotNull(extras[APPLICATION_KEY])
+
+                return CredentialSelectorViewModel(
+                    requestRepository = (application as CredentialSelectorApp).requestRepository,
+                ) as T
+            }
+        }
+    }
+}
+
+sealed class CredentialSelectorUiState {
+    data object Idle : CredentialSelectorUiState()
+    sealed class Get : CredentialSelectorUiState() {
+        data object SingleProviderSinglePasskey : Get()
+        data object SingleProviderSinglePassword : Get()
+
+        // TODO: b/301206470 add the remaining states
+    }
+
+    data object Create : CredentialSelectorUiState()
+    data class Cancel(val appName: String) : CredentialSelectorUiState()
+    data object Close : CredentialSelectorUiState()
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/di/DI.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/DI.kt
new file mode 100644
index 0000000..a11017b
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/DI.kt
@@ -0,0 +1,17 @@
+package com.android.credentialmanager.di
+
+import android.app.Application
+import com.android.credentialmanager.CredentialSelectorApp
+import com.android.credentialmanager.repository.RequestRepository
+
+// TODO b/301601582 add Hilt for dependency injection
+
+fun CredentialSelectorApp.inject() {
+    requestRepository = requestRepository(application = this)
+}
+
+private fun requestRepository(
+    application: Application,
+): RequestRepository = RequestRepository(
+    application = application,
+)
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorActivity.kt
deleted file mode 100644
index 53122ba..0000000
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorActivity.kt
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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.0N
- *
- * 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.android.credentialmanager.ui
-
-import android.content.Intent
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.activity.viewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
-import androidx.wear.compose.material.MaterialTheme
-import kotlinx.coroutines.launch
-
-class CredentialSelectorActivity : ComponentActivity() {
-
-    private val viewModel: CredentialSelectorViewModel by viewModels()
-
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-
-        setTheme(android.R.style.Theme_DeviceDefault)
-
-        lifecycleScope.launch {
-            repeatOnLifecycle(Lifecycle.State.STARTED) {
-                viewModel.uiState.collect { uiState ->
-                    when (uiState) {
-                        CredentialSelectorUiState.Idle -> {
-                            // Don't display anything, assuming that there should be minimal latency
-                            // to parse the Credential Manager intent and define the state of the
-                            // app. If latency is big, then a "loading" screen should be displayed
-                            // to the user.
-                        }
-
-                        is CredentialSelectorUiState.Get -> {
-                            setContent {
-                                MaterialTheme {
-                                    WearApp()
-                                }
-                            }
-                        }
-
-                        CredentialSelectorUiState.Create -> {
-                            // TODO: b/301206624 - Implement create flow
-                            finish()
-                        }
-
-                        is CredentialSelectorUiState.Cancel -> {
-                            // TODO: b/300422310 - Implement cancel with message flow
-                            finish()
-                        }
-
-                        CredentialSelectorUiState.Finish -> {
-                            finish()
-                        }
-                    }
-                }
-            }
-        }
-
-        viewModel.onNewIntent(intent)
-    }
-
-    override fun onNewIntent(intent: Intent) {
-        super.onNewIntent(intent)
-
-        val previousIntent = getIntent()
-        setIntent(intent)
-
-        viewModel.onNewIntent(intent, previousIntent)
-    }
-}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorViewModel.kt
deleted file mode 100644
index d22d5d1..0000000
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/CredentialSelectorViewModel.kt
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * 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.0N
- *
- * 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.android.credentialmanager.ui
-
-import android.app.Application
-import android.content.Intent
-import android.util.Log
-import androidx.lifecycle.AndroidViewModel
-import androidx.lifecycle.viewModelScope
-import com.android.credentialmanager.TAG
-import com.android.credentialmanager.parse
-import com.android.credentialmanager.ktx.appLabel
-import com.android.credentialmanager.ktx.requestInfo
-import com.android.credentialmanager.mapper.toGet
-import com.android.credentialmanager.ui.model.PasskeyUiModel
-import com.android.credentialmanager.ui.model.PasswordUiModel
-import com.android.credentialmanager.model.Request
-import com.android.credentialmanager.ui.mapper.toGet
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.launch
-
-class CredentialSelectorViewModel(
-    private val application: Application
-) : AndroidViewModel(application = application) {
-
-    private val _uiState =
-        MutableStateFlow<CredentialSelectorUiState>(CredentialSelectorUiState.Idle)
-    val uiState: StateFlow<CredentialSelectorUiState> = _uiState
-
-    fun onNewIntent(intent: Intent, previousIntent: Intent? = null) {
-        viewModelScope.launch {
-            val request = intent.parse()
-            if (shouldFinishActivity(request = request, previousIntent = previousIntent)) {
-                _uiState.value = CredentialSelectorUiState.Finish
-            } else {
-                when (request) {
-                    is Request.Cancel -> {
-                        request.appPackageName?.let { appPackageName ->
-                            application.packageManager.appLabel(appPackageName)?.let { appLabel ->
-                                _uiState.value = CredentialSelectorUiState.Cancel(appLabel)
-                            } ?: run {
-                                Log.d(TAG,
-                                    "Received UI cancel request with an invalid package name.")
-                                _uiState.value = CredentialSelectorUiState.Finish
-                            }
-                        } ?: run {
-                            Log.d(TAG, "Received UI cancel request with an invalid package name.")
-                            _uiState.value = CredentialSelectorUiState.Finish
-                        }
-                    }
-
-                    Request.Create -> {
-                        _uiState.value = CredentialSelectorUiState.Create
-                    }
-
-                    is Request.Get -> {
-                        _uiState.value = request.toGet()
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Check if backend requested the UI activity to be cancelled. Different from the other
-     * finishing flows, this one does not report anything back to the Credential Manager service
-     * backend.
-     */
-    private fun shouldFinishActivity(request: Request, previousIntent: Intent? = null): Boolean {
-        if (request !is Request.Cancel) {
-            return false
-        } else {
-            Log.d(
-                TAG, "Received UI cancellation intent. Should show cancellation" +
-                " ui = ${request.showCancellationUi}")
-
-            previousIntent?.let {
-                val previousUiRequest = previousIntent.parse()
-
-                if (previousUiRequest is Request.Cancel) {
-                    val previousToken = previousIntent.requestInfo?.token
-                    val currentToken = previousIntent.requestInfo?.token
-
-                    if (previousToken != currentToken) {
-                        // Cancellation was for a different request, don't cancel the current UI.
-                        return false
-                    }
-                }
-            }
-
-            return !request.showCancellationUi
-        }
-    }
-}
-
-sealed class CredentialSelectorUiState {
-    data object Idle : CredentialSelectorUiState()
-    sealed class Get : CredentialSelectorUiState() {
-        data class SingleProviderSinglePasskey(val passkeyUiModel: PasskeyUiModel) : Get()
-        data class SingleProviderSinglePassword(val passwordUiModel: PasswordUiModel) : Get()
-
-        // TODO: b/301206470 add the remaining states
-    }
-
-    data object Create : CredentialSelectorUiState()
-    data class Cancel(val appName: String) : CredentialSelectorUiState()
-    data object Finish : CredentialSelectorUiState()
-}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt
new file mode 100644
index 0000000..da5697d
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Navigation.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.0N
+ *
+ * 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.android.credentialmanager.ui
+
+import androidx.navigation.NavController
+
+fun NavController.navigateToLoading() {
+    navigate(Screen.Loading.route)
+}
+
+fun NavController.navigateToSinglePasswordScreen() {
+    navigate(Screen.SinglePasswordScreen.route)
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt
index 7d1a49b..c3919a0 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/Screen.kt
@@ -19,5 +19,7 @@
 sealed class Screen(
     val route: String,
 ) {
-    data object Main : Screen("main")
+    data object Loading : Screen("loading")
+
+    data object SinglePasswordScreen : Screen("singlePasswordScreen")
 }
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
index 19ea9ed..7e0ea30 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/WearApp.kt
@@ -19,28 +19,94 @@
 package com.android.credentialmanager.ui
 
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.navigation.NavController
 import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState
 import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
 import androidx.wear.compose.navigation.rememberSwipeDismissableNavHostState
-import com.android.credentialmanager.ui.screens.MainScreen
+import com.android.credentialmanager.CredentialSelectorUiState
+import com.android.credentialmanager.CredentialSelectorViewModel
+import com.android.credentialmanager.ui.screens.LoadingScreen
+import com.android.credentialmanager.ui.screens.single.password.SinglePasswordScreen
 import com.google.android.horologist.annotations.ExperimentalHorologistApi
 import com.google.android.horologist.compose.navscaffold.WearNavScaffold
 import com.google.android.horologist.compose.navscaffold.composable
+import com.google.android.horologist.compose.navscaffold.scrollable
 
 @Composable
-fun WearApp() {
+fun WearApp(
+    viewModel: CredentialSelectorViewModel,
+    onCloseApp: () -> Unit,
+) {
     val navController = rememberSwipeDismissableNavController()
     val swipeToDismissBoxState = rememberSwipeToDismissBoxState()
     val navHostState =
         rememberSwipeDismissableNavHostState(swipeToDismissBoxState = swipeToDismissBoxState)
 
+    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
+
     WearNavScaffold(
-        startDestination = Screen.Main.route,
+        startDestination = Screen.Loading.route,
         navController = navController,
         state = navHostState,
     ) {
-        composable(Screen.Main.route) {
-            MainScreen()
+        composable(Screen.Loading.route) {
+            LoadingScreen()
+        }
+
+        scrollable(Screen.SinglePasswordScreen.route) {
+            SinglePasswordScreen(
+                columnState = it.columnState,
+                onCloseApp = onCloseApp,
+            )
+        }
+    }
+
+    when (val state = uiState) {
+        CredentialSelectorUiState.Idle -> {
+            if (navController.currentDestination?.route != Screen.Loading.route) {
+                navController.navigateToLoading()
+            }
+        }
+
+        is CredentialSelectorUiState.Get -> {
+            handleGetNavigation(
+                navController = navController,
+                state = state,
+                onCloseApp = onCloseApp,
+            )
+        }
+
+        CredentialSelectorUiState.Create -> {
+            // TODO: b/301206624 - Implement create flow
+            onCloseApp()
+        }
+
+        is CredentialSelectorUiState.Cancel -> {
+            // TODO: b/300422310 - Implement cancel with message flow
+            onCloseApp()
+        }
+
+        CredentialSelectorUiState.Close -> {
+            onCloseApp()
+        }
+    }
+}
+
+private fun handleGetNavigation(
+    navController: NavController,
+    state: CredentialSelectorUiState.Get,
+    onCloseApp: () -> Unit,
+) {
+    when (state) {
+        is CredentialSelectorUiState.Get.SingleProviderSinglePassword -> {
+            navController.navigateToSinglePasswordScreen()
+        }
+
+        else -> {
+            // TODO: b/301206470 - Implement other get flows
+            onCloseApp()
         }
     }
 }
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mapper/CredentialSelectorUiStateGetMapper.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mapper/CredentialSelectorUiStateGetMapper.kt
deleted file mode 100644
index 5ceec178..0000000
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mapper/CredentialSelectorUiStateGetMapper.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.android.credentialmanager.ui.mapper
-
-import androidx.credentials.provider.CustomCredentialEntry
-import androidx.credentials.provider.PasswordCredentialEntry
-import androidx.credentials.provider.PublicKeyCredentialEntry
-import com.android.credentialmanager.ui.CredentialSelectorUiState
-import com.android.credentialmanager.factory.fromSlice
-import com.android.credentialmanager.ui.model.PasswordUiModel
-import com.android.credentialmanager.model.Request
-
-fun Request.Get.toGet(): CredentialSelectorUiState.Get {
-    if (this.providers.isEmpty()) {
-        throw IllegalStateException("Invalid GetCredential request with empty list of providers.")
-    }
-
-    if (this.entries.isEmpty()) {
-        throw IllegalStateException("Invalid GetCredential request with empty list of entries.")
-    }
-
-    if (this.providers.size == 1) {
-        if (this.entries.size == 1) {
-            val slice = this.entries.first().slice
-            when (val credentialEntry = fromSlice(slice)) {
-                is PasswordCredentialEntry -> {
-                    return CredentialSelectorUiState.Get.SingleProviderSinglePassword(
-                        PasswordUiModel(credentialEntry.displayName.toString())
-                    )
-                }
-
-                is PublicKeyCredentialEntry -> {
-                    TODO("b/301206470 - to be implemented")
-                }
-
-                is CustomCredentialEntry -> {
-                    TODO("b/301206470 - to be implemented")
-                }
-
-                else -> {
-                    throw IllegalStateException(
-                        "Encountered unrecognized credential entry (${slice.spec?.type}) for " +
-                            "GetCredential request with single account"
-                    )
-                }
-            }
-        } else {
-            TODO("b/301206470 - to be implemented")
-        }
-    } else {
-        TODO("b/301206470 - to be implemented")
-    }
-}
\ No newline at end of file
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
new file mode 100644
index 0000000..f2f878e
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.0N
+ *
+ * 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.android.credentialmanager.ui.mappers
+
+import com.android.credentialmanager.model.Request
+import com.android.credentialmanager.CredentialSelectorUiState
+
+fun Request.Get.toGet(): CredentialSelectorUiState.Get {
+    // TODO: b/301206470 returning a hard coded state for MVP
+    if (true) return CredentialSelectorUiState.Get.SingleProviderSinglePassword
+
+    return if (providers.size == 1) {
+        if (passwordEntries.size == 1) {
+            CredentialSelectorUiState.Get.SingleProviderSinglePassword
+        } else {
+            TODO() // b/301206470 - Implement other get flows
+        }
+    } else {
+        TODO() // b/301206470 - Implement other get flows
+    }
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/MainScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/LoadingScreen.kt
similarity index 74%
rename from packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/MainScreen.kt
rename to packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/LoadingScreen.kt
index 94a671e..b3ab0c4 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/MainScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/LoadingScreen.kt
@@ -16,17 +16,15 @@
 
 package com.android.credentialmanager.ui.screens
 
-import androidx.compose.foundation.layout.Box
 import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.wear.compose.material.Text
 
 @Composable
-fun MainScreen(
+fun LoadingScreen(
     modifier: Modifier = Modifier
 ) {
-    Box(modifier = modifier, contentAlignment = Alignment.Center) {
-        Text("This is a placeholder for the main screen.")
-    }
+    // Don't display anything, assuming that there should be minimal latency
+    // to parse the Credential Manager intent and define the state of the
+    // app. If latency is big, then a "loading" screen should be displayed
+    // to the user.
 }
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasswordScreen.kt
deleted file mode 100644
index d863d3c..0000000
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasswordScreen.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 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
- *
- *      https://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.
- */
-
-@file:OptIn(ExperimentalHorologistApi::class)
-
-package com.android.credentialmanager.ui.screens
-
-import androidx.compose.foundation.layout.padding
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.dp
-import com.android.credentialmanager.R
-import com.android.credentialmanager.ui.components.DialogButtonsRow
-import com.android.credentialmanager.ui.components.PasswordRow
-import com.android.credentialmanager.ui.components.SignInHeader
-import com.google.android.horologist.annotations.ExperimentalHorologistApi
-import com.google.android.horologist.compose.layout.ScalingLazyColumnState
-import com.google.android.horologist.compose.layout.belowTimeTextPreview
-import com.google.android.horologist.compose.tools.WearPreview
-
-@Composable
-fun SinglePasswordScreen(
-    email: String,
-    onCancelClick: () -> Unit,
-    onOKClick: () -> Unit,
-    columnState: ScalingLazyColumnState,
-    modifier: Modifier = Modifier,
-) {
-    SingleAccountScreen(
-        headerContent = {
-            SignInHeader(
-                icon = R.drawable.passkey_icon,
-                title = stringResource(R.string.use_password_title),
-            )
-        },
-        accountContent = {
-            PasswordRow(
-                email = email,
-                modifier = Modifier.padding(top = 10.dp),
-            )
-        },
-        columnState = columnState,
-        modifier = modifier.padding(horizontal = 10.dp)
-    ) {
-        item {
-            DialogButtonsRow(
-                onCancelClick = onCancelClick,
-                onOKClick = onOKClick,
-                modifier = Modifier.padding(top = 10.dp)
-            )
-        }
-    }
-}
-
-@WearPreview
-@Composable
-fun SinglePasswordScreenPreview() {
-    SinglePasswordScreen(
-        email = "beckett_bakery@gmail.com",
-        onCancelClick = {},
-        onOKClick = {},
-        columnState = belowTimeTextPreview(),
-    )
-}
-
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SingleAccountScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/SingleAccountScreen.kt
similarity index 97%
rename from packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SingleAccountScreen.kt
rename to packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/SingleAccountScreen.kt
index f344ad0..8532783 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SingleAccountScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/SingleAccountScreen.kt
@@ -16,7 +16,7 @@
 
 @file:OptIn(ExperimentalHorologistApi::class)
 
-package com.android.credentialmanager.ui.screens
+package com.android.credentialmanager.ui.screens.single
 
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.padding
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasskeyScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt
similarity index 94%
rename from packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasskeyScreen.kt
rename to packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt
index c8f871e..c9b0230 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/SinglePasskeyScreen.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/passkey/SinglePasskeyScreen.kt
@@ -16,7 +16,7 @@
 
 @file:OptIn(ExperimentalHorologistApi::class)
 
-package com.android.credentialmanager.ui.screens
+package com.android.credentialmanager.ui.screens.single.passkey
 
 import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
@@ -27,6 +27,7 @@
 import com.android.credentialmanager.ui.components.AccountRow
 import com.android.credentialmanager.ui.components.DialogButtonsRow
 import com.android.credentialmanager.ui.components.SignInHeader
+import com.android.credentialmanager.ui.screens.single.SingleAccountScreen
 import com.google.android.horologist.annotations.ExperimentalHorologistApi
 import com.google.android.horologist.compose.layout.ScalingLazyColumnState
 import com.google.android.horologist.compose.layout.belowTimeTextPreview
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
new file mode 100644
index 0000000..c885ec4
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreen.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright 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
+ *
+ *      https://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.
+ */
+
+@file:OptIn(ExperimentalHorologistApi::class)
+
+package com.android.credentialmanager.ui.screens.single.password
+
+import android.util.Log
+import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.android.credentialmanager.R
+import com.android.credentialmanager.TAG
+import com.android.credentialmanager.activity.StartBalIntentSenderForResultContract
+import com.android.credentialmanager.ui.components.DialogButtonsRow
+import com.android.credentialmanager.ui.components.PasswordRow
+import com.android.credentialmanager.ui.components.SignInHeader
+import com.android.credentialmanager.ui.screens.single.SingleAccountScreen
+import com.google.android.horologist.annotations.ExperimentalHorologistApi
+import com.google.android.horologist.compose.layout.ScalingLazyColumnState
+import com.google.android.horologist.compose.layout.belowTimeTextPreview
+import com.google.android.horologist.compose.tools.WearPreview
+
+@Composable
+fun SinglePasswordScreen(
+    columnState: ScalingLazyColumnState,
+    onCloseApp: () -> Unit,
+    modifier: Modifier = Modifier,
+    viewModel: SinglePasswordScreenViewModel =
+        viewModel(factory = SinglePasswordScreenViewModel.Factory),
+) {
+    viewModel.initialize()
+
+    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
+
+    when (val state = uiState) {
+        SinglePasswordScreenUiState.Idle -> {
+            // TODO: b/301206470 implement latency version of the screen
+        }
+
+        is SinglePasswordScreenUiState.Loaded -> {
+            val model = state.passwordUiModel
+            SinglePasswordScreen(
+                email = model.email,
+                onCancelClick = viewModel::onCancelClick,
+                onOKClick = viewModel::onOKClick,
+                columnState = columnState,
+                modifier = modifier
+            )
+        }
+
+        is SinglePasswordScreenUiState.PasswordSelected -> {
+            val launcher = rememberLauncherForActivityResult(
+                StartBalIntentSenderForResultContract()
+            ) {
+                viewModel.onPasswordInfoRetrieved(it.resultCode, it.data)
+            }
+
+            SideEffect {
+                launcher.launch(state.intentSenderRequest)
+            }
+        }
+
+        SinglePasswordScreenUiState.Cancel -> {
+            // TODO: b/301206470 implement navigation for when user taps cancel
+        }
+
+        SinglePasswordScreenUiState.Error -> {
+            // TODO: b/301206470 implement navigation for when there is an error to load screen
+        }
+
+        SinglePasswordScreenUiState.Completed -> {
+            Log.d(TAG, "Received signal to finish the activity.")
+            onCloseApp()
+        }
+    }
+}
+
+@Composable
+fun SinglePasswordScreen(
+    email: String,
+    onCancelClick: () -> Unit,
+    onOKClick: () -> Unit,
+    columnState: ScalingLazyColumnState,
+    modifier: Modifier = Modifier,
+) {
+    SingleAccountScreen(
+        headerContent = {
+            SignInHeader(
+                icon = R.drawable.passkey_icon,
+                title = stringResource(R.string.use_password_title),
+            )
+        },
+        accountContent = {
+            PasswordRow(
+                email = email,
+                modifier = Modifier.padding(top = 10.dp),
+            )
+        },
+        columnState = columnState,
+        modifier = modifier.padding(horizontal = 10.dp)
+    ) {
+        item {
+            DialogButtonsRow(
+                onCancelClick = onCancelClick,
+                onOKClick = onOKClick,
+                modifier = Modifier.padding(top = 10.dp)
+            )
+        }
+    }
+}
+
+@WearPreview
+@Composable
+fun SinglePasswordScreenPreview() {
+    SinglePasswordScreen(
+        email = "beckett_bakery@gmail.com",
+        onCancelClick = {},
+        onOKClick = {},
+        columnState = belowTimeTextPreview(),
+    )
+}
+
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
new file mode 100644
index 0000000..9b06622
--- /dev/null
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/screens/single/password/SinglePasswordScreenViewModel.kt
@@ -0,0 +1,154 @@
+/*
+ * 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.0N
+ *
+ * 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.android.credentialmanager.ui.screens.single.password
+
+import android.content.Intent
+import android.credentials.ui.BaseDialogResult
+import android.credentials.ui.ProviderPendingIntentResponse
+import android.credentials.ui.UserSelectionDialogResult
+import android.os.Bundle
+import android.util.Log
+import androidx.activity.result.IntentSenderRequest
+import androidx.annotation.MainThread
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
+import androidx.lifecycle.viewModelScope
+import androidx.lifecycle.viewmodel.CreationExtras
+import com.android.credentialmanager.CredentialSelectorApp
+import com.android.credentialmanager.IS_AUTO_SELECTED_KEY
+import com.android.credentialmanager.TAG
+import com.android.credentialmanager.model.Password
+import com.android.credentialmanager.model.Request
+import com.android.credentialmanager.repository.RequestRepository
+import com.android.credentialmanager.ui.model.PasswordUiModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
+
+class SinglePasswordScreenViewModel(
+    private val requestRepository: RequestRepository,
+) : ViewModel() {
+
+    private var initializeCalled = false
+
+    private lateinit var requestGet: Request.Get
+    private lateinit var password: Password
+
+    private val _uiState =
+        MutableStateFlow<SinglePasswordScreenUiState>(SinglePasswordScreenUiState.Idle)
+    val uiState: StateFlow<SinglePasswordScreenUiState> = _uiState
+
+    @MainThread
+    fun initialize() {
+        if (initializeCalled) return
+        initializeCalled = true
+
+        viewModelScope.launch {
+            val request = requestRepository.requests.first()
+            Log.d(TAG, "request: $request")
+
+            if (request !is Request.Get) {
+                _uiState.value = SinglePasswordScreenUiState.Error
+            } else {
+                requestGet = request
+                if (requestGet.passwordEntries.isEmpty()) {
+                    Log.d(TAG, "Empty passwordEntries")
+                    _uiState.value = SinglePasswordScreenUiState.Error
+                } else {
+                    password = requestGet.passwordEntries.first()
+                    _uiState.value = SinglePasswordScreenUiState.Loaded(
+                        PasswordUiModel(
+                            email = password.passwordCredentialEntry.username.toString(),
+                        )
+                    )
+                }
+            }
+        }
+    }
+
+    fun onCancelClick() {
+        _uiState.value = SinglePasswordScreenUiState.Cancel
+    }
+
+    fun onOKClick() {
+        // TODO: b/301206470 move this code to shared module
+        val entryIntent = password.entry.frameworkExtrasIntent
+        entryIntent?.putExtra(IS_AUTO_SELECTED_KEY, false)
+        val intentSenderRequest = IntentSenderRequest.Builder(
+            pendingIntent = password.passwordCredentialEntry.pendingIntent
+        ).setFillInIntent(entryIntent).build()
+
+        _uiState.value = SinglePasswordScreenUiState.PasswordSelected(
+            intentSenderRequest = intentSenderRequest
+        )
+    }
+
+    fun onPasswordInfoRetrieved(
+        resultCode: Int? = null,
+        resultData: Intent? = null,
+    ) {
+        // TODO: b/301206470 move this code to shared module
+        Log.d(TAG, "credential selected: {provider=${password.providerId}" +
+            ", key=${password.entry.key}, subkey=${password.entry.subkey}}")
+
+        val userSelectionDialogResult = UserSelectionDialogResult(
+            requestGet.token,
+            password.providerId,
+            password.entry.key,
+            password.entry.subkey,
+            if (resultCode != null) ProviderPendingIntentResponse(resultCode, resultData) else null
+        )
+        val resultDataBundle = Bundle()
+        UserSelectionDialogResult.addToBundle(userSelectionDialogResult, resultDataBundle)
+        requestGet.resultReceiver?.send(
+            BaseDialogResult.RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION,
+            resultDataBundle
+        )
+
+        _uiState.value = SinglePasswordScreenUiState.Completed
+    }
+
+    companion object {
+        val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
+            @Suppress("UNCHECKED_CAST")
+            override fun <T : ViewModel> create(
+                modelClass: Class<T>,
+                extras: CreationExtras
+            ): T {
+                val application = checkNotNull(extras[APPLICATION_KEY])
+
+                return SinglePasswordScreenViewModel(
+                    requestRepository = (application as CredentialSelectorApp).requestRepository,
+                ) as T
+            }
+        }
+    }
+}
+
+sealed class SinglePasswordScreenUiState {
+    data object Idle : SinglePasswordScreenUiState()
+    data class Loaded(val passwordUiModel: PasswordUiModel) : SinglePasswordScreenUiState()
+    data class PasswordSelected(
+        val intentSenderRequest: IntentSenderRequest
+    ) : SinglePasswordScreenUiState()
+
+    data object Cancel : SinglePasswordScreenUiState()
+    data object Error : SinglePasswordScreenUiState()
+    data object Completed : SinglePasswordScreenUiState()
+}