diff --git a/auth-google/src/main/java/com/lighthouse/auth/google/exception/FailedApiException.kt b/auth-google/src/main/java/com/lighthouse/auth/google/exception/FailedApiException.kt deleted file mode 100644 index e15d97d89..000000000 --- a/auth-google/src/main/java/com/lighthouse/auth/google/exception/FailedApiException.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.lighthouse.auth.google.exception - -class FailedApiException : Exception() diff --git a/auth-google/src/main/java/com/lighthouse/auth/google/exception/FailedLoginException.kt b/auth-google/src/main/java/com/lighthouse/auth/google/exception/FailedLoginException.kt deleted file mode 100644 index 20ed8c684..000000000 --- a/auth-google/src/main/java/com/lighthouse/auth/google/exception/FailedLoginException.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.lighthouse.auth.google.exception - -class FailedLoginException( - message: String = "로그인에 실패 했습니다." -) : Exception(message) diff --git a/auth-google/src/main/java/com/lighthouse/auth/google/exception/FailedSignOutException.kt b/auth-google/src/main/java/com/lighthouse/auth/google/exception/FailedSignOutException.kt deleted file mode 100644 index 510ca25c7..000000000 --- a/auth-google/src/main/java/com/lighthouse/auth/google/exception/FailedSignOutException.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.lighthouse.auth.google.exception - -class FailedSignOutException( - message: String = "로그 아웃에 실패 했습니다." -) : Exception(message) diff --git a/auth-google/src/main/java/com/lighthouse/auth/google/repository/GoogleClient.kt b/auth-google/src/main/java/com/lighthouse/auth/google/repository/GoogleClient.kt index 792d90d98..3468738f0 100644 --- a/auth-google/src/main/java/com/lighthouse/auth/google/repository/GoogleClient.kt +++ b/auth-google/src/main/java/com/lighthouse/auth/google/repository/GoogleClient.kt @@ -6,9 +6,9 @@ import com.google.firebase.auth.AuthResult interface GoogleClient { - fun googleSignInIntent(): Intent + fun signInIntent(): Intent - suspend fun googleSignIn(result: ActivityResult): Result + suspend fun signIn(result: ActivityResult): Result - suspend fun googleSignOut(): Result + suspend fun signOut(): Result } diff --git a/auth-google/src/main/java/com/lighthouse/auth/google/repository/GoogleClientImpl.kt b/auth-google/src/main/java/com/lighthouse/auth/google/repository/GoogleClientImpl.kt index 8df64647e..bd94132e4 100644 --- a/auth-google/src/main/java/com/lighthouse/auth/google/repository/GoogleClientImpl.kt +++ b/auth-google/src/main/java/com/lighthouse/auth/google/repository/GoogleClientImpl.kt @@ -7,16 +7,12 @@ import androidx.activity.result.ActivityResult import com.google.android.gms.auth.api.signin.GoogleSignIn import com.google.android.gms.auth.api.signin.GoogleSignInAccount import com.google.android.gms.auth.api.signin.GoogleSignInOptions -import com.google.android.gms.tasks.Task import com.google.firebase.auth.AuthResult import com.google.firebase.auth.GoogleAuthProvider import com.google.firebase.auth.ktx.auth import com.google.firebase.ktx.Firebase import com.lighthouse.auth.google.R -import com.lighthouse.auth.google.exception.FailedApiException import com.lighthouse.auth.google.exception.FailedConnectException -import com.lighthouse.auth.google.exception.FailedLoginException -import com.lighthouse.auth.google.exception.FailedSignOutException import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow @@ -34,34 +30,57 @@ internal class GoogleClientImpl @Inject constructor( GoogleSignIn.getClient(context, gso) } - override fun googleSignInIntent(): Intent { + override fun signInIntent(): Intent { return googleSignInClient.signInIntent } - private suspend fun signInWithAccount(account: GoogleSignInAccount): Task { + private suspend fun getGoogleAccount(result: ActivityResult): GoogleSignInAccount = + callbackFlow { + GoogleSignIn.getSignedInAccountFromIntent(result.data).addOnCompleteListener { + if (it.isSuccessful) { + trySend(it.result) + } + close(it.exception) + } + awaitClose() + }.first() + + private suspend fun signInWithAccount(account: GoogleSignInAccount): AuthResult { val credential = GoogleAuthProvider.getCredential(account.idToken, null) return callbackFlow { Firebase.auth.signInWithCredential(credential).addOnCompleteListener { - trySend(it) - close() + if (it.isSuccessful) { + trySend(it.result) + } + close(it.exception) } awaitClose() }.first() } - override suspend fun googleSignIn(result: ActivityResult): Result { + override suspend fun signIn(result: ActivityResult): Result { return if (result.resultCode == Activity.RESULT_OK) { runCatching { - val task = GoogleSignIn.getSignedInAccountFromIntent(result.data) - signInWithAccount(task.getResult(FailedApiException::class.java)) - .getResult(FailedLoginException::class.java) + val account = getGoogleAccount(result) + signInWithAccount(account) } } else { Result.failure(FailedConnectException()) } } - override suspend fun googleSignOut(): Result = runCatching { - googleSignInClient.signOut().getResult(FailedSignOutException::class.java) + override suspend fun signOut(): Result { + Firebase.auth.currentUser ?: return Result.success(Unit) + return runCatching { + callbackFlow { + googleSignInClient.signOut().addOnCompleteListener { + if (it.isSuccessful) { + trySend(Unit) + } + close(it.exception) + } + awaitClose() + }.first() + } } } diff --git a/auth/src/main/java/com/lighthouse/auth/repository/AuthRepositoryImpl.kt b/auth/src/main/java/com/lighthouse/auth/repository/AuthRepositoryImpl.kt index 0ac0cae97..595a4402b 100644 --- a/auth/src/main/java/com/lighthouse/auth/repository/AuthRepositoryImpl.kt +++ b/auth/src/main/java/com/lighthouse/auth/repository/AuthRepositoryImpl.kt @@ -7,6 +7,7 @@ import com.lighthouse.domain.repository.auth.AuthRepository import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.first import javax.inject.Inject internal class AuthRepositoryImpl @Inject constructor() : AuthRepository { @@ -25,6 +26,21 @@ internal class AuthRepositoryImpl @Inject constructor() : AuthRepository { return Firebase.auth.currentUser?.uid ?: GUEST_ID } + override suspend fun withdrawal(): Result { + val user = Firebase.auth.currentUser ?: return Result.success(Unit) + return runCatching { + callbackFlow { + user.delete().addOnCompleteListener { + if (it.isSuccessful) { + trySend(Unit) + } + close(it.exception) + } + awaitClose() + }.first() + } + } + companion object { private const val GUEST_ID = "Guest" } diff --git a/data/data-preference/src/main/java/com/lighthouse/data/preference/repository/UserPreferenceRepositoryImpl.kt b/data/data-preference/src/main/java/com/lighthouse/data/preference/repository/UserPreferenceRepositoryImpl.kt index 3aaeb3ea0..05aa76812 100644 --- a/data/data-preference/src/main/java/com/lighthouse/data/preference/repository/UserPreferenceRepositoryImpl.kt +++ b/data/data-preference/src/main/java/com/lighthouse/data/preference/repository/UserPreferenceRepositoryImpl.kt @@ -140,7 +140,11 @@ internal class UserPreferenceRepositoryImpl @Inject constructor( clearData(oldUserId) } - override suspend fun clearData(userId: String): Result = runCatchingPref { + override suspend fun withdrawal(userId: String): Result = runCatchingPref { + clearData(userId) + } + + private suspend fun clearData(userId: String) { dataStore.edit { pref -> pref.remove(byteArrayKey(userId, KEY_NAME_IV)) pref.remove(byteArrayKey(userId, KEY_NAME_PIN_PASSWORD)) diff --git a/data/data/src/main/java/com/lighthouse/repository/user/UserPreferenceRepository.kt b/data/data/src/main/java/com/lighthouse/repository/user/UserPreferenceRepository.kt index a9c04078b..8fd68459e 100644 --- a/data/data/src/main/java/com/lighthouse/repository/user/UserPreferenceRepository.kt +++ b/data/data/src/main/java/com/lighthouse/repository/user/UserPreferenceRepository.kt @@ -40,5 +40,5 @@ interface UserPreferenceRepository { newUserId: String ): Result - suspend fun clearData(userId: String): Result + suspend fun withdrawal(userId: String): Result } diff --git a/data/data/src/main/java/com/lighthouse/repository/user/UserRepositoryImpl.kt b/data/data/src/main/java/com/lighthouse/repository/user/UserRepositoryImpl.kt index fc3b510b4..13ebde742 100644 --- a/data/data/src/main/java/com/lighthouse/repository/user/UserRepositoryImpl.kt +++ b/data/data/src/main/java/com/lighthouse/repository/user/UserRepositoryImpl.kt @@ -21,7 +21,11 @@ internal class UserRepositoryImpl @Inject constructor( return userPreferenceRepository.setLoginUserUid(userId) } -// override fun getUserId(): String { + override suspend fun signOut(): Result { + return userPreferenceRepository.setLoginUserUid("") + } + + // override fun getUserId(): String { // return authRepository.getCurrentUserId() // } // @@ -76,11 +80,12 @@ internal class UserRepositoryImpl @Inject constructor( return userPreferenceRepository.getFilterExpired(userId) } - override suspend fun transferData(oldUserId: String, newUserId: String): Result { - return userPreferenceRepository.transferData(oldUserId, newUserId) + override suspend fun transferData(userId: String, newUserId: String): Result { + return userPreferenceRepository.transferData(userId, newUserId) } - override suspend fun clearData(userId: String): Result { - return userPreferenceRepository.clearData(userId) + override suspend fun withdrawal(userId: String): Result = runCatching { + userPreferenceRepository.withdrawal(userId).getOrThrow() + userPreferenceRepository.setLoginUserUid("").getOrThrow() } } diff --git a/domain/src/main/java/com/lighthouse/domain/repository/auth/AuthRepository.kt b/domain/src/main/java/com/lighthouse/domain/repository/auth/AuthRepository.kt index 6d3361991..391647102 100644 --- a/domain/src/main/java/com/lighthouse/domain/repository/auth/AuthRepository.kt +++ b/domain/src/main/java/com/lighthouse/domain/repository/auth/AuthRepository.kt @@ -7,4 +7,6 @@ interface AuthRepository { fun isGuest(): Flow fun getCurrentUserId(): String + + suspend fun withdrawal(): Result } diff --git a/domain/src/main/java/com/lighthouse/domain/repository/user/UserRepository.kt b/domain/src/main/java/com/lighthouse/domain/repository/user/UserRepository.kt index a7e8af561..2d3196e93 100644 --- a/domain/src/main/java/com/lighthouse/domain/repository/user/UserRepository.kt +++ b/domain/src/main/java/com/lighthouse/domain/repository/user/UserRepository.kt @@ -8,6 +8,7 @@ interface UserRepository { fun isLogin(): Flow suspend fun login(userId: String): Result + suspend fun signOut(): Result suspend fun setPinPassword(userId: String, encryptData: EncryptData): Result fun getPinPassword(userId: String): Flow> @@ -22,5 +23,5 @@ interface UserRepository { fun getFilterExpired(userId: String): Flow> suspend fun transferData(userId: String, newUserId: String): Result - suspend fun clearData(userId: String): Result + suspend fun withdrawal(userId: String): Result } diff --git a/domain/src/main/java/com/lighthouse/domain/usecase/user/SignOutUseCase.kt b/domain/src/main/java/com/lighthouse/domain/usecase/user/SignOutUseCase.kt new file mode 100644 index 000000000..55cb665c9 --- /dev/null +++ b/domain/src/main/java/com/lighthouse/domain/usecase/user/SignOutUseCase.kt @@ -0,0 +1,13 @@ +package com.lighthouse.domain.usecase.user + +import com.lighthouse.domain.repository.user.UserRepository +import javax.inject.Inject + +class SignOutUseCase @Inject constructor( + private val userRepository: UserRepository +) { + + suspend operator fun invoke(): Result = runCatching { + userRepository.signOut().getOrThrow() + } +} diff --git a/domain/src/main/java/com/lighthouse/domain/usecase/user/WithdrawalUseCase.kt b/domain/src/main/java/com/lighthouse/domain/usecase/user/WithdrawalUseCase.kt new file mode 100644 index 000000000..98cb7f0da --- /dev/null +++ b/domain/src/main/java/com/lighthouse/domain/usecase/user/WithdrawalUseCase.kt @@ -0,0 +1,17 @@ +package com.lighthouse.domain.usecase.user + +import com.lighthouse.domain.repository.auth.AuthRepository +import com.lighthouse.domain.repository.user.UserRepository +import javax.inject.Inject + +class WithdrawalUseCase @Inject constructor( + private val authRepository: AuthRepository, + private val userRepository: UserRepository +) { + + suspend operator fun invoke(): Result = runCatching { + val userId = authRepository.getCurrentUserId() + authRepository.withdrawal().getOrThrow() + userRepository.withdrawal(userId).getOrThrow() + } +} diff --git a/features/ui-intro/src/main/java/com/lighthouse/features/intro/ui/IntroFragment.kt b/features/ui-intro/src/main/java/com/lighthouse/features/intro/ui/IntroFragment.kt index 6a0311659..bc0fe5a89 100644 --- a/features/ui-intro/src/main/java/com/lighthouse/features/intro/ui/IntroFragment.kt +++ b/features/ui-intro/src/main/java/com/lighthouse/features/intro/ui/IntroFragment.kt @@ -8,9 +8,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import com.google.android.material.snackbar.Snackbar -import com.lighthouse.auth.google.exception.FailedApiException import com.lighthouse.auth.google.exception.FailedConnectException -import com.lighthouse.auth.google.exception.FailedLoginException import com.lighthouse.auth.google.repository.GoogleClient import com.lighthouse.features.common.binding.viewBindings import com.lighthouse.features.common.dialog.progress.ProgressDialog @@ -40,7 +38,7 @@ class IntroFragment : Fragment(R.layout.fragment_intro) { private val loginLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> lifecycleScope.launch { - val exception = googleClient.googleSignIn(result).exceptionOrNull() + val exception = googleClient.signIn(result).exceptionOrNull() if (exception != null) { viewModel.setState(SignInState.Failed(exception)) } else { @@ -88,7 +86,7 @@ class IntroFragment : Fragment(R.layout.fragment_intro) { private fun setUpBtnGoogleLogin() { binding.btnGoogleLogin.onThrottleClick { viewModel.setState(SignInState.Loading) - loginLauncher.launch(googleClient.googleSignInIntent()) + loginLauncher.launch(googleClient.signInIntent()) } } @@ -117,8 +115,6 @@ class IntroFragment : Fragment(R.layout.fragment_intro) { private fun signInFailed(e: Exception) { val message = when (e) { - is FailedApiException -> getString(R.string.google_login_failed) - is FailedLoginException -> getString(R.string.login_failed) is FailedSaveLoginUserException -> getString(R.string.error_save_login_user) is FailedConnectException -> getString(R.string.google_connect_fail) else -> getString(R.string.error_unknown) diff --git a/features/ui-setting/src/main/java/com/lighthouse/features/setting/ui/SettingFragment.kt b/features/ui-setting/src/main/java/com/lighthouse/features/setting/ui/SettingFragment.kt index 2d51c438a..4c7e636ca 100644 --- a/features/ui-setting/src/main/java/com/lighthouse/features/setting/ui/SettingFragment.kt +++ b/features/ui-setting/src/main/java/com/lighthouse/features/setting/ui/SettingFragment.kt @@ -7,7 +7,9 @@ import androidx.activity.addCallback import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.NavHostFragment +import com.google.android.material.snackbar.Snackbar import com.lighthouse.auth.google.repository.GoogleClient import com.lighthouse.features.common.binding.viewBindings import com.lighthouse.features.common.ext.repeatOnStarted @@ -20,6 +22,7 @@ import com.lighthouse.navs.app.navigator.AppNavigationViewModel import com.lighthouse.navs.main.model.MainNavigationItem import com.lighthouse.navs.main.navigator.MainNavigationViewModel import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint @@ -115,9 +118,23 @@ class SettingFragment : Fragment(R.layout.fragment_setting) { } private fun signOut() { + lifecycleScope.launch { + val exception = googleClient.signOut().exceptionOrNull() + if (exception != null) { + showSnackBar(getString(R.string.user_sign_out_exception)) + } + viewModel.signOut() + } } private fun withdrawal() { + lifecycleScope.launch { + val exception = googleClient.signOut().exceptionOrNull() + if (exception != null) { + showSnackBar(getString(R.string.user_withdrawal_exception)) + } + viewModel.withdrawal() + } } private fun setUpMenuOnCheckedChange(menu: SettingMenu, isChecked: Boolean) { @@ -126,4 +143,8 @@ class SettingFragment : Fragment(R.layout.fragment_setting) { else -> Unit } } + + private fun showSnackBar(string: String) { + Snackbar.make(binding.root, string, Snackbar.LENGTH_SHORT).show() + } } diff --git a/features/ui-setting/src/main/java/com/lighthouse/features/setting/ui/SettingViewModel.kt b/features/ui-setting/src/main/java/com/lighthouse/features/setting/ui/SettingViewModel.kt index 6df2611f6..baa994fb7 100644 --- a/features/ui-setting/src/main/java/com/lighthouse/features/setting/ui/SettingViewModel.kt +++ b/features/ui-setting/src/main/java/com/lighthouse/features/setting/ui/SettingViewModel.kt @@ -8,10 +8,11 @@ import com.lighthouse.domain.usecase.setting.GetNotificationOptionUseCase import com.lighthouse.domain.usecase.setting.GetSecurityOptionUseCase import com.lighthouse.domain.usecase.setting.SetNotificationOptionUseCase import com.lighthouse.domain.usecase.user.IsGuestUseCase +import com.lighthouse.domain.usecase.user.SignOutUseCase +import com.lighthouse.domain.usecase.user.WithdrawalUseCase import com.lighthouse.features.setting.R import com.lighthouse.features.setting.ext.settingGroup import com.lighthouse.features.setting.ext.settingItems -import com.lighthouse.features.setting.model.SettingGroup import com.lighthouse.features.setting.model.SettingMenu import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow @@ -24,10 +25,12 @@ import javax.inject.Inject @HiltViewModel internal class SettingViewModel @Inject constructor( - private val isGuestUseCase: IsGuestUseCase, + isGuestUseCase: IsGuestUseCase, getNotificationOptionUseCase: GetNotificationOptionUseCase, getSecurityOptionUseCase: GetSecurityOptionUseCase, - private val setNotificationOptionUseCase: SetNotificationOptionUseCase + private val setNotificationOptionUseCase: SetNotificationOptionUseCase, + private val signOutUseCase: SignOutUseCase, + private val withdrawalUseCase: WithdrawalUseCase ) : ViewModel() { private val notificationEnable = getNotificationOptionUseCase().map { @@ -71,7 +74,7 @@ internal class SettingViewModel @Inject constructor( addStateButton(SettingMenu.SECURITY, securityOption) addStateButton(SettingMenu.LOCATION, locationEnableText) } - }.stateIn(viewModelScope, SharingStarted.Eagerly, SettingGroup.Empty) + } private val userGroup = isGuestUseCase().map { isGuest -> settingGroup { @@ -83,7 +86,19 @@ internal class SettingViewModel @Inject constructor( } addStateButton(SettingMenu.WITHDRAWAL) } - }.stateIn(viewModelScope, SharingStarted.Eagerly, SettingGroup.Empty) + } + + fun signOut() { + viewModelScope.launch { + signOutUseCase() + } + } + + fun withdrawal() { + viewModelScope.launch { + withdrawalUseCase() + } + } val settingMenus = combine( configGroup, diff --git a/features/ui-setting/src/main/res/values/strings.xml b/features/ui-setting/src/main/res/values/strings.xml index f8b98a8c3..14831fbdb 100644 --- a/features/ui-setting/src/main/res/values/strings.xml +++ b/features/ui-setting/src/main/res/values/strings.xml @@ -15,8 +15,10 @@ 사용자 로그아웃 + 로그아웃에 실패 했습니다. 구글 계정 연동 회원 탈퇴 + 회원 탈퇴에 실패 했습니다. 기타 개발자에게 커피 사주기 diff --git a/model/src/main/java/com/lighthouse/beep/model/exception/auth/InvalidUserException.kt b/model/src/main/java/com/lighthouse/beep/model/exception/auth/InvalidUserException.kt new file mode 100644 index 000000000..b4c928a41 --- /dev/null +++ b/model/src/main/java/com/lighthouse/beep/model/exception/auth/InvalidUserException.kt @@ -0,0 +1,3 @@ +package com.lighthouse.beep.model.exception.auth + +class InvalidUserException(message: String? = "유효하지 않은 유저입니다.") : Exception(message)