1. शुरू करने से पहले
Credential Manager का इस्तेमाल करके, Android पर 'Google से साइन इन करें' सुविधा को लागू करने का तरीका जानें.
ज़रूरी शर्तें
- Android डेवलपमेंट के लिए, Kotlin की बुनियादी जानकारी.
- Jetpack Compose की बुनियादी जानकारी (ज़्यादा जानें).
आपको क्या सीखने को मिलेगा
- Google Cloud प्रोजेक्ट और OAuth क्लाइंट बनाएं.
- साइन इन करने के लिए, खातों की सूची वाला फ़्लो लागू करें.
- बटन के ज़रिए साइन इन करने की सुविधा लागू करें.
आपको इन चीज़ों की ज़रूरत पड़ेगी
- Android Studio इंस्टॉल हो.
- आपके पास ऐसा कंप्यूटर हो जो Android Studio और Emulator के लिए सिस्टम से जुड़ी ज़रूरी शर्तें पूरी करता हो.
- Java Development Kit (JDK) इंस्टॉल होना चाहिए.
2. Android Studio प्रोजेक्ट बनाना
शुरू करने के लिए, Android Studio में एक नया प्रोजेक्ट बनाएं:
- Android Studio खोलें और नया प्रोजेक्ट पर क्लिक करें.

- फ़ोन और टैबलेट > Empty Activity चुनें. इसके बाद, आगे बढ़ें पर क्लिक करें.

- प्रोजेक्ट सेटिंग कॉन्फ़िगर करें:
- नाम: प्रोजेक्ट का नाम चुनें.
- पैकेज का नाम: डिफ़ॉल्ट नाम का इस्तेमाल करें या अपना नाम चुनें.
- SDK टूल का कम से कम वर्शन: सबसे नया स्टेबल वर्शन या > Android 14 चुनें.

- हो गया पर क्लिक करें और प्रोजेक्ट के शुरुआती बिल्ड के पूरा होने का इंतज़ार करें.

3. Google Cloud प्रोजेक्ट सेट अप करना
Google Cloud प्रोजेक्ट बनाना
- Google Cloud Console पर जाएं और कोई प्रोजेक्ट चुनें या बनाएं.

- एपीआई और सेवाएं > OAuth के लिए सहमति वाली स्क्रीन पर जाएं.

- शुरू करें पर क्लिक करें और ज़रूरी फ़ील्ड में जानकारी भरें:
- ऐप्लिकेशन का नाम: अपने Android ऐप्लिकेशन का नाम इस्तेमाल करें.
- उपयोगकर्ता सहायता ईमेल: अपना Google खाता चुनें.
- दर्शक: बाहरी चुनें.
- संपर्क जानकारी: अपना ईमेल पता डालें.

- Google API सेवाएं: उपयोगकर्ता के डेटा से जुड़ी नीति पढ़ें और बनाएं पर क्लिक करें.

OAuth क्लाइंट सेट अप करना
पुष्टि के लिए क्लाइंट आईडी पाने के लिए, आपको Google Cloud Console में वेब क्लाइंट और Android क्लाइंट, दोनों बनाने होंगे.
- Android क्लाइंट: यह आपके ऐप्लिकेशन के पैकेज के नाम और SHA-1 हस्ताक्षर की पुष्टि करके अनुरोधों को सुरक्षित करता है.
- वेब क्लाइंट: यह Google Sign-in सेवा के लिए बैकएंड क्लाइंट के तौर पर काम करता है.
Android OAuth 2.0 क्लाइंट बनाना
- क्लाइंट पेज पर, क्लाइंट बनाएं पर क्लिक करें. इसके बाद, ऐप्लिकेशन टाइप के तौर पर Android चुनें.

- अपने ऐप्लिकेशन के पैकेज का नाम डालें. यह नाम,
MainActivity.ktकी पहली लाइन से मेल खाना चाहिए. - अपना SHA-1 हस्ताक्षर जनरेट करें. Android Studio टर्मिनल खोलें और यह कमांड चलाएं:macOS/Linux:
Windows:keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
keytool -list -v -keystore "C:\Users\USERNAME\.android\debug.keystore" -alias androiddebugkey -storepass android -keypass android
- कमांड के आउटपुट से SHA-1 फ़िंगरप्रिंट कॉपी करें. इसके बाद, उसे कंसोल में SHA-1 फ़िंगरप्रिंट फ़ील्ड में चिपकाएं और बनाएं पर क्लिक करें.

वेब OAuth 2.0 क्लाइंट बनाना
- क्लाइंट बनाएं पर फिर से क्लिक करें. इसके बाद, ऐप्लिकेशन टाइप के तौर पर वेब ऐप्लिकेशन चुनें.
- अपने वेब क्लाइंट का नाम डालें. यूआरएल/ओरिजन फ़ील्ड को खाली छोड़ दें. इसके बाद, बनाएं पर क्लिक करें.

- पुष्टि करने वाले डायलॉग बॉक्स से, जनरेट किया गया Client-ID कॉपी करें. इसका इस्तेमाल अपने Kotlin कोड में करें.

4. Android वर्चुअल डिवाइस सेट अप करना
ऐप्लिकेशन की जांच करने के लिए, किसी फ़िज़िकल Android डिवाइस या Android वर्चुअल डिवाइस (AVD) का इस्तेमाल किया जा सकता है.
AVD बनाना और उसे चलाना
- Android Studio में, डिवाइस मैनेजर खोलें. इसके बाद, वर्चुअल डिवाइस बनाएं या + आइकॉन पर क्लिक करें. इसके बाद, मीडियम फ़ोन चुनें.
- सिस्टम इमेज के तौर पर सबसे नया स्टेबल वर्शन चुनें. इसके बाद, हो गया पर क्लिक करें.
- डिवाइस के बगल में मौजूद, चलाएं/रन करें आइकॉन पर क्लिक करके, एम्युलेटर लॉन्च करें.

डिवाइस पर किसी Google खाते में साइन इन करना
- एम्युलेटर पर, सेटिंग ऐप्लिकेशन खोलें और Google पर जाएं.
- अपने Google खाते में साइन इन करें पर क्लिक करें और निर्देशों का पालन करें.

5. डिपेंडेंसी जोड़ें
अपने प्रोजेक्ट में, पुष्टि करने और Google आईडी इंटिग्रेशन के लिए ज़रूरी लाइब्रेरी जोड़ें:
- फ़ाइल > प्रोजेक्ट स्ट्रक्चर > डिपेंडेंसी > ऐप्लिकेशन पर जाएं.
- + > लाइब्रेरी डिपेंडेंसी पर क्लिक करें. इसके बाद,
com.google.android.libraries.identity.googleid:googleidखोजें और नया वर्शन (जैसे,1.1.1) चुनें. - + > लाइब्रेरी डिपेंडेंसी पर फिर से क्लिक करें. इसके बाद,
play-services-authखोजें और ग्रुप आईडीcom.google.android.gmsवाली लाइब्रेरी चुनें. - बदलाव लागू करने और अपने प्रोजेक्ट को सिंक करने के लिए, ठीक है पर क्लिक करें.

6. बॉटम शीट फ़्लो लागू करना

बॉटम शीट फ़्लो, Credential Manager API का इस्तेमाल करता है. इससे लोगों को Android पर अपने Google खातों से आपके ऐप्लिकेशन में साइन इन करने का आसान तरीका मिलता है. इस फ़्लो को तेज़ी से और आसानी से इस्तेमाल किया जा सकता है. खास तौर पर, यह उन उपयोगकर्ताओं के लिए फ़ायदेमंद है जो पहले भी ऐप्लिकेशन का इस्तेमाल कर चुके हैं. इस फ़्लो को ऐप्लिकेशन लॉन्च करने पर ट्रिगर किया जाना चाहिए.
साइन-इन करने का अनुरोध बनाना
- शुरू करने के लिए,
MainActivity.ktखोलें और डिफ़ॉल्टGreeting()औरGreetingPreview()फ़ंक्शन हटाएं. - लाइन 3 से शुरू होने वाले मौजूदा इंपोर्ट स्टेटमेंट के बाद, ये इंपोर्ट स्टेटमेंट जोड़ें:
import android.content.Context import android.os.Build import android.util.Log import android.widget.Toast import androidx.annotation.RequiresApi import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.credentials.CredentialManager import androidx.credentials.CustomCredential import androidx.credentials.GetCredentialRequest import androidx.credentials.exceptions.GetCredentialCancellationException import androidx.credentials.exceptions.GetCredentialCustomException import androidx.credentials.exceptions.GetCredentialException import androidx.credentials.exceptions.NoCredentialException import com.google.android.libraries.identity.googleid.GetGoogleIdOption import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException import java.security.SecureRandom import java.util.Base64 import kotlinx.coroutines.delay import kotlinx.coroutines.launch const val TAG = "MainActivity" - अपनी
MainActivity.ktफ़ाइल में,MainActivityक्लास के नीचे यह कंपोज़ेबल फ़ंक्शन जोड़ें:@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) @Composable fun BottomSheet(webClientId: String) { val context = LocalContext.current // LaunchedEffect is used to run a suspend function when the composable is first launched. LaunchedEffect(Unit) { // Create a Google ID option with filtering by authorized accounts enabled. val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder() .setFilterByAuthorizedAccounts(true) .setServerClientId(webClientId) .setNonce(generateSecureRandomNonce()) .build() // Create a credential request with the Google ID option. val request: GetCredentialRequest = GetCredentialRequest.Builder() .addCredentialOption(googleIdOption) .build() // Attempt to sign in with the created request using an authorized account val e = signIn(request, context) // If the sign-in fails with NoCredentialException, there are no authorized accounts. // In this case, we attempt to sign in again with filtering disabled. if (e is NoCredentialException) { val googleIdOptionFalse: GetGoogleIdOption = GetGoogleIdOption.Builder() .setFilterByAuthorizedAccounts(false) .setServerClientId(webClientId) .setNonce(generateSecureRandomNonce()) .build() val requestFalse: GetCredentialRequest = GetCredentialRequest.Builder() .addCredentialOption(googleIdOptionFalse) .build() //We will build out this function in a moment signIn(requestFalse, context) } } } //This function is used to generate a secure nonce to pass in with our request fun generateSecureRandomNonce(byteLength: Int = 32): String { val randomBytes = ByteArray(byteLength) SecureRandom.getInstanceStrong().nextBytes(randomBytes) return Base64.getUrlEncoder().withoutPadding().encodeToString(randomBytes) }
कोड ब्रेकडाउन
LaunchedEffect(Unit): इस विकल्प को चुनने पर, कंपोज़ेबल पहली बार दिखने पर ही साइन-इन करने की प्रोसेस शुरू हो जाती है.GetGoogleIdOption.Builder(): यह Google आईडी टोकन के अनुरोध को कॉन्फ़िगर करता है.setFilterByAuthorizedAccounts(true): यह सबसे पहले साइलेंट साइन-इन की कोशिश करता है. इसके लिए, यह उन खातों को फ़िल्टर करता है जिनके लिए उपयोगकर्ता ने पहले ही इस ऐप्लिकेशन को अनुमति दी है. इससे, बार-बार आने वाले उपयोगकर्ताओं को कम परेशानी होती है.setNonce(...): यह कुकी,generateSecureRandomNonce()के हर अनुरोध के लिए जनरेट किया गया एक सुरक्षित रैंडम नॉनस पास करती है, ताकि रीप्ले अटैक को रोका जा सके.
signIn(request, context): अनुरोध को पूरा करता है. अगर यहNoCredentialExceptionके साथ काम नहीं करता है, तो इसका मतलब है कि पहले से कोई भी खाता अधिकृत नहीं है. ऐसे में, फ़्लोsetFilterByAuthorizedAccounts(false)पर वापस चला जाता है, ताकि उपयोगकर्ता डिवाइस पर साइन इन किए गए किसी भी Google खाते को चुन सके.
साइन इन करने का अनुरोध करना
साइन-इन करने का अनुरोध तैयार होने के बाद, साइन-इन करने की प्रोसेस पूरी करने के लिए, Credential Manager का इस्तेमाल किया जा सकता है. signIn नाम का एक फ़ंक्शन बनाएं. यह फ़ंक्शन अनुरोध को पूरा करता है और आम तौर पर होने वाली गड़बड़ियों को ठीक करता है.
अपनी MainActivity.kt फ़ाइल में, BottomSheet फ़ंक्शन के नीचे यह फ़ंक्शन जोड़ें:
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
suspend fun signIn(request: GetCredentialRequest, context: Context): Exception? {
val credentialManager = CredentialManager.create(context)
val failureMessage = "Sign in failed!"
//using delay() here helps prevent NoCredentialException when the BottomSheet Flow is triggered
//on the initial running of our app
delay(250)
return try {
// The getCredential is called to request a credential from Credential Manager.
val result = credentialManager.getCredential(
request = request,
context = context,
)
Log.i(TAG, result.toString())
val credential = result.credential
if (credential is CustomCredential &&
credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
val googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credential.data)
Log.i(TAG, "Signed in as: ${googleIdTokenCredential.id}")
}
Toast.makeText(context, "Sign in successful!", Toast.LENGTH_SHORT).show()
Log.i(TAG, "(☞゚ヮ゚)☞ Sign in Successful! ☜(゚ヮ゚☜)")
null
} catch (e: GoogleIdTokenParsingException) {
Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
Log.e(TAG, failureMessage + ": Issue with parsing received GoogleIdToken", e)
e
} catch (e: NoCredentialException) {
Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
Log.e(TAG, failureMessage + ": No credentials found", e)
e
} catch (e: GetCredentialCancellationException) {
Toast.makeText(context, "Sign-in cancelled", Toast.LENGTH_SHORT).show()
Log.e(TAG, failureMessage + ": Sign-in was cancelled", e)
e
} catch (e: GetCredentialCustomException) {
Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
Log.e(TAG, failureMessage + ": Issue with custom credential request", e)
e
} catch (e: GetCredentialException) {
Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
Log.e(TAG, failureMessage + ": Failure getting credentials", e)
e
}
}
कोड ब्रेकडाउन
credentialManager.getCredential(...): यह Credential Manager API को कॉल करता है, ताकि सिस्टम खाता चुनने वाले बॉटम शीट या डायलॉग को दिखाया जा सके.delay(250): यह कुछ समय के लिए रुक जाता है, ताकि क्रेडेंशियल मैनेजर सेवा के शुरू होने से पहले ही ऐप्लिकेशन के शुरू होने पर बॉटम शीट ट्रिगर होने की स्थिति को रोका जा सके.- अपवाद हैंडलिंग: यह सामान्य क्रेडेंशियल से जुड़ी गड़बड़ियों (जैसे, रद्द करना, क्रेडेंशियल मौजूद न होना या टोकन पार्स करने से जुड़ी समस्याएं) का पता लगाता है और उन्हें लॉग करता है. साथ ही, सूचनाओं का इस्तेमाल करके उपयोगकर्ता को सुझाव देता है.
बॉटम शीट फ़्लो को ट्रिगर करना
स्टार्टअप पर BottomSheet() को कॉल करने के लिए, अपनी MainActivity क्लास को अपडेट करें. YOUR_CLIENT_ID_HERE की जगह अपना वेब ऐप्लिकेशन क्लाइंट आईडी डालें:
class MainActivity : ComponentActivity() {
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//replace with your own web client ID from Google Cloud Console
val webClientId = "YOUR_CLIENT_ID_HERE"
setContent {
//ExampleTheme - this is derived from the name of the project not any added library
//e.g. if this project was named "Testing" it would be generated as TestingTheme
ExampleTheme {
Surface(
modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background,
) {
//This will trigger on launch
BottomSheet(webClientId)
}
}
}
}
}
अपने प्रोजेक्ट को सेव करें (File > Save) और ऐप्लिकेशन चलाएं:
- 'चलाएं' बटन दबाएं:

- एम्युलेटर पर ऐप्लिकेशन लॉन्च होने पर, साइन-इन बॉटम शीट दिखनी चाहिए. फ़्लो को टेस्ट करने के लिए, जारी रखें पर क्लिक करें.

- आपको एक सूचना दिखेगी, जिसमें पुष्टि की जाएगी कि साइन-इन हो गया है.

7. बटन फ़्लो लागू करना

बटन फ़्लो में, उपयोगकर्ताओं को साइन इन या साइन अप करने का विकल्प मिलता है. स्टैंडर्ड ब्रैंडिंग का इस्तेमाल करने से, लोगों को एक जैसा अनुभव मिलता है. पहले से मंज़ूरी पा चुकी ऐसी ऐसेट का इस्तेमाल करें जो 'Google से साइन इन करें' सुविधा के ब्रैंडिंग दिशा-निर्देशों के मुताबिक हों.
ब्रैंड का आइकॉन जोड़ना
- ब्रैंड ऐसेट को यहां से डाउनलोड करें और ZIP फ़ाइल को अनज़िप करें.
signin-assets/Android/png@2x/neutral/android_neutral_sq_SI@2x.pngकॉपी करें.- Android Studio में, फ़ाइल को res > drawable फ़ोल्डर में चिपकाएं. इसके बाद, उसका नाम बदलकर
siwg_button.pngकरें और OK पर क्लिक करें.
बटन फ़्लो कोड
यह फ़्लो, signIn हेल्पर फ़ंक्शन का फिर से इस्तेमाल करता है. हालांकि, यह GetGoogleIdOption के बजाय GetSignInWithGoogleOption पास करता है. बॉटम शीट फ़्लो के उलट, बटन फ़्लो में पहले से फ़िल्टर नहीं किया जाता. साथ ही, सेव किए गए क्रेडेंशियल या पासकी के लिए अपने-आप प्रॉम्प्ट नहीं किया जाता. इस कंपोज़ेबल फ़ंक्शन को BottomSheet फ़ंक्शन के नीचे चिपकाएं:
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Composable
fun ButtonUI(webClientId: String) {
val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
val onClick: () -> Unit = {
val signInWithGoogleOption: GetSignInWithGoogleOption = GetSignInWithGoogleOption
.Builder(serverClientId = webClientId)
.setNonce(generateSecureRandomNonce())
.build()
val request: GetCredentialRequest = GetCredentialRequest.Builder()
.addCredentialOption(signInWithGoogleOption)
.build()
coroutineScope.launch {
signIn(request, context)
}
}
Image(
painter = painterResource(id = R.drawable.siwg_button),
contentDescription = "",
modifier = Modifier
.fillMaxSize()
.clickable(enabled = true, onClick = onClick)
)
}
कोड ब्रेकडाउन
GetSignInWithGoogleOption: बॉटम शीट फ़्लो के उलट, बटन फ़्लो में इस विकल्प का इस्तेमाल किया जाता है. इससे उपयोगकर्ताओं को अपने Google खाते को चुनने के लिए कहा जाता है. इसमें खातों को अपने-आप फ़िल्टर नहीं किया जाता.coroutineScope.launch: यह बटन पर क्लिक करने पर, एसिंक्रोनस तरीके से सस्पेंडsignInफ़ंक्शन को एक्ज़ीक्यूट करने के लिए, एक कोरूटीन लॉन्च करता है.Image: ब्रैंड किए गएsiwg_buttonड्रॉएबल को दिखाता है. साथ ही, फ़्लो को ट्रिगर करने के लिए क्लिक लिसनर अटैच करता है.
बटन को यूज़र इंटरफ़ेस (यूआई) लेआउट में जोड़ना
अपने MainActivity लेआउट को अपडेट करें, ताकि वर्टिकल अलाइनमेंट में ऑटोमैटिक BottomSheet और साफ़ तौर पर बताए गए ButtonUI, दोनों दिखें:
class MainActivity : ComponentActivity() {
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//replace with your own web client ID from Google Cloud Console
val webClientId = "YOUR_CLIENT_ID_HERE"
setContent {
//ExampleTheme - this is derived from the name of the project not any added library
//e.g. if this project was named "Testing" it would be generated as TestingTheme
ExampleTheme {
Surface(
modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background,
) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
//This will trigger on launch
BottomSheet(webClientId)
//This requires the user to press the button
ButtonUI(webClientId)
}
}
}
}
}
}
बटन फ़्लो की जांच करना
- ऐप्लिकेशन चलाएं.
- शीट के बाहर क्लिक करके, शुरुआती बॉटम शीट को खारिज करें.
- साइन-इन डायलॉग लॉन्च करने के लिए, 'Google से साइन इन करें' बटन पर क्लिक करें. इसके बाद, अपना खाता चुनें.

- नतीजे की पुष्टि करें: Logcat में जाकर देखें कि आपके उपयोगकर्ता नाम/ईमेल पते का प्रिंटआउट मिला है या नहीं.
8. नतीजा
बधाई हो! आपने Android क्रेडेंशियल मैनेजर का इस्तेमाल करके, 'Google से साइन इन करें' सुविधा को लागू कर लिया है.
अन्य संसाधन
- Android Credential Manager का दस्तावेज़
- Credential Manager से जुड़ी समस्या हल करने के लिए गाइड
- Credential Manager के बारे में अक्सर पूछे जाने वाले सवाल
- StackOverflow की सहायता टीम
MainActivity.kt का पूरा कोड
रेफ़रंस के लिए, MainActivity.kt का पूरा कोड यहां दिया गया है:
package com.example.example
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.example.example.ui.theme.ExampleTheme
import android.content.ContentValues.TAG
import android.content.Context
import android.util.Log
import android.widget.Toast
import androidx.credentials.exceptions.GetCredentialException
import androidx.compose.foundation.clickable
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.credentials.CredentialManager
import androidx.credentials.exceptions.GetCredentialCancellationException
import androidx.credentials.exceptions.GetCredentialCustomException
import androidx.credentials.exceptions.NoCredentialException
import androidx.credentials.GetCredentialRequest
import com.google.android.libraries.identity.googleid.GetGoogleIdOption
import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption
import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException
import java.security.SecureRandom
import java.util.Base64
import kotlinx.coroutines.CoroutineScope
import androidx.compose.runtime.LaunchedEffect
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class MainActivity : ComponentActivity() {
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//replace with your own web client ID from Google Cloud Console
val webClientId = "YOUR_CLIENT_ID_HERE"
setContent {
//ExampleTheme - this is derived from the name of the project not any added library
//e.g. if this project was named "Testing" it would be generated as TestingTheme
ExampleTheme {
Surface(
modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background,
) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
//This will trigger on launch
BottomSheet(webClientId)
//This requires the user to press the button
ButtonUI(webClientId)
}
}
}
}
}
}
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Composable
fun BottomSheet(webClientId: String) {
val context = LocalContext.current
// LaunchedEffect is used to run a suspend function when the composable is first launched.
LaunchedEffect(Unit) {
// Create a Google ID option with filtering by authorized accounts enabled.
val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
.setFilterByAuthorizedAccounts(true)
.setServerClientId(webClientId)
.setNonce(generateSecureRandomNonce())
.build()
// Create a credential request with the Google ID option.
val request: GetCredentialRequest = GetCredentialRequest.Builder()
.addCredentialOption(googleIdOption)
.build()
// Attempt to sign in with the created request using an authorized account
val e = signIn(request, context)
// If the sign-in fails with NoCredentialException, there are no authorized accounts.
// In this case, we attempt to sign in again with filtering disabled.
if (e is NoCredentialException) {
val googleIdOptionFalse: GetGoogleIdOption = GetGoogleIdOption.Builder()
.setFilterByAuthorizedAccounts(false)
.setServerClientId(webClientId)
.setNonce(generateSecureRandomNonce())
.build()
val requestFalse: GetCredentialRequest = GetCredentialRequest.Builder()
.addCredentialOption(googleIdOptionFalse)
.build()
signIn(requestFalse, context)
}
}
}
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Composable
fun ButtonUI(webClientId: String) {
val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
val onClick: () -> Unit = {
val signInWithGoogleOption: GetSignInWithGoogleOption = GetSignInWithGoogleOption
.Builder(serverClientId = webClientId)
.setNonce(generateSecureRandomNonce())
.build()
val request: GetCredentialRequest = GetCredentialRequest.Builder()
.addCredentialOption(signInWithGoogleOption)
.build()
coroutineScope.launch {
signIn(request, context)
}
}
Image(
painter = painterResource(id = R.drawable.siwg_button),
contentDescription = "",
modifier = Modifier
.fillMaxSize()
.clickable(onClick = onClick)
)
}
fun generateSecureRandomNonce(byteLength: Int = 32): String {
val randomBytes = ByteArray(byteLength)
SecureRandom.getInstanceStrong().nextBytes(randomBytes)
return Base64.getUrlEncoder().withoutPadding().encodeToString(randomBytes)
}
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
suspend fun signIn(request: GetCredentialRequest, context: Context): Exception? {
val credentialManager = CredentialManager.create(context)
val failureMessage = "Sign in failed!"
//using delay() here helps prevent NoCredentialException when the BottomSheet Flow is triggered
//on the initial running of our app
delay(250)
return try {
// The getCredential is called to request a credential from Credential Manager.
val result = credentialManager.getCredential(
request = request,
context = context,
)
Log.i(TAG, result.toString())
val credential = result.credential
if (credential is CustomCredential &&
credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
val googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credential.data)
Log.i(TAG, "Signed in as: ${googleIdTokenCredential.id}")
}
Toast.makeText(context, "Sign in successful!", Toast.LENGTH_SHORT).show()
Log.i(TAG, "(☞゚ヮ゚)☞ Sign in Successful! ☜(゚ヮ゚☜)")
null
} catch (e: GoogleIdTokenParsingException) {
Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
Log.e(TAG, failureMessage + ": Issue with parsing received GoogleIdToken", e)
e
} catch (e: NoCredentialException) {
Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
Log.e(TAG, failureMessage + ": No credentials found", e)
e
} catch (e: GetCredentialCancellationException) {
Toast.makeText(context, "Sign-in cancelled", Toast.LENGTH_SHORT).show()
Log.e(TAG, failureMessage + ": Sign-in was cancelled", e)
e
} catch (e: GetCredentialCustomException) {
Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
Log.e(TAG, failureMessage + ": Issue with custom credential request", e)
e
} catch (e: GetCredentialException) {
Toast.makeText(context, failureMessage, Toast.LENGTH_SHORT).show()
Log.e(TAG, failureMessage + ": Failure getting credentials", e)
e
}
}