diff --git a/app/src/main/java/hous/release/android/di/RetrofitModule.kt b/app/src/main/java/hous/release/android/di/RetrofitModule.kt index 3779de3ff..47ca561df 100644 --- a/app/src/main/java/hous/release/android/di/RetrofitModule.kt +++ b/app/src/main/java/hous/release/android/di/RetrofitModule.kt @@ -60,9 +60,7 @@ object RetrofitModule { ): Interceptor = Interceptor { chain -> if (!context.isNetworkConnected()) { context.startActivity( - Intent( - context, NetworkErrorActivity::class.java - ).apply { + Intent(context, NetworkErrorActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) } ) @@ -71,10 +69,7 @@ object RetrofitModule { var response = chain.proceed( request .newBuilder() - .addHeader( - HEADER_AUTHORIZATION, - BEARER + localPrefTokenDataSource.accessToken - ) + .addHeader(HEADER_AUTHORIZATION, BEARER + localPrefTokenDataSource.accessToken) .addHeader(HEADER_OS_TYPE, OS_TYPE) .addHeader(HEADER_VERSION, BuildConfig.VERSION_NAME) .build() diff --git a/app/src/main/java/hous/release/android/presentation/hous/HousViewModel.kt b/app/src/main/java/hous/release/android/presentation/hous/HousViewModel.kt index b30f99cbe..728267cdd 100644 --- a/app/src/main/java/hous/release/android/presentation/hous/HousViewModel.kt +++ b/app/src/main/java/hous/release/android/presentation/hous/HousViewModel.kt @@ -29,7 +29,7 @@ class HousViewModel @Inject constructor( .onSuccess { response -> _hous.value = response _homies.value = response.homies - Timber.e("${hous.value.homies[0].homieId}") + Timber.d("${hous.value.homies[0].homieId}") } .onFailure { Timber.d(it.message.toString()) } } diff --git a/app/src/main/java/hous/release/android/presentation/login/LoginActivity.kt b/app/src/main/java/hous/release/android/presentation/login/LoginActivity.kt index d7f954862..c12db16b4 100644 --- a/app/src/main/java/hous/release/android/presentation/login/LoginActivity.kt +++ b/app/src/main/java/hous/release/android/presentation/login/LoginActivity.kt @@ -14,9 +14,8 @@ import hous.release.android.util.binding.BindingActivity import hous.release.android.util.dialog.ConfirmClickListener import hous.release.android.util.dialog.WarningDialogFragment import hous.release.android.util.dialog.WarningType -import hous.release.android.util.extension.EventObserver +import hous.release.android.util.extension.repeatOnStarted import hous.release.data.service.KakaoLoginService -import timber.log.Timber import javax.inject.Inject import kotlin.system.exitProcess @@ -30,35 +29,44 @@ class LoginActivity : BindingActivity(R.layout.activity_lo override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) initKakaoLoginBtnClickListener() - initIsSuccessKakaoLoginObserver() - initIsInitUserInfoObserver() initBackPressedCallback() - initIsJoiningRoomObserve() - initIsUserObserve() - initIsPermitAccessObserve() + collectIsJoiningRoom() + collectIsSignedUp() + collectIsKakaoLogin() + collectIsMiultipleAccess() } - private fun initIsUserObserve() { - loginViewModel.isJoiningRoom.observe(this) { - if (loginViewModel.isJoiningRoom.value == true) { - ToastMessageUtil.showToast(this@LoginActivity, getString(R.string.login_toast)) - val toMain = Intent(this, MainActivity::class.java) - startActivity(toMain) - finishAffinity() - } else { - val toEnterRoom = Intent(this, EnterRoomActivity::class.java) - startActivity(toEnterRoom) - finishAffinity() + private fun collectIsJoiningRoom() { + repeatOnStarted { + loginViewModel.isJoiningRoom.collect { joiningRoom -> + if (joiningRoom) { + ToastMessageUtil.showToast(this@LoginActivity, getString(R.string.login_toast)) + startActivity( + Intent(this, MainActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + } + ) + } else { + startActivity( + Intent(this@LoginActivity, EnterRoomActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + } + ) + } } } } - private fun initIsJoiningRoomObserve() { - loginViewModel.isUser.observe(this) { - if (loginViewModel.isUser.value == false) { - val toUserInput = Intent(this, UserInputActivity::class.java) - startActivity(toUserInput) - finishAffinity() + private fun collectIsSignedUp() { + repeatOnStarted { + loginViewModel.isSignedUp.collect { signedUp -> + if (!signedUp) { + startActivity( + Intent(this@LoginActivity, UserInputActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + } + ) + } } } } @@ -89,43 +97,33 @@ class LoginActivity : BindingActivity(R.layout.activity_lo kakaoLoginService.startKakaoLogin(loginViewModel.kakaoLoginCallback) } - private fun initIsSuccessKakaoLoginObserver() { - loginViewModel.isSuccessKakaoLogin.observe( - this, - EventObserver { isSuccess -> - if (isSuccess) { - Timber.e("카카오 로그인 성공") - } else { - Timber.e("카카오 로그인 실패") + private fun collectIsKakaoLogin() { + repeatOnStarted { + loginViewModel.isKakaoLogin.collect { success -> + if (success) { + loginViewModel.postLogin() } } - ) - } - - private fun initIsInitUserInfoObserver() { - loginViewModel.isInitUserInfo.observe( - this, - EventObserver { isSuccess -> - if (isSuccess) loginViewModel.postLogin() - } - ) + } } - private fun initIsPermitAccessObserve() { - loginViewModel.isMultipleAccess.observe(this) { isMultipleAccess -> - if (isMultipleAccess == true) { - WarningDialogFragment().apply { - arguments = Bundle().apply { - putSerializable( - WarningDialogFragment.WARNING_TYPE, - WarningType.WARNING_SPLASH - ) - putParcelable( - WarningDialogFragment.CONFIRM_ACTION, - ConfirmClickListener(confirmAction = { loginViewModel.initIsPermitAccess() }) - ) - } - }.show(supportFragmentManager, WarningDialogFragment.DIALOG_WARNING) + private fun collectIsMiultipleAccess() { + repeatOnStarted { + loginViewModel.isMiultipleAccess.collect { accessLogin -> + if (accessLogin) { + WarningDialogFragment().apply { + arguments = Bundle().apply { + putSerializable( + WarningDialogFragment.WARNING_TYPE, + WarningType.WARNING_SPLASH + ) + putParcelable( + WarningDialogFragment.CONFIRM_ACTION, + ConfirmClickListener(confirmAction = { loginViewModel.postForceLogin() }) + ) + } + }.show(supportFragmentManager, WarningDialogFragment.DIALOG_WARNING) + } } } } diff --git a/app/src/main/java/hous/release/android/presentation/login/LoginViewModel.kt b/app/src/main/java/hous/release/android/presentation/login/LoginViewModel.kt index 7ebeb8376..b8dcd70f4 100644 --- a/app/src/main/java/hous/release/android/presentation/login/LoginViewModel.kt +++ b/app/src/main/java/hous/release/android/presentation/login/LoginViewModel.kt @@ -1,21 +1,22 @@ package hous.release.android.presentation.login -import androidx.lifecycle.LiveData -import androidx.lifecycle.MediatorLiveData -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.kakao.sdk.auth.model.OAuthToken -import com.kakao.sdk.common.model.AuthErrorCause import dagger.hilt.android.lifecycle.HiltViewModel -import hous.release.android.util.extension.Event +import hous.release.android.util.KakaoLoginCallback import hous.release.domain.entity.SplashState import hous.release.domain.usecase.GetFcmTokenUseCase import hous.release.domain.usecase.InitHousTokenUseCase -import hous.release.domain.usecase.InitTokenUseCase +import hous.release.domain.usecase.InitLoginTokenUseCase import hous.release.domain.usecase.PostForceLoginUseCase import hous.release.domain.usecase.PostLoginUseCase import hous.release.domain.usecase.SetSplashStateUseCase +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch import retrofit2.HttpException import timber.log.Timber @@ -24,74 +25,32 @@ import javax.inject.Inject @HiltViewModel class LoginViewModel @Inject constructor( private val postLoginUseCase: PostLoginUseCase, - private val initTokenUseCase: InitTokenUseCase, private val initHousTokenUseCase: InitHousTokenUseCase, + private val initLoginTokenUseCase: InitLoginTokenUseCase, private val getFcmTokenUseCase: GetFcmTokenUseCase, private val postForceLoginUseCase: PostForceLoginUseCase, private val setSplashStateUseCase: SetSplashStateUseCase ) : ViewModel() { - private val kakaoToken = MutableLiveData() + private val kakaoToken = MutableStateFlow("") + private val fcmToken = MutableStateFlow("") - private val fcmToken = MutableLiveData() + private val _isSignedUp = MutableSharedFlow() + val isSignedUp = _isSignedUp.asSharedFlow() - private val _isSuccessKakaoLogin = MutableLiveData>() - val isSuccessKakaoLogin: LiveData> = _isSuccessKakaoLogin + private val _isJoiningRoom = MutableSharedFlow() + val isJoiningRoom = _isJoiningRoom.asSharedFlow() - private val _isJoiningRoom = MutableLiveData() - val isJoiningRoom: LiveData = _isJoiningRoom + private val _isMultipleAccess = MutableStateFlow(false) + val isMiultipleAccess = _isMultipleAccess.asStateFlow() - private val _isUser = MutableLiveData() - val isUser: LiveData = _isUser - - private val _isMultipleAccess = MutableLiveData() - val isMultipleAccess: LiveData = _isMultipleAccess - - private val _isInitUserInfo = MediatorLiveData>().apply { - addSource(kakaoToken) { token -> - value = Event(token.isNotBlank() && fcmToken.value != null) - } - addSource(fcmToken) { token -> - value = Event(token.isNotBlank() && kakaoToken.value != null) - } + val isKakaoLogin = combine(kakaoToken, fcmToken) { kakaoToken, fcmToken -> + kakaoToken.isNotBlank() && fcmToken.isNotBlank() } - val isInitUserInfo: LiveData> = _isInitUserInfo val kakaoLoginCallback: (OAuthToken?, Throwable?) -> Unit = { token, error -> - if (error != null) { - when { - error.toString() == AuthErrorCause.AccessDenied.toString() -> { - Timber.e(error, "접근이 거부 됨(동의 취소)") - } - error.toString() == AuthErrorCause.InvalidClient.toString() -> { - Timber.e(error, "유효하지 않은 앱") - } - error.toString() == AuthErrorCause.InvalidGrant.toString() -> { - Timber.e(error, "인증 수단이 유효하지 않아 인증할 수 없는 상태") - } - error.toString() == AuthErrorCause.InvalidRequest.toString() -> { - Timber.e(error, "요청 파라미터 오류") - } - error.toString() == AuthErrorCause.InvalidScope.toString() -> { - Timber.e(error, "유효하지 않은 scope ID") - } - error.toString() == AuthErrorCause.Misconfigured.toString() -> { - Timber.e(error, "설정이 올바르지 않음(android key hash)") - } - error.toString() == AuthErrorCause.ServerError.toString() -> { - Timber.e(error, "서버 내부 에러") - } - error.toString() == AuthErrorCause.Unauthorized.toString() -> { - Timber.e(error, "앱이 요청 권한이 없음") - } - else -> { - Timber.e(error, "기타 에러") - } - } - } else if (token != null) { - Timber.e("카카오 로그인 성공 ${token.accessToken}") - kakaoToken.value = token.accessToken - _isSuccessKakaoLogin.value = Event(true) - } + KakaoLoginCallback { accessToken -> + kakaoToken.value = accessToken + }.handleResult(token, error) } init { @@ -101,19 +60,12 @@ class LoginViewModel @Inject constructor( fun postLogin() { viewModelScope.launch { postLoginUseCase( - fcmToken = requireNotNull(fcmToken.value), - socialType = "KAKAO", - token = requireNotNull(kakaoToken.value) + fcmToken = fcmToken.value, + socialType = SOCIAL_TYPE, + token = kakaoToken.value ).onSuccess { response -> - initTokenUseCase( - fcmToken = requireNotNull(fcmToken.value), - socialType = SOCIAL_TYPE, - token = requireNotNull(kakaoToken.value) - ) - initHousTokenUseCase( - token = response.token - ) - _isJoiningRoom.value = response.isJoiningRoom + initHousTokenUseCase(token = response.token) + _isJoiningRoom.emit(response.isJoiningRoom) setSplashStateUseCase( if (response.isJoiningRoom) SplashState.MAIN else SplashState.ENTER_ROOM @@ -122,12 +74,12 @@ class LoginViewModel @Inject constructor( if (throwable is HttpException) { when (throwable.code()) { NOT_SIGN_UP -> { - initTokenUseCase( - fcmToken = requireNotNull(fcmToken.value), + initLoginTokenUseCase( + fcmToken = fcmToken.value, socialType = SOCIAL_TYPE, - token = requireNotNull(kakaoToken.value) + token = kakaoToken.value ) - _isUser.value = false + _isSignedUp.emit(false) Timber.e(throwable.message) } ALREADY_LOGIN -> { @@ -142,22 +94,15 @@ class LoginViewModel @Inject constructor( } } - fun initIsPermitAccess() { + fun postForceLogin() { viewModelScope.launch { postForceLoginUseCase( - fcmToken = requireNotNull(fcmToken.value), - socialType = "KAKAO", - token = requireNotNull(kakaoToken.value) + fcmToken = fcmToken.value, + socialType = SOCIAL_TYPE, + token = kakaoToken.value ).onSuccess { response -> - _isJoiningRoom.value = response.isJoiningRoom - initTokenUseCase( - fcmToken = requireNotNull(fcmToken.value), - socialType = SOCIAL_TYPE, - token = requireNotNull(kakaoToken.value) - ) - initHousTokenUseCase( - token = response.token - ) + _isJoiningRoom.emit(response.isJoiningRoom) + initHousTokenUseCase(token = response.token) setSplashStateUseCase( if (response.isJoiningRoom) SplashState.MAIN else SplashState.ENTER_ROOM diff --git a/app/src/main/java/hous/release/android/util/KakaoLoginCallback.kt b/app/src/main/java/hous/release/android/util/KakaoLoginCallback.kt new file mode 100644 index 000000000..a2a483a4b --- /dev/null +++ b/app/src/main/java/hous/release/android/util/KakaoLoginCallback.kt @@ -0,0 +1,46 @@ +package hous.release.android.util + +import com.kakao.sdk.auth.model.OAuthToken +import com.kakao.sdk.common.model.AuthErrorCause +import timber.log.Timber + +class KakaoLoginCallback( + private val onSuccess: (accessToken: String) -> Unit +) { + fun handleResult(token: OAuthToken?, error: Throwable?) { + if (error != null) { + when { + error.toString() == AuthErrorCause.AccessDenied.toString() -> { + Timber.e(error, "접근이 거부 됨(동의 취소)") + } + error.toString() == AuthErrorCause.InvalidClient.toString() -> { + Timber.e(error, "유효하지 않은 앱") + } + error.toString() == AuthErrorCause.InvalidGrant.toString() -> { + Timber.e(error, "인증 수단이 유효하지 않아 인증할 수 없는 상태") + } + error.toString() == AuthErrorCause.InvalidRequest.toString() -> { + Timber.e(error, "요청 파라미터 오류") + } + error.toString() == AuthErrorCause.InvalidScope.toString() -> { + Timber.e(error, "유효하지 않은 scope ID") + } + error.toString() == AuthErrorCause.Misconfigured.toString() -> { + Timber.e(error, "설정이 올바르지 않음(android key hash)") + } + error.toString() == AuthErrorCause.ServerError.toString() -> { + Timber.e(error, "서버 내부 에러") + } + error.toString() == AuthErrorCause.Unauthorized.toString() -> { + Timber.e(error, "앱이 요청 권한이 없음") + } + else -> { + Timber.e(error, "기타 에러") + } + } + } else if (token != null) { + Timber.d("카카오 로그인 성공 ${token.accessToken}") + onSuccess(token.accessToken) + } + } +} diff --git a/data/src/main/java/hous/release/data/repository/AuthRepositoryImpl.kt b/data/src/main/java/hous/release/data/repository/AuthRepositoryImpl.kt index d03ae279f..df22f36b7 100644 --- a/data/src/main/java/hous/release/data/repository/AuthRepositoryImpl.kt +++ b/data/src/main/java/hous/release/data/repository/AuthRepositoryImpl.kt @@ -11,8 +11,8 @@ import hous.release.domain.entity.Token import hous.release.domain.entity.response.Login import hous.release.domain.entity.response.SignUp import hous.release.domain.repository.AuthRepository -import javax.inject.Inject import timber.log.Timber +import javax.inject.Inject class AuthRepositoryImpl @Inject constructor( private val authDataSource: AuthDataSource, @@ -48,12 +48,7 @@ class AuthRepositoryImpl @Inject constructor( ) }.map { response -> response.data.toSignUp() } - override fun initHousToken(token: Token) { - localPrefTokenDataSource.accessToken = token.accessToken - localPrefTokenDataSource.refreshToken = token.refreshToken - } - - override fun initToken( + override fun initLoginToken( fcmToken: String, socialType: String, token: String @@ -63,6 +58,11 @@ class AuthRepositoryImpl @Inject constructor( localPrefTokenDataSource.token = token } + override fun initHousToken(token: Token) { + localPrefTokenDataSource.accessToken = token.accessToken + localPrefTokenDataSource.refreshToken = token.refreshToken + } + override fun getFCMToken(setFCMToken: (String) -> Unit) { FirebaseMessaging.getInstance().token.addOnCompleteListener( OnCompleteListener { task -> diff --git a/domain/src/main/java/hous/release/domain/repository/AuthRepository.kt b/domain/src/main/java/hous/release/domain/repository/AuthRepository.kt index 8ecceb5ea..940a351c7 100644 --- a/domain/src/main/java/hous/release/domain/repository/AuthRepository.kt +++ b/domain/src/main/java/hous/release/domain/repository/AuthRepository.kt @@ -19,9 +19,9 @@ interface AuthRepository { nickname: String ): Result - fun initHousToken(token: Token) + fun initLoginToken(fcmToken: String, socialType: String, token: String) - fun initToken(fcmToken: String, socialType: String, token: String) + fun initHousToken(token: Token) fun getFCMToken(setFCMToken: (String) -> Unit) diff --git a/domain/src/main/java/hous/release/domain/usecase/InitTokenUseCase.kt b/domain/src/main/java/hous/release/domain/usecase/InitLoginTokenUseCase.kt similarity index 68% rename from domain/src/main/java/hous/release/domain/usecase/InitTokenUseCase.kt rename to domain/src/main/java/hous/release/domain/usecase/InitLoginTokenUseCase.kt index 6aaca57de..9f5a31230 100644 --- a/domain/src/main/java/hous/release/domain/usecase/InitTokenUseCase.kt +++ b/domain/src/main/java/hous/release/domain/usecase/InitLoginTokenUseCase.kt @@ -3,9 +3,9 @@ package hous.release.domain.usecase import hous.release.domain.repository.AuthRepository import javax.inject.Inject -class InitTokenUseCase @Inject constructor( +class InitLoginTokenUseCase @Inject constructor( private val authRepository: AuthRepository ) { operator fun invoke(fcmToken: String, socialType: String, token: String) = - authRepository.initToken(fcmToken, socialType, token) + authRepository.initLoginToken(fcmToken, socialType, token) }