From 386c7fc7404c0789927626b597ac9b107a38f5fc Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Fri, 4 Oct 2024 18:37:22 +0900 Subject: [PATCH 1/4] =?UTF-8?q?refactor=20LoginManager=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/login/LoginManager.kt | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginManager.kt diff --git a/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginManager.kt b/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginManager.kt new file mode 100644 index 00000000..7c41b5e8 --- /dev/null +++ b/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginManager.kt @@ -0,0 +1,61 @@ +package com.goalpanzi.mission_mate.feature.login + +import android.content.Context +import androidx.credentials.CredentialManager +import androidx.credentials.CustomCredential +import androidx.credentials.GetCredentialRequest +import androidx.credentials.GetCredentialResponse +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 kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +sealed class LoginData { + data class Success(val email : String) : LoginData() + data class Failed(val exception: Exception) : LoginData() +} + +class LoginManager ( + private val context : Context +) { + suspend fun request() : LoginData = withContext(Dispatchers.IO) { + val credentialManager = CredentialManager.create(context) + val signInWithGoogleOption: GetSignInWithGoogleOption = + GetSignInWithGoogleOption.Builder(BuildConfig.CREDENTIAL_WEB_CLIENT_ID) + .build() + + val request: GetCredentialRequest = GetCredentialRequest.Builder() + .addCredentialOption(signInWithGoogleOption) + .build() + + return@withContext try { + val result = credentialManager.getCredential(context, request) + handleSignIn(result) + } catch (e: Exception) { + e.printStackTrace() + LoginData.Failed(e) + } + } + + private fun handleSignIn(response: GetCredentialResponse) : LoginData { + return when (val credential = response.credential) { + is CustomCredential -> { + if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) { + try { + val googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credential.data) + LoginData.Success(googleIdTokenCredential.id) + } catch (e: GoogleIdTokenParsingException) { + LoginData.Failed(e) + } + }else { + LoginData.Failed(RuntimeException("Credential Type not equals TYPE_GOOGLE_ID_TOKEN_CREDENTIAL")) + } + } + + else -> { + LoginData.Failed(RuntimeException("Credential is not CustomCredential")) + } + } + } +} From 792c655551cbbe42bf37cab7f2bb8ae59531a08b Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Fri, 4 Oct 2024 18:39:01 +0900 Subject: [PATCH 2/4] =?UTF-8?q?refactor=20LoginViewModel=20context=20?= =?UTF-8?q?=EC=B0=B8=EC=A1=B0=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mission_mate/feature/login/LoginScreen.kt | 23 ++++++++-- .../feature/login/LoginViewModel.kt | 46 ++++--------------- 2 files changed, 27 insertions(+), 42 deletions(-) diff --git a/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginScreen.kt b/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginScreen.kt index c7f9c1fe..51948f04 100644 --- a/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginScreen.kt +++ b/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginScreen.kt @@ -11,13 +11,14 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -33,16 +34,18 @@ import androidx.hilt.navigation.compose.hiltViewModel import com.goalpanzi.mission_mate.core.designsystem.theme.ColorWhite_FFFFFFFF import com.goalpanzi.mission_mate.core.designsystem.theme.Color_FFFF5632 import com.goalpanzi.mission_mate.core.designsystem.theme.MissionMateTypography +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch @Composable fun LoginRoute( onLoginSuccess: (Boolean) -> Unit, modifier: Modifier = Modifier, + coroutineScope : CoroutineScope = rememberCoroutineScope(), + loginManager: LoginManager = rememberLoginManager(), viewModel: LoginViewModel = hiltViewModel() ) { - val context = LocalContext.current - LaunchedEffect(true) { viewModel.eventFlow.collectLatest { when (it) { @@ -54,10 +57,22 @@ fun LoginRoute( LoginScreen( modifier = modifier, - onGoogleLoginClick = { viewModel.request(context) } + onGoogleLoginClick = { + coroutineScope.launch { + viewModel.login(loginManager.request()) + } + } ) } +@Composable +private fun rememberLoginManager(): LoginManager { + val context = LocalContext.current + return remember { + LoginManager(context) + } +} + @Composable fun LoginScreen( modifier: Modifier = Modifier, diff --git a/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginViewModel.kt b/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginViewModel.kt index b1fb9526..143639c2 100644 --- a/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginViewModel.kt +++ b/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginViewModel.kt @@ -1,16 +1,8 @@ package com.goalpanzi.mission_mate.feature.login -import android.content.Context -import androidx.credentials.CredentialManager -import androidx.credentials.CustomCredential -import androidx.credentials.GetCredentialRequest -import androidx.credentials.GetCredentialResponse import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.goalpanzi.mission_mate.core.domain.auth.usecase.LoginUseCase -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 dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow @@ -25,33 +17,12 @@ class LoginViewModel @Inject constructor( private val _eventFlow = MutableSharedFlow() val eventFlow = _eventFlow.asSharedFlow() - fun request(context: Context) { - viewModelScope.launch { - val credentialManager = CredentialManager.create(context) - val signInWithGoogleOption: GetSignInWithGoogleOption = - GetSignInWithGoogleOption.Builder(BuildConfig.CREDENTIAL_WEB_CLIENT_ID) - .build() - - val request: GetCredentialRequest = GetCredentialRequest.Builder() - .addCredentialOption(signInWithGoogleOption) - .build() - - try { - val result = credentialManager.getCredential(context, request) - handleSignIn(result) - } catch (e: Exception) { - e.printStackTrace() - } - } - } - - private suspend fun handleSignIn(response: GetCredentialResponse) { - when (val credential = response.credential) { - is CustomCredential -> { - if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) { + fun login(loginData: LoginData){ + when(loginData){ + is LoginData.Success -> { + viewModelScope.launch { try { - val googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credential.data) - val result = loginUseCase.requestGoogleLogin(email = googleIdTokenCredential.id) + val result = loginUseCase.requestGoogleLogin(email = loginData.email) _eventFlow.emit( result?.let { LoginEvent.Success(it.isProfileSet) @@ -59,14 +30,13 @@ class LoginViewModel @Inject constructor( LoginEvent.Error } ) - } catch (e: GoogleIdTokenParsingException) { + }catch (e: Exception){ e.printStackTrace() } } } - - else -> { - // TODO : error event + is LoginData.Failed -> { + loginData.exception.printStackTrace() } } } From 5a0e8f56d0f405122c32d82c5cdb8ea763c19457 Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Mon, 7 Oct 2024 19:19:30 +0900 Subject: [PATCH 3/4] =?UTF-8?q?refactor:=20Exception=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/goalpanzi/mission_mate/feature/login/LoginManager.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginManager.kt b/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginManager.kt index 7c41b5e8..1988505c 100644 --- a/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginManager.kt +++ b/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginManager.kt @@ -49,12 +49,12 @@ class LoginManager ( LoginData.Failed(e) } }else { - LoginData.Failed(RuntimeException("Credential Type not equals TYPE_GOOGLE_ID_TOKEN_CREDENTIAL")) + LoginData.Failed(RuntimeException("NoDataException")) } } else -> { - LoginData.Failed(RuntimeException("Credential is not CustomCredential")) + LoginData.Failed(IllegalStateException()) } } } From 9267cfe0594a946481370e539639f0e83d7ac988 Mon Sep 17 00:00:00 2001 From: eshc123 <> Date: Sat, 12 Oct 2024 18:58:54 +0900 Subject: [PATCH 4/4] =?UTF-8?q?refactor:=20=EC=A4=91=EC=B2=A9=EB=90=9C=20?= =?UTF-8?q?=EC=BD=94=EB=A3=A8=ED=8B=B4=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/login/LoginViewModel.kt | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginViewModel.kt b/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginViewModel.kt index 143639c2..0ff81393 100644 --- a/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginViewModel.kt +++ b/feature/login/src/main/java/com/goalpanzi/mission_mate/feature/login/LoginViewModel.kt @@ -17,22 +17,20 @@ class LoginViewModel @Inject constructor( private val _eventFlow = MutableSharedFlow() val eventFlow = _eventFlow.asSharedFlow() - fun login(loginData: LoginData){ + suspend fun login(loginData: LoginData){ when(loginData){ is LoginData.Success -> { - viewModelScope.launch { - try { - val result = loginUseCase.requestGoogleLogin(email = loginData.email) - _eventFlow.emit( - result?.let { - LoginEvent.Success(it.isProfileSet) - } ?: run { - LoginEvent.Error - } - ) - }catch (e: Exception){ - e.printStackTrace() - } + try { + val result = loginUseCase.requestGoogleLogin(email = loginData.email) + _eventFlow.emit( + result?.let { + LoginEvent.Success(it.isProfileSet) + } ?: run { + LoginEvent.Error + } + ) + }catch (e: Exception){ + e.printStackTrace() } } is LoginData.Failed -> {