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
2 changes: 1 addition & 1 deletion CatchMate/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ android {
defaultConfig {
applicationId = "com.catchmate.android"
versionCode = 1
versionName = "1.0.0"
versionName = "1.0.1"

buildConfigField("String", "KAKAO_NATIVE_APP_KEY", kakaoNativeAppKey)
buildConfigField("String", "NAVER_CLIENT_ID", naverClientId)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
package com.catchmate.app

import com.android.build.api.dsl.ApplicationExtension
import com.android.build.api.dsl.CommonExtension
import com.android.build.api.dsl.LibraryExtension
import com.android.build.gradle.LibraryExtension
import com.android.build.gradle.internal.dsl.BaseAppModuleExtension
import org.gradle.api.Project
import org.gradle.api.artifacts.VersionCatalog
import org.gradle.api.artifacts.VersionCatalogsExtension
import org.gradle.api.plugins.ExtensionContainer
import org.gradle.kotlin.dsl.getByType

internal val Project.applicationExtension: CommonExtension<*, *, *, *>
get() = extensions.getByType<ApplicationExtension>()
internal val Project.applicationExtension: BaseAppModuleExtension
get() = extensions.getByType<BaseAppModuleExtension>()

internal val Project.libraryExtension: CommonExtension<*, *, *, *>
internal val Project.libraryExtension: LibraryExtension
get() = extensions.getByType<LibraryExtension>()

internal val Project.androidExtension: CommonExtension<*, *, *, *>
internal val Project.androidExtension
get() =
runCatching { libraryExtension }
.recoverCatching { applicationExtension }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.catchmate.app

import com.android.build.gradle.TestedExtension
import org.gradle.api.Project

internal fun Project.configureKotestAndroid() {
Expand All @@ -8,9 +9,9 @@ internal fun Project.configureKotestAndroid() {
}

internal fun Project.configureJUnitAndroid() {
androidExtension.apply {
testOptions {
unitTests.all { it.useJUnitPlatform() }
}
val extension = androidExtension as TestedExtension

extension.testOptions {
unitTests.all { it.useJUnitPlatform() }
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.catchmate.app

import com.android.build.gradle.BaseExtension
import com.android.build.gradle.internal.dsl.DefaultConfig
import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.kotlin.dsl.dependencies
Expand All @@ -12,11 +14,12 @@ internal fun Project.configureKotlinAndroid() {
pluginManager.apply("org.jetbrains.kotlin.android")

// Android Settings
androidExtension.apply {
compileSdk = 34
(androidExtension as BaseExtension).apply {
compileSdkVersion(34)

defaultConfig {
minSdk = 31
(this as DefaultConfig).minSdk = 31
targetSdk = 34
}

compileOptions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import androidx.credentials.CredentialManager
import androidx.credentials.CustomCredential
import androidx.credentials.GetCredentialRequest
import androidx.credentials.GetCredentialResponse
import androidx.credentials.exceptions.NoCredentialException
import com.catchmate.data.BuildConfig
import com.catchmate.data.datasource.remote.FCMTokenService
import com.catchmate.data.dto.auth.PostLoginRequestDTO
import com.catchmate.domain.exception.GoogleLoginException
import com.catchmate.domain.exception.Result
import com.catchmate.domain.model.enumclass.LoginPlatform
import com.google.android.libraries.identity.googleid.GetGoogleIdOption
import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
Expand All @@ -23,7 +24,7 @@ class GoogleLoginDataSource
constructor(
private val fcmTokenService: FCMTokenService,
) {
suspend fun getCredential(activity: Activity): GetCredentialResponse? {
suspend fun getCredential(activity: Activity): Result<GetCredentialResponse> {
val credentialManager = CredentialManager.create(activity)

val googleIdOption: GetGoogleIdOption =
Expand All @@ -41,14 +42,27 @@ class GoogleLoginDataSource
.build()

return try {
credentialManager.getCredential(activity, request)
} catch (e: NoCredentialException) {
Log.e("GoogleLoginError", "No credentials found: ${e.message}")
null
val result = credentialManager.getCredential(activity, request)
Result.Success(result)
} catch (e: Exception) {
when (e) {
is GoogleLoginException.NoCredentials -> {
Log.e("GOOGLE - NOCredentials", "")
Result.Error(exception = e)
}
is GoogleLoginException.Cancelled -> {
Log.e("GOOGLE - Cancelled", "")
Result.Error(exception = e)
}
else -> {
Log.e("GOOGLE - ELSE", "")
Result.Error(exception = e)
}
}
}
}

fun handleSignIn(result: GetCredentialResponse): PostLoginRequestDTO? {
fun handleSignIn(result: GetCredentialResponse): Result<PostLoginRequestDTO> {
val credential = result.credential

return if (credential is CustomCredential) {
Expand All @@ -60,27 +74,32 @@ class GoogleLoginDataSource
val profileUri = googleIdTokenCredential.profilePictureUri

Log.i("GoogleInfoSuccess", "idToken : $idToken email : $email profileUri : $profileUri")
PostLoginRequestDTO(
email = email,
providerId = idToken,
provider = LoginPlatform.GOOGLE.toString().lowercase(),
picture = profileUri.toString(),
fcmToken =
runBlocking(Dispatchers.IO) {
fcmTokenService.getToken()
},
)
val loginRequestDTO =
PostLoginRequestDTO(
email = email,
providerId = idToken,
provider = LoginPlatform.GOOGLE.toString().lowercase(),
picture = profileUri.toString(),
fcmToken =
runBlocking(Dispatchers.IO) {
fcmTokenService.getToken()
},
)
Result.Success(loginRequestDTO)
} catch (e: GoogleIdTokenParsingException) {
Log.e("GoogleInfoError", "Received an invalid google id token response", e)
null
Result.Error(exception = e)
} catch (e: Exception) {
Log.e("GoogleInfoError", "Unexpected error parsing credentials", e)
Result.Error(exception = e)
}
} else {
Log.e("GoogleInfoError", "Unexpected type of credential")
null
Result.Error(exception = IllegalArgumentException("Unexpected credential type"))
}
} else {
Log.e("GoogleInfoError", "Unexpected type of credential")
null
Result.Error(exception = IllegalArgumentException("Unexpected credential type"))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ object AuthMapper {
)
}

fun toGooglePostLoginRequest(postLoginRequestDTO: PostLoginRequestDTO): PostLoginRequest =
PostLoginRequest(
providerId = postLoginRequestDTO.providerId,
provider = postLoginRequestDTO.provider,
email = postLoginRequestDTO.email,
picture = postLoginRequestDTO.picture,
fcmToken = postLoginRequestDTO.fcmToken,
)

fun toPostLoginResponse(postLoginResponseDTO: PostLoginResponseDTO): PostLoginResponse =
PostLoginResponse(
accessToken = postLoginResponseDTO.accessToken,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import android.app.Activity
import com.catchmate.data.datasource.local.GoogleLoginDataSource
import com.catchmate.data.datasource.local.KakaoLoginDataSource
import com.catchmate.data.datasource.local.NaverLoginDataSource
import com.catchmate.data.mapper.AuthMapper
import com.catchmate.data.mapper.AuthMapper.toGooglePostLoginRequest
import com.catchmate.data.mapper.AuthMapper.toPostLoginRequest
import com.catchmate.domain.exception.GoogleLoginException
import com.catchmate.domain.exception.Result
import com.catchmate.domain.model.auth.PostLoginRequest
import com.catchmate.domain.repository.LoginRepository
import javax.inject.Inject
Expand All @@ -18,21 +21,49 @@ class LoginRepositoryImpl
) : LoginRepository {
override suspend fun loginWithKakao(): PostLoginRequest? {
val postLoginRequestDTO = kakaoLoginDataSource.loginWithKakao()
return AuthMapper.toPostLoginRequest(postLoginRequestDTO)
return toPostLoginRequest(postLoginRequestDTO)
}

override suspend fun loginWithNaver(activity: Activity): PostLoginRequest? {
val postLoginRequestDTO = naverLoginDataSource.loginWithNaver(activity)
return AuthMapper.toPostLoginRequest(postLoginRequestDTO)
return toPostLoginRequest(postLoginRequestDTO)
}

override suspend fun loginWithGoogle(activity: Activity): PostLoginRequest? {
val result = googleLoginDataSource.getCredential(activity)
val postLoginRequestDTO = result?.let { googleLoginDataSource.handleSignIn(it) }
return if (postLoginRequestDTO == null) {
null
} else {
AuthMapper.toPostLoginRequest(postLoginRequestDTO)
override suspend fun loginWithGoogle(activity: Activity): Result<PostLoginRequest> =
try {
val credentialResult = googleLoginDataSource.getCredential(activity)

when (credentialResult) {
is Result.Success -> {
val signInResult = googleLoginDataSource.handleSignIn(credentialResult.data)

when (signInResult) {
is Result.Success -> {
val loginDTO = signInResult.data
Result.Success(
toGooglePostLoginRequest(loginDTO),
)
}
is Result.Error -> {
when (signInResult.exception) {
is GoogleLoginException.TokenParsing -> Result.Error(exception = GoogleLoginException.TokenParsing)
else -> Result.Error(exception = GoogleLoginException.Unknown(signInResult.exception!!))
}
}
}
}
is Result.Error -> {
when (credentialResult.exception) {
is GoogleLoginException.Cancelled ->
Result.Error(exception = GoogleLoginException.Cancelled)
is GoogleLoginException.NoCredentials ->
Result.Error(exception = GoogleLoginException.NoCredentials)
else ->
Result.Error(exception = GoogleLoginException.Unknown(credentialResult.exception!!))
}
}
}
} catch (e: Exception) {
Result.Error(exception = GoogleLoginException.Unknown(e))
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.catchmate.domain.exception

sealed class GoogleLoginException(
message: String,
) : Exception(message) {
object Cancelled : GoogleLoginException("로그인이 취소되었습니다.")

object NoCredentials : GoogleLoginException("자격증명을 찾을 수 없습니다.")

object TokenParsing : GoogleLoginException("토큰 파싱에 실패했습니다.")

data class Unknown(
val originalException: Throwable,
) : GoogleLoginException("알 수 없는 오류가 발생했습니다.")
}

sealed class Result<out T> {
data class Success<T>(
val data: T,
) : Result<T>()

data class Error(
val code: String? = null,
val message: String? = null,
val exception: Throwable? = null,
) : Result<Nothing>()
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package com.catchmate.domain.repository

import android.app.Activity
import com.catchmate.domain.exception.Result
import com.catchmate.domain.model.auth.PostLoginRequest

interface LoginRepository {
suspend fun loginWithKakao(): PostLoginRequest?

suspend fun loginWithNaver(activity: Activity): PostLoginRequest?

suspend fun loginWithGoogle(activity: Activity): PostLoginRequest?
suspend fun loginWithGoogle(activity: Activity): Result<PostLoginRequest>
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.catchmate.domain.usecase.auth

import android.app.Activity
import com.catchmate.domain.exception.Result
import com.catchmate.domain.model.auth.PostLoginRequest
import com.catchmate.domain.repository.LoginRepository
import javax.inject.Inject
Expand All @@ -14,5 +15,5 @@ class SocialLoginUseCase

suspend fun loginWithNaver(activity: Activity): PostLoginRequest? = loginRepository.loginWithNaver(activity)

suspend fun loginWithGoogle(activity: Activity): PostLoginRequest? = loginRepository.loginWithGoogle(activity)
suspend fun loginWithGoogle(activity: Activity): Result<PostLoginRequest> = loginRepository.loginWithGoogle(activity)
}
2 changes: 1 addition & 1 deletion CatchMate/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[versions]
# Application
android-gradle-plugin = "8.0.2"
android-gradle-plugin = "8.1.4"
android-desugar-jdk-libs = "1.2.2"

# AndroidX
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.catchmate.presentation.viewmodel

import android.app.Activity
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.catchmate.domain.exception.GoogleLoginException
import com.catchmate.domain.exception.Result
import com.catchmate.domain.model.auth.PostLoginRequest
import com.catchmate.domain.model.auth.PostLoginResponse
import com.catchmate.domain.usecase.auth.PostAuthLoginUseCase
Expand Down Expand Up @@ -55,10 +58,29 @@ class LoginViewModel
fun googleLogin(activity: Activity) {
viewModelScope.launch {
val result = socialLoginUseCase.loginWithGoogle(activity)
if (result != null) {
_postLoginRequest.value = result!!
} else {
_noCredentialException.value = "앱 로그인을 위해서 기기에 Google 계정을 추가해주세요."
when (result) {
is Result.Success -> {
_postLoginRequest.value = result.data
}
is Result.Error -> {
when (result.exception) {
is GoogleLoginException.Cancelled -> {
Log.e("GoogleLoginError", "로그인이 취소되었습니다.")
}
is GoogleLoginException.NoCredentials -> {
_noCredentialException.value = "앱 로그인을 위해서 기기에 Google 계정을 등록해주세요."
}
is GoogleLoginException.TokenParsing -> {
Log.e("GoogleLoginError", "로그인 정보 처리 중 오류가 발생했습니다.")
}
is GoogleLoginException.Unknown -> {
Log.e("GoogleLoginError", "알 수 없는 오류가 발생했습니다.")
}
else -> {
Log.e("GoogleLoginError", "로그인 중 오류가 발생했습니다.")
}
}
}
}
}
}
Expand Down