Skip to content
Merged
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
8 changes: 8 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ android {
val kakaoNativeAppKey = properties["kakao.native.app.key"].toString()
buildConfigField("String", "KAKAO_NATIVE_APP_KEY", "\"$kakaoNativeAppKey\"")
manifestPlaceholders["KAKAO_NATIVE_APP_KEY"] = kakaoNativeAppKey

val googleClientID = properties["google.web.client.id"].toString()
buildConfigField("String", "GOOGLE_CLIENT_ID", "\"$googleClientID\"")
}

signingConfigs {
Expand Down Expand Up @@ -125,6 +128,11 @@ dependencies {

// calendar
implementation(libs.kizitonwose.calendar)

// Google
implementation(libs.androidx.credentials)
implementation(libs.androidx.credentials.play.services.auth)
implementation(libs.googleid)
}

ktlint {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package org.sopt.certi.presentation.ui.login

import android.app.Activity
import androidx.credentials.CredentialManager
import androidx.credentials.GetCredentialRequest
import androidx.credentials.GetCredentialResponse
import androidx.credentials.exceptions.GetCredentialException
import androidx.credentials.exceptions.NoCredentialException
import androidx.credentials.CustomCredential
import com.google.android.libraries.identity.googleid.GetGoogleIdOption
import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.sopt.certi.BuildConfig
import javax.inject.Inject

class GoogleLoginManager @Inject constructor() {
companion object {
const val WEB_CLIENT_ID = BuildConfig.GOOGLE_CLIENT_ID
}

fun login(
activity: Activity,
onResult: (Result<String>) -> Unit
) {
val credentialManager = CredentialManager.create(activity)

val googleIdOption = GetGoogleIdOption.Builder()
.setServerClientId(WEB_CLIENT_ID)
.setFilterByAuthorizedAccounts(false)
.setAutoSelectEnabled(true)
.build()

val request = GetCredentialRequest.Builder()
.addCredentialOption(googleIdOption)
.build()

CoroutineScope(Dispatchers.Main).launch {
try {
val response = credentialManager.getCredential(
request = request,
context = activity
)
onResult(handleResponse(response))
} catch (e: NoCredentialException) {
onResult(Result.failure(e))
} catch (e: GetCredentialException) {
onResult(Result.failure(e))
} catch (e: Throwable) {
onResult(Result.failure(e))
}
}
}

private fun handleResponse(response: GetCredentialResponse): Result<String> {
return try {
val credential = response.credential

when (credential) {
is CustomCredential -> {
if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
val googleCred = GoogleIdTokenCredential.createFrom(credential.data)
val idToken = googleCred.idToken
Result.success(idToken)
} else {
Result.failure(IllegalStateException("Unexpected credential type: ${credential.type}"))
}
}

else -> {
Result.failure(IllegalStateException("Unexpected credential class: ${credential::class.java.name}"))
}
}
} catch (e: GoogleIdTokenParsingException) {
Result.failure(e)
} catch (e: Throwable) {
Result.failure(e)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import org.sopt.certi.R
import org.sopt.certi.core.util.findActivity
import org.sopt.certi.core.util.heightForScreenPercentage
import org.sopt.certi.core.util.screenHeightDp
import org.sopt.certi.core.util.screenWidthDp
Expand All @@ -38,9 +39,11 @@ fun LoginRoute(
navigateToOnBoarding: () -> Unit,
navigateToHome: () -> Unit,
viewModel: LoginViewModel = hiltViewModel(),
kakaoLoginManager: KakaoLoginManager = KakaoLoginManager()
kakaoLoginManager: KakaoLoginManager = KakaoLoginManager(),
googleLoginManager: GoogleLoginManager = GoogleLoginManager()
) {
val context = LocalContext.current
val activity = context.findActivity()

LoginScreen(
padding = padding,
Expand All @@ -65,7 +68,23 @@ fun LoginRoute(
}
}
},
onGoogleLoginClick = { navigateToHome() }
onGoogleLoginClick = {
val safeActivity = activity

googleLoginManager.login(safeActivity) { result ->
result.onSuccess { idToken ->
viewModel.signInWithGoogleIdToken(
idToken = idToken,
onSuccess = { needSignUp ->
if (needSignUp) navigateToOnBoarding() else navigateToHome()
},
onFailure = { error -> Timber.e(error) }
)
}.onFailure { error ->
Timber.e(error)
}
}
}
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,31 @@ class LoginViewModel @Inject constructor(
)
}
}

fun signInWithGoogleIdToken(
idToken: String,
onSuccess: (needSignUp: Boolean) -> Unit,
onFailure: (Throwable) -> Unit
) {
viewModelScope.launch {
signInUseCase(idToken, SocialLoginType.GOOGLE.name.lowercase(Locale.ROOT)).fold(
onSuccess = { result ->
onSuccess(result.needSignUp)
if (result.needSignUp) {
tokenManager.savePreSignupToken(result.preSignupToken)
tokenManager.saveUserInformation(result.userInformation)
} else {
result.jwtResponse?.let {
tokenManager.saveToken(it.accessToken)
tokenManager.saveRefreshToken(it.refreshToken)
}
}
},
onFailure = {
Timber.e(it)
onFailure(it)
}
)
}
}
}
2 changes: 1 addition & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<string name="onboarding_nickname_textfield_invalid">욕설과 비속어 등은 사용할 수 없습니다.</string>
<string name="onboarding_nickname_duplicate_check">닉네임 중복 확인</string>
<string name="onboarding_info_nickname">%s님,</string>
<string name="onboarding_info_title">이제 따와 함께 해요!</string>
<string name="onboarding_info_title">이제 따요와 함께 해요!</string>
<string name="onboarding_info_final_education_title">최종학력</string>
<string name="onboarding_info_major_title">학과</string>
<string name="onboarding_info_category_title">희망직무</string>
Expand Down
5 changes: 5 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ accompanist = "0.25.1"
# Core
coreKtx = "1.16.0"
appcompat = "1.7.0"
credentials = "1.5.0"
googleid = "1.2.0"
lifecycleRuntimeKtx = "2.8.7"

# Kotlin & Tools
Expand Down Expand Up @@ -55,12 +57,15 @@ calendar = "2.6.0"
[libraries]
# Core
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-credentials = { module = "androidx.credentials:credentials", version.ref = "credentials" }
androidx-credentials-play-services-auth = { module = "androidx.credentials:credentials-play-services-auth", version.ref = "credentials" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
androidx-lifecycle-runtime-compose-android = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose-android", version.ref = "lifecycleRuntimeComposeAndroid" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }

# Kotlin
googleid = { module = "com.google.android.libraries.identity.googleid:googleid", version.ref = "googleid" }
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
kotlinx-immutable = { group = "org.jetbrains.kotlinx", name = "kotlinx-collections-immutable", version.ref = "kotlinxImmutable" }

Expand Down