diff --git a/feature/src/main/AndroidManifest.xml b/feature/src/main/AndroidManifest.xml index 75bf52d..214feab 100644 --- a/feature/src/main/AndroidManifest.xml +++ b/feature/src/main/AndroidManifest.xml @@ -9,7 +9,7 @@ android:supportsRtl="true" android:theme="@style/Theme.NOWSOPTAndroid"> @@ -17,13 +17,6 @@ - - \ No newline at end of file diff --git a/feature/src/main/java/com/sopt/now/feature/MainActivity.kt b/feature/src/main/java/com/sopt/now/feature/MainActivity.kt index c922a13..047ec3b 100644 --- a/feature/src/main/java/com/sopt/now/feature/MainActivity.kt +++ b/feature/src/main/java/com/sopt/now/feature/MainActivity.kt @@ -1,10 +1,15 @@ package com.sopt.now.feature +import android.util.Log +import android.view.MotionEvent import android.view.View +import android.view.inputmethod.InputMethodManager import androidx.activity.OnBackPressedCallback +import androidx.activity.viewModels import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import androidx.navigation.NavController +import androidx.navigation.Navigation import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.findNavController import androidx.navigation.ui.setupWithNavController @@ -23,9 +28,12 @@ class MainActivity : BindingActivity(R.layout.activity_main private var backPressedTime = 0L private val backPressedFlow = MutableSharedFlow() + private val viewModel: MainViewModel by viewModels() + override fun initView() { initMainBottomNavigation() initBackDoublePressed() + observeAutoLogin() } private fun initMainBottomNavigation() { @@ -33,6 +41,7 @@ class MainActivity : BindingActivity(R.layout.activity_main (supportFragmentManager.findFragmentById(R.id.fcv_home) as NavHostFragment) .findNavController() binding.bnvHome.setupWithNavController(navController) + navController.navigate(R.id.fragment_login) doubleBackPressedOnHomeTab(navController) setBottomNavigationVisibility(navController) } @@ -82,4 +91,47 @@ class MainActivity : BindingActivity(R.layout.activity_main snackBar(binding.root, getString(R.string.main_back_once_pressed_exit)) } } + + private fun observeAutoLogin() { + viewModel.autoLoginState.flowWithLifecycle(lifecycle).onEach { isAutoLogin -> + val navController = Navigation.findNavController(this, R.id.fcv_home) + when (isAutoLogin) { + true -> navController.navigate(R.id.fragment_home) + false -> navController.navigate(R.id.fragment_login) + } + }.launchIn(lifecycleScope) + } + + override fun dispatchTouchEvent(ev: MotionEvent): Boolean { + val imm: InputMethodManager = + getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager + imm.hideSoftInputFromWindow(currentFocus?.windowToken, 0) + return super.dispatchTouchEvent(ev) + } + + override fun onResume() { + super.onResume() + + logFragmentStack() + } + + private fun logFragmentStack() { + val fragmentManager = + supportFragmentManager.findFragmentById(R.id.fcv_home) as NavHostFragment + + fragmentManager.childFragmentManager.addOnBackStackChangedListener { + if (fragmentManager.childFragmentManager.backStackEntryCount == 0) { + Log.i( + "backstack", + fragmentManager.childFragmentManager.backStackEntryCount.toString() + ) + + } else { + Log.i( + "backstack", + fragmentManager.childFragmentManager.backStackEntryCount.toString() + ) + } + } + } } diff --git a/feature/src/main/java/com/sopt/now/feature/MainViewModel.kt b/feature/src/main/java/com/sopt/now/feature/MainViewModel.kt new file mode 100644 index 0000000..83d4361 --- /dev/null +++ b/feature/src/main/java/com/sopt/now/feature/MainViewModel.kt @@ -0,0 +1,31 @@ +package com.sopt.now.feature + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.sopt.now.domain.usecase.sharedprefusecase.GetCheckLoginUseCase +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class MainViewModel @Inject constructor( + private val getCheckLoginUseCase: GetCheckLoginUseCase, +) : ViewModel() { + private val _autoLoginState = MutableStateFlow(false) + val autoLoginState: StateFlow get() = _autoLoginState.asStateFlow() + + init { + checkAutoLogin() + } + + private fun checkAutoLogin() { + viewModelScope.launch { + _autoLoginState.value = isAutoLogin() + } + } + + private fun isAutoLogin(): Boolean = getCheckLoginUseCase.invoke() +} \ No newline at end of file diff --git a/feature/src/main/java/com/sopt/now/feature/auth/LoginActivity.kt b/feature/src/main/java/com/sopt/now/feature/auth/LoginActivity.kt deleted file mode 100644 index f119526..0000000 --- a/feature/src/main/java/com/sopt/now/feature/auth/LoginActivity.kt +++ /dev/null @@ -1,101 +0,0 @@ -package com.sopt.now.feature.auth - -import android.content.Intent -import android.view.MotionEvent -import android.view.inputmethod.InputMethodManager -import androidx.activity.result.ActivityResultLauncher -import androidx.activity.result.contract.ActivityResultContracts -import androidx.activity.viewModels -import androidx.lifecycle.flowWithLifecycle -import androidx.lifecycle.lifecycleScope -import com.sopt.now.core.base.BindingActivity -import com.sopt.now.core.util.context.snackBar -import com.sopt.now.core.util.context.toast -import com.sopt.now.core.util.intent.getSafeParcelable -import com.sopt.now.core.util.intent.navigateTo -import com.sopt.now.core.view.UiState -import com.sopt.now.feature.MainActivity -import com.sopt.now.feature.R -import com.sopt.now.feature.databinding.ActivityLoginBinding -import com.sopt.now.feature.model.User -import com.sopt.now.feature.util.KeyStorage -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach - -@AndroidEntryPoint -class LoginActivity : BindingActivity(R.layout.activity_login) { - private lateinit var resultLauncher: ActivityResultLauncher - private val viewModel by viewModels() - - override fun initView() { - initAutoLoginStateObserve() - initRegisterResultLauncher() - initBtnClickListener() - initSignUpStateObserve() - } - - private fun initAutoLoginStateObserve() { - viewModel.autoLoginState.flowWithLifecycle(lifecycle).onEach { isAutoLogin -> - when (isAutoLogin) { - true -> navigateTo(this@LoginActivity) - false -> return@onEach - } - }.launchIn(lifecycleScope) - } - - private fun initRegisterResultLauncher() { - resultLauncher = - registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> - if (result.resultCode == RESULT_OK) { - result.data?.getSafeParcelable(name = KeyStorage.USER_INPUT) - ?.let { receivedUserInput -> - viewModel.setSavedUserInfo(receivedUserInput.toUserEntity()) - } - } - } - } - - private fun initBtnClickListener() { - initLoginBtnClickListener() - initSignUpBtnClickListener() - } - - private fun initLoginBtnClickListener() = with(binding) { - btnLogin.setOnClickListener { - viewModel.setLogin( - id = etLoginId.text.toString(), - pwd = etLoginPwd.text.toString() - ) - } - } - - private fun initSignUpBtnClickListener() { - binding.tvLoginSignUp.setOnClickListener { - val intent = Intent(this@LoginActivity, SignUpActivity::class.java) - resultLauncher.launch(intent) - } - } - - private fun initSignUpStateObserve() { - viewModel.loginState.flowWithLifecycle(lifecycle).onEach { state -> - when (state) { - is UiState.Success -> { - toast(getString(R.string.login_completed, getString(R.string.login))) - viewModel.saveCheckLoginSharedPreference(true) - navigateTo(this) - } - - is UiState.Failure -> snackBar(binding.root, state.errorMessage) - else -> Unit - } - }.launchIn(lifecycleScope) - } - - override fun dispatchTouchEvent(ev: MotionEvent): Boolean { - val imm: InputMethodManager = - getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager - imm.hideSoftInputFromWindow(currentFocus?.windowToken, 0) - return super.dispatchTouchEvent(ev) - } -} diff --git a/feature/src/main/java/com/sopt/now/feature/auth/LoginFragment.kt b/feature/src/main/java/com/sopt/now/feature/auth/LoginFragment.kt new file mode 100644 index 0000000..c80701e --- /dev/null +++ b/feature/src/main/java/com/sopt/now/feature/auth/LoginFragment.kt @@ -0,0 +1,64 @@ +package com.sopt.now.feature.auth + +import androidx.fragment.app.viewModels +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import androidx.navigation.NavOptions +import androidx.navigation.fragment.findNavController +import com.sopt.now.core.base.BindingFragment +import com.sopt.now.core.util.fragment.snackBar +import com.sopt.now.core.util.fragment.toast +import com.sopt.now.core.view.UiState +import com.sopt.now.feature.R +import com.sopt.now.feature.databinding.FragmentLoginBinding +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach + +@AndroidEntryPoint +class LoginFragment : BindingFragment(R.layout.fragment_login) { + private val viewModel by viewModels() + + override fun initView() { + initBtnClickListener() + initSignUpStateObserve() + } + + private fun initBtnClickListener() { + initLoginBtnClickListener() + initSignUpBtnClickListener() + } + + private fun initLoginBtnClickListener() = with(binding) { + btnLogin.setOnClickListener { + viewModel.setLogin( + id = etLoginId.text.toString(), + pwd = etLoginPwd.text.toString() + ) + } + } + + private fun initSignUpBtnClickListener() { + binding.tvLoginSignUp.setOnClickListener { + findNavController().navigate(R.id.fragment_sign_up) + } + } + + private fun initSignUpStateObserve() { + viewModel.loginState.flowWithLifecycle(lifecycle).onEach { state -> + when (state) { + is UiState.Success -> { + toast(getString(R.string.login_completed, getString(R.string.login))) + viewModel.saveCheckLoginSharedPreference(true) + val navOptions = NavOptions.Builder() + .setPopUpTo(R.id.nav_graph, true) + .build() + findNavController().navigate(R.id.fragment_home, null, navOptions) + } + + is UiState.Failure -> snackBar(binding.root, state.errorMessage) + else -> Unit + } + }.launchIn(lifecycleScope) + } +} diff --git a/feature/src/main/java/com/sopt/now/feature/auth/LoginViewModel.kt b/feature/src/main/java/com/sopt/now/feature/auth/LoginViewModel.kt index c2dd1a4..d4bb0ca 100644 --- a/feature/src/main/java/com/sopt/now/feature/auth/LoginViewModel.kt +++ b/feature/src/main/java/com/sopt/now/feature/auth/LoginViewModel.kt @@ -2,27 +2,24 @@ package com.sopt.now.feature.auth import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.sopt.now.feature.util.StringResources import com.sopt.now.core.view.UiState import com.sopt.now.domain.entity.UserEntity -import com.sopt.now.domain.usecase.sharedprefusecase.GetCheckLoginUseCase import com.sopt.now.domain.usecase.sharedprefusecase.GetUserInfoUseCase import com.sopt.now.domain.usecase.sharedprefusecase.SaveCheckLoginUseCase +import com.sopt.now.feature.util.StringResources import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class LoginViewModel @Inject constructor( private val getUserInfoUseCase: GetUserInfoUseCase, - private val getCheckLoginUseCase: GetCheckLoginUseCase, - private val saveCheckLoginUseCase: SaveCheckLoginUseCase + private val saveCheckLoginUseCase: SaveCheckLoginUseCase, ) : ViewModel() { private val _savedUserInfo = MutableStateFlow( UserEntity( @@ -37,32 +34,21 @@ class LoginViewModel @Inject constructor( private val _loginState = MutableSharedFlow>() val loginState: SharedFlow> get() = _loginState.asSharedFlow() - private val _autoLoginState = MutableStateFlow(false) - val autoLoginState: StateFlow get() = _autoLoginState.asStateFlow() - init { - checkAutoLogin() + getUserInfo() } - private fun checkAutoLogin() { + private fun getUserInfo() { viewModelScope.launch { - _autoLoginState.value = isAutoLogin() - if (!isAutoLogin()) { - _savedUserInfo.value = getUserInfoUseCase.invoke() - } + _savedUserInfo.value = getUserInfoUseCase.invoke() + } } - private fun isAutoLogin(): Boolean = getCheckLoginUseCase.invoke() - fun saveCheckLoginSharedPreference(input: Boolean) { saveCheckLoginUseCase.invoke(input) } - fun setSavedUserInfo(input: UserEntity) { - _savedUserInfo.value = input - } - fun setLogin(id: String, pwd: String) { checkLoginValidate(id, pwd) } diff --git a/feature/src/main/java/com/sopt/now/feature/auth/SignUpActivity.kt b/feature/src/main/java/com/sopt/now/feature/auth/SignUpFragment.kt similarity index 60% rename from feature/src/main/java/com/sopt/now/feature/auth/SignUpActivity.kt rename to feature/src/main/java/com/sopt/now/feature/auth/SignUpFragment.kt index 479b558..236e0a5 100644 --- a/feature/src/main/java/com/sopt/now/feature/auth/SignUpActivity.kt +++ b/feature/src/main/java/com/sopt/now/feature/auth/SignUpFragment.kt @@ -1,25 +1,22 @@ package com.sopt.now.feature.auth -import android.content.Intent -import android.view.MotionEvent -import android.view.inputmethod.InputMethodManager -import androidx.activity.viewModels +import androidx.fragment.app.viewModels import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope -import com.sopt.now.core.base.BindingActivity -import com.sopt.now.core.util.context.snackBar -import com.sopt.now.core.util.context.toast +import androidx.navigation.fragment.findNavController +import com.sopt.now.core.base.BindingFragment +import com.sopt.now.core.util.fragment.snackBar +import com.sopt.now.core.util.fragment.toast import com.sopt.now.core.view.UiState import com.sopt.now.feature.R -import com.sopt.now.feature.databinding.ActivitySignUpBinding +import com.sopt.now.feature.databinding.FragmentSignUpBinding import com.sopt.now.feature.model.User -import com.sopt.now.feature.util.KeyStorage import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @AndroidEntryPoint -class SignUpActivity : BindingActivity(R.layout.activity_sign_up) { +class SignUpFragment : BindingFragment(R.layout.fragment_sign_up) { private val viewModel by viewModels() override fun initView() { @@ -50,7 +47,7 @@ class SignUpActivity : BindingActivity(R.layout.activity_ is UiState.Success -> { toast(getString(R.string.login_completed, getString(R.string.sign_up))) viewModel.saveUserInfoSharedPreference(state.data.toUserEntity()) - navigateToLoginActivity(state.data) + findNavController().navigate(R.id.fragment_login) } is UiState.Failure -> { @@ -62,19 +59,4 @@ class SignUpActivity : BindingActivity(R.layout.activity_ }.launchIn(lifecycleScope) } - private fun navigateToLoginActivity(userInputData: User) { - Intent().apply { - putExtra(KeyStorage.USER_INPUT, userInputData) - }.also { - setResult(RESULT_OK, it) - finish() - } - } - - override fun dispatchTouchEvent(ev: MotionEvent): Boolean { - val imm: InputMethodManager = - getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager - imm.hideSoftInputFromWindow(currentFocus?.windowToken, 0) - return super.dispatchTouchEvent(ev) - } } diff --git a/feature/src/main/java/com/sopt/now/feature/mypage/MyPageFragment.kt b/feature/src/main/java/com/sopt/now/feature/mypage/MyPageFragment.kt index fdeb08f..df94e6e 100644 --- a/feature/src/main/java/com/sopt/now/feature/mypage/MyPageFragment.kt +++ b/feature/src/main/java/com/sopt/now/feature/mypage/MyPageFragment.kt @@ -1,11 +1,11 @@ package com.sopt.now.feature.mypage import androidx.fragment.app.viewModels +import androidx.navigation.NavOptions +import androidx.navigation.fragment.findNavController import com.sopt.now.core.base.BindingFragment import com.sopt.now.core.util.fragment.toast -import com.sopt.now.core.util.intent.navigateTo import com.sopt.now.feature.R -import com.sopt.now.feature.auth.LoginActivity import com.sopt.now.feature.databinding.FragmentMyPageBinding import dagger.hilt.android.AndroidEntryPoint @@ -26,7 +26,12 @@ class MyPageFragment : BindingFragment(R.layout.fragment_ binding.tvMainSignOut.setOnClickListener { viewModel.updateCheckLoginState(false) toast(getString(R.string.login_completed, getString(R.string.main_logout_under_bar))) - navigateTo(requireContext()) + + val navOptions = NavOptions.Builder() + .setPopUpTo(R.id.nav_graph, true) + .build() + + findNavController().navigate(R.id.fragment_login, null, navOptions) } } @@ -39,7 +44,11 @@ class MyPageFragment : BindingFragment(R.layout.fragment_ getString(R.string.main_clear_user_under_bar) ) ) - navigateTo(requireContext()) + + val navOptions = NavOptions.Builder() + .setPopUpTo(R.id.nav_graph, true) + .build() + findNavController().navigate(R.id.fragment_login, null, navOptions) } } diff --git a/feature/src/main/res/layout/activity_login.xml b/feature/src/main/res/layout/fragment_login.xml similarity index 100% rename from feature/src/main/res/layout/activity_login.xml rename to feature/src/main/res/layout/fragment_login.xml diff --git a/feature/src/main/res/layout/activity_sign_up.xml b/feature/src/main/res/layout/fragment_sign_up.xml similarity index 100% rename from feature/src/main/res/layout/activity_sign_up.xml rename to feature/src/main/res/layout/fragment_sign_up.xml diff --git a/feature/src/main/res/navigation/nav_graph.xml b/feature/src/main/res/navigation/nav_graph.xml index 71c194a..42369a0 100644 --- a/feature/src/main/res/navigation/nav_graph.xml +++ b/feature/src/main/res/navigation/nav_graph.xml @@ -15,7 +15,9 @@ app:destination="@id/fragment_home_detail" /> + app:destination="@id/fragment_my_page" + app:popUpTo="@id/fragment_home" + app:popUpToInclusive="true" /> + tools:layout="@layout/fragment_my_page"> + + + + +