구글 로그인 추가
This commit is contained in:
		| @@ -52,6 +52,7 @@ android { | |||||||
|             buildConfigField 'String', 'NOTIFLY_PROJECT_ID', '"765102ec85855aa680da35f1b0f55712"' |             buildConfigField 'String', 'NOTIFLY_PROJECT_ID', '"765102ec85855aa680da35f1b0f55712"' | ||||||
|             buildConfigField 'String', 'NOTIFLY_USERNAME', '"voiceon"' |             buildConfigField 'String', 'NOTIFLY_USERNAME', '"voiceon"' | ||||||
|             buildConfigField 'String', 'NOTIFLY_PASSWORD', '"c6c585db0aaa4189be44d0467c7d66b6@A"' |             buildConfigField 'String', 'NOTIFLY_PASSWORD', '"c6c585db0aaa4189be44d0467c7d66b6@A"' | ||||||
|  |             buildConfigField 'String', 'GOOGLE_CLIENT_ID', '"983594297130-5hrmkh6vpskeq6v34350kmilf74574h2.apps.googleusercontent.com"' | ||||||
|             manifestPlaceholders = [ |             manifestPlaceholders = [ | ||||||
|                 URISCHEME            : "voiceon", |                 URISCHEME            : "voiceon", | ||||||
|                 APPLINK_HOST         : "voiceon.onelink.me", |                 APPLINK_HOST         : "voiceon.onelink.me", | ||||||
| @@ -74,6 +75,7 @@ android { | |||||||
|             buildConfigField 'String', 'NOTIFLY_PROJECT_ID', '"5f7ebe90d1ce5f0392164b8a53a662bc"' |             buildConfigField 'String', 'NOTIFLY_PROJECT_ID', '"5f7ebe90d1ce5f0392164b8a53a662bc"' | ||||||
|             buildConfigField 'String', 'NOTIFLY_USERNAME', '"voiceon"' |             buildConfigField 'String', 'NOTIFLY_USERNAME', '"voiceon"' | ||||||
|             buildConfigField 'String', 'NOTIFLY_PASSWORD', '"c6c585db0aaa4189be44d0467c7d66b6@A"' |             buildConfigField 'String', 'NOTIFLY_PASSWORD', '"c6c585db0aaa4189be44d0467c7d66b6@A"' | ||||||
|  |             buildConfigField 'String', 'GOOGLE_CLIENT_ID', '"758414412471-mosodbj2chno7l1j0iihldh6edmk0gk9.apps.googleusercontent.com"' | ||||||
|             manifestPlaceholders = [ |             manifestPlaceholders = [ | ||||||
|                 URISCHEME            : "voiceon-test", |                 URISCHEME            : "voiceon-test", | ||||||
|                 APPLINK_HOST         : "voiceon-test.onelink.me", |                 APPLINK_HOST         : "voiceon-test.onelink.me", | ||||||
| @@ -154,6 +156,10 @@ dependencies { | |||||||
|     implementation 'com.google.firebase:firebase-messaging-ktx' |     implementation 'com.google.firebase:firebase-messaging-ktx' | ||||||
|     implementation 'com.google.firebase:firebase-config-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 |     // bootpay | ||||||
|     implementation "io.github.bootpay:android:4.4.3" |     implementation "io.github.bootpay:android:4.4.3" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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.find_password.ForgotPasswordRequest | ||||||
| import kr.co.vividnext.sodalive.user.login.LoginRequest | import kr.co.vividnext.sodalive.user.login.LoginRequest | ||||||
| import kr.co.vividnext.sodalive.user.login.LoginResponse | 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 kr.co.vividnext.sodalive.user.signup.SignUpRequest | ||||||
| import okhttp3.MultipartBody | import okhttp3.MultipartBody | ||||||
| import retrofit2.http.Body | import retrofit2.http.Body | ||||||
| @@ -162,4 +163,10 @@ interface UserApi { | |||||||
|         @Path("id") id: Long, |         @Path("id") id: Long, | ||||||
|         @Header("Authorization") authHeader: String |         @Header("Authorization") authHeader: String | ||||||
|     ): Single<ApiResponse<GetMemberProfileResponse>> |     ): Single<ApiResponse<GetMemberProfileResponse>> | ||||||
|  |  | ||||||
|  |     @POST("/member/login/google") | ||||||
|  |     fun loginGoogle( | ||||||
|  |         @Body request: SocialLoginRequest, | ||||||
|  |         @Header("Authorization") authHeader: String | ||||||
|  |     ): Single<ApiResponse<LoginResponse>> | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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.settings.signout.SignOutRequest | ||||||
| import kr.co.vividnext.sodalive.user.find_password.ForgotPasswordRequest | import kr.co.vividnext.sodalive.user.find_password.ForgotPasswordRequest | ||||||
| import kr.co.vividnext.sodalive.user.login.LoginRequest | 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 kr.co.vividnext.sodalive.user.signup.SignUpRequest | ||||||
| import okhttp3.MultipartBody | import okhttp3.MultipartBody | ||||||
|  |  | ||||||
| @@ -124,4 +125,12 @@ class UserRepository(private val userApi: UserApi) { | |||||||
|         id: Long, |         id: Long, | ||||||
|         token: String |         token: String | ||||||
|     ) = userApi.getMemberProfile(id = id, authHeader = token) |     ) = userApi.getMemberProfile(id = id, authHeader = token) | ||||||
|  |  | ||||||
|  |     fun googleLogin( | ||||||
|  |         request: SocialLoginRequest, | ||||||
|  |         token: String | ||||||
|  |     ) = userApi.loginGoogle( | ||||||
|  |         request = request, | ||||||
|  |         authHeader = token | ||||||
|  |     ) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -14,10 +14,22 @@ import android.widget.Toast | |||||||
| import androidx.annotation.OptIn | import androidx.annotation.OptIn | ||||||
| import androidx.constraintlayout.widget.ConstraintLayout | import androidx.constraintlayout.widget.ConstraintLayout | ||||||
| import androidx.constraintlayout.widget.ConstraintSet | 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 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.jakewharton.rxbinding4.widget.textChanges | ||||||
|  | import com.orhanobut.logger.Logger | ||||||
| import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers | import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers | ||||||
| import io.reactivex.rxjava3.schedulers.Schedulers | 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.base.BaseActivity | ||||||
| import kr.co.vividnext.sodalive.common.Constants | import kr.co.vividnext.sodalive.common.Constants | ||||||
| import kr.co.vividnext.sodalive.common.LoadingDialog | import kr.co.vividnext.sodalive.common.LoadingDialog | ||||||
| @@ -101,6 +113,67 @@ class LoginActivity : BaseActivity<ActivityLoginBinding>(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() { |     private fun login() { | ||||||
|   | |||||||
| @@ -22,6 +22,45 @@ class LoginViewModel(private val repository: UserRepository) : BaseViewModel() { | |||||||
|     val isLoading: LiveData<Boolean> |     val isLoading: LiveData<Boolean> | ||||||
|         get() = _isLoading |         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) { |     fun login(onSuccess: (String?) -> Unit) { | ||||||
|         if (email.isBlank()) { |         if (email.isBlank()) { | ||||||
|             _toastLiveData.postValue("이메일을 입력하세요.") |             _toastLiveData.postValue("이메일을 입력하세요.") | ||||||
|   | |||||||
| @@ -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 | ||||||
|  | ) | ||||||
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-xxhdpi/ic_login_google.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/src/main/res/drawable-xxhdpi/ic_login_google.png
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 6.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable-xxhdpi/ic_login_kakao.png
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/src/main/res/drawable-xxhdpi/ic_login_kakao.png
									
									
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 2.9 KiB | 
| @@ -96,6 +96,21 @@ | |||||||
|             android:textColor="@color/white" |             android:textColor="@color/white" | ||||||
|             android:textSize="15sp" /> |             android:textSize="15sp" /> | ||||||
|  |  | ||||||
|  |         <LinearLayout | ||||||
|  |             android:layout_width="match_parent" | ||||||
|  |             android:layout_height="wrap_content" | ||||||
|  |             android:layout_marginHorizontal="13.3dp" | ||||||
|  |             android:layout_marginTop="20dp" | ||||||
|  |             android:gravity="center"> | ||||||
|  |  | ||||||
|  |             <ImageView | ||||||
|  |                 android:id="@+id/iv_login_google" | ||||||
|  |                 android:layout_width="wrap_content" | ||||||
|  |                 android:layout_height="wrap_content" | ||||||
|  |                 android:contentDescription="@null" | ||||||
|  |                 android:src="@drawable/ic_login_google" /> | ||||||
|  |         </LinearLayout> | ||||||
|  |  | ||||||
|         <TextView |         <TextView | ||||||
|             android:id="@+id/tv_forgot_password" |             android:id="@+id/tv_forgot_password" | ||||||
|             android:layout_width="wrap_content" |             android:layout_width="wrap_content" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user