Skip to content

Commit afd8991

Browse files
authored
Merge pull request #159 from SOLPLY/feature/#155-google-login
Feature/#155 google login
2 parents d7cdb5f + d18d30d commit afd8991

File tree

12 files changed

+162
-6
lines changed

12 files changed

+162
-6
lines changed

app/proguard-rules.pro

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,7 @@
1818

1919
# If you keep the line number information, uncomment this to
2020
# hide the original source file name.
21-
#-renamesourcefileattribute SourceFile
21+
#-renamesourcefileattribute SourceFile
22+
23+
-keep class * extends com.google.gson.TypeAdapter
24+
-keep class com.google.googlesignin.** { *; }

build-logic/convention/src/main/java/com/teamsolply/solply/convention/BuildConfig.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ internal fun Project.configureBuildConfig(
3434
"NAVER_DEVELOPERS_CLIENT_SECRET",
3535
gradleLocalProperties(rootDir, providers).getProperty("naver.developers.client.secret")
3636
)
37+
buildConfigField(
38+
"String",
39+
"GOOGLE_WEB_CLIENT_ID",
40+
gradleLocalProperties(rootDir, providers).getProperty("google.web.client.id")
41+
)
3742
}
3843

3944
buildFeatures {

core/buildconfig/src/main/java/com/teamsolply/solply/buildconfig/impl/BuildConfigFieldsProviderImpl.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.teamsolply.solply.buildconfig.impl
22

33
import com.teamsolply.solply.buildconfig.BuildConfig.BASE_URL
4+
import com.teamsolply.solply.buildconfig.BuildConfig.GOOGLE_WEB_CLIENT_ID
45
import com.teamsolply.solply.buildconfig.BuildConfig.KAKAO_NATIVE_KEY
56
import com.teamsolply.solply.buildconfig.BuildConfig.NAVER_CLIENT_ID
67
import com.teamsolply.solply.buildconfig.BuildConfig.NAVER_DEVELOPERS_CLIENT_ID
@@ -14,6 +15,7 @@ class BuildConfigFieldsProviderImpl @Inject constructor() : BuildConfigFieldProv
1415
BuildConfigFields(
1516
baseUrl = BASE_URL,
1617
kakaoNativeKey = KAKAO_NATIVE_KEY,
18+
googleWebClientId = GOOGLE_WEB_CLIENT_ID,
1719
naverClientId = NAVER_CLIENT_ID,
1820
naverDevelopersClientId = NAVER_DEVELOPERS_CLIENT_ID,
1921
naverDevelopersClientSecret = NAVER_DEVELOPERS_CLIENT_SECRET,

core/common/src/main/java/com/teamsolply/solply/common/buildconfig/BuildConfigFields.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.teamsolply.solply.common.buildconfig
33
data class BuildConfigFields(
44
val baseUrl: String,
55
val kakaoNativeKey: String,
6+
val googleWebClientId: String,
67
val naverClientId: String,
78
val naverDevelopersClientId: String,
89
val naverDevelopersClientSecret: String,

feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditScreen.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ fun ProfileEditScreen(
127127
onBackButtonClick = onBackButtonClick
128128
)
129129
Spacer(
130-
modifier = Modifier.weight(1f)
130+
modifier = Modifier.height(16.dp)
131131
)
132132
Image(
133133
painter = painterResource(R.drawable.img_basic_profile),
@@ -138,7 +138,7 @@ fun ProfileEditScreen(
138138
.clip(CircleShape),
139139
contentScale = ContentScale.Fit
140140
)
141-
Spacer(modifier = Modifier.weight(2f))
141+
Spacer(modifier = Modifier.height(32.dp))
142142
Column(
143143
modifier = Modifier.padding(horizontal = 16.dp),
144144
verticalArrangement = Arrangement.Top,
@@ -166,7 +166,7 @@ fun ProfileEditScreen(
166166
modifier = Modifier.padding(top = 12.dp)
167167
)
168168
}
169-
Spacer(modifier = Modifier.weight(1.5f))
169+
Spacer(modifier = Modifier.height(24.dp))
170170
Column(
171171
modifier = Modifier.padding(horizontal = 16.dp),
172172
verticalArrangement = Arrangement.Top,

feature/oauth/build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import com.teamsolply.solply.convention.extension.implementation
2+
13
plugins {
24
alias(libs.plugins.solply.feature)
35
}
@@ -8,5 +10,9 @@ android {
810

911
dependencies {
1012
implementation(libs.kakao.login)
13+
implementation(libs.google.id)
14+
implementation(libs.credentials.play.auth)
15+
implementation(libs.credentials)
1116
implementation(projects.domain.oauth)
17+
implementation(projects.core.buildconfig)
1218
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package com.teamsolply.solply.oauth
2+
3+
import android.content.Context
4+
import android.util.Log
5+
import androidx.credentials.CredentialManager
6+
import androidx.credentials.CustomCredential
7+
import androidx.credentials.GetCredentialRequest
8+
import androidx.credentials.GetCredentialResponse
9+
import androidx.credentials.exceptions.GetCredentialException
10+
import com.google.android.libraries.identity.googleid.GetGoogleIdOption
11+
import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
12+
import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException
13+
import kotlinx.coroutines.CoroutineScope
14+
import kotlinx.coroutines.Dispatchers
15+
import kotlinx.coroutines.launch
16+
import java.util.UUID
17+
18+
class GoogleLoginHelper(
19+
private val context: Context
20+
) {
21+
22+
companion object {
23+
const val TAG = "GoogleLogin"
24+
const val WEB_CLIENT_ID = com.teamsolply.solply.buildconfig.BuildConfig.GOOGLE_WEB_CLIENT_ID
25+
// const val SERVER_URL = ""
26+
}
27+
28+
private val credentialManager: CredentialManager = CredentialManager.create(context)
29+
private val nonce = UUID.randomUUID().toString()
30+
private val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
31+
.setServerClientId(WEB_CLIENT_ID) // 웹 클라이언트 ID
32+
.setFilterByAuthorizedAccounts(false)
33+
// .setNonce(nonce)
34+
.build()
35+
private val request: GetCredentialRequest = GetCredentialRequest.Builder()
36+
.addCredentialOption(googleIdOption)
37+
.build()
38+
private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.Main)
39+
40+
fun requestGoogleLogin(
41+
onSuccess: (String) -> Unit,
42+
onFailure: (String) -> Unit
43+
) {
44+
coroutineScope.launch {
45+
try {
46+
val result = credentialManager.getCredential(
47+
request = request,
48+
context = context
49+
)
50+
handleSignInResult(result, onSuccess, onFailure)
51+
} catch (e: GetCredentialException) {
52+
Log.e("Google Sign-in failed", " ${e.localizedMessage}")
53+
}
54+
}
55+
}
56+
57+
private fun handleSignInResult(
58+
result: GetCredentialResponse,
59+
onSuccess: (String) -> Unit,
60+
onFailure: (String) -> Unit
61+
) {
62+
when (val credential = result.credential) {
63+
is CustomCredential -> {
64+
if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
65+
try {
66+
val googleIdTokenCredential =
67+
GoogleIdTokenCredential.createFrom(credential.data)
68+
val idToken = googleIdTokenCredential.idToken
69+
// Log.d(TAG, idToken) //토큰
70+
// Log.d(TAG, googleIdTokenCredential.id) //이메일
71+
// googleIdTokenCredential.displayName?.let { Log.d(TAG, it) } //이름
72+
onSuccess(idToken) // 성공 시 처리 함수, 서버 응답 후 실행, 여기서는 테스트를 위해 이곳에서 실행
73+
} catch (e: GoogleIdTokenParsingException) {
74+
// Log.e(TAG, "Received an invalid google id token response", e)
75+
}
76+
} else {
77+
// Log.e(TAG, "Unexpected type of credential")
78+
onFailure("구글 로그인에 실패하였습니다. 다시 시도해주세요.")
79+
}
80+
}
81+
82+
else -> {
83+
// Log.e(TAG, "Unexpected type of credential")
84+
onFailure("구글 로그인에 실패하였습니다. 다시 시도해주세요.")
85+
}
86+
}
87+
}
88+
}

feature/oauth/src/main/java/com/teamsolply/solply/oauth/OauthContract.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ sealed interface OauthIntent : UiIntent {
1212
data object KakaoLoginClick : OauthIntent
1313
data class KakaoLoginSuccess(val provider: String, val accessToken: String) : OauthIntent
1414
data class KakaoLoginFailure(val error: Throwable) : OauthIntent
15+
data object GoogleLoginClick : OauthIntent
16+
data class GoogleLoginSuccess(val provider: String, val accessToken: String) : OauthIntent
17+
data class GoogleLoginFailure(val error: Throwable) : OauthIntent
1518
data class SaveJwtToken(
1619
val accessToken: String,
1720
val refreshToken: String,
@@ -21,6 +24,7 @@ sealed interface OauthIntent : UiIntent {
2124

2225
sealed interface OauthSideEffect : SideEffect {
2326
data object StartKakaoLogin : OauthSideEffect
27+
data object StartGoogleLogin : OauthSideEffect
2428
data object NavigateToOnBoarding : OauthSideEffect
2529
data object NavigateToPlace : OauthSideEffect
2630
}

feature/oauth/src/main/java/com/teamsolply/solply/oauth/OauthScreen.kt

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import androidx.compose.material3.Icon
1919
import androidx.compose.material3.Text
2020
import androidx.compose.runtime.Composable
2121
import androidx.compose.runtime.getValue
22+
import androidx.compose.runtime.remember
2223
import androidx.compose.ui.Alignment
2324
import androidx.compose.ui.Modifier
2425
import androidx.compose.ui.graphics.Color
@@ -46,6 +47,7 @@ fun OauthRoute(
4647
viewModel: OauthViewModel = hiltViewModel()
4748
) {
4849
val context = LocalContext.current
50+
val googleLoginHelper = remember { GoogleLoginHelper(context = context) }
4951
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
5052

5153
LaunchedEffectWithLifecycle {
@@ -63,20 +65,46 @@ fun OauthRoute(
6365
}
6466
)
6567

68+
OauthSideEffect.StartGoogleLogin -> googleLoginHelper.requestGoogleLogin(
69+
onSuccess = { accessToken ->
70+
viewModel.sendIntent(
71+
OauthIntent.GoogleLoginSuccess(
72+
provider = "GOOGLE",
73+
accessToken = accessToken
74+
)
75+
)
76+
},
77+
onFailure = {}
78+
)
79+
6680
OauthSideEffect.NavigateToOnBoarding -> navigateToOnBoarding()
6781
OauthSideEffect.NavigateToPlace -> navigateToPlace()
6882
}
6983
}
7084
}
7185

7286
OauthScreen(
73-
kakaoLoginClick = { viewModel.sendIntent(OauthIntent.KakaoLoginClick) }
87+
kakaoLoginClick = { viewModel.sendIntent(OauthIntent.KakaoLoginClick) },
88+
googleLoginClick = {
89+
googleLoginHelper.requestGoogleLogin(
90+
onSuccess = { accessToken ->
91+
viewModel.sendIntent(
92+
OauthIntent.GoogleLoginSuccess(
93+
provider = "GOOGLE",
94+
accessToken = accessToken
95+
)
96+
)
97+
},
98+
onFailure = {}
99+
)
100+
}
74101
)
75102
}
76103

77104
@Composable
78105
fun OauthScreen(
79106
kakaoLoginClick: () -> Unit,
107+
googleLoginClick: () -> Unit,
80108
modifier: Modifier = Modifier
81109
) {
82110
Column(
@@ -157,7 +185,7 @@ fun OauthScreen(
157185
.customClickable(
158186
rippleEnabled = false
159187
) {
160-
kakaoLoginClick()
188+
googleLoginClick()
161189
},
162190
verticalAlignment = Alignment.CenterVertically,
163191
horizontalArrangement = Arrangement.Start

feature/oauth/src/main/java/com/teamsolply/solply/oauth/OauthViewModel.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@ class OauthViewModel @Inject constructor(
2424
TODO()
2525
}
2626

27+
OauthIntent.GoogleLoginClick -> postSideEffect(OauthSideEffect.StartGoogleLogin)
28+
29+
is OauthIntent.GoogleLoginSuccess -> postSocialLogin(
30+
provider = intent.provider,
31+
oauthAccessToken = intent.accessToken
32+
)
33+
34+
is OauthIntent.GoogleLoginFailure -> {
35+
TODO()
36+
}
37+
2738
is OauthIntent.SaveJwtToken -> {
2839
viewModelScope.launch {
2940
oauthRepository.saveJwtToken(

0 commit comments

Comments
 (0)