11package com.sopt.presentation.auth.login
22
33import android.content.Context
4+ import android.content.Intent
45import androidx.annotation.StringRes
5- import androidx.credentials.Credential
6- import androidx.credentials.CredentialManager
7- import androidx.credentials.GetCredentialRequest
86import androidx.lifecycle.viewModelScope
9- import com.google.android.libraries.identity.googleid.GetGoogleIdOption
10- import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
7+ import com.google.android.gms.auth.api.signin.GoogleSignIn
8+ import com.google.android.gms.auth.api.signin.GoogleSignInClient
9+ import com.google.android.gms.auth.api.signin.GoogleSignInOptions
1110import com.kakao.sdk.auth.model.OAuthToken
1211import com.kakao.sdk.common.model.ClientError
1312import com.kakao.sdk.common.model.ClientErrorCause
@@ -19,28 +18,50 @@ import com.sopt.domain.repository.UserInfoRepository
1918import com.sopt.domain.usecase.PostSocialLoginUseCase
2019import com.sopt.presentation.R
2120import dagger.hilt.android.lifecycle.HiltViewModel
21+ import kotlinx.coroutines.CoroutineScope
22+ import kotlinx.coroutines.Dispatchers
2223import kotlinx.coroutines.flow.MutableStateFlow
2324import kotlinx.coroutines.flow.StateFlow
2425import kotlinx.coroutines.flow.update
2526import kotlinx.coroutines.launch
26- import timber.log.Timber
27+ import okhttp3.FormBody
28+ import okhttp3.OkHttpClient
29+ import okhttp3.Request
30+ import org.json.JSONObject
2731import javax.inject.Inject
2832import javax.inject.Named
2933
3034@HiltViewModel
3135class LoginViewModel @Inject constructor(
3236 @Named(" GoogleClientId" ) private val googleClientId : String ,
37+ @Named(" GoogleClientSecret" ) private val googleClientSecret : String ,
3338 private val userInfoRepository : UserInfoRepository ,
3439 private val postSocialLoginUseCase : PostSocialLoginUseCase
3540) : BaseViewModel<LoginSideEffect>() {
3641
3742 private val _showDialog = MutableStateFlow (Pair (DialogType .NETWORK_LOGIN_GOOGLE_FAILURE , false ))
3843 val showDialog: StateFlow <Pair <DialogType , Boolean >> get() = _showDialog
3944
45+ private val _googleSignInIntent = MutableStateFlow <Intent ?>(null )
46+ val googleSignInIntent: StateFlow <Intent ?> get() = _googleSignInIntent
47+
4048 fun showDialog (dialogType : DialogType , isVisible : Boolean ) {
4149 _showDialog .update { it.copy(first = dialogType, second = isVisible) }
4250 }
4351
52+ fun prepareGoogleSignInIntent (context : Context ) {
53+ val gso = GoogleSignInOptions .Builder (GoogleSignInOptions .DEFAULT_SIGN_IN )
54+ .requestEmail()
55+ .requestServerAuthCode(googleClientId, true )
56+ .build()
57+ val client: GoogleSignInClient = GoogleSignIn .getClient(context, gso)
58+ _googleSignInIntent .value = client.signInIntent
59+ }
60+
61+ fun clearGoogleSignInIntent () {
62+ _googleSignInIntent .value = null
63+ }
64+
4465 // Kakao Login
4566 fun kakaoLogin (context : Context ) {
4667 val loginCallback: (OAuthToken ? , Throwable ? ) -> Unit = this ::handleKakaoLoginResult
@@ -69,39 +90,45 @@ class LoginViewModel @Inject constructor(
6990 }
7091 }
7192
72- // Google Login
73- fun googleLogin (context : Context ) {
74- val credentialManager = CredentialManager .create(context)
75-
76- val googleIdOption = GetGoogleIdOption .Builder ()
77- .setFilterByAuthorizedAccounts(false )
78- .setServerClientId(googleClientId)
79- .setAutoSelectEnabled(true )
80- .build()
81-
82- val request = GetCredentialRequest .Builder ()
83- .addCredentialOption(googleIdOption)
84- .build()
85-
86- viewModelScope.launch {
87- runCatching {
88- val result = credentialManager.getCredential(context, request)
89- handleGoogleLoginResult(result.credential)
90- }.onFailure { exception ->
91- handleError(exception, R .string.toast_google_login_failed)
93+ // Get Google AccessToken
94+ fun exchangeAuthCodeForAccessToken (authCode : String ) {
95+ CoroutineScope (Dispatchers .IO ).launch {
96+ try {
97+ val requestBody = FormBody .Builder ()
98+ .add(" grant_type" , " authorization_code" )
99+ .add(" code" , authCode)
100+ .add(" client_id" , googleClientId)
101+ .add(" client_secret" , googleClientSecret)
102+ .add(" redirect_uri" , " " )
103+ .build()
104+
105+ val request = Request .Builder ()
106+ .url(" https://oauth2.googleapis.com/token" )
107+ .post(requestBody)
108+ .build()
109+
110+ val client = OkHttpClient ()
111+ val response = client.newCall(request).execute()
112+ val jsonString = response.body?.string()
113+ val json = JSONObject (jsonString ? : " " )
114+
115+ val accessToken = json.optString(" access_token" )
116+
117+ if (accessToken.isNotBlank()) {
118+ onGoogleAccessTokenReceived(accessToken)
119+ } else {
120+ showDialog(DialogType .NETWORK_LOGIN_GOOGLE_FAILURE , true )
121+ }
122+ } catch (e: Exception ) {
92123 showDialog(DialogType .NETWORK_LOGIN_GOOGLE_FAILURE , true )
93124 }
94125 }
95126 }
96127
97- private fun handleGoogleLoginResult (credential : Credential ) {
98- if (credential.type == GoogleIdTokenCredential .TYPE_GOOGLE_ID_TOKEN_CREDENTIAL ) {
99- val googleIdTokenCredential = GoogleIdTokenCredential .createFrom(credential.data)
100- postSocialLogin(googleIdTokenCredential.id, GOOGLE )
101- showToast(R .string.toast_google_login_success)
102- } else {
103- showDialog(DialogType .NETWORK_LOGIN_GOOGLE_FAILURE , true )
104- }
128+ // Google Login
129+ private fun onGoogleAccessTokenReceived (accessToken : String ) {
130+ postSocialLogin(BEARER + accessToken, GOOGLE )
131+ showToast(R .string.toast_google_login_success)
105132 }
106133
107134 private fun handleError (error : Throwable ? , @StringRes errorMessageResId : Int ) {
@@ -134,7 +161,6 @@ class LoginViewModel @Inject constructor(
134161 },
135162 onFailure = { error ->
136163 emitSideEffect(LoginSideEffect .NavigateToOnboarding (accessToken, socialType))
137- Timber .e(" postSocialLogin Failed: ${error.message} " )
138164 }
139165 )
140166 }
0 commit comments