Skip to content

Commit

Permalink
[#30] 로그인 기능 추가
Browse files Browse the repository at this point in the history
- 신규 유저일 경우, IdToken, ProviderType 을 ProfileEditActivity로 전달
- ProfileEditActivity 에서 다음 버튼을 눌렀을 때 IdToken 과 ProviderType, 입력한 프로필 정보를 바탕으로 회원가입 진행할 수 있도록 밑작업
  • Loading branch information
ethan-223 committed Sep 4, 2022
1 parent 71d3f4a commit b05ecc9
Show file tree
Hide file tree
Showing 13 changed files with 151 additions and 39 deletions.
5 changes: 4 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
android:supportsRtl="true"
android:theme="@style/Theme.MoyeoRunandroid">
<activity
android:name=".login.LoginActivity"
android:name=".profile.ProfileEditActivity"
android:exported="false"/>
<activity
android:name=".login.ui.LoginActivity"
android:exported="false" />
<activity
android:name=".MainActivity"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.moyerun.moyeorun_android.login

import android.os.Parcelable
import kotlinx.parcelize.Parcelize

@Parcelize
enum class ProviderType : Parcelable {
Google
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
package com.moyerun.moyeorun_android.login.data

import com.moyerun.moyeorun_android.common.Lg
import com.moyerun.moyeorun_android.login.ProviderType
import com.moyerun.moyeorun_android.login.data.model.LoginRequest
import com.moyerun.moyeorun_android.login.data.model.LoginResponse
import com.moyerun.moyeorun_android.login.data.model.Token
import com.moyerun.moyeorun_android.login.data.model.TokenPair
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class LoginRepository(
private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO
) {

// Todo: 모여런 서버 붙일 때 반환 타입 조정
suspend fun signIn(idToken: String): LoginResponse {
suspend fun signIn(idToken: String, providerType: ProviderType): LoginResponse {
return withContext(coroutineDispatcher) {
val request = LoginRequest(idToken, providerType.name.uppercase())
// Todo: 모여런 서버 signIn
Lg.d("Try sign in ! : $request")
LoginResponse(TokenPair(Token("", ""), Token("", "")), true, "1234")
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.moyerun.moyeorun_android.login.data.model

data class LoginRequest(
val idToken: String,
val providerType: String
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.moyerun.moyeorun_android.login.data
package com.moyerun.moyeorun_android.login.data.model

data class LoginResponse(
val token: TokenPair,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.moyerun.moyeorun_android.login.data
package com.moyerun.moyeorun_android.login.data.model

data class Token(
val token: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,10 @@ class LoginActivity : AppCompatActivity() {
val idToken = credential.googleIdToken
if (idToken != null) {
Lg.i("Success. token : $idToken")
viewModel.signIn(idToken)
viewModel.googleSignIn(idToken)
} else {
showUnknownErrorToast()
//Todo: #31 을 rebase 하고 주석 풀기
// Lg.fe("No ID token")
Lg.fe("No ID token")
}
} catch (e: ApiException) {
when (e.statusCode) {
Expand All @@ -54,8 +53,7 @@ class LoginActivity : AppCompatActivity() {
}
else -> {
showUnknownErrorToast()
//Todo: #31 을 rebase 하고 주석 풀기
// Lg.fe("Couldn't get credential from result.", e)
Lg.fe("Couldn't get credential from result.", e)
}
}
}
Expand All @@ -67,16 +65,16 @@ class LoginActivity : AppCompatActivity() {
val binding = ActivityLoginBinding.inflate(layoutInflater)
setContentView(binding.root)

observeEvent(viewModel.loginEvent) {
when (it) {
LoginEvent.RegisteredUser -> {
// Todo: 메인화면 진입
observeEvent(viewModel.loginEvent) { event ->
when (event) {
is LoginEvent.RegisteredUser -> {
// Todo: 메인 화면으로 이동
Lg.d("Login!")
}
LoginEvent.NewUser -> {
ProfileEditActivity.startActivity(this)
is LoginEvent.NewUser -> {
ProfileEditActivity.startActivity(this, event.signUpMetaData)
}
LoginEvent.Error -> {
is LoginEvent.Error -> {
showUnknownErrorToast()
}
}
Expand All @@ -91,15 +89,14 @@ class LoginActivity : AppCompatActivity() {
beginSignInResultLauncher.launch(intentSenderRequest)
} catch (e: IntentSender.SendIntentException) {
showUnknownErrorToast()
// Lg.fe("Couldn't start One Tab UI", e)
Lg.fe("Couldn't start One Tab UI", e)
}
}
.addOnFailureListener(this) {
// 기기에 등록된 계정이 없는 경우 호출
startDeviceGoogleSignInActivity()
//Todo: #31 을 rebase 하고 주석 풀기
// 간혹 등록된 계정이 있는데도 해당 콜백을 타는 경우가 있어서 로깅
// Lg.fe("No Google Accounts found", it)
Lg.fe("No Google Accounts found", it)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.moyerun.moyeorun_android.login.ui

enum class LoginEvent {
RegisteredUser, NewUser, Error
import com.moyerun.moyeorun_android.profile.SignUpMetaData

sealed class LoginEvent {
object RegisteredUser : LoginEvent()
data class NewUser(val signUpMetaData: SignUpMetaData) : LoginEvent()
object Error : LoginEvent()
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.moyerun.moyeorun_android.common.EventLiveData
import com.moyerun.moyeorun_android.common.MutableEventLiveData
import com.moyerun.moyeorun_android.login.ProviderType
import com.moyerun.moyeorun_android.profile.SignUpMetaData
import com.moyerun.moyeorun_android.login.data.LoginRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
Expand All @@ -24,15 +26,16 @@ class LoginViewModel @Inject constructor(
val loginEvent: EventLiveData<LoginEvent>
get() = _loginEvent

fun signIn(idToken: String) {
fun googleSignIn(idToken: String) {
signInInternal(idToken, ProviderType.Google)
}

private fun signInInternal(idToken: String, providerType: ProviderType) {
viewModelScope.launch {
_isLoading.value = true
// Todo: 반환 타입 결정 후 분기
// Todo: Firebase crashlytics userId 세팅
// Todo: SharedPreference 에 유저 메타데이터 세팅
val result = loginRepository.signIn(idToken)
val result = loginRepository.signIn(idToken, providerType)
_loginEvent.event = if (result.isNewUser) {
LoginEvent.NewUser
LoginEvent.NewUser(SignUpMetaData(idToken, providerType))
} else {
LoginEvent.RegisteredUser
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.widget.EditText
import androidx.activity.viewModels
import androidx.core.widget.doAfterTextChanged
import com.moyerun.moyeorun_android.R
import com.moyerun.moyeorun_android.common.Lg
import com.moyerun.moyeorun_android.common.extension.*
import com.moyerun.moyeorun_android.databinding.ActivityProfileBinding
import dagger.hilt.android.AndroidEntryPoint
Expand All @@ -26,8 +27,9 @@ class ProfileEditActivity : AppCompatActivity() {
val binding = ActivityProfileBinding.inflate(layoutInflater)
setContentView(binding.root)

val signUpMetaData: SignUpMetaData? = intent.getParcelableExtra(EXTRA_SIGN_UP_META_DATA)
val originalProfile: ProfileUiModel? = intent.getParcelableExtra(EXTRA_PROFILE_UI_MODEL)
val isNewProfile = originalProfile == null
val isNewProfile = (originalProfile == null && signUpMetaData != null)

if (isNewProfile) {
binding.textviewProfileTitle.text = "기본 정보"
Expand All @@ -37,7 +39,7 @@ class ProfileEditActivity : AppCompatActivity() {
binding.buttonProfileConfirm.text = "완료"
}

viewModel.updateProfile(originalProfile)
viewModel.updateData(signUpMetaData, originalProfile)

binding.edittextProfileName.doAfterTextChanged {
viewModel.onNameChanged(it?.toString().orEmpty())
Expand All @@ -60,6 +62,12 @@ class ProfileEditActivity : AppCompatActivity() {
}
}

binding.buttonProfileConfirm.setOnDebounceClickListener {
if (isNewProfile) {
viewModel.signUp()
}
}

repeatOnStart {
launch {
viewModel.profileUiModel
Expand All @@ -86,6 +94,24 @@ class ProfileEditActivity : AppCompatActivity() {
}
}
}

observeEvent(viewModel.profileEvent) {
when (it) {
is ProfileEvent.SuccessSignUp -> {
// Todo: 환영 액티비티로 이동
Lg.d("observeEvent : Go to welcome activity!")
}
is ProfileEvent.Error -> {
when (it.error) {
ProfileError.WRONG_ACCESS -> {
Lg.fw("Wrong access. signUpMetadata: $signUpMetaData, originProfile: $originalProfile")
toast("잘못된 접근입니다.")
finish()
}
}
}
}
}
}

private fun isValidText(text: String): Boolean {
Expand All @@ -106,11 +132,20 @@ class ProfileEditActivity : AppCompatActivity() {

companion object {
private const val EXTRA_PROFILE_UI_MODEL = "profileUiModel"
private const val EXTRA_SIGN_UP_META_DATA = "signUpMetaData"

fun startActivity(context: Context, profileUiModel: ProfileUiModel? = null) {
// 프로필 수정 시 사용
fun startActivity(context: Context, profileUiModel: ProfileUiModel) {
context.startActivity(Intent(context, ProfileEditActivity::class.java).apply {
putExtra(EXTRA_PROFILE_UI_MODEL, profileUiModel)
})
}

// 회원가입 시 사용
fun startActivity(context: Context, signUpMetaData: SignUpMetaData) {
context.startActivity(Intent(context, ProfileEditActivity::class.java).apply {
putExtra(EXTRA_SIGN_UP_META_DATA, signUpMetaData)
})
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.moyerun.moyeorun_android.profile

import androidx.lifecycle.ViewModel
import com.moyerun.moyeorun_android.common.EventLiveData
import com.moyerun.moyeorun_android.common.Lg
import com.moyerun.moyeorun_android.common.MutableEventLiveData
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
Expand All @@ -11,15 +14,30 @@ class ProfileEditViewModel: ViewModel() {
val profileUiModel: StateFlow<ProfileUiModel>
get() = _profileUiModel

private val _profileEvent = MutableEventLiveData<ProfileEvent>()
val profileEvent: EventLiveData<ProfileEvent>
get() = _profileEvent

private var signUpMetaData: SignUpMetaData? = null
private var oldProfileUiModel: ProfileUiModel? = null

private var isNewPost = true
private var isNewProfile = true

fun updateProfile(profileUiModel: ProfileUiModel?) {
if (profileUiModel == null) return
_profileUiModel.update { profileUiModel }
oldProfileUiModel = profileUiModel
isNewPost = false
fun updateData(signUpMetaData: SignUpMetaData?, profileUiModel: ProfileUiModel?) {
when {
signUpMetaData == null && profileUiModel != null -> {
_profileUiModel.update { profileUiModel }
oldProfileUiModel = profileUiModel
isNewProfile = false
}
signUpMetaData != null && profileUiModel == null -> {
this.signUpMetaData = signUpMetaData
isNewProfile = true
}
else -> {
_profileEvent.event = ProfileEvent.Error(ProfileError.WRONG_ACCESS)
}
}
}

fun onNameChanged(name: String) {
Expand All @@ -39,4 +57,10 @@ class ProfileEditViewModel: ViewModel() {
it.copy(imageUrl = imageUrl)
}
}

fun signUp() {
// Todo: 회원가입 API 호출
Lg.d("SignUp : ${profileUiModel.value}, $signUpMetaData")
_profileEvent.event = ProfileEvent.SuccessSignUp
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,13 @@ data class ProfileUiModel(
val imageUrl: String = "",
val name: String = "",
val nickname: String = ""
): Parcelable
): Parcelable

sealed class ProfileEvent {
object SuccessSignUp: ProfileEvent()
data class Error(val error: ProfileError): ProfileEvent()
}

enum class ProfileError {
WRONG_ACCESS
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.moyerun.moyeorun_android.profile

import android.os.Parcelable
import com.moyerun.moyeorun_android.login.ProviderType
import kotlinx.parcelize.Parcelize

/**
* 회원가입을 위해 프로필 설정 화면으로 진입 시
* 회원가입 API 호출에 필요한 메타 데이터를 전달하기 위한
* DataHolder
*/
@Parcelize
data class SignUpMetaData(
val idToken: String,
val providerType: ProviderType
) : Parcelable

0 comments on commit b05ecc9

Please sign in to comment.