From a9885874ee022c23dca50a812ccb04dbc685425c Mon Sep 17 00:00:00 2001 From: klaus Date: Fri, 4 Apr 2025 18:40:22 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B5=AC=EA=B8=80=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 6 ++ .../kr/co/vividnext/sodalive/user/UserApi.kt | 7 ++ .../vividnext/sodalive/user/UserRepository.kt | 9 +++ .../sodalive/user/login/LoginActivity.kt | 73 ++++++++++++++++++ .../sodalive/user/login/LoginViewModel.kt | 39 ++++++++++ .../sodalive/user/login/SocialLoginRequest.kt | 10 +++ .../res/drawable-xxhdpi/ic_login_google.png | Bin 0 -> 6493 bytes .../res/drawable-xxhdpi/ic_login_kakao.png | Bin 0 -> 2969 bytes app/src/main/res/layout/activity_login.xml | 15 ++++ 9 files changed, 159 insertions(+) create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/user/login/SocialLoginRequest.kt create mode 100755 app/src/main/res/drawable-xxhdpi/ic_login_google.png create mode 100755 app/src/main/res/drawable-xxhdpi/ic_login_kakao.png diff --git a/app/build.gradle b/app/build.gradle index 059aed0..14c687c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -52,6 +52,7 @@ android { buildConfigField 'String', 'NOTIFLY_PROJECT_ID', '"765102ec85855aa680da35f1b0f55712"' buildConfigField 'String', 'NOTIFLY_USERNAME', '"voiceon"' buildConfigField 'String', 'NOTIFLY_PASSWORD', '"c6c585db0aaa4189be44d0467c7d66b6@A"' + buildConfigField 'String', 'GOOGLE_CLIENT_ID', '"983594297130-5hrmkh6vpskeq6v34350kmilf74574h2.apps.googleusercontent.com"' manifestPlaceholders = [ URISCHEME : "voiceon", APPLINK_HOST : "voiceon.onelink.me", @@ -74,6 +75,7 @@ android { buildConfigField 'String', 'NOTIFLY_PROJECT_ID', '"5f7ebe90d1ce5f0392164b8a53a662bc"' buildConfigField 'String', 'NOTIFLY_USERNAME', '"voiceon"' buildConfigField 'String', 'NOTIFLY_PASSWORD', '"c6c585db0aaa4189be44d0467c7d66b6@A"' + buildConfigField 'String', 'GOOGLE_CLIENT_ID', '"758414412471-mosodbj2chno7l1j0iihldh6edmk0gk9.apps.googleusercontent.com"' manifestPlaceholders = [ URISCHEME : "voiceon-test", APPLINK_HOST : "voiceon-test.onelink.me", @@ -154,6 +156,10 @@ dependencies { implementation 'com.google.firebase:firebase-messaging-ktx' implementation 'com.google.firebase:firebase-config-ktx' + implementation 'androidx.credentials:credentials:1.3.0' + implementation 'androidx.credentials:credentials-play-services-auth:1.3.0' + implementation 'com.google.android.libraries.identity.googleid:googleid:1.1.1' + // bootpay implementation "io.github.bootpay:android:4.4.3" diff --git a/app/src/main/java/kr/co/vividnext/sodalive/user/UserApi.kt b/app/src/main/java/kr/co/vividnext/sodalive/user/UserApi.kt index 61520a3..7734122 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/user/UserApi.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/user/UserApi.kt @@ -17,6 +17,7 @@ import kr.co.vividnext.sodalive.settings.signout.SignOutRequest import kr.co.vividnext.sodalive.user.find_password.ForgotPasswordRequest import kr.co.vividnext.sodalive.user.login.LoginRequest import kr.co.vividnext.sodalive.user.login.LoginResponse +import kr.co.vividnext.sodalive.user.login.SocialLoginRequest import kr.co.vividnext.sodalive.user.signup.SignUpRequest import okhttp3.MultipartBody import retrofit2.http.Body @@ -162,4 +163,10 @@ interface UserApi { @Path("id") id: Long, @Header("Authorization") authHeader: String ): Single> + + @POST("/member/login/google") + fun loginGoogle( + @Body request: SocialLoginRequest, + @Header("Authorization") authHeader: String + ): Single> } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/user/UserRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/user/UserRepository.kt index 1ae028c..b0caa46 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/user/UserRepository.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/user/UserRepository.kt @@ -13,6 +13,7 @@ import kr.co.vividnext.sodalive.settings.notification.UpdateNotificationSettingR import kr.co.vividnext.sodalive.settings.signout.SignOutRequest import kr.co.vividnext.sodalive.user.find_password.ForgotPasswordRequest import kr.co.vividnext.sodalive.user.login.LoginRequest +import kr.co.vividnext.sodalive.user.login.SocialLoginRequest import kr.co.vividnext.sodalive.user.signup.SignUpRequest import okhttp3.MultipartBody @@ -124,4 +125,12 @@ class UserRepository(private val userApi: UserApi) { id: Long, token: String ) = userApi.getMemberProfile(id = id, authHeader = token) + + fun googleLogin( + request: SocialLoginRequest, + token: String + ) = userApi.loginGoogle( + request = request, + authHeader = token + ) } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/user/login/LoginActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/user/login/LoginActivity.kt index 782f7b0..a9ce6e3 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/user/login/LoginActivity.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/user/login/LoginActivity.kt @@ -14,10 +14,22 @@ import android.widget.Toast import androidx.annotation.OptIn import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet +import androidx.credentials.Credential +import androidx.credentials.CredentialManager +import androidx.credentials.CustomCredential +import androidx.credentials.GetCredentialRequest +import androidx.credentials.exceptions.GetCredentialException +import androidx.lifecycle.lifecycleScope import androidx.media3.common.util.UnstableApi +import com.google.android.libraries.identity.googleid.GetGoogleIdOption +import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential +import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential.Companion.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL import com.jakewharton.rxbinding4.widget.textChanges +import com.orhanobut.logger.Logger import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.schedulers.Schedulers +import kotlinx.coroutines.launch +import kr.co.vividnext.sodalive.BuildConfig import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.LoadingDialog @@ -101,6 +113,67 @@ class LoginActivity : BaseActivity(ActivityLoginBinding::i ) ) } + + binding.ivLoginGoogle.setOnClickListener { + loadingDialog.show(width = screenWidth) + val credentialManager = CredentialManager.create(this) + + val googleIdOption = GetGoogleIdOption.Builder() + .setServerClientId(BuildConfig.GOOGLE_CLIENT_ID) + .setFilterByAuthorizedAccounts(false) + .setAutoSelectEnabled(false) + .build() + + // Create the Credential Manager request + val request = GetCredentialRequest.Builder() + .addCredentialOption(googleIdOption) + .build() + + lifecycleScope.launch { + try { + // Launch Credential Manager UI + val result = credentialManager.getCredential( + context = this@LoginActivity, + request = request + ) + loadingDialog.dismiss() + + // Extract credential from the result returned by Credential Manager + handleSignIn(result.credential) + } catch (e: GetCredentialException) { + showToast("로그인을 하지 못했습니다. 다시 시도해 주세요") + Logger.e("Couldn't retrieve user's credentials: ${e.localizedMessage}") + loadingDialog.dismiss() + } + } + } + } + + private fun handleSignIn(credential: Credential) { + // Check if credential is of type Google ID + if (credential is CustomCredential && credential.type == TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) { + // Create Google ID Token + val googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credential.data) + viewModel.googleLogin(idToken = googleIdTokenCredential.idToken) { + finishAffinity() + val nextIntent = Intent(applicationContext, MainActivity::class.java) + val extras = intent.getBundleExtra(Constants.EXTRA_DATA) + ?: if (intent.extras != null) { + intent.extras + } else { + null + } + if (extras != null) { + nextIntent.putExtra(Constants.EXTRA_DATA, extras) + } + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) + startActivity(nextIntent) + } + } else { + showToast("로그인을 하지 못했습니다. 다시 시도해 주세요") + Logger.e("Credential is not of type Google ID!") + } } private fun login() { diff --git a/app/src/main/java/kr/co/vividnext/sodalive/user/login/LoginViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/user/login/LoginViewModel.kt index 6c4ecc1..26cc1cb 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/user/login/LoginViewModel.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/user/login/LoginViewModel.kt @@ -22,6 +22,45 @@ class LoginViewModel(private val repository: UserRepository) : BaseViewModel() { val isLoading: LiveData get() = _isLoading + fun googleLogin(idToken: String, onSuccess: () -> Unit) { + _isLoading.value = true + + compositeDisposable.add( + repository.googleLogin( + request = SocialLoginRequest(marketingPid = SharedPreferenceManager.marketingPid), + token = "Bearer $idToken" + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + _isLoading.value = false + if (it.success && it.data != null) { + SharedPreferenceManager.token = it.data.token + SharedPreferenceManager.email = it.data.email + SharedPreferenceManager.userId = it.data.userId + SharedPreferenceManager.nickname = it.data.nickname + SharedPreferenceManager.profileImage = it.data.profileImage + onSuccess() + } else { + if (it.message != null) { + _toastLiveData.postValue(it.message) + } else { + _toastLiveData.postValue( + "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." + ) + } + } + }, + { + _isLoading.value = false + it.message?.let { message -> Logger.e(message) } + _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") + } + ) + ) + } + fun login(onSuccess: (String?) -> Unit) { if (email.isBlank()) { _toastLiveData.postValue("이메일을 입력하세요.") diff --git a/app/src/main/java/kr/co/vividnext/sodalive/user/login/SocialLoginRequest.kt b/app/src/main/java/kr/co/vividnext/sodalive/user/login/SocialLoginRequest.kt new file mode 100644 index 0000000..3e0450b --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/user/login/SocialLoginRequest.kt @@ -0,0 +1,10 @@ +package kr.co.vividnext.sodalive.user.login + +import androidx.annotation.Keep +import com.google.gson.annotations.SerializedName + +@Keep +data class SocialLoginRequest( + @SerializedName("container") val container: String = "aos", + @SerializedName("marketingPid") val marketingPid: String +) diff --git a/app/src/main/res/drawable-xxhdpi/ic_login_google.png b/app/src/main/res/drawable-xxhdpi/ic_login_google.png new file mode 100755 index 0000000000000000000000000000000000000000..8ede9b697eaf875a232900934479616550d05346 GIT binary patch literal 6493 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 P00000NkvXXu0mjfr9y08 literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 0c388ef..7bf6871 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -96,6 +96,21 @@ android:textColor="@color/white" android:textSize="15sp" /> + + + + +