diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index db8c2188..35ebeb7f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -31,28 +31,34 @@
+
+
@@ -61,6 +67,7 @@
android:exported="true">
+
diff --git a/app/src/main/java/com/android/go/sopt/winey/data/model/remote/response/ResponseGetNicknameDuplicateCheckDto.kt b/app/src/main/java/com/android/go/sopt/winey/data/model/remote/response/ResponseGetNicknameDuplicateCheckDto.kt
new file mode 100644
index 00000000..b13c3986
--- /dev/null
+++ b/app/src/main/java/com/android/go/sopt/winey/data/model/remote/response/ResponseGetNicknameDuplicateCheckDto.kt
@@ -0,0 +1,10 @@
+package com.android.go.sopt.winey.data.model.remote.response
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class ResponseGetNicknameDuplicateCheckDto(
+ @SerialName("isDuplicated")
+ val isDuplicated: Boolean
+)
diff --git a/app/src/main/java/com/android/go/sopt/winey/data/repository/AuthRepositoryImpl.kt b/app/src/main/java/com/android/go/sopt/winey/data/repository/AuthRepositoryImpl.kt
index 22d2f353..702c365a 100644
--- a/app/src/main/java/com/android/go/sopt/winey/data/repository/AuthRepositoryImpl.kt
+++ b/app/src/main/java/com/android/go/sopt/winey/data/repository/AuthRepositoryImpl.kt
@@ -3,6 +3,7 @@ package com.android.go.sopt.winey.data.repository
import com.android.go.sopt.winey.data.model.remote.request.RequestCreateGoalDto
import com.android.go.sopt.winey.data.model.remote.request.RequestLoginDto
import com.android.go.sopt.winey.data.model.remote.request.RequestPostLikeDto
+import com.android.go.sopt.winey.data.model.remote.response.ResponseGetNicknameDuplicateCheckDto
import com.android.go.sopt.winey.data.model.remote.response.ResponseLoginDto
import com.android.go.sopt.winey.data.model.remote.response.ResponsePostWineyFeedDto
import com.android.go.sopt.winey.data.model.remote.response.ResponseReIssueTokenDto
@@ -79,4 +80,9 @@ class AuthRepositoryImpl @Inject constructor(
runCatching {
authDataSource.postReIssueToken(refreshToken).data
}
+
+ override suspend fun getNicknameDuplicateCheck(nickname: String): Result =
+ runCatching {
+ authDataSource.getNicknameDuplicateCheck(nickname).data
+ }
}
diff --git a/app/src/main/java/com/android/go/sopt/winey/data/service/AuthService.kt b/app/src/main/java/com/android/go/sopt/winey/data/service/AuthService.kt
index c64493e7..20a21b58 100644
--- a/app/src/main/java/com/android/go/sopt/winey/data/service/AuthService.kt
+++ b/app/src/main/java/com/android/go/sopt/winey/data/service/AuthService.kt
@@ -4,6 +4,7 @@ import com.android.go.sopt.winey.data.model.remote.request.RequestCreateGoalDto
import com.android.go.sopt.winey.data.model.remote.request.RequestLoginDto
import com.android.go.sopt.winey.data.model.remote.request.RequestPostLikeDto
import com.android.go.sopt.winey.data.model.remote.response.ResponseCreateGoalDto
+import com.android.go.sopt.winey.data.model.remote.response.ResponseGetNicknameDuplicateCheckDto
import com.android.go.sopt.winey.data.model.remote.response.ResponseGetRecommendListDto
import com.android.go.sopt.winey.data.model.remote.response.ResponseGetUserDto
import com.android.go.sopt.winey.data.model.remote.response.ResponseGetWineyFeedListDto
@@ -26,6 +27,10 @@ import retrofit2.http.Path
import retrofit2.http.Query
interface AuthService {
+ /**
+ *
+ */
+
@GET("user")
suspend fun getUser(): BaseResponse
@@ -77,4 +82,9 @@ interface AuthService {
suspend fun postReIssueToken(
@Header("refreshToken") refreshToken: String
): BaseResponse
+
+ @GET("user/nickname/is-exist")
+ suspend fun getNicknameDuplicateCheck(
+ @Query("nickname") nickname: String
+ ): BaseResponse
}
diff --git a/app/src/main/java/com/android/go/sopt/winey/data/source/AuthDataSource.kt b/app/src/main/java/com/android/go/sopt/winey/data/source/AuthDataSource.kt
index 532fb32a..7d963514 100644
--- a/app/src/main/java/com/android/go/sopt/winey/data/source/AuthDataSource.kt
+++ b/app/src/main/java/com/android/go/sopt/winey/data/source/AuthDataSource.kt
@@ -4,6 +4,7 @@ import com.android.go.sopt.winey.data.model.remote.request.RequestCreateGoalDto
import com.android.go.sopt.winey.data.model.remote.request.RequestLoginDto
import com.android.go.sopt.winey.data.model.remote.request.RequestPostLikeDto
import com.android.go.sopt.winey.data.model.remote.response.ResponseCreateGoalDto
+import com.android.go.sopt.winey.data.model.remote.response.ResponseGetNicknameDuplicateCheckDto
import com.android.go.sopt.winey.data.model.remote.response.ResponseGetRecommendListDto
import com.android.go.sopt.winey.data.model.remote.response.ResponseGetUserDto
import com.android.go.sopt.winey.data.model.remote.response.ResponseGetWineyFeedListDto
@@ -59,4 +60,7 @@ class AuthDataSource @Inject constructor(
refreshToken: String
): BaseResponse =
authService.postReIssueToken(refreshToken)
+
+ suspend fun getNicknameDuplicateCheck(nickname: String): BaseResponse =
+ authService.getNicknameDuplicateCheck(nickname)
}
diff --git a/app/src/main/java/com/android/go/sopt/winey/domain/repository/AuthRepository.kt b/app/src/main/java/com/android/go/sopt/winey/domain/repository/AuthRepository.kt
index 951abdf3..5be4336b 100644
--- a/app/src/main/java/com/android/go/sopt/winey/domain/repository/AuthRepository.kt
+++ b/app/src/main/java/com/android/go/sopt/winey/domain/repository/AuthRepository.kt
@@ -3,6 +3,7 @@ package com.android.go.sopt.winey.domain.repository
import com.android.go.sopt.winey.data.model.remote.request.RequestCreateGoalDto
import com.android.go.sopt.winey.data.model.remote.request.RequestLoginDto
import com.android.go.sopt.winey.data.model.remote.request.RequestPostLikeDto
+import com.android.go.sopt.winey.data.model.remote.response.ResponseGetNicknameDuplicateCheckDto
import com.android.go.sopt.winey.data.model.remote.response.ResponseLoginDto
import com.android.go.sopt.winey.data.model.remote.response.ResponsePostWineyFeedDto
import com.android.go.sopt.winey.data.model.remote.response.ResponseReIssueTokenDto
@@ -39,4 +40,6 @@ interface AuthRepository {
): Result
suspend fun postReIssueToken(refreshToken: String): Result
+
+ suspend fun getNicknameDuplicateCheck(nickname: String): Result
}
diff --git a/app/src/main/java/com/android/go/sopt/winey/presentation/main/MainActivity.kt b/app/src/main/java/com/android/go/sopt/winey/presentation/main/MainActivity.kt
index 69c72cef..008a9e8b 100644
--- a/app/src/main/java/com/android/go/sopt/winey/presentation/main/MainActivity.kt
+++ b/app/src/main/java/com/android/go/sopt/winey/presentation/main/MainActivity.kt
@@ -36,23 +36,22 @@ class MainActivity : BindingActivity(R.layout.activity_main
}
}
- fun syncBottomNavigationSelection() {
+ private fun syncBottomNavigationSelection() {
supportFragmentManager.addOnBackStackChangedListener {
syncBottomNavigation()
}
}
- fun syncBottomNavigation() {
+ private fun syncBottomNavigation() {
val currentFragment = supportFragmentManager.findFragmentById(R.id.fcv_main)
when (currentFragment) {
is WineyFeedFragment -> binding.bnvMain.selectedItemId = R.id.menu_feed
is RecommendFragment -> binding.bnvMain.selectedItemId = R.id.menu_recommend
is MyPageFragment -> binding.bnvMain.selectedItemId = R.id.menu_mypage
- // 다른 프래그먼트도 추가가능
}
}
- inline fun navigateTo() {
+ private inline fun navigateTo() {
supportFragmentManager.commit {
replace(R.id.fcv_main, T::class.simpleName)
}
diff --git a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/upload/amount/AmountFragment.kt b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/upload/amount/AmountFragment.kt
index 5b0984a6..5eacf827 100644
--- a/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/upload/amount/AmountFragment.kt
+++ b/app/src/main/java/com/android/go/sopt/winey/presentation/main/feed/upload/amount/AmountFragment.kt
@@ -11,12 +11,12 @@ import androidx.lifecycle.flowWithLifecycle
import com.android.go.sopt.winey.R
import com.android.go.sopt.winey.databinding.FragmentAmountBinding
import com.android.go.sopt.winey.presentation.main.feed.upload.loading.LoadingActivity
-import com.android.go.sopt.winey.util.multipart.UriToRequestBody
import com.android.go.sopt.winey.util.binding.BindingFragment
import com.android.go.sopt.winey.util.context.hideKeyboard
import com.android.go.sopt.winey.util.fragment.snackBar
import com.android.go.sopt.winey.util.fragment.viewLifeCycle
import com.android.go.sopt.winey.util.fragment.viewLifeCycleScope
+import com.android.go.sopt.winey.util.multipart.UriToRequestBody
import com.android.go.sopt.winey.util.view.UiState
import com.android.go.sopt.winey.util.view.setOnSingleClickListener
import dagger.hilt.android.AndroidEntryPoint
diff --git a/app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/KakaoLoginCallback.kt b/app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/login/KakaoLoginCallback.kt
similarity index 97%
rename from app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/KakaoLoginCallback.kt
rename to app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/login/KakaoLoginCallback.kt
index aa22fab8..04421b56 100644
--- a/app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/KakaoLoginCallback.kt
+++ b/app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/login/KakaoLoginCallback.kt
@@ -1,4 +1,4 @@
-package com.android.go.sopt.winey.presentation.onboarding
+package com.android.go.sopt.winey.presentation.onboarding.login
import com.kakao.sdk.auth.model.OAuthToken
import com.kakao.sdk.common.model.AuthErrorCause
diff --git a/app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/LoginActivity.kt b/app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/login/LoginActivity.kt
similarity index 69%
rename from app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/LoginActivity.kt
rename to app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/login/LoginActivity.kt
index 253a04c3..b2e22b43 100644
--- a/app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/LoginActivity.kt
+++ b/app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/login/LoginActivity.kt
@@ -1,5 +1,6 @@
-package com.android.go.sopt.winey.presentation.onboarding
+package com.android.go.sopt.winey.presentation.onboarding.login
+import android.app.Activity
import android.content.Intent
import android.os.Bundle
import androidx.activity.viewModels
@@ -8,12 +9,14 @@ import androidx.lifecycle.lifecycleScope
import com.android.go.sopt.winey.R
import com.android.go.sopt.winey.databinding.ActivityLoginBinding
import com.android.go.sopt.winey.presentation.main.MainActivity
+import com.android.go.sopt.winey.presentation.onboarding.nickname.NicknameActivity
import com.android.go.sopt.winey.util.binding.BindingActivity
import com.android.go.sopt.winey.util.context.snackBar
import com.android.go.sopt.winey.util.view.UiState
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
+import timber.log.Timber
@AndroidEntryPoint
class LoginActivity :
@@ -40,15 +43,15 @@ class LoginActivity :
}
is UiState.Success -> {
+ Timber.e("${state.data?.userId}")
+ Timber.e("${state.data?.accessToken}")
+ Timber.e("${state.data?.refreshToken}")
+ Timber.e("${state.data?.isRegistered}")
+
if (state.data?.isRegistered == true) {
- val intent = Intent(this@LoginActivity, MainActivity::class.java)
- startActivity(intent)
- finish()
+ navigateTo()
} else {
- //TODO : isRegistered false일경우 닉네임 설정화면으로
- val intent = Intent(this@LoginActivity, MainActivity::class.java)
- startActivity(intent)
- finish()
+ navigateTo()
}
}
@@ -61,4 +64,11 @@ class LoginActivity :
}
}.launchIn(lifecycleScope)
}
+
+ private inline fun navigateTo() {
+ Intent(this@LoginActivity, T::class.java).apply {
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
+ startActivity(this)
+ }
+ }
}
diff --git a/app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/LoginViewModel.kt b/app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/login/LoginViewModel.kt
similarity index 98%
rename from app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/LoginViewModel.kt
rename to app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/login/LoginViewModel.kt
index bff76e53..c626d88d 100644
--- a/app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/LoginViewModel.kt
+++ b/app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/login/LoginViewModel.kt
@@ -1,4 +1,4 @@
-package com.android.go.sopt.winey.presentation.onboarding
+package com.android.go.sopt.winey.presentation.onboarding.login
import android.content.Context
import androidx.lifecycle.ViewModel
diff --git a/app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/nickname/NicknameActivity.kt b/app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/nickname/NicknameActivity.kt
new file mode 100644
index 00000000..3169aa2c
--- /dev/null
+++ b/app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/nickname/NicknameActivity.kt
@@ -0,0 +1,74 @@
+package com.android.go.sopt.winey.presentation.onboarding.nickname
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+import android.text.Editable
+import android.text.TextWatcher
+import androidx.activity.viewModels
+import com.android.go.sopt.winey.R
+import com.android.go.sopt.winey.databinding.ActivityNicknameBinding
+import com.android.go.sopt.winey.presentation.main.MainActivity
+import com.android.go.sopt.winey.util.binding.BindingActivity
+import com.android.go.sopt.winey.util.context.hideKeyboard
+import dagger.hilt.android.AndroidEntryPoint
+
+@AndroidEntryPoint
+class NicknameActivity : BindingActivity(R.layout.activity_nickname) {
+ private val viewModel by viewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ binding.vm = viewModel
+
+ initRootLayoutClickListener()
+ initEditTextWatcher()
+ initDuplicateCheckButtonClickListener()
+ initCompleteButtonClickListener()
+ }
+
+ private fun initEditTextWatcher() {
+ var prevText = ""
+ binding.etNickname.addTextChangedListener(object : TextWatcher {
+ override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
+ override fun afterTextChanged(s: Editable?) {}
+ override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
+ val inputText = s.toString()
+ viewModel.updateTextChangedState(inputText.isNotBlank() && inputText != prevText)
+ prevText = inputText
+ }
+ })
+ }
+
+ private fun initDuplicateCheckButtonClickListener() {
+ binding.btnNicknameDuplicateCheck.setOnClickListener {
+ if (viewModel.isTextChanged.value) {
+ viewModel.apply {
+ updateDuplicateCheckButtonState(true)
+ getNicknameDuplicateCheck()
+ }
+ }
+ }
+ }
+
+ private fun initCompleteButtonClickListener() {
+ binding.btnNicknameComplete.setOnClickListener {
+ // todo: 서버에 닉네임 PATCH
+ navigateTo()
+ }
+ }
+
+ private fun initRootLayoutClickListener() {
+ binding.root.setOnClickListener {
+ hideKeyboard(binding.root)
+ binding.etNickname.clearFocus()
+ }
+ }
+
+ private inline fun navigateTo() {
+ Intent(this@NicknameActivity, T::class.java).apply {
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
+ startActivity(this)
+ }
+ }
+}
diff --git a/app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/nickname/NicknameViewModel.kt b/app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/nickname/NicknameViewModel.kt
new file mode 100644
index 00000000..51a43632
--- /dev/null
+++ b/app/src/main/java/com/android/go/sopt/winey/presentation/onboarding/nickname/NicknameViewModel.kt
@@ -0,0 +1,166 @@
+package com.android.go.sopt.winey.presentation.onboarding.nickname
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.android.go.sopt.winey.domain.repository.AuthRepository
+import com.android.go.sopt.winey.util.code.NicknameErrorCode
+import com.android.go.sopt.winey.util.code.NicknameErrorCode.CODE_DUPLICATE
+import com.android.go.sopt.winey.util.code.NicknameErrorCode.CODE_INVALID_LENGTH
+import com.android.go.sopt.winey.util.code.NicknameErrorCode.CODE_SPACE_SPECIAL_CHAR
+import com.android.go.sopt.winey.util.code.NicknameErrorCode.CODE_UNCHECKED_DUPLICATION
+import com.android.go.sopt.winey.util.view.InputUiState
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+import timber.log.Timber
+import javax.inject.Inject
+
+@HiltViewModel
+class NicknameViewModel @Inject constructor(
+ private val authRepository: AuthRepository
+) : ViewModel() {
+ val _nickname = MutableStateFlow("")
+ val nickname: String get() = _nickname.value
+
+ private val _inputUiState: MutableStateFlow =
+ _nickname.map { updateInputUiState(it) }
+ .mutableStateIn(
+ initialValue = InputUiState.Empty,
+ scope = viewModelScope
+ )
+
+ val inputUiState: StateFlow = _inputUiState.asStateFlow()
+
+ val isValidNickname: StateFlow = _inputUiState.map { validateNickname(it) }
+ .stateIn(
+ initialValue = false,
+ scope = viewModelScope,
+ started = SharingStarted.WhileSubscribed(PRODUCE_STOP_TIMEOUT)
+ )
+
+ private fun validateNickname(state: InputUiState) = state == InputUiState.Success
+
+ private val _isTextChanged = MutableStateFlow(false)
+ val isTextChanged: StateFlow = _isTextChanged.asStateFlow()
+
+ private val _isCheckBtnClicked = MutableStateFlow(false)
+ val isCheckBtnClicked: StateFlow = _isCheckBtnClicked.asStateFlow()
+
+ private var prevCheckResult: Pair? = null
+
+ fun getNicknameDuplicateCheck() {
+ viewModelScope.launch {
+ authRepository.getNicknameDuplicateCheck(nickname)
+ .onSuccess { response ->
+ if (response == null) return@onSuccess
+
+ response.isDuplicated.let {
+ Timber.e("SUCCESS GET DUPLICATION CHECK: $it")
+ updateDuplicateCheckState(it)
+ saveDuplicateCheckState(it)
+ }
+ }
+ .onFailure { t ->
+ Timber.e("${t.message}")
+ }
+ }
+ }
+
+ private fun updateDuplicateCheckState(isDuplicated: Boolean) {
+ _inputUiState.value = if (isDuplicated) {
+ InputUiState.Failure(NicknameErrorCode.CODE_DUPLICATE)
+ } else {
+ InputUiState.Success
+ }
+ }
+
+ // 서버통신 할 때마다 현재 닉네임과 중복여부를 저장한다.
+ private fun saveDuplicateCheckState(isDuplicated: Boolean) {
+ prevCheckResult = Pair(nickname, isDuplicated)
+ }
+
+ private fun updateInputUiState(nickname: String): InputUiState {
+ if (nickname.isEmpty()) return InputUiState.Empty
+ if (!checkLength(nickname)) return InputUiState.Failure(CODE_INVALID_LENGTH)
+ if (containsSpaceOrSpecialChar(nickname)) {
+ return InputUiState.Failure(
+ CODE_SPACE_SPECIAL_CHAR
+ )
+ }
+
+ // 텍스트가 바뀌었는데 중복체크 버튼을 누르지 않은 경우
+ if (isTextChanged.value && !isCheckBtnClicked.value) {
+ return comparePrevCheckResult()
+ }
+
+ return InputUiState.Empty
+ }
+
+ private fun comparePrevCheckResult(): InputUiState {
+ // 이전에 서버통신 한 결과가 없는 경우
+ if (prevCheckResult == null) {
+ Timber.d("CASE 1")
+ return InputUiState.Failure(CODE_UNCHECKED_DUPLICATION)
+ }
+
+ // 현재 입력값이 이전에 서버통신 했던 닉네임과 일치하는 경우
+ if (nickname == prevCheckResult?.first) {
+ // 그때 당시의 서버통신 결과 그대로 반환
+ if (prevCheckResult?.second == true) {
+ Timber.d("CASE 2")
+ return InputUiState.Failure(CODE_DUPLICATE)
+ }
+
+ Timber.d("CASE 3")
+ return InputUiState.Success
+ }
+
+ // 이전에 서버통신한 결과가 있지만, 입력값이 다른 경우
+ Timber.d("CASE 4")
+ return InputUiState.Failure(CODE_UNCHECKED_DUPLICATION)
+ }
+
+ private fun checkLength(nickname: String) = nickname.length in MIN_LENGTH..MAX_LENGTH
+
+ private fun containsSpaceOrSpecialChar(nickname: String) =
+ !Regex(REGEX_PATTERN).matches(nickname)
+
+ fun updateTextChangedState(state: Boolean) { // updated in Activity
+ _isTextChanged.value = state
+ }
+
+ fun updateDuplicateCheckButtonState(state: Boolean) { // updated in Activity
+ _isCheckBtnClicked.value = state
+ initDuplicateCheckButtonState()
+ }
+
+ private fun initDuplicateCheckButtonState() {
+ _isCheckBtnClicked.value = false
+ }
+
+ // _nickname.map{} Flow -> MutableStateFlow 변환을 위한 확장 함수
+ private fun Flow.mutableStateIn(
+ initialValue: T,
+ scope: CoroutineScope
+ ): MutableStateFlow {
+ val flow = MutableStateFlow(initialValue)
+ scope.launch {
+ this@mutableStateIn.collect(flow)
+ }
+ return flow
+ }
+
+ companion object {
+ private const val MIN_LENGTH = 1
+ const val MAX_LENGTH = 8
+ private const val REGEX_PATTERN = "^[0-9|a-z|A-Z|ㄱ-ㅎ|ㅏ-ㅣ|가-힣]*$"
+ private const val PRODUCE_STOP_TIMEOUT = 5000L
+ }
+}
diff --git a/app/src/main/java/com/android/go/sopt/winey/presentation/splash/SplashActivity.kt b/app/src/main/java/com/android/go/sopt/winey/presentation/splash/SplashActivity.kt
index 99fd9617..21f19002 100644
--- a/app/src/main/java/com/android/go/sopt/winey/presentation/splash/SplashActivity.kt
+++ b/app/src/main/java/com/android/go/sopt/winey/presentation/splash/SplashActivity.kt
@@ -1,5 +1,6 @@
package com.android.go.sopt.winey.presentation.splash
+import android.app.Activity
import android.content.Intent
import android.os.Bundle
import androidx.lifecycle.lifecycleScope
@@ -7,7 +8,8 @@ import com.android.go.sopt.winey.R
import com.android.go.sopt.winey.databinding.ActivitySplashBinding
import com.android.go.sopt.winey.domain.repository.DataStoreRepository
import com.android.go.sopt.winey.presentation.main.MainActivity
-import com.android.go.sopt.winey.presentation.onboarding.LoginActivity
+import com.android.go.sopt.winey.presentation.onboarding.login.LoginActivity
+import com.android.go.sopt.winey.presentation.onboarding.nickname.NicknameActivity
import com.android.go.sopt.winey.util.binding.BindingActivity
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.delay
@@ -16,6 +18,10 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import javax.inject.Inject
+/**
+ * Splash -> Onboarding -> Login -> 여기서 서버 오류 발생 -> Nickname -> Main
+ * -> 자동 로그인 -> Main
+ * */
@AndroidEntryPoint
class SplashActivity : BindingActivity(R.layout.activity_splash) {
@Inject
@@ -25,33 +31,25 @@ class SplashActivity : BindingActivity(R.layout.activity_
lifecycleScope.launch {
delay(DELAY_TIME)
- checkAutoLogin()
+ //checkAutoLogin()
+ navigateTo()
}
}
private fun checkAutoLogin() {
val accessToken = runBlocking { dataStoreRepository.getAccessToken().firstOrNull() }
if (accessToken.isNullOrBlank()) {
- navigateToOnBoardingScreen()
+ navigateTo()
} else {
- navigateToMainScreen()
+ navigateTo()
}
}
- private fun navigateToOnBoardingScreen() {
- Intent(this@SplashActivity, LoginActivity::class.java).apply {
- addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ private inline fun navigateTo() {
+ Intent(this@SplashActivity, T::class.java).apply {
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(this)
}
- finish()
- }
-
- private fun navigateToMainScreen() {
- Intent(this@SplashActivity, MainActivity::class.java).apply {
- addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
- startActivity(this)
- }
- finish()
}
companion object {
diff --git a/app/src/main/java/com/android/go/sopt/winey/util/binding/BindingAdapter.kt b/app/src/main/java/com/android/go/sopt/winey/util/binding/BindingAdapter.kt
index 41ea322f..d22f7bd1 100644
--- a/app/src/main/java/com/android/go/sopt/winey/util/binding/BindingAdapter.kt
+++ b/app/src/main/java/com/android/go/sopt/winey/util/binding/BindingAdapter.kt
@@ -2,6 +2,8 @@ package com.android.go.sopt.winey.util.binding
import android.graphics.drawable.Drawable
import android.net.Uri
+import android.view.View
+import android.widget.EditText
import android.widget.ImageView
import android.widget.ImageView.ScaleType
import android.widget.TextView
@@ -9,6 +11,12 @@ import androidx.databinding.BindingAdapter
import coil.load
import coil.transform.RoundedCornersTransformation
import com.android.go.sopt.winey.R
+import com.android.go.sopt.winey.util.code.NicknameErrorCode.*
+import com.android.go.sopt.winey.util.context.colorOf
+import com.android.go.sopt.winey.util.context.drawableOf
+import com.android.go.sopt.winey.util.context.stringOf
+import com.android.go.sopt.winey.util.view.InputUiState
+import com.android.go.sopt.winey.util.view.InputUiState.*
import java.text.DecimalFormat
@BindingAdapter("likedAmount")
@@ -53,3 +61,45 @@ fun ImageView.setRoundedImage(imageUri: Uri?, drawable: Drawable) {
transformations(RoundedCornersTransformation(10f))
}
}
+
+@BindingAdapter("setBackground")
+fun EditText.setBackground(inputUiState: InputUiState) {
+ background = when (inputUiState) {
+ is Empty -> context.drawableOf(R.drawable.sel_nickname_edittext_focus_color)
+ is Success -> context.drawableOf(R.drawable.shape_blue_line_5_rect)
+ is Failure -> context.drawableOf(R.drawable.shape_red_line_5_rect)
+ }
+}
+
+@BindingAdapter("setHelperText")
+fun TextView.setHelperText(inputUiState: InputUiState) {
+ when (inputUiState) {
+ is Empty -> {
+ visibility = View.INVISIBLE
+ }
+
+ is Success -> {
+ visibility = View.VISIBLE
+ text = context.stringOf(R.string.nickname_valid)
+ }
+
+ is Failure -> {
+ visibility = View.VISIBLE
+ text = when (inputUiState.code) {
+ CODE_INVALID_LENGTH -> context.stringOf(R.string.nickname_invalid_length_error)
+ CODE_SPACE_SPECIAL_CHAR -> context.stringOf(R.string.nickname_space_special_char_error)
+ CODE_UNCHECKED_DUPLICATION -> context.stringOf(R.string.nickname_unchecked_duplication_error)
+ CODE_DUPLICATE -> context.stringOf(R.string.nickname_duplicate_error)
+ }
+ }
+ }
+}
+
+@BindingAdapter("setHelperTextColor")
+fun TextView.setHelperTextColor(inputUiState: InputUiState) {
+ when (inputUiState) {
+ is Empty -> setTextColor(context.colorOf(R.color.gray_200))
+ is Success -> setTextColor(context.colorOf(R.color.blue_500))
+ is Failure -> setTextColor(context.colorOf(R.color.red_500))
+ }
+}
diff --git a/app/src/main/java/com/android/go/sopt/winey/util/code/NicknameErrorCode.kt b/app/src/main/java/com/android/go/sopt/winey/util/code/NicknameErrorCode.kt
new file mode 100644
index 00000000..d480bae1
--- /dev/null
+++ b/app/src/main/java/com/android/go/sopt/winey/util/code/NicknameErrorCode.kt
@@ -0,0 +1,8 @@
+package com.android.go.sopt.winey.util.code
+
+enum class NicknameErrorCode {
+ CODE_INVALID_LENGTH,
+ CODE_SPACE_SPECIAL_CHAR,
+ CODE_UNCHECKED_DUPLICATION,
+ CODE_DUPLICATE
+}
diff --git a/app/src/main/java/com/android/go/sopt/winey/util/view/InputUiState.kt b/app/src/main/java/com/android/go/sopt/winey/util/view/InputUiState.kt
new file mode 100644
index 00000000..47de4e72
--- /dev/null
+++ b/app/src/main/java/com/android/go/sopt/winey/util/view/InputUiState.kt
@@ -0,0 +1,9 @@
+package com.android.go.sopt.winey.util.view
+
+import com.android.go.sopt.winey.util.code.NicknameErrorCode
+
+sealed class InputUiState {
+ object Empty : InputUiState()
+ object Success : InputUiState()
+ data class Failure(val code: NicknameErrorCode) : InputUiState()
+}
diff --git a/app/src/main/res/drawable/sel_nickname_edittext_focus_color.xml b/app/src/main/res/drawable/sel_nickname_edittext_focus_color.xml
new file mode 100644
index 00000000..3828bbf9
--- /dev/null
+++ b/app/src/main/res/drawable/sel_nickname_edittext_focus_color.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/sel_upload_next_btn_bg_color.xml b/app/src/main/res/drawable/sel_upload_nickname_btn_color.xml
similarity index 100%
rename from app/src/main/res/drawable/sel_upload_next_btn_bg_color.xml
rename to app/src/main/res/drawable/sel_upload_nickname_btn_color.xml
diff --git a/app/src/main/res/drawable/sel_upload_next_btn_text_color.xml b/app/src/main/res/drawable/sel_upload_nickname_btn_text_color.xml
similarity index 100%
rename from app/src/main/res/drawable/sel_upload_next_btn_text_color.xml
rename to app/src/main/res/drawable/sel_upload_nickname_btn_text_color.xml
diff --git a/app/src/main/res/drawable/shape_blue_line_5_rect.xml b/app/src/main/res/drawable/shape_blue_line_5_rect.xml
new file mode 100644
index 00000000..8de00935
--- /dev/null
+++ b/app/src/main/res/drawable/shape_blue_line_5_rect.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/shape_gray_line_5_rect.xml b/app/src/main/res/drawable/shape_gray_line_5_rect.xml
new file mode 100644
index 00000000..fd9d6378
--- /dev/null
+++ b/app/src/main/res/drawable/shape_gray_line_5_rect.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/shape_purple_line_5_rect.xml b/app/src/main/res/drawable/shape_purple_line_5_rect.xml
new file mode 100644
index 00000000..5d4874c1
--- /dev/null
+++ b/app/src/main/res/drawable/shape_purple_line_5_rect.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/shape_red_line_5_rect.xml b/app/src/main/res/drawable/shape_red_line_5_rect.xml
new file mode 100644
index 00000000..3df03090
--- /dev/null
+++ b/app/src/main/res/drawable/shape_red_line_5_rect.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_nickname.xml b/app/src/main/res/layout/activity_nickname.xml
new file mode 100644
index 00000000..813f267a
--- /dev/null
+++ b/app/src/main/res/layout/activity_nickname.xml
@@ -0,0 +1,134 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_amount.xml b/app/src/main/res/layout/fragment_amount.xml
index 762c41d5..42ca172e 100644
--- a/app/src/main/res/layout/fragment_amount.xml
+++ b/app/src/main/res/layout/fragment_amount.xml
@@ -122,12 +122,12 @@
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="14dp"
- android:background="@drawable/sel_upload_next_btn_bg_color"
- android:enabled="@{vm.isValidAmount, default=false}"
+ android:background="@drawable/sel_upload_nickname_btn_color"
+ android:enabled="@{vm.isValidAmount ? true : false, default=false}"
android:stateListAnimator="@null"
android:text="@string/upload_last_btn_text"
android:textAppearance="@style/TextAppearance.WINEY.body_m_16"
- android:textColor="@drawable/sel_upload_next_btn_text_color"
+ android:textColor="@drawable/sel_upload_nickname_btn_text_color"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
diff --git a/app/src/main/res/layout/fragment_content.xml b/app/src/main/res/layout/fragment_content.xml
index f4c498f4..7e201ddc 100644
--- a/app/src/main/res/layout/fragment_content.xml
+++ b/app/src/main/res/layout/fragment_content.xml
@@ -134,12 +134,12 @@
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="14dp"
- android:background="@drawable/sel_upload_next_btn_bg_color"
+ android:background="@drawable/sel_upload_nickname_btn_color"
android:enabled="@{vm.isValidContent, default=false}"
android:stateListAnimator="@null"
android:text="@string/upload_next_btn_text"
android:textAppearance="@style/TextAppearance.WINEY.body_m_16"
- android:textColor="@drawable/sel_upload_next_btn_text_color"
+ android:textColor="@drawable/sel_upload_nickname_btn_text_color"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
diff --git a/app/src/main/res/layout/fragment_photo.xml b/app/src/main/res/layout/fragment_photo.xml
index c046fdf0..a2b116e6 100644
--- a/app/src/main/res/layout/fragment_photo.xml
+++ b/app/src/main/res/layout/fragment_photo.xml
@@ -102,12 +102,12 @@
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="14dp"
- android:background="@drawable/sel_upload_next_btn_bg_color"
+ android:background="@drawable/sel_upload_nickname_btn_color"
android:enabled="@{vm.photoUploadState, default=false}"
android:stateListAnimator="@null"
android:text="@string/upload_next_btn_text"
android:textAppearance="@style/TextAppearance.WINEY.body_m_16"
- android:textColor="@drawable/sel_upload_next_btn_text_color"
+ android:textColor="@drawable/sel_upload_nickname_btn_text_color"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
diff --git a/app/src/main/res/layout/fragment_target_amount_bottom_sheet.xml b/app/src/main/res/layout/fragment_target_amount_bottom_sheet.xml
index 89fcad72..9af9546c 100644
--- a/app/src/main/res/layout/fragment_target_amount_bottom_sheet.xml
+++ b/app/src/main/res/layout/fragment_target_amount_bottom_sheet.xml
@@ -207,12 +207,12 @@
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="15dp"
- android:background="@drawable/sel_upload_next_btn_bg_color"
+ android:background="@drawable/sel_upload_nickname_btn_color"
android:enabled="false"
android:stateListAnimator="@null"
android:text="@string/target_amount_save"
android:textAppearance="@style/TextAppearance.WINEY.body_m_16"
- android:textColor="@drawable/sel_upload_next_btn_text_color"
+ android:textColor="@drawable/sel_upload_nickname_btn_text_color"
app:layout_constraintBottom_toBottomOf="parent" />
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9a6ea2da..c43a3809 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -26,6 +26,7 @@
적어주신 글을 썸네일과 함께 표시합니다 :)
Ex) 버스 타고 가는 길을 운동 삼아 20분 일찍 일어나 걸어갔어요!\n
(%d/36)
+ 6자 이상 작성해주세요
얼마나 절약했나요?
절약하신 금액을 작성해주세요!
원
@@ -95,8 +96,23 @@
예비 황제를 위한 추천 절약법
쉿, 그대를 위한 절약방법이 도착했어.\n황실 내에서도 비밀인데 특별히 알려줄게:)
- 6자 이상 작성해주세요
+
+ 위니제국에 들어온 당신,\n어떤 닉네임으로 불리고 싶나요?
+ 공백 또는 특수문자는 사용할 수 없습니다
+ 닉네임 입력
+ (%d/8)
+ 중복확인
+ 확인
+ 1~8자로 입력해주세요 :(
+ 공백과 특수문자는 사용할 수 없습니다 :(
+ 닉네임 중복확인을 해주세요 :(
+ 중복된 닉네임입니다 :(
+ 사용 가능한 닉네임입니다 :)
+
절약을 더 쉽고 재밌게
카카오톡으로 3초만에 시작하기
+
+
+