Merge "Improvements to MVP: address TODOs to move code to shared module." into main
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
index 8986e52..98ad22c 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/IntentParser.kt
@@ -29,7 +29,7 @@
     packageManager: PackageManager,
     previousIntent: Intent? = null,
 ): Request {
-    this.toRequestClose(packageManager, previousIntent)?.let { closeRequest ->
+    this.toRequestClose(previousIntent)?.let { closeRequest ->
         return closeRequest
     }
 
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/PasswordKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/PasswordKtx.kt
new file mode 100644
index 0000000..3471070
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/PasswordKtx.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.ktx
+
+import androidx.activity.result.IntentSenderRequest
+import com.android.credentialmanager.IS_AUTO_SELECTED_KEY
+import com.android.credentialmanager.model.Password
+
+fun Password.getIntentSenderRequest(
+    isAutoSelected: Boolean = false
+): IntentSenderRequest {
+    val entryIntent = entry.frameworkExtrasIntent
+    entryIntent?.putExtra(IS_AUTO_SELECTED_KEY, isAutoSelected)
+
+    return IntentSenderRequest.Builder(
+        pendingIntent = passwordCredentialEntry.pendingIntent
+    ).setFillInIntent(entryIntent).build()
+}
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 555a86f..99dc9ec 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCancelMapper.kt
@@ -31,9 +31,6 @@
             Log.d(TAG, "Received UI cancel request with an invalid package name.")
             null
         } else {
-            Request.Cancel(
-                showCancellationUi = cancelUiRequest.shouldShowCancellationUi(),
-                appName = appLabel
-            )
+            Request.Cancel(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
index 6de3e7d..02ee77b 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCloseMapper.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestCloseMapper.kt
@@ -17,31 +17,33 @@
 package com.android.credentialmanager.mapper
 
 import android.content.Intent
-import android.content.pm.PackageManager
+import com.android.credentialmanager.ktx.cancelUiRequest
 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
+    this.cancelUiRequest?.let { cancelUiRequest ->
 
-    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.
+        if (cancelUiRequest.shouldShowCancellationUi()) {
+            // 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
     }
 
-    return Request.Close
+    return null
 }
\ No newline at end of file
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 6011a1c..ed98f3e 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/Request.kt
@@ -37,7 +37,6 @@
      * Request to close the app, displaying a message to the user.
      */
     data class Cancel(
-        val showCancellationUi: Boolean,
         val appName: String
     ) : Request()
 
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/PasswordRepository.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/PasswordRepository.kt
new file mode 100644
index 0000000..1cce3ba
--- /dev/null
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/repository/PasswordRepository.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.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 com.android.credentialmanager.TAG
+import com.android.credentialmanager.model.Password
+import com.android.credentialmanager.model.Request
+
+class PasswordRepository {
+
+    suspend fun selectPassword(
+        password: Password,
+        request: Request.Get,
+        resultCode: Int? = null,
+        resultData: Intent? = null,
+    ) {
+        Log.d(TAG, "password selected: {provider=${password.providerId}" +
+            ", key=${password.entry.key}, subkey=${password.entry.subkey}}")
+
+        val userSelectionDialogResult = UserSelectionDialogResult(
+            request.token,
+            password.providerId,
+            password.entry.key,
+            password.entry.subkey,
+            if (resultCode != null) ProviderPendingIntentResponse(resultCode, resultData) else null
+        )
+        val resultDataBundle = Bundle()
+        UserSelectionDialogResult.addToBundle(userSelectionDialogResult, resultDataBundle)
+        request.resultReceiver?.send(
+            BaseDialogResult.RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION,
+            resultDataBundle
+        )
+    }
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
index 7c81fd0..e8e4033 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorApp.kt
@@ -18,11 +18,13 @@
 
 import android.app.Application
 import com.android.credentialmanager.di.inject
+import com.android.credentialmanager.repository.PasswordRepository
 import com.android.credentialmanager.repository.RequestRepository
 
 class CredentialSelectorApp : Application() {
 
     lateinit var requestRepository: RequestRepository
+    lateinit var passwordRepository: PasswordRepository
 
     override fun onCreate() {
         super.onCreate()
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/di/DI.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/DI.kt
index a11017b..1e8f83d 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/di/DI.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/di/DI.kt
@@ -2,12 +2,14 @@
 
 import android.app.Application
 import com.android.credentialmanager.CredentialSelectorApp
+import com.android.credentialmanager.repository.PasswordRepository
 import com.android.credentialmanager.repository.RequestRepository
 
 // TODO b/301601582 add Hilt for dependency injection
 
 fun CredentialSelectorApp.inject() {
     requestRepository = requestRepository(application = this)
+    passwordRepository = passwordRepository()
 }
 
 private fun requestRepository(
@@ -15,3 +17,5 @@
 ): RequestRepository = RequestRepository(
     application = application,
 )
+
+private fun passwordRepository(): PasswordRepository = PasswordRepository()
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
index c885ec4..c87cfd3 100644
--- 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
@@ -35,6 +35,7 @@
 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.model.PasswordUiModel
 import com.android.credentialmanager.ui.screens.single.SingleAccountScreen
 import com.google.android.horologist.annotations.ExperimentalHorologistApi
 import com.google.android.horologist.compose.layout.ScalingLazyColumnState
@@ -59,9 +60,8 @@
         }
 
         is SinglePasswordScreenUiState.Loaded -> {
-            val model = state.passwordUiModel
             SinglePasswordScreen(
-                email = model.email,
+                passwordUiModel = state.passwordUiModel,
                 onCancelClick = viewModel::onCancelClick,
                 onOKClick = viewModel::onOKClick,
                 columnState = columnState,
@@ -98,7 +98,7 @@
 
 @Composable
 fun SinglePasswordScreen(
-    email: String,
+    passwordUiModel: PasswordUiModel,
     onCancelClick: () -> Unit,
     onOKClick: () -> Unit,
     columnState: ScalingLazyColumnState,
@@ -113,7 +113,7 @@
         },
         accountContent = {
             PasswordRow(
-                email = email,
+                email = passwordUiModel.email,
                 modifier = Modifier.padding(top = 10.dp),
             )
         },
@@ -134,7 +134,7 @@
 @Composable
 fun SinglePasswordScreenPreview() {
     SinglePasswordScreen(
-        email = "beckett_bakery@gmail.com",
+        passwordUiModel = PasswordUiModel(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
index 9b06622..3167e67 100644
--- 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
@@ -17,10 +17,6 @@
 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
@@ -30,10 +26,11 @@
 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.ktx.getIntentSenderRequest
 import com.android.credentialmanager.model.Password
 import com.android.credentialmanager.model.Request
+import com.android.credentialmanager.repository.PasswordRepository
 import com.android.credentialmanager.repository.RequestRepository
 import com.android.credentialmanager.ui.model.PasswordUiModel
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -43,6 +40,7 @@
 
 class SinglePasswordScreenViewModel(
     private val requestRepository: RequestRepository,
+    private val passwordRepository: PasswordRepository,
 ) : ViewModel() {
 
     private var initializeCalled = false
@@ -87,15 +85,8 @@
     }
 
     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
+            intentSenderRequest = password.getIntentSenderRequest()
         )
     }
 
@@ -103,25 +94,16 @@
         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}}")
+        viewModelScope.launch {
+            passwordRepository.selectPassword(
+                password = password,
+                request = requestGet,
+                resultCode = resultCode,
+                resultData = resultData
+            )
 
-        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
+            _uiState.value = SinglePasswordScreenUiState.Completed
+        }
     }
 
     companion object {
@@ -135,6 +117,7 @@
 
                 return SinglePasswordScreenViewModel(
                     requestRepository = (application as CredentialSelectorApp).requestRepository,
+                    passwordRepository = application.passwordRepository,
                 ) as T
             }
         }