diff --git a/presentation/src/main/java/com/going/presentation/designsystem/edittext/EmojiCounterEditText.kt b/presentation/src/main/java/com/going/presentation/designsystem/edittext/EmojiCounterEditText.kt index 2ed5a129..4dd8d26a 100644 --- a/presentation/src/main/java/com/going/presentation/designsystem/edittext/EmojiCounterEditText.kt +++ b/presentation/src/main/java/com/going/presentation/designsystem/edittext/EmojiCounterEditText.kt @@ -2,6 +2,7 @@ package com.going.presentation.designsystem.edittext import android.content.Context import android.content.res.TypedArray +import android.text.InputType import android.text.method.ScrollingMovementMethod import android.util.AttributeSet import android.view.LayoutInflater diff --git a/presentation/src/main/java/com/going/presentation/designsystem/edittext/EmojiCounterEditTextMultiLine.kt b/presentation/src/main/java/com/going/presentation/designsystem/edittext/EmojiCounterEditTextMultiLine.kt new file mode 100644 index 00000000..25166983 --- /dev/null +++ b/presentation/src/main/java/com/going/presentation/designsystem/edittext/EmojiCounterEditTextMultiLine.kt @@ -0,0 +1,152 @@ +package com.going.presentation.designsystem.edittext + +import android.content.Context +import android.content.res.TypedArray +import android.text.method.ScrollingMovementMethod +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View.OnFocusChangeListener +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.content.res.ResourcesCompat +import androidx.core.view.isVisible +import androidx.core.widget.doAfterTextChanged +import com.going.presentation.R +import com.going.presentation.databinding.ViewEmojiCounterEdittextMultilineBinding +import com.going.ui.extension.colorOf +import com.going.ui.extension.getGraphemeLength +import com.going.ui.extension.setOnSingleClickListener + +class EmojiCounterEditTextMultiLine(context: Context, attrs: AttributeSet) : + ConstraintLayout(context, attrs) { + + private val binding: ViewEmojiCounterEdittextMultilineBinding + + private var maxLen: Int = 0 + private var canBlankError: Boolean = false + lateinit var overWarning: String + var blankWarning: String = "" + + private val editTextStateMap by lazy { + mapOf( + EditTextState.SUCCESS to Triple( + R.color.gray_700, + R.drawable.shape_rect_4_gray700_line, + "" + ), + EditTextState.EMPTY to Triple( + R.color.gray_200, + R.drawable.shape_rect_4_gray200_line, + "" + ), + EditTextState.BLANK to Triple( + R.color.red_500, + R.drawable.shape_rect_4_red500_line, + blankWarning + ), + EditTextState.OVER to Triple( + R.color.red_500, + R.drawable.shape_rect_4_red500_line, + overWarning + ), + ) + } + + val editText + get() = binding.etEmojiCounterEtContent + + var state: EditTextState = EditTextState.EMPTY + set(value) { + field = value + + binding.run { + btnDeleteText.isVisible = + (value != EditTextState.EMPTY) && etEmojiCounterEtContent.hasFocus() + } + + editTextStateMap[field]?.let { setEditTextState(it) } + } + + init { + val typedArray = context.obtainStyledAttributes(attrs, R.styleable.EmojiCounterEditText) + binding = ViewEmojiCounterEdittextMultilineBinding.inflate( + LayoutInflater.from(context), + this, + true, + ) + + initDeleteBtnClickListener() + setBindingContent(typedArray) + initEtFocusChangeListener() + + typedArray.recycle() + + checkTextAvailable() + } + + private fun initDeleteBtnClickListener() = with(binding) { + btnDeleteText.setOnSingleClickListener { + etEmojiCounterEtContent.text = null + } + } + + private fun setBindingContent(typedArray: TypedArray) { + with(binding) { + tvEmojiCounterEtTitle.text = + typedArray.getString(R.styleable.EmojiCounterEditText_title) + etEmojiCounterEtContent.hint = + typedArray.getString(R.styleable.EmojiCounterEditText_hint) + etEmojiCounterEtContent.minLines = + typedArray.getInt(R.styleable.EmojiCounterEditText_minLines, 1) + etEmojiCounterEtContent.maxLines = + typedArray.getInt(R.styleable.EmojiCounterEditText_minLines, 2) + etEmojiCounterEtContent.movementMethod = ScrollingMovementMethod() + tvEmojiCounterEtNameCounter.text = context.getString(R.string.counter, 0, maxLen) + } + canBlankError = typedArray.getBoolean(R.styleable.EmojiCounterEditText_canBlankError, false) + } + + private fun initEtFocusChangeListener() { + binding.etEmojiCounterEtContent.onFocusChangeListener = + OnFocusChangeListener { _, hasFocus -> + binding.btnDeleteText.isVisible = hasFocus && (state != EditTextState.EMPTY) + } + } + + private fun checkTextAvailable() { + binding.etEmojiCounterEtContent.doAfterTextChanged { text -> + val len = text.toString().getGraphemeLength() + + state = when { + text.toString().isBlank() && len != 0 && canBlankError -> EditTextState.BLANK + len > maxLen -> EditTextState.OVER + len > 0 -> EditTextState.SUCCESS + else -> EditTextState.EMPTY + } + + binding.tvEmojiCounterEtNameCounter.text = + context.getString(R.string.counter, len, maxLen) + } + } + + fun setMaxLen(len: Int) { + maxLen = len + binding.tvEmojiCounterEtNameCounter.text = context.getString(R.string.counter, 0, maxLen) + } + + private fun setEditTextState(info: Triple) { + val color = info.first + val background = info.second + val text = info.third + + with(binding) { + tvEmojiCounterEtWarningMessage.isVisible = color == R.color.red_500 + tvEmojiCounterEtNameCounter.setTextColor(context.colorOf(color)) + etEmojiCounterEtContent.background = ResourcesCompat.getDrawable( + this@EmojiCounterEditTextMultiLine.resources, + background, + context.theme, + ) + tvEmojiCounterEtWarningMessage.text = text + } + } +} diff --git a/presentation/src/main/java/com/going/presentation/profile/edit/ProfileEditActivity.kt b/presentation/src/main/java/com/going/presentation/profile/edit/ProfileEditActivity.kt index d1422875..17336779 100644 --- a/presentation/src/main/java/com/going/presentation/profile/edit/ProfileEditActivity.kt +++ b/presentation/src/main/java/com/going/presentation/profile/edit/ProfileEditActivity.kt @@ -53,13 +53,13 @@ class ProfileEditActivity : private fun observeNameTextChanged() { binding.etProfileEditNickname.editText.doAfterTextChanged { name -> - viewModel.checkIsNameChanged(name.toString()) + viewModel.checkIsNameChanged(name.toString(), binding.etProfileEditNickname.state, binding.etProfileEditInfo.state) } } private fun observeInfoTextChanged() { binding.etProfileEditInfo.editText.doAfterTextChanged { info -> - viewModel.checkIsInfoChanged(info.toString()) + viewModel.checkIsInfoChanged(info.toString(), binding.etProfileEditNickname.state, binding.etProfileEditInfo.state) } } diff --git a/presentation/src/main/java/com/going/presentation/profile/edit/ProfileEditViewModel.kt b/presentation/src/main/java/com/going/presentation/profile/edit/ProfileEditViewModel.kt index 5735bff2..a7a9cb97 100644 --- a/presentation/src/main/java/com/going/presentation/profile/edit/ProfileEditViewModel.kt +++ b/presentation/src/main/java/com/going/presentation/profile/edit/ProfileEditViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.going.domain.entity.request.UserProfileRequestModel import com.going.domain.repository.ProfileRepository +import com.going.presentation.designsystem.edittext.EditTextState import com.going.presentation.onboarding.signup.SignUpViewModel import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow @@ -40,23 +41,23 @@ class ProfileEditViewModel @Inject constructor( defaultInfo = info } - fun checkIsNameChanged(name: String) { + fun checkIsNameChanged(name: String, nameState: EditTextState, infoState: EditTextState) { nowName = name isNameChanged = name != defaultName - checkIsValueChanged() + checkIsValueChanged(nameState, infoState) } - fun checkIsInfoChanged(info: String) { + fun checkIsInfoChanged(info: String, nameState: EditTextState, infoState: EditTextState) { nowInfo = info isInfoChanged = info != defaultInfo - checkIsValueChanged() + checkIsValueChanged(nameState, infoState) } - private fun checkIsValueChanged() { + private fun checkIsValueChanged(nameState: EditTextState, infoState: EditTextState) { _isValueChanged.value = - nowName.isNotBlank() && nowName.length <= getMaxNameLen() && nowInfo.isNotBlank() && nowInfo.length <= getMaxInfoLen() && (isInfoChanged || isNameChanged) + nowName.isNotBlank() && nameState == EditTextState.SUCCESS && nowInfo.isNotBlank() && infoState == EditTextState.SUCCESS && (isInfoChanged || isNameChanged) } fun patchUserInfo() { diff --git a/presentation/src/main/java/com/going/presentation/profile/participant/ParticipantProfileActivity.kt b/presentation/src/main/java/com/going/presentation/profile/participant/ParticipantProfileActivity.kt index 9c10e4ba..ff889a19 100644 --- a/presentation/src/main/java/com/going/presentation/profile/participant/ParticipantProfileActivity.kt +++ b/presentation/src/main/java/com/going/presentation/profile/participant/ParticipantProfileActivity.kt @@ -88,6 +88,8 @@ class ParticipantProfileActivity : } } + setFragmentHeight(profile.result == -1) + tvProfileName.text = profile.name tvProfileOneLine.text = profile.intro @@ -149,7 +151,7 @@ class ParticipantProfileActivity : setFragmentHeight() } - private fun setFragmentHeight(temp: Boolean = true) { + private fun setFragmentHeight(isEmpty: Boolean = true) { val displayHeight = getWindowHeight() val toolbarHeight = binding.tbTripProfile.height val appBarHeight = binding.appbarTripProfile.totalScrollRange @@ -157,7 +159,7 @@ class ParticipantProfileActivity : binding.vpTripProfile.layoutParams = binding.vpTripProfile.layoutParams.also { it.height = - if (temp) displayHeight - toolbarHeight - appBarHeight - tabHeight else displayHeight - toolbarHeight - tabHeight + if (isEmpty) displayHeight - toolbarHeight - appBarHeight - tabHeight else displayHeight - toolbarHeight - tabHeight } } diff --git a/presentation/src/main/res/drawable/shape_line_gray100_fill_dash_5_vertical.xml b/presentation/src/main/res/drawable/shape_line_gray100_fill_dash_5_vertical.xml index c69c0986..1ae92cfb 100644 --- a/presentation/src/main/res/drawable/shape_line_gray100_fill_dash_5_vertical.xml +++ b/presentation/src/main/res/drawable/shape_line_gray100_fill_dash_5_vertical.xml @@ -1,5 +1,11 @@ - + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/activity_profile.xml b/presentation/src/main/res/layout/activity_profile.xml index db7a617d..4ae1208d 100644 --- a/presentation/src/main/res/layout/activity_profile.xml +++ b/presentation/src/main/res/layout/activity_profile.xml @@ -106,11 +106,12 @@ diff --git a/presentation/src/main/res/layout/activity_todo_change.xml b/presentation/src/main/res/layout/activity_todo_change.xml index c671d460..1453cb20 100644 --- a/presentation/src/main/res/layout/activity_todo_change.xml +++ b/presentation/src/main/res/layout/activity_todo_change.xml @@ -229,7 +229,7 @@ - - @@ -36,12 +37,11 @@ android:id="@+id/linear_layout_chart_description" android:layout_width="0dp" android:layout_height="match_parent" - android:layout_marginStart="26dp" android:orientation="vertical" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_weight="7" - app:layout_constraintStart_toEndOf="@id/tv_chart_title" + app:layout_constraintHorizontal_weight="6.8" + app:layout_constraintStart_toEndOf="@id/img_chart_dash_vertical" app:layout_constraintTop_toTopOf="parent"> + + + + + + + + + + + + +