Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Identity Credentials example. #97

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions IdentityCredentials/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
*.iml
.gradle
/local.properties
.DS_Store
.idea
/build
/captures
.externalNativeBuild
.cxx
local.properties
12 changes: 12 additions & 0 deletions IdentityCredentials/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Android Identity Credential Sample
===================================

This sample demonstrates how to use the Identity Credential API. The Identity Credential API allows your app to store user credentials that it can later retrieve to re-authenticate users on a new device. This sample demonstrates that by allowing you to save text, uninstall the app, reinstall it and retreive that text.

Running the sample
-When you run the sample you will see a screen with an EditText and a button that says Sign In.
-To save text, type text into the EditText and press register. There will now be a TextView displaying the text you just typed in.
-To test Identity Credential, uninstall the app and reinstall it. Your text will still be displayed in the TextView.
-To clear your text press Sign Out.

To learn more about Identity check out the documentation: https://developers.google.com/identity/credential-management.
1 change: 1 addition & 0 deletions IdentityCredentials/app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
88 changes: 88 additions & 0 deletions IdentityCredentials/app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright 2021 Google LLC
*
* 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.
*/
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.hilt.android)
alias(libs.plugins.kotlin.kapt)
}

android {
compileSdk 35
namespace "com.google.android.gms.identity.credentials.sample"
defaultConfig {
applicationId "com.google.android.gms.identity.credentials.sample"
minSdk 21
targetSdk 35
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildFeatures {
viewBinding true
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}

kotlinOptions {
jvmTarget = '17'
}
}

dependencies {
implementation libs.kotlin.stdlib
implementation libs.material.design
implementation libs.androidx.activity.ktx
implementation libs.androidx.core.ktx
implementation libs.androidx.appcompat
implementation libs.androidx.constraintlayout
implementation libs.androidx.test.espresso.idling.resource

implementation libs.google.play.services.identity.credentials
implementation libs.androidx.credentials.play.services.auth

// Required because the broadcast pending-intent otherwise lacks the intent-flag.
// IllegalArgumentException: Targeting S+ (version 31 and above) requires that one of
// FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.
implementation libs.androidx.work.runtime

// Lifecycle
implementation libs.bundles.androidx.lifecycle

// Coroutines
implementation libs.kotlinx.coroutines.play.services

// Hilt
implementation libs.hilt.android
kapt libs.hilt.android.compiler
implementation libs.hilt.work
kapt libs.hilt.compiler

testImplementation libs.junit
debugImplementation libs.androidx.test.monitor
androidTestImplementation libs.bundles.androidx.test
}
21 changes: 21 additions & 0 deletions IdentityCredentials/app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
36 changes: 36 additions & 0 deletions IdentityCredentials/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 Google, Inc.
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.
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android">

<application
android:allowBackup="false"
android:name="com.google.android.gms.identity.credentials.sample.MainApplication"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.BlockStore">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2024 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.
*/
package com.google.android.gms.identity.credentials.sample

import android.content.Context
import com.google.android.gms.identitycredentials.IdentityCredentialManager
import com.google.android.gms.identitycredentials.IdentityCredentialClient
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

/**
* Hilt module that provides singleton (application-scoped) objects.
*/
@Module
@InstallIn(SingletonComponent::class)
class AppModule {
@Singleton
@Provides
fun provideIdentityCredentialClient(@ApplicationContext context: Context): IdentityCredentialClient =
IdentityCredentialManager.getClient(context)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright 2024 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.
*/
package com.google.android.gms.identity.credentials.sample

import android.os.Bundle
import android.os.ResultReceiver
import com.google.android.gms.identitycredentials.ClearRegistryRequest
import com.google.android.gms.identitycredentials.ClearRegistryResponse
import com.google.android.gms.identitycredentials.CredentialOption
import com.google.android.gms.identitycredentials.GetCredentialRequest
import com.google.android.gms.identitycredentials.IdentityCredentialClient
import com.google.android.gms.identitycredentials.PendingGetCredentialHandle
import com.google.android.gms.identitycredentials.RegistrationRequest
import com.google.android.gms.identitycredentials.RegistrationResponse
import com.google.android.gms.tasks.Task
import javax.inject.Inject

/**
* The Repository handles data operations and provides a clean API so that
* the rest of the app can retrieve the Identity Credential data easily.
* @see <a href="https://developers.google.com/android/reference/com/google/android/gms/identitycredentials/IdentityCredentialClient">IdentityCredentialClient</a>
* @see <a href="https://developers.google.com/android/reference/com/google/android/gms/common/api/CommonStatusCodes">CommonStatusCodes</a>
*/
class IdentityCredentialsRepository @Inject constructor(
private val client: IdentityCredentialClient
) {
/**
* Returns a Task which asynchronously generates a RegistrationResponse on success
* or throws an OperationException on failure, when attempting to write to the registry.
*
* eg. com.google.android.gms.common.api.ApiException: 17 means:
* The client attempted to call a method from an API that failed to connect.
*/
fun registerCredentials(
credentials: ByteArray, matcher: ByteArray, type: String,
requestType: String, protocolTypes: List<String>, id: String
): Task<RegistrationResponse> {
return client.registerCredentials(
RegistrationRequest(credentials, matcher, type, requestType, protocolTypes, id)
)
}

/** Returns a Task which asynchronously generates a pending intent to get credentials. */
fun getCredential(
credentialOptions: List<CredentialOption>, data: Bundle,
origin: String?, resultReceiver: ResultReceiver
): Task<PendingGetCredentialHandle> {
return client.getCredential(
GetCredentialRequest(credentialOptions, data, origin, resultReceiver)
)
}

/**
* Returns a Task which asynchronously generates a ClearRegistryResponse on success
* or throws an OperationException on failure, when attempting to clear from the registry.
*/
fun clearRegistry(): Task<ClearRegistryResponse> {
return client.clearRegistry(
ClearRegistryRequest()
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright 2024 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.
*/
package com.google.android.gms.identity.credentials.sample

import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.ResultReceiver
import android.text.Editable
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.google.android.gms.identity.credentials.sample.databinding.ActivityMainBinding
import com.google.android.gms.identitycredentials.CredentialOption
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

private val binding: ActivityMainBinding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
private val viewModel: MainViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContentView(binding.root)

lifecycleScope.launch {
viewModel.state
.collect { state ->
binding.buttonClearRegistry.isVisible = state.hasCredential
binding.buttonRegisterCredentials.isVisible = !state.hasCredential
if (state.hasCredential) {
binding.nameTextView.text.insert(0, state.credential.toString())
}
}
}

binding.buttonRegisterCredentials.setOnClickListener {
val credentialBytes = binding.nameTextView.text.toString().toByteArray()
val matcherBytes = "".toByteArray()

// IllegalArgumentException: Either type: default,
// or requestType: default and protocolTypes: [] must be specified,
// but all were blank, or for protocolTypes, empty or full of blank elements.
viewModel.registerCredentials(
credentialBytes,
matcherBytes,
"default",
"",
emptyList(),
"1"
)
}

binding.buttonGetCredential.setOnClickListener {
val credentialOptions: List<CredentialOption> = listOf()
val data: Bundle = Bundle()
val origin: String = ""
val receiver: ResultReceiver = ResultReceiver(Handler(Looper.getMainLooper()))
viewModel.launchCredentialSelector(credentialOptions, data, origin, receiver)
}

binding.buttonClearRegistry.setOnClickListener {
viewModel.clearRegistry()
}
}
}
Loading