Merge "Initial Design for Play Auth Module Connection" into androidx-main
diff --git a/credentials/credentials-play-services-auth/OWNERS b/credentials/credentials-play-services-auth/OWNERS
new file mode 100644
index 0000000..7b0e47f
--- /dev/null
+++ b/credentials/credentials-play-services-auth/OWNERS
@@ -0,0 +1,8 @@
+# Bug Component: 1218609
+sgjerry@google.com
+helenqin@google.com
+reemabajwa@google.com
+duqinmei@google.com
+beccahughes@google.com
+leecam@google.com
+akaphle@google.com
diff --git a/credentials/credentials-play-services-auth/build.gradle b/credentials/credentials-play-services-auth/build.gradle
index 725338d..430b672 100644
--- a/credentials/credentials-play-services-auth/build.gradle
+++ b/credentials/credentials-play-services-auth/build.gradle
@@ -26,7 +26,22 @@
api(libs.kotlinStdlib)
api project(":credentials:credentials")
- // Add dependencies here
+ implementation project(path: ':activity:activity')
+ // Closed source dependencies
+ implementation(libs.playServicesAuth) {
+ exclude group: "androidx.loader"
+ exclude group: "androidx.fragment"
+ }
+ implementation(libs.playServicesFido)
+
+ androidTestImplementation(libs.junit)
+ androidTestImplementation(libs.testExtJunit)
+ androidTestImplementation(libs.testCore)
+ androidTestImplementation(libs.testRunner)
+ androidTestImplementation(libs.testRules)
+ androidTestImplementation(libs.truth)
+ androidTestImplementation(project(":internal-testutils-truth"))
+ androidTestImplementation(libs.kotlinCoroutinesAndroid)
}
android {
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderPlayServicesImpl.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderPlayServicesImpl.kt
index 59ad679..6e59651 100644
--- a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderPlayServicesImpl.kt
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/CredentialProviderPlayServicesImpl.kt
@@ -18,12 +18,16 @@
import android.app.Activity
import android.os.CancellationSignal
+import android.util.Log
import androidx.credentials.CreateCredentialRequest
import androidx.credentials.CreateCredentialResponse
+import androidx.credentials.CreatePasswordRequest
+import androidx.credentials.CreatePublicKeyCredentialRequest
import androidx.credentials.CredentialManagerCallback
import androidx.credentials.CredentialProvider
import androidx.credentials.GetCredentialRequest
import androidx.credentials.GetCredentialResponse
+import androidx.credentials.playservices.controllers.CreatePassword.CredentialProviderCreatePasswordController
import java.util.concurrent.Executor
/**
@@ -32,6 +36,7 @@
*
* @hide
*/
+@Suppress("deprecation")
class CredentialProviderPlayServicesImpl : CredentialProvider {
override fun onGetCredential(
request: GetCredentialRequest,
@@ -40,6 +45,10 @@
executor: Executor,
callback: CredentialManagerCallback<GetCredentialResponse>
) {
+ if (cancellationSignal != null) {
+ Log.i(TAG, "onCreateCredential cancellationSignal not used")
+ TODO("Use Cancel Operations Properly")
+ }
TODO("Not yet implemented")
}
@@ -50,10 +59,31 @@
executor: Executor,
callback: CredentialManagerCallback<CreateCredentialResponse>
) {
- TODO("Not yet implemented")
+ if (cancellationSignal != null) {
+ Log.i(TAG, "onCreateCredential cancellationSignal not used")
+ TODO("Use Cancel Operations Properly")
+ }
+ val fragmentManager: android.app.FragmentManager = activity!!.fragmentManager
+ // TODO("Manage Fragment Lifecycle and Fragment Manager Properly")
+ if (request is CreatePasswordRequest) {
+ CredentialProviderCreatePasswordController.getInstance(
+ fragmentManager).invokePlayServices(
+ request,
+ callback,
+ executor)
+ } else if (request is CreatePublicKeyCredentialRequest) {
+ TODO("Not yet implemented")
+ } else {
+ throw UnsupportedOperationException(
+ "Unsupported request; not password or publickeycredential")
+ }
}
override fun isAvailableOnDevice(): Boolean {
TODO("Not yet implemented")
}
+
+ companion object {
+ private val TAG = CredentialProviderPlayServicesImpl::class.java.name
+ }
}
\ No newline at end of file
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt
new file mode 100644
index 0000000..994429a
--- /dev/null
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/BeginSignIn/CredentialProviderBeginSignInController.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright 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 androidx.credentials.playservices.controllers.BeginSignIn
+
+import android.content.Intent
+import android.util.Log
+import androidx.credentials.CredentialManagerCallback
+import androidx.credentials.GetCredentialRequest
+import androidx.credentials.GetCredentialResponse
+import androidx.credentials.playservices.controllers.CredentialProviderController
+import com.google.android.gms.auth.api.identity.BeginSignInRequest
+import com.google.android.gms.auth.api.identity.SignInCredential
+import java.util.concurrent.Executor
+
+/**
+ * A controller to handle the BeginSignIn flow with play services.
+ *
+ * @hide
+ */
+@Suppress("deprecation")
+class CredentialProviderBeginSignInController : CredentialProviderController<
+ GetCredentialRequest,
+ BeginSignInRequest,
+ SignInCredential,
+ GetCredentialResponse>() {
+
+ /**
+ * The callback object state, used in the protected handleResponse method.
+ */
+ private lateinit var callback: CredentialManagerCallback<GetCredentialResponse>
+ /**
+ * The callback requires an executor to invoke it.
+ */
+ private lateinit var executor: Executor
+
+ override fun invokePlayServices(
+ request: GetCredentialRequest,
+ callback: CredentialManagerCallback<GetCredentialResponse>,
+ executor: Executor
+ ) {
+ TODO("Not yet implemented")
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ handleResponse(requestCode, resultCode, data)
+ }
+
+ private fun handleResponse(uniqueRequestCode: Int, resultCode: Int, data: Intent?) {
+ Log.i(TAG, "$uniqueRequestCode $resultCode $data")
+ TODO("Not yet implemented")
+ }
+
+ override fun convertToPlayServices(request: GetCredentialRequest): BeginSignInRequest {
+ TODO("Not yet implemented")
+ }
+
+ override fun convertToCredentialProvider(response: SignInCredential): GetCredentialResponse {
+ TODO("Not yet implemented")
+ }
+
+ companion object {
+ private val TAG = CredentialProviderBeginSignInController::class.java.name
+ private const val REQUEST_CODE_BEGIN_SIGN_IN: Int = 1
+ // TODO("Ensure this works with the lifecycle")
+
+ /**
+ * This finds a past version of the BeginSignInController if it exists, otherwise
+ * it generates a new instance.
+ *
+ * @param fragmentManager a fragment manager pulled from an android activity
+ * @return a credential provider controller for a specific credential request
+ */
+ @JvmStatic
+ fun getInstance(fragmentManager: android.app.FragmentManager):
+ CredentialProviderBeginSignInController {
+ var controller = findPastController(REQUEST_CODE_BEGIN_SIGN_IN, fragmentManager)
+ if (controller == null) {
+ controller = CredentialProviderBeginSignInController()
+ fragmentManager.beginTransaction().add(controller,
+ REQUEST_CODE_BEGIN_SIGN_IN.toString())
+ .commitAllowingStateLoss()
+ fragmentManager.executePendingTransactions()
+ }
+ return controller
+ }
+
+ internal fun findPastController(
+ requestCode: Int,
+ fragmentManager: android.app.FragmentManager
+ ): CredentialProviderBeginSignInController? {
+ try {
+ return fragmentManager.findFragmentByTag(requestCode.toString())
+ as CredentialProviderBeginSignInController?
+ } catch (e: Exception) {
+ Log.i(TAG, "Old fragment found of different type - replacement required")
+ // TODO("Ensure this is well tested for fragment issues")
+ return null
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePassword/CredentialProviderCreatePasswordController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePassword/CredentialProviderCreatePasswordController.kt
new file mode 100644
index 0000000..7015a9a
--- /dev/null
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePassword/CredentialProviderCreatePasswordController.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright 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 androidx.credentials.playservices.controllers.CreatePassword
+
+import android.content.Intent
+import android.util.Log
+import androidx.credentials.CreateCredentialResponse
+import androidx.credentials.CreatePasswordRequest
+import androidx.credentials.CredentialManagerCallback
+import androidx.credentials.playservices.controllers.CredentialProviderController
+import com.google.android.gms.auth.api.identity.SavePasswordRequest
+import java.util.concurrent.Executor
+
+/**
+ * A controller to handle the CreatePassword flow with play services.
+ *
+ * @hide
+ */
+@Suppress("deprecation")
+class CredentialProviderCreatePasswordController : CredentialProviderController<
+ CreatePasswordRequest,
+ SavePasswordRequest,
+ Intent,
+ CreateCredentialResponse>() {
+
+ /**
+ * The callback object state, used in the protected handleResponse method.
+ */
+ private lateinit var callback: CredentialManagerCallback<CreateCredentialResponse>
+
+ /**
+ * The callback requires an executor to invoke it.
+ */
+ private lateinit var executor: Executor
+
+ override fun invokePlayServices(
+ request: CreatePasswordRequest,
+ callback: CredentialManagerCallback<CreateCredentialResponse>,
+ executor: Executor
+ ) {
+ TODO("Not yet implemented")
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ handleResponse(requestCode, resultCode, data)
+ }
+
+ private fun handleResponse(uniqueRequestCode: Int, resultCode: Int, data: Intent?) {
+ Log.i(TAG, "$uniqueRequestCode $resultCode $data")
+ TODO("Not yet implemented")
+ }
+
+ override fun convertToPlayServices(request: CreatePasswordRequest): SavePasswordRequest {
+ TODO("Not yet implemented")
+ }
+
+ override fun convertToCredentialProvider(response: Intent): CreateCredentialResponse {
+ TODO("Not yet implemented")
+ }
+
+ companion object {
+ private val TAG = CredentialProviderCreatePasswordController::class.java.name
+ private const val REQUEST_CODE_GIS_SAVE_PASSWORD: Int = 1
+ // TODO("Ensure this works with the lifecycle")
+
+ /**
+ * This finds a past version of the
+ * [CredentialProviderCreatePasswordController] if it exists, otherwise
+ * it generates a new instance.
+ *
+ * @param fragmentManager a fragment manager pulled from an android activity
+ * @return a credential provider controller for CreatePublicKeyCredential
+ */
+ @JvmStatic
+ fun getInstance(fragmentManager: android.app.FragmentManager):
+ CredentialProviderCreatePasswordController {
+ var controller = findPastController(REQUEST_CODE_GIS_SAVE_PASSWORD, fragmentManager)
+ if (controller == null) {
+ controller = CredentialProviderCreatePasswordController()
+ fragmentManager.beginTransaction().add(controller,
+ REQUEST_CODE_GIS_SAVE_PASSWORD.toString())
+ .commitAllowingStateLoss()
+ fragmentManager.executePendingTransactions()
+ }
+ return controller
+ }
+
+ internal fun findPastController(
+ requestCode: Int,
+ fragmentManager: android.app.FragmentManager
+ ): CredentialProviderCreatePasswordController? {
+ try {
+ return fragmentManager.findFragmentByTag(requestCode.toString())
+ as CredentialProviderCreatePasswordController
+ } catch (e: Exception) {
+ Log.i(TAG, "Old fragment found of different type - replacement required")
+ // TODO("Ensure this is well tested for fragment issues")
+ return null
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt
new file mode 100644
index 0000000..d6e8763
--- /dev/null
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CreatePublicKeyCredential/CredentialProviderCreatePublicKeyCredentialController.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright 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 androidx.credentials.playservices.controllers.CreatePublicKeyCredential
+
+import android.content.Intent
+import android.util.Log
+import androidx.credentials.CreateCredentialResponse
+import androidx.credentials.CreatePublicKeyCredentialRequest
+import androidx.credentials.CredentialManagerCallback
+import androidx.credentials.playservices.controllers.CredentialProviderController
+import com.google.android.gms.fido.fido2.api.common.PublicKeyCredential
+import com.google.android.gms.fido.fido2.api.common.PublicKeyCredentialCreationOptions
+import java.util.concurrent.Executor
+
+/**
+ * A controller to handle the CreatePublicKeyCredential flow with play services.
+ *
+ * @hide
+ */
+@Suppress("deprecation")
+class CredentialProviderCreatePublicKeyCredentialController :
+ CredentialProviderController<
+ CreatePublicKeyCredentialRequest,
+ PublicKeyCredentialCreationOptions,
+ PublicKeyCredential,
+ CreateCredentialResponse>() {
+
+ /**
+ * The callback object state, used in the protected handleResponse method.
+ */
+ private lateinit var callback: CredentialManagerCallback<CreateCredentialResponse>
+
+ /**
+ * The callback requires an executor to invoke it.
+ */
+ private lateinit var executor: Executor
+
+ override fun invokePlayServices(
+ request: CreatePublicKeyCredentialRequest,
+ callback: CredentialManagerCallback<CreateCredentialResponse>,
+ executor: Executor
+ ) {
+ TODO("Not yet implemented")
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ handleResponse(requestCode, resultCode, data)
+ }
+
+ private fun handleResponse(uniqueRequestCode: Int, resultCode: Int, data: Intent?) {
+ Log.i(TAG, "$uniqueRequestCode $resultCode $data")
+ TODO("Not yet implemented")
+ }
+
+ override fun convertToPlayServices(request: CreatePublicKeyCredentialRequest):
+ PublicKeyCredentialCreationOptions {
+ TODO("Not yet implemented")
+ }
+
+ override fun convertToCredentialProvider(response: PublicKeyCredential):
+ CreateCredentialResponse {
+ TODO("Not yet implemented")
+ }
+
+ companion object {
+ private val TAG = CredentialProviderCreatePublicKeyCredentialController::class.java.name
+ private const val REQUEST_CODE_GIS_SAVE_PUBLIC_KEY_CREDENTIAL: Int = 1
+ // TODO("Ensure this works with the lifecycle")
+
+ /**
+ * This finds a past version of the
+ * [CredentialProviderCreatePublicKeyCredentialController] if it exists, otherwise
+ * it generates a new instance.
+ *
+ * @param fragmentManager a fragment manager pulled from an android activity
+ * @return a credential provider controller for CreatePublicKeyCredential
+ */
+ @JvmStatic
+ fun getInstance(fragmentManager: android.app.FragmentManager):
+ CredentialProviderCreatePublicKeyCredentialController {
+ var controller = findPastController(
+ REQUEST_CODE_GIS_SAVE_PUBLIC_KEY_CREDENTIAL,
+ fragmentManager)
+ if (controller == null) {
+ controller = CredentialProviderCreatePublicKeyCredentialController()
+ fragmentManager.beginTransaction().add(controller,
+ REQUEST_CODE_GIS_SAVE_PUBLIC_KEY_CREDENTIAL.toString())
+ .commitAllowingStateLoss()
+ fragmentManager.executePendingTransactions()
+ }
+ return controller
+ }
+
+ internal fun findPastController(
+ requestCode: Int,
+ fragmentManager: android.app.FragmentManager
+ ): CredentialProviderCreatePublicKeyCredentialController? {
+ try {
+ return fragmentManager.findFragmentByTag(requestCode.toString())
+ as CredentialProviderCreatePublicKeyCredentialController
+ } catch (e: Exception) {
+ Log.i(TAG, "Old fragment found of different type - replacement required")
+ // TODO("Ensure this is well tested for fragment issues")
+ return null
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CredentialProviderController.kt b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CredentialProviderController.kt
new file mode 100644
index 0000000..12ab060
--- /dev/null
+++ b/credentials/credentials-play-services-auth/src/main/java/androidx/credentials/playservices/controllers/CredentialProviderController.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 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 androidx.credentials.playservices.controllers
+
+import androidx.credentials.CredentialManagerCallback
+import java.util.concurrent.Executor
+
+/**
+ * Extensible abstract class for credential controllers. Please implement this class per every
+ * request/response credential type. Unique logic is left to the use case of the implementation.
+ * If you are building your own version as an OEM, the below can be mimicked to your own
+ * credential provider equivalent and whatever internal service you invoke.
+ *
+ * @param T1 the credential request type from credential manager
+ * @param T2 the credential request type converted to play services
+ * @param R2 the credential response type from play services
+ * @param R1 the credential response type converted back to that used by credential manager
+ *
+ * @hide
+ */
+@Suppress("deprecation")
+abstract class CredentialProviderController<T1 : Any, T2 : Any, R2 : Any, R1 : Any> : android.app
+ .Fragment() {
+
+ /**
+ * Invokes the flow that starts retrieving credential data. In this use case, we invoke
+ * play service modules.
+ *
+ * @param request a credential provider request
+ * @param callback a credential manager callback with a credential provider response
+ * @param executor to be used in any multi-threaded operation calls, such as listenable futures
+ */
+ abstract fun invokePlayServices(
+ request: T1,
+ callback: CredentialManagerCallback<R1>,
+ executor: Executor
+ )
+
+ /**
+ * Allows converting from a credential provider request to a play service request.
+ *
+ * @param request a credential provider request
+ * @return a play service request
+ */
+ protected abstract fun convertToPlayServices(request: T1): T2
+
+ /**
+ * Allows converting from a play service response to a credential provider response.
+ *
+ * @param response a play service response
+ * @return a credential provider response
+ */
+ protected abstract fun convertToCredentialProvider(response: R2): R1
+}
\ No newline at end of file
diff --git a/credentials/credentials/src/main/java/androidx/credentials/CredentialProvider.kt b/credentials/credentials/src/main/java/androidx/credentials/CredentialProvider.kt
index c0c51a0..0be5f0c 100644
--- a/credentials/credentials/src/main/java/androidx/credentials/CredentialProvider.kt
+++ b/credentials/credentials/src/main/java/androidx/credentials/CredentialProvider.kt
@@ -55,7 +55,7 @@
*/
fun onGetCredential(
request: GetCredentialRequest,
- activity: Activity?,
+ activity: Activity?, // TODO("Update on optionality")
cancellationSignal: CancellationSignal?,
executor: Executor,
callback: CredentialManagerCallback<GetCredentialResponse>,
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 54cd813..60da42a 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -193,8 +193,11 @@
okhttpMockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version = "3.14.7" }
okio = { module = "com.squareup.okio:okio", version = "3.1.0" }
playFeatureDelivery = { module = "com.google.android.play:feature-delivery", version = "2.0.1" }
+playCore = { module = "com.google.android.play:core", version = "1.10.3" }
+playServicesAuth = {module = "com.google.android.gms:play-services-auth", version = "20.3.0"}
playServicesBase = { module = "com.google.android.gms:play-services-base", version = "17.0.0" }
playServicesBasement = { module = "com.google.android.gms:play-services-basement", version = "17.0.0" }
+playServicesFido = {module = "com.google.android.gms:play-services-fido", version = "19.0.0-beta"}
playServicesWearable = { module = "com.google.android.gms:play-services-wearable", version = "17.1.0" }
paparazzi = { module = "app.cash.paparazzi:paparazzi", version.ref = "paparazzi" }
paparazziNativeJvm = { module = "app.cash.paparazzi:layoutlib-native-jdk11", version.ref = "paparazziNative" }