diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 310c0ae4..4c29b984 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,9 +4,7 @@ - + - diff --git a/domain/src/main/kotlin/com/going/domain/entity/NameState.kt b/domain/src/main/kotlin/com/going/domain/entity/NameState.kt index 413b375a..765922df 100644 --- a/domain/src/main/kotlin/com/going/domain/entity/NameState.kt +++ b/domain/src/main/kotlin/com/going/domain/entity/NameState.kt @@ -1,5 +1,5 @@ package com.going.domain.entity enum class NameState { - Empty, Success, Blank + Empty, Success, Blank, OVER } diff --git a/presentation/src/main/java/com/going/presentation/entertrip/createtrip/choosedate/CreateTripActivity.kt b/presentation/src/main/java/com/going/presentation/entertrip/createtrip/choosedate/CreateTripActivity.kt index 31b5c0b0..1eb96960 100644 --- a/presentation/src/main/java/com/going/presentation/entertrip/createtrip/choosedate/CreateTripActivity.kt +++ b/presentation/src/main/java/com/going/presentation/entertrip/createtrip/choosedate/CreateTripActivity.kt @@ -23,7 +23,6 @@ class CreateTripActivity : super.onCreate(savedInstanceState) initBindingViewModel() - observeTextLength() observeIsNameAvailable() observeCheckStartDateAvailable() observeCheckEndDateAvailable() @@ -37,24 +36,10 @@ class CreateTripActivity : binding.viewModel = viewModel } - private fun observeTextLength() { - viewModel.nameLength.observe(this) { length -> - val maxNameLength = viewModel.getMaxNameLen() - - if (length > maxNameLength) { - binding.etCreateTripName.apply { - setText(text?.subSequence(0, maxNameLength)) - setSelection(maxNameLength) - } - } - } - } - private fun observeIsNameAvailable() { viewModel.isNameAvailable.observe(this) { state -> setColors( - false, - viewModel.nameLength.value ?: 0, + state, binding.tvNameCounter, ) { background -> binding.etCreateTripName.background = ResourcesCompat.getDrawable( @@ -99,17 +84,17 @@ class CreateTripActivity : } private fun setColors( - hasFocus: Boolean, - length: Int, + state: NameState, counter: TextView, setBackground: (Int) -> Unit, ) { - val (color, background) = when { - viewModel.isNameAvailable.value != NameState.Blank && hasFocus -> R.color.gray_700 to R.drawable.shape_rect_4_gray700_line - length == 0 -> R.color.gray_200 to R.drawable.shape_rect_4_gray200_line - viewModel.isNameAvailable.value == NameState.Blank && counter == binding.tvNameCounter -> R.color.red_500 to R.drawable.shape_rect_4_red500_line - else -> R.color.gray_700 to R.drawable.shape_rect_4_gray700_line + val (color, background) = when (state) { + NameState.Empty -> R.color.gray_200 to R.drawable.shape_rect_4_gray200_line + NameState.Success -> R.color.gray_700 to R.drawable.shape_rect_4_gray700_line + NameState.Blank -> R.color.red_500 to R.drawable.shape_rect_4_red500_line + NameState.OVER -> R.color.red_500 to R.drawable.shape_rect_4_red500_line } + setCounterColor(counter, color) setBackground(background) } diff --git a/presentation/src/main/java/com/going/presentation/entertrip/createtrip/choosedate/CreateTripViewModel.kt b/presentation/src/main/java/com/going/presentation/entertrip/createtrip/choosedate/CreateTripViewModel.kt index 80485bfc..0b8cbe63 100644 --- a/presentation/src/main/java/com/going/presentation/entertrip/createtrip/choosedate/CreateTripViewModel.kt +++ b/presentation/src/main/java/com/going/presentation/entertrip/createtrip/choosedate/CreateTripViewModel.kt @@ -24,13 +24,12 @@ class CreateTripViewModel : ViewModel() { val isTripAvailable = MutableLiveData(false) var isCheckTripAvailable = MutableLiveData(false) - fun getMaxNameLen() = MAX_TRIP_LEN - fun checkNameAvailable() { nameLength.value = name.value?.getGraphemeLength() isNameAvailable.value = when { nameLength.value == 0 -> NameState.Empty + (nameLength.value ?: 0) > MAX_TRIP_LEN -> NameState.OVER name.value.isNullOrBlank() -> NameState.Blank else -> NameState.Success } @@ -60,7 +59,6 @@ class CreateTripViewModel : ViewModel() { } else { isEndDateAvailable.value = false checkTripAvailable() - } } diff --git a/presentation/src/main/java/com/going/presentation/onboarding/signup/OnboardingProfileSettingActivity.kt b/presentation/src/main/java/com/going/presentation/onboarding/signup/OnboardingProfileSettingActivity.kt index a9798d0e..24f18f38 100644 --- a/presentation/src/main/java/com/going/presentation/onboarding/signup/OnboardingProfileSettingActivity.kt +++ b/presentation/src/main/java/com/going/presentation/onboarding/signup/OnboardingProfileSettingActivity.kt @@ -33,10 +33,9 @@ class OnboardingProfileSettingActivity : initBindingViewModel() initOnLineInfoEditorActionListener() - initSetOnFocusChangeListener() initSignUpBtnClickListener() observeIsNameAvailable() - observeTextLength() + observeIsInfoAvailable() observeIsSignUpState() initOnBackPressedListener() } @@ -52,12 +51,17 @@ class OnboardingProfileSettingActivity : } } - private fun initSetOnFocusChangeListener() { - binding.etOnboardingProfileSettingName.setOnFocusChangeListener { _, hasFocus -> + private fun initSignUpBtnClickListener() { + binding.btnOnboardingProfileSettingFinish.setOnSingleClickListener { + viewModel.startSignUp() + } + } + + private fun observeIsNameAvailable() { + viewModel.isNameAvailable.observe(this) { state -> setColors( - hasFocus, - viewModel.nowNameLength.value ?: 0, binding.tvNameCounter, + state, ) { background -> binding.etOnboardingProfileSettingName.background = ResourcesCompat.getDrawable( this.resources, @@ -66,12 +70,13 @@ class OnboardingProfileSettingActivity : ) } } + } - binding.etOnboardingProfileSettingInfo.setOnFocusChangeListener { _, hasFocus -> + private fun observeIsInfoAvailable() { + viewModel.isInfoAvailable.observe(this) { state -> setColors( - hasFocus, - viewModel.nowInfoLength.value ?: 0, binding.tvInfoCounter, + state, ) { background -> binding.etOnboardingProfileSettingInfo.background = ResourcesCompat.getDrawable( this.resources, @@ -82,39 +87,16 @@ class OnboardingProfileSettingActivity : } } - private fun initSignUpBtnClickListener() { - binding.btnOnboardingProfileSettingFinish.setOnSingleClickListener { - viewModel.startSignUp() - } - } - - private fun observeIsNameAvailable() { - viewModel.isNameAvailable.observe(this) { state -> - setColors( - false, - viewModel.nowNameLength.value ?: 0, - binding.tvNameCounter, - ) { background -> - binding.etOnboardingProfileSettingName.background = ResourcesCompat.getDrawable( - this.resources, - background, - theme, - ) - } - } - } - private fun setColors( - hasFocus: Boolean, - length: Int, counter: TextView, + state: NameState, setBackground: (Int) -> Unit, ) { - val (color, background) = when { - viewModel.isNameAvailable.value != NameState.Blank && hasFocus -> R.color.gray_700 to R.drawable.shape_rect_4_gray700_line - length == 0 -> R.color.gray_200 to R.drawable.shape_rect_4_gray200_line - viewModel.isNameAvailable.value == NameState.Blank && counter == binding.tvNameCounter -> R.color.red_500 to R.drawable.shape_rect_4_red500_line - else -> R.color.gray_700 to R.drawable.shape_rect_4_gray700_line + val (color, background) = when (state) { + NameState.Empty -> R.color.gray_200 to R.drawable.shape_rect_4_gray200_line + NameState.Success -> R.color.gray_700 to R.drawable.shape_rect_4_gray700_line + NameState.Blank -> R.color.red_500 to R.drawable.shape_rect_4_red500_line + NameState.OVER -> R.color.red_500 to R.drawable.shape_rect_4_red500_line } setCounterColor(counter, color) @@ -125,31 +107,6 @@ class OnboardingProfileSettingActivity : counter.setTextColor(getColor(color)) } - // 커스텀 글자수 제한 함수 - private fun observeTextLength() { - viewModel.nowNameLength.observe(this) { length -> - val maxNameLength = viewModel.getMaxNameLen() - - if (length > maxNameLength) { - binding.etOnboardingProfileSettingName.apply { - setText(text?.subSequence(0, maxNameLength)) - setSelection(maxNameLength) - } - } - } - - viewModel.nowInfoLength.observe(this) { length -> - val maxInfoLength = viewModel.getMaxInfoLen() - - if (length > maxInfoLength) { - binding.etOnboardingProfileSettingInfo.apply { - setText(text?.subSequence(0, maxInfoLength)) - setSelection(maxInfoLength) - } - } - } - } - private fun observeIsSignUpState() { viewModel.isSignUpState.flowWithLifecycle(lifecycle).onEach { state -> when (state) { diff --git a/presentation/src/main/java/com/going/presentation/onboarding/signup/OnboardingProfileSettingViewModel.kt b/presentation/src/main/java/com/going/presentation/onboarding/signup/OnboardingProfileSettingViewModel.kt index fdaf97c9..02c39836 100644 --- a/presentation/src/main/java/com/going/presentation/onboarding/signup/OnboardingProfileSettingViewModel.kt +++ b/presentation/src/main/java/com/going/presentation/onboarding/signup/OnboardingProfileSettingViewModel.kt @@ -30,14 +30,12 @@ class OnboardingProfileSettingViewModel @Inject constructor( val nowInfoLength = MutableLiveData(0) val isNameAvailable = MutableLiveData(NameState.Empty) + val isInfoAvailable = MutableLiveData(NameState.Empty) val isProfileAvailable = MutableLiveData(false) private val _isSignUpState = MutableStateFlow(AuthState.LOADING) val isSignUpState: StateFlow = _isSignUpState - fun getMaxNameLen() = MAX_NAME_LEN - fun getMaxInfoLen() = MAX_INFO_LEN - fun checkProfileAvailable() { nowNameLength.value = name.value.getGraphemeLength() nowInfoLength.value = info.value.getGraphemeLength() @@ -45,13 +43,18 @@ class OnboardingProfileSettingViewModel @Inject constructor( isNameAvailable.value = when { nowNameLength.value == 0 -> NameState.Empty name.value.isBlank() -> NameState.Blank + (nowNameLength.value ?: 0) > 3 -> NameState.OVER else -> NameState.Success } - val isInfoAvailable = nowInfoLength.value in 1..MAX_INFO_LEN + isInfoAvailable.value = when { + nowInfoLength.value == 0 -> NameState.Empty + (nowInfoLength.value ?: 0) > MAX_INFO_LEN -> NameState.OVER + else -> NameState.Success + } isProfileAvailable.value = - (isNameAvailable.value == NameState.Success) && isInfoAvailable + (isNameAvailable.value == NameState.Success) && (isInfoAvailable.value == NameState.Success) } fun startSignUp() { diff --git a/presentation/src/main/java/com/going/presentation/onboarding/splash/SplashActivity.kt b/presentation/src/main/java/com/going/presentation/onboarding/splash/SplashActivity.kt index 2d314a1b..e6a57847 100644 --- a/presentation/src/main/java/com/going/presentation/onboarding/splash/SplashActivity.kt +++ b/presentation/src/main/java/com/going/presentation/onboarding/splash/SplashActivity.kt @@ -30,7 +30,6 @@ class SplashActivity : BaseActivity(R.layout.activity_spl setStatusBarColor() checkConnectedNetwork() observeUserState() - } private fun setStatusBarColor() { @@ -105,5 +104,4 @@ class SplashActivity : BaseActivity(R.layout.activity_spl } finish() } - } diff --git a/presentation/src/main/java/com/going/presentation/todo/mytodo/create/MyTodoCreateActivity.kt b/presentation/src/main/java/com/going/presentation/todo/mytodo/create/MyTodoCreateActivity.kt index 15d0e0f8..26039134 100644 --- a/presentation/src/main/java/com/going/presentation/todo/mytodo/create/MyTodoCreateActivity.kt +++ b/presentation/src/main/java/com/going/presentation/todo/mytodo/create/MyTodoCreateActivity.kt @@ -7,9 +7,9 @@ import androidx.activity.viewModels import androidx.core.content.res.ResourcesCompat import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope +import com.going.domain.entity.NameState import com.going.presentation.R import com.going.presentation.databinding.ActivityMyTodoCreateBinding -import com.going.presentation.todo.TodoActivity import com.going.presentation.todo.TodoActivity.Companion.EXTRA_TRIP_ID import com.going.presentation.todo.ourtodo.create.OurTodoCreateActivity.Companion.EXTRA_PARTICIPANT_ID import com.going.ui.base.BaseActivity @@ -32,8 +32,6 @@ class MyTodoCreateActivity : super.onCreate(savedInstanceState) initViewModel() - initTodoFocusListener() - initMemoFocusListener() initDateClickListener() initFinishBtnListener() initBackBtnListener() @@ -48,30 +46,6 @@ class MyTodoCreateActivity : binding.vm = viewModel } - private fun initTodoFocusListener() { - binding.etMyTodoCreateTodo.setOnFocusChangeListener { _, hasFocus -> - setColors( - hasFocus, - viewModel.nowTodoLength.value ?: 0, - binding.tvMyTodoTodoCounter, - ) { background -> - binding.etMyTodoCreateTodo.background = setBackgroundColor(background) - } - } - } - - private fun initMemoFocusListener() { - binding.etMyTodoCreateMemo.setOnFocusChangeListener { _, hasFocus -> - setColors( - hasFocus, - viewModel.nowMemoLength.value ?: 0, - binding.tvMyTodoMemoCounter, - ) { background -> - binding.etMyTodoCreateMemo.background = setBackgroundColor(background) - } - } - } - private fun initDateClickListener() { binding.etMyTodoCreateDate.setOnSingleClickListener { myTodoCreateBottomSheet = MyTodoCreateBottomSheet() @@ -92,8 +66,8 @@ class MyTodoCreateActivity : } private fun getId() { - viewModel.tripId = intent.getLongExtra(EXTRA_TRIP_ID,0) - viewModel.participantId = intent.getLongExtra(EXTRA_PARTICIPANT_ID,0) + viewModel.tripId = intent.getLongExtra(EXTRA_TRIP_ID, 0) + viewModel.participantId = intent.getLongExtra(EXTRA_PARTICIPANT_ID, 0) } private fun observeTodoCreateState() { @@ -114,18 +88,9 @@ class MyTodoCreateActivity : } private fun observeTextLength() { - viewModel.nowTodoLength.observe(this) { length -> - val maxTodoLen = viewModel.getMaxTodoLen() - - if (length > maxTodoLen) { - binding.etMyTodoCreateTodo.apply { - setText(text?.subSequence(0, maxTodoLen)) - setSelection(maxTodoLen) - } - } + viewModel.isTodoAvailable.observe(this) { state -> setColors( - false, - viewModel.nowTodoLength.value ?: 0, + state, binding.tvMyTodoTodoCounter, ) { background -> binding.etMyTodoCreateTodo.background = setBackgroundColor(background) @@ -134,17 +99,9 @@ class MyTodoCreateActivity : } private fun observeMemoLength() { - viewModel.nowMemoLength.observe(this) { length -> - val maxMemoLen = viewModel.getMaxMemoLen() - if (length > maxMemoLen) { - binding.etMyTodoCreateTodo.apply { - setText(text?.subSequence(0, maxMemoLen)) - setSelection(maxMemoLen) - } - } + viewModel.isMemoAvailable.observe(this) { state -> setColors( - false, - viewModel.nowMemoLength.value ?: 0, + state, binding.tvMyTodoMemoCounter, ) { background -> binding.etMyTodoCreateMemo.background = setBackgroundColor(background) @@ -163,16 +120,17 @@ class MyTodoCreateActivity : } private fun setColors( - hasFocus: Boolean, - length: Int, + state: NameState, counter: TextView, setBackground: (Int) -> Unit, ) { - val (color, background) = when { - hasFocus || viewModel.nowTodoLength.value != 0 -> R.color.gray_700 to R.drawable.shape_rect_4_gray700_line - length == 0 -> R.color.gray_200 to R.drawable.shape_rect_4_gray200_line - else -> R.color.gray_700 to R.drawable.shape_rect_4_gray700_line + val (color, background) = when (state) { + NameState.Empty -> R.color.gray_200 to R.drawable.shape_rect_4_gray200_line + NameState.Success -> R.color.gray_700 to R.drawable.shape_rect_4_gray700_line + NameState.Blank -> R.color.red_500 to R.drawable.shape_rect_4_red500_line + NameState.OVER -> R.color.red_500 to R.drawable.shape_rect_4_red500_line } + setCounterColor(counter, color) setBackground(background) } @@ -197,4 +155,4 @@ class MyTodoCreateActivity : companion object { private const val DATE_BOTTOM_SHEET = "DATE_BOTTOM_SHEET" } -} \ No newline at end of file +} diff --git a/presentation/src/main/java/com/going/presentation/todo/mytodo/create/MyTodoCreateViewModel.kt b/presentation/src/main/java/com/going/presentation/todo/mytodo/create/MyTodoCreateViewModel.kt index 634ce1d5..edcf9a9a 100644 --- a/presentation/src/main/java/com/going/presentation/todo/mytodo/create/MyTodoCreateViewModel.kt +++ b/presentation/src/main/java/com/going/presentation/todo/mytodo/create/MyTodoCreateViewModel.kt @@ -3,8 +3,10 @@ package com.going.presentation.todo.mytodo.create import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.going.domain.entity.NameState import com.going.domain.entity.request.TodoCreateRequestModel import com.going.domain.repository.TodoRepository +import com.going.presentation.todo.ourtodo.create.OurTodoCreateViewModel import com.going.ui.extension.UiState import com.going.ui.extension.getGraphemeLength import dagger.hilt.android.lifecycle.HiltViewModel @@ -19,11 +21,13 @@ class MyTodoCreateViewModel @Inject constructor( ) : ViewModel() { val todo = MutableLiveData("") + val isTodoAvailable = MutableLiveData(NameState.Empty) val nowTodoLength = MutableLiveData(0) val endDate = MutableLiveData("") val memo = MutableLiveData("") + val isMemoAvailable = MutableLiveData(NameState.Empty) val nowMemoLength = MutableLiveData(0) val isFinishAvailable = MutableLiveData(false) @@ -34,15 +38,25 @@ class MyTodoCreateViewModel @Inject constructor( var tripId: Long = 0 var participantId: Long = 0 - fun getMaxTodoLen() = MAX_TODO_LEN - - fun getMaxMemoLen() = MAX_MEMO_LEN - fun checkIsFinishAvailable() { nowTodoLength.value = todo.value?.getGraphemeLength() + isTodoAvailable.value = when { + nowTodoLength.value == 0 -> NameState.Empty + (nowTodoLength.value ?: 0) > MAX_TODO_LEN -> NameState.OVER + todo.value.isNullOrBlank() -> NameState.Blank + else -> NameState.Success + } + nowMemoLength.value = memo.value?.getGraphemeLength() + isMemoAvailable.value = when { + nowMemoLength.value == 0 -> NameState.Empty + (nowMemoLength.value ?: 0) > MAX_MEMO_LEN -> NameState.OVER + memo.value.isNullOrBlank() -> NameState.Blank + else -> NameState.Success + } + isFinishAvailable.value = - todo.value?.isNotEmpty() == true && endDate.value?.isNotEmpty() == true + todo.value?.isNotEmpty() == true && endDate.value?.isNotEmpty() == true && isTodoAvailable.value == NameState.Success && isMemoAvailable.value != NameState.OVER } fun postToCreateTodoFromServer() { @@ -71,5 +85,4 @@ class MyTodoCreateViewModel @Inject constructor( const val MAX_TODO_LEN = 15 const val MAX_MEMO_LEN = 1000 } - } diff --git a/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/OurTodoCreateActivity.kt b/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/OurTodoCreateActivity.kt index 86d0a192..a4dbaefb 100644 --- a/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/OurTodoCreateActivity.kt +++ b/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/OurTodoCreateActivity.kt @@ -7,6 +7,7 @@ import androidx.activity.viewModels import androidx.core.content.res.ResourcesCompat import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope +import com.going.domain.entity.NameState import com.going.domain.entity.response.TripParticipantModel import com.going.presentation.R import com.going.presentation.databinding.ActivityOurTodoCreateBinding @@ -36,8 +37,6 @@ class OurTodoCreateActivity : initViewModel() initNameListAdapter() - initTodoFocusListener() - initMemoFocusListener() initDateClickListener() initFinishBtnListener() initBackBtnListener() @@ -62,30 +61,6 @@ class OurTodoCreateActivity : binding.rvOurTodoCreatePerson.adapter = adapter } - private fun initTodoFocusListener() { - binding.etOurTodoCreateTodo.setOnFocusChangeListener { _, hasFocus -> - setColors( - hasFocus, - viewModel.nowTodoLength.value ?: 0, - binding.tvOurTodoTodoCounter, - ) { background -> - binding.etOurTodoCreateTodo.background = setBackgroundColor(background) - } - } - } - - private fun initMemoFocusListener() { - binding.etOurTodoCreateMemo.setOnFocusChangeListener { _, hasFocus -> - setColors( - hasFocus, - viewModel.nowMemoLength.value ?: 0, - binding.tvOurTodoMemoCounter, - ) { background -> - binding.etOurTodoCreateMemo.background = setBackgroundColor(background) - } - } - } - private fun initDateClickListener() { binding.etOurTodoCreateDate.setOnSingleClickListener { ourTodoCreateBottomSheet = OurTodoCreateBottomSheet() @@ -107,7 +82,7 @@ class OurTodoCreateActivity : } private fun getTripId() { - viewModel.tripId = intent.getLongExtra(EXTRA_TRIP_ID,0) + viewModel.tripId = intent.getLongExtra(EXTRA_TRIP_ID, 0) } private fun setParticipantList() { @@ -140,18 +115,9 @@ class OurTodoCreateActivity : } private fun observeTextLength() { - viewModel.nowTodoLength.observe(this) { length -> - val maxTodoLen = viewModel.getMaxTodoLen() - - if (length > maxTodoLen) { - binding.etOurTodoCreateTodo.apply { - setText(text?.subSequence(0, maxTodoLen)) - setSelection(maxTodoLen) - } - } + viewModel.isTodoAvailable.observe(this) { state -> setColors( - false, - viewModel.nowTodoLength.value ?: 0, + state, binding.tvOurTodoTodoCounter, ) { background -> binding.etOurTodoCreateTodo.background = setBackgroundColor(background) @@ -160,17 +126,9 @@ class OurTodoCreateActivity : } private fun observeMemoLength() { - viewModel.nowMemoLength.observe(this) { length -> - val maxMemoLen = viewModel.getMaxMemoLen() - if (length > maxMemoLen) { - binding.etOurTodoCreateTodo.apply { - setText(text?.subSequence(0, maxMemoLen)) - setSelection(maxMemoLen) - } - } + viewModel.isMemoAvailable.observe(this) { state -> setColors( - false, - viewModel.nowMemoLength.value ?: 0, + state, binding.tvOurTodoMemoCounter, ) { background -> binding.etOurTodoCreateMemo.background = setBackgroundColor(background) @@ -189,16 +147,17 @@ class OurTodoCreateActivity : } private fun setColors( - hasFocus: Boolean, - length: Int, + state: NameState, counter: TextView, setBackground: (Int) -> Unit, ) { - val (color, background) = when { - hasFocus || viewModel.nowTodoLength.value != 0 -> R.color.gray_700 to R.drawable.shape_rect_4_gray700_line - length == 0 -> R.color.gray_200 to R.drawable.shape_rect_4_gray200_line - else -> R.color.gray_700 to R.drawable.shape_rect_4_gray700_line + val (color, background) = when (state) { + NameState.Empty -> R.color.gray_200 to R.drawable.shape_rect_4_gray200_line + NameState.Success -> R.color.gray_700 to R.drawable.shape_rect_4_gray700_line + NameState.Blank -> R.color.red_500 to R.drawable.shape_rect_4_red500_line + NameState.OVER -> R.color.red_500 to R.drawable.shape_rect_4_red500_line } + setCounterColor(counter, color) setBackground(background) } @@ -228,5 +187,4 @@ class OurTodoCreateActivity : const val EXTRA_NAME = "EXTRA_NAME" const val EXTRA_RESULT = "EXTRA_RESULT" } - -} \ No newline at end of file +} diff --git a/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/OurTodoCreateViewModel.kt b/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/OurTodoCreateViewModel.kt index d9103cbf..237dcfcd 100644 --- a/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/OurTodoCreateViewModel.kt +++ b/presentation/src/main/java/com/going/presentation/todo/ourtodo/create/OurTodoCreateViewModel.kt @@ -1,8 +1,10 @@ package com.going.presentation.todo.ourtodo.create +import android.util.Log import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.going.domain.entity.NameState import com.going.domain.entity.request.TodoCreateRequestModel import com.going.domain.entity.response.TripParticipantModel import com.going.domain.repository.TodoRepository @@ -12,7 +14,6 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch -import java.text.BreakIterator import javax.inject.Inject @HiltViewModel @@ -21,11 +22,13 @@ class OurTodoCreateViewModel @Inject constructor( ) : ViewModel() { val todo = MutableLiveData("") + val isTodoAvailable = MutableLiveData(NameState.Empty) val nowTodoLength = MutableLiveData(0) val endDate = MutableLiveData("") val memo = MutableLiveData("") + val isMemoAvailable = MutableLiveData(NameState.Empty) val nowMemoLength = MutableLiveData(0) val isFinishAvailable = MutableLiveData(false) @@ -38,15 +41,24 @@ class OurTodoCreateViewModel @Inject constructor( var tripId: Long = 0 - fun getMaxTodoLen() = MAX_TODO_LEN - - fun getMaxMemoLen() = MAX_MEMO_LEN - fun checkIsFinishAvailable() { nowTodoLength.value = todo.value?.getGraphemeLength() + isTodoAvailable.value = when { + nowTodoLength.value == 0 -> NameState.Empty + (nowTodoLength.value ?: 0) > MAX_TODO_LEN -> NameState.OVER + todo.value.isNullOrBlank() -> NameState.Blank + else -> NameState.Success + } + nowMemoLength.value = memo.value?.getGraphemeLength() + isMemoAvailable.value = when { + nowMemoLength.value == 0 -> NameState.Empty + (nowMemoLength.value ?: 0) > MAX_MEMO_LEN -> NameState.OVER + else -> NameState.Success + } + isFinishAvailable.value = - todo.value?.isNotEmpty() == true && endDate.value?.isNotEmpty() == true && participantList.any { it.isSelected } + todo.value?.isNotEmpty() == true && endDate.value?.isNotEmpty() == true && participantList.any { it.isSelected } && isTodoAvailable.value == NameState.Success && isMemoAvailable.value != NameState.OVER } fun postToCreateTodoFromServer() { diff --git a/presentation/src/main/java/com/going/presentation/util/ActivityExt.kt b/presentation/src/main/java/com/going/presentation/util/ActivityExt.kt index a8ba1ec9..ebf7e59c 100644 --- a/presentation/src/main/java/com/going/presentation/util/ActivityExt.kt +++ b/presentation/src/main/java/com/going/presentation/util/ActivityExt.kt @@ -1,38 +1,42 @@ package com.going.presentation.util -import android.Manifest import android.app.Activity -import android.content.pm.PackageManager +import android.app.AlertDialog +import android.content.Intent import android.graphics.Bitmap import android.graphics.BitmapFactory import android.media.MediaScannerConnection import android.os.Build import android.os.Environment +import android.os.Environment.DIRECTORY_DOWNLOADS +import android.provider.Settings import androidx.activity.ComponentActivity import androidx.activity.OnBackPressedCallback -import androidx.core.app.ActivityCompat -import androidx.core.content.ContextCompat import com.going.presentation.R -import com.going.presentation.tendency.result.TendencyResultActivity import com.going.presentation.tendency.result.UserTendencyResultList import com.going.ui.extension.toast import java.io.File import java.io.FileOutputStream fun Activity.downloadImage(number: Int) { - val downloadPath = "/Download/" val downloadImageName = "img_tendency_result%s.png" - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU && ContextCompat.checkSelfPermission( - this, - Manifest.permission.WRITE_EXTERNAL_STORAGE, - ) != PackageManager.PERMISSION_GRANTED - ) { - ActivityCompat.requestPermissions( - this, - arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), - TendencyResultActivity.PERMISSION_REQUEST_CODE, - ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) { + AlertDialog.Builder(this) + .setTitle(R.string.notice) + .setMessage(R.string.profile_image_permission_error) + .setCancelable(false) + .setPositiveButton( + R.string.okay, + ) { _, _ -> + val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION) + this.startActivity(intent) + } + .setNegativeButton( + R.string.setting_logout_negative, + ) { _, _ -> } + .create() + .show() } else { val imageBitmap: Bitmap = BitmapFactory.decodeResource( resources, @@ -41,26 +45,31 @@ fun Activity.downloadImage(number: Int) { val imageFileName = downloadImageName.replace("%s", number.toString()) - val uploadFolder = Environment.getExternalStoragePublicDirectory(downloadPath) + val uploadFolder = + Environment.getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS) + if (!uploadFolder.exists()) { uploadFolder.mkdirs() } - val imageFile = File(uploadFolder, imageFileName) + val imageFile = File(uploadFolder.toString(), imageFileName) - val outputStream = FileOutputStream(imageFile) - imageBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream) - outputStream.flush() - outputStream.close() + try { + val outputStream = FileOutputStream(imageFile) + imageBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream) + outputStream.flush() + outputStream.close() - MediaScannerConnection.scanFile( - this, - arrayOf(imageFile.absolutePath), - arrayOf("image/jpeg"), - null, - ) - - toast(getString(R.string.profile_image_download_success)) + MediaScannerConnection.scanFile( + this, + arrayOf(imageFile.absolutePath), + arrayOf("image/jpeg"), + null, + ) + toast(getString(R.string.profile_image_download_success)) + } catch (e: Exception) { + toast(getString(R.string.profile_image_download_error)) + } } } diff --git a/presentation/src/main/res/drawable/ic_kakao.xml b/presentation/src/main/res/drawable/ic_kakao.xml new file mode 100644 index 00000000..beecf8a0 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_kakao.xml @@ -0,0 +1,11 @@ + + + diff --git a/presentation/src/main/res/drawable/shape_kakao_yellow_fill_8_rect.xml b/presentation/src/main/res/drawable/shape_kakao_yellow_fill_8_rect.xml new file mode 100644 index 00000000..fe492bfa --- /dev/null +++ b/presentation/src/main/res/drawable/shape_kakao_yellow_fill_8_rect.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/activity_create_trip.xml b/presentation/src/main/res/layout/activity_create_trip.xml index 2d6b3cea..840395bf 100644 --- a/presentation/src/main/res/layout/activity_create_trip.xml +++ b/presentation/src/main/res/layout/activity_create_trip.xml @@ -87,9 +87,9 @@ android:layout_height="wrap_content" android:layout_marginStart="4dp" android:layout_marginTop="4dp" - android:text="@string/name_blank_error" - android:textColor="@{viewModel.isNameAvailable() == NameState.Blank ? @color/red_500 : @color/gray_700}" - android:visibility="@{viewModel.isNameAvailable() == NameState.Blank ? View.VISIBLE : View.GONE}" + android:text="@{viewModel.isNameAvailable() == NameState.Blank ? @string/name_blank_error : @string/trip_over_error}" + android:textColor="@color/red_500" + android:visibility="@{(viewModel.isNameAvailable() == NameState.Blank) || (viewModel.isNameAvailable() == NameState.OVER) ? View.VISIBLE : View.GONE}" app:layout_constraintStart_toStartOf="@id/et_create_trip_name" app:layout_constraintTop_toBottomOf="@id/et_create_trip_name" /> diff --git a/presentation/src/main/res/layout/activity_my_todo_create.xml b/presentation/src/main/res/layout/activity_my_todo_create.xml index 0c1d2acd..0f1dc0e1 100644 --- a/presentation/src/main/res/layout/activity_my_todo_create.xml +++ b/presentation/src/main/res/layout/activity_my_todo_create.xml @@ -7,6 +7,8 @@ + + @@ -96,7 +98,7 @@ android:includeFontPadding="false" android:inputType="text" android:afterTextChanged="@{(text) -> vm.checkIsFinishAvailable()}" - android:maxLines="1" + android:maxLines="5" android:paddingHorizontal="12dp" android:paddingVertical="19dp" android:text="@={vm.todo}" @@ -106,6 +108,18 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_my_todo_create_todo_title" /> + + + + - \ No newline at end of file + diff --git a/presentation/src/main/res/layout/activity_onboarding_profile_setting.xml b/presentation/src/main/res/layout/activity_onboarding_profile_setting.xml index f1d07fdf..3a86eb1a 100644 --- a/presentation/src/main/res/layout/activity_onboarding_profile_setting.xml +++ b/presentation/src/main/res/layout/activity_onboarding_profile_setting.xml @@ -68,9 +68,9 @@ android:layout_height="wrap_content" android:layout_marginStart="4dp" android:layout_marginTop="4dp" - android:text="@string/name_blank_error" - android:textColor="@{viewModel.isNameAvailable() == NameState.Blank ? @color/red_500 : @color/gray_700}" - android:visibility="@{viewModel.isNameAvailable() == NameState.Blank ? View.VISIBLE : View.GONE}" + android:text="@{viewModel.isNameAvailable() == NameState.Blank ? @string/name_blank_error : @string/name_over_error}" + android:textColor="@color/red_500" + android:visibility="@{(viewModel.isNameAvailable() == NameState.Blank) || (viewModel.isNameAvailable() == NameState.OVER) ? View.VISIBLE : View.GONE}" app:layout_constraintStart_toStartOf="@id/et_onboarding_profile_setting_name" app:layout_constraintTop_toBottomOf="@id/et_onboarding_profile_setting_name" /> @@ -115,6 +115,18 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_onboarding_profile_setting_on_line_info_title" /> + + + + @@ -106,6 +108,19 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_our_todo_create_todo_title" /> + + + + + - \ No newline at end of file + diff --git a/presentation/src/main/res/layout/activity_signin.xml b/presentation/src/main/res/layout/activity_signin.xml index 5075e3d5..740c8c72 100644 --- a/presentation/src/main/res/layout/activity_signin.xml +++ b/presentation/src/main/res/layout/activity_signin.xml @@ -32,17 +32,42 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintBottom_toTopOf="@id/btn_terms"> + + + + + + + + app:layout_constraintStart_toEndOf="@id/tv_sign_in_terms_icon" /> 서버 통신에 실패했습니다 이름에는 공백만 입력할 수 없어요 + 이름은 3자 이하여야 합니다 + 자기소개는 20자 이하여야 합니다 + 여행이름은 15자 이하여야 합니다 + 할일은 15자 이하여야 합니다 + 메모는 1000자 이하여야 합니다 해당 기능은 추후 업데이트 예정이에요 :) + 카카오 로그인 우리만의 여행을 하다 + 개인정보처리방침 버튼을 한번 더 누르면 종료됩니다 @@ -162,8 +169,9 @@ 나는 두릅이 좋다. 다시 해볼래요 존재하지 않는 여행입니다. - 이미지가 저장되었어요\n친구에게 내 캐릭터를 공유해 보세요 + 이미지가 저장되었어요.\n친구들에게 공유해보세요! 저장할 수 없습니다\ndoorip에 사진에 대한 엑세스 권한이 없습니다 + "사진 접근 권한이 없습니다.\n설정으로 이동하여 권한 설정을 해주세요" 여행 입장하기 @@ -172,6 +180,8 @@ 잘못된 초대코드예요. 초대코드 확인하기 존재하지 않는 여행입니다 + 존재하지 않는 여행입니다. + 입장할 수 있는 최대 인원은 6명입니다. 초대받은 여행이 맞는지\n 확인해 주세요