diff --git a/app/src/main/java/com/example/com_us/data/default_repository/DefaultHomeRepository.kt b/app/src/main/java/com/example/com_us/data/default_repository/DefaultHomeRepository.kt index 7fb9102..025b154 100644 --- a/app/src/main/java/com/example/com_us/data/default_repository/DefaultHomeRepository.kt +++ b/app/src/main/java/com/example/com_us/data/default_repository/DefaultHomeRepository.kt @@ -21,7 +21,7 @@ sealed class NetworkError : Exception(){ ) : NetworkError() data class NetworkException( - override val cause : Throwable + override val cause : Throwable, ) : NetworkError() data class NullDataError( diff --git a/app/src/main/java/com/example/com_us/data/model/question/response/question/ResponseAnswerDetailDto.kt b/app/src/main/java/com/example/com_us/data/model/question/response/question/ResponseAnswerDetailDto.kt index 2b1d8e8..f3f1bd3 100644 --- a/app/src/main/java/com/example/com_us/data/model/question/response/question/ResponseAnswerDetailDto.kt +++ b/app/src/main/java/com/example/com_us/data/model/question/response/question/ResponseAnswerDetailDto.kt @@ -5,11 +5,11 @@ import kotlinx.serialization.Serializable @Serializable data class ResponseAnswerDetailDto( @SerialName("id") - val id: Long, + val id: Long=0L, @SerialName("signLanguageName") - val signLanguageName: String, + val signLanguageName: String = "", @SerialName("signLanguageVideoUrl") - val signLanguageVideoUrl: String, + val signLanguageVideoUrl: String = "", @SerialName("signLanguageDescription") - val signLanguageDescription: String, + val signLanguageDescription: String = "", ) diff --git a/app/src/main/java/com/example/com_us/ui/UiState.kt b/app/src/main/java/com/example/com_us/ui/UiState.kt new file mode 100644 index 0000000..d02272d --- /dev/null +++ b/app/src/main/java/com/example/com_us/ui/UiState.kt @@ -0,0 +1,9 @@ +package com.example.com_us.ui + + +// 성공 / 실패에 따른 UI 처리 +sealed class UiState { + data object Initial : UiState() + data class Success(val data : T) : UiState() + data class Error(val message : String) : UiState() +} diff --git a/app/src/main/java/com/example/com_us/ui/question/block/CollectBlockActivity.kt b/app/src/main/java/com/example/com_us/ui/question/block/CollectBlockActivity.kt index 6809ebe..fe7a211 100644 --- a/app/src/main/java/com/example/com_us/ui/question/block/CollectBlockActivity.kt +++ b/app/src/main/java/com/example/com_us/ui/question/block/CollectBlockActivity.kt @@ -5,6 +5,7 @@ import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.os.Handler import androidx.activity.OnBackPressedCallback +import com.example.com_us.MainActivity import com.example.com_us.databinding.ActivityQuestionCollectBlockBinding import com.example.com_us.ui.question.result.ResultAfterSignActivity import com.example.com_us.util.ThemeType @@ -20,10 +21,9 @@ class CollectBlockActivity : AppCompatActivity() { super.onCreate(savedInstanceState) binding = ActivityQuestionCollectBlockBinding.inflate(layoutInflater) setContentView(binding.root) - category = intent.getStringExtra("category").toString() - if(!category.isNullOrEmpty()){ + if(category.isNotEmpty()){ setTheme() } @@ -31,9 +31,10 @@ class CollectBlockActivity : AppCompatActivity() { Handler().postDelayed({ val intent = Intent(this, ResultAfterSignActivity::class.java) + intent.putExtra("category",category) startActivity(intent) finish() - }, 1500) // 3초 + }, 3000) // 3초 } private fun setTheme() { diff --git a/app/src/main/java/com/example/com_us/ui/question/list/AllQuestionListFragment.kt b/app/src/main/java/com/example/com_us/ui/question/list/AllQuestionListFragment.kt index b06047d..0a8a7de 100644 --- a/app/src/main/java/com/example/com_us/ui/question/list/AllQuestionListFragment.kt +++ b/app/src/main/java/com/example/com_us/ui/question/list/AllQuestionListFragment.kt @@ -14,12 +14,17 @@ import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.example.com_us.R import com.example.com_us.databinding.FragmentQuestionBinding +import com.example.com_us.ui.UiState import com.example.com_us.ui.compose.QuestionListItem import com.example.com_us.ui.question.select.SelectAnswerActivity import com.example.com_us.util.ThemeType import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch // 바텀 네비게이션바에서 햄버거 버튼 클릭 시 이동하는 질문 리스트 화면 @AndroidEntryPoint @@ -27,9 +32,7 @@ class AllQuestionListFragment : Fragment(), View.OnClickListener { private var _binding: FragmentQuestionBinding? = null private val binding get() = _binding!! - private val viewModel: AllQuestionListViewModel by viewModels() - private var lastSelectedView: TextView? = null private lateinit var selectedView:TextView private lateinit var themeKor:String @@ -68,31 +71,41 @@ class AllQuestionListFragment : Fragment(), View.OnClickListener { // 각 질문을 담을 아이템 private fun setComposeList() { // 데이터 리스트 - viewModel.questionListByCate.observe(viewLifecycleOwner) { - if (it.isNotEmpty()) { - setThemeSelected() - binding.constraintQuestion.visibility = View.VISIBLE - binding.textviewQuestionCount.text = String.format( - resources.getString(R.string.question_title_question_count), - it.size - ) - binding.composeviewQuestion.apply { - setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) - setContent { - LazyColumn(modifier = Modifier.fillMaxSize()) { - items(it.size) { idx -> - QuestionListItem( - data = it[idx], - onClick = { movieToSelectAnswer(it[idx].id) }) + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED){ + viewModel.uiState.collect { + when(it) { + is UiState.Success -> { + if (it.data.isNotEmpty()) { + setThemeSelected() + binding.constraintQuestion.visibility = View.VISIBLE + binding.textviewQuestionCount.text = String.format( + resources.getString(R.string.question_title_question_count), + it.data.size + ) + binding.composeviewQuestion.apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + LazyColumn(modifier = Modifier.fillMaxSize()) { + items(it.data.size) { idx -> + QuestionListItem( + data = it.data[idx], + onClick = { movieToSelectAnswer(it.data[idx].id) }) + } + } + } + } } } + else -> { + binding.constraintQuestion.visibility = View.GONE + Toast.makeText(context, it.toString(), Toast.LENGTH_SHORT).show() + } } } } - else { - Toast.makeText(context,"질문을 준비중이에요!",Toast.LENGTH_SHORT).show() - } } + } private fun setThemeClickListener(){ diff --git a/app/src/main/java/com/example/com_us/ui/question/list/AllQuestionListViewModel.kt b/app/src/main/java/com/example/com_us/ui/question/list/AllQuestionListViewModel.kt index 2722a24..e2b71e8 100644 --- a/app/src/main/java/com/example/com_us/ui/question/list/AllQuestionListViewModel.kt +++ b/app/src/main/java/com/example/com_us/ui/question/list/AllQuestionListViewModel.kt @@ -6,15 +6,19 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.example.com_us.R import com.example.com_us.data.default_repository.NetworkError +import com.example.com_us.data.model.home.ResponseHomeDataDto +import com.example.com_us.data.model.question.response.question.ResponseAnswerDetailWithDateDto import com.example.com_us.data.model.question.response.question.ResponseQuestionDto import com.example.com_us.data.repository.QuestionRepository -import com.example.com_us.ui.question.sign.QuestionUiState +import com.example.com_us.ui.UiState 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 AllQuestionListViewModel @Inject constructor( private val questionRepository: QuestionRepository @@ -30,8 +34,8 @@ class AllQuestionListViewModel @Inject constructor( // ui 상태 변수 - private val _uiState = MutableStateFlow(QuestionUiState.Initial) - val uiState: StateFlow = _uiState + private val _uiState = MutableStateFlow>>(UiState.Initial) + val uiState = _uiState.asStateFlow() // 선택한 테마의 id @@ -51,10 +55,8 @@ class AllQuestionListViewModel @Inject constructor( fun loadQuestionListByCate(category: String){ viewModelScope.launch { questionRepository.getQuestionListByCate(category) - .onSuccess { - _uiState.value = QuestionUiState.Success(it) - _questionListByCate.value = it + _uiState.value = UiState.Success(it) } .onFailure { val errorMessage = when(it){ @@ -63,7 +65,7 @@ class AllQuestionListViewModel @Inject constructor( else -> "알 수 없는 에러가 발생했습니다. 다시 시도해주세요!" } if (errorMessage != null) { - _uiState.value = QuestionUiState.Error(errorMessage) + _uiState.value = UiState.Error(errorMessage) } } } } diff --git a/app/src/main/java/com/example/com_us/ui/question/previous/PreviousAnswerActivity.kt b/app/src/main/java/com/example/com_us/ui/question/previous/PreviousAnswerActivity.kt index 94a47bc..b95bacd 100644 --- a/app/src/main/java/com/example/com_us/ui/question/previous/PreviousAnswerActivity.kt +++ b/app/src/main/java/com/example/com_us/ui/question/previous/PreviousAnswerActivity.kt @@ -3,18 +3,24 @@ package com.example.com_us.ui.question.previous import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.MenuItem +import android.widget.Toast import androidx.activity.viewModels import androidx.compose.foundation.layout.Row import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.example.com_us.R import com.example.com_us.data.model.question.response.question.Answer import com.example.com_us.databinding.ActivityQuestionPreviousAnswerBinding +import com.example.com_us.ui.UiState import com.example.com_us.ui.compose.AnswerHistoryItem import com.example.com_us.ui.compose.AnswerTypeTag import com.example.com_us.util.ColorMatch import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch // 이전 답변을 보여주는 화면 @AndroidEntryPoint @@ -38,11 +44,23 @@ class PreviousAnswerActivity : AppCompatActivity() { println(questionId) if(questionId > -1) viewModel.loadPreviousAnswer(questionId) - viewModel.answerPrevious.observe(this) { - setQuestion(it.question.questionCount, it.question.questionContent) - setQuestionTypeCompose(it.question.category, it.question.answerType) - setComposeList(it.answerList) - } + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED){ + viewModel.uiState.collect{ + when(it){ + is UiState.Success -> { + setQuestion(it.data.question.questionCount, it.data.question.questionContent) + setQuestionTypeCompose(it.data.question.category, it.data.question.answerType) + setComposeList(it.data.answerList) + } + else -> + Toast.makeText(this@PreviousAnswerActivity, it.toString(), Toast.LENGTH_SHORT).show() + + } + } + + } + } } private fun setActionBar() { diff --git a/app/src/main/java/com/example/com_us/ui/question/previous/PreviousAnswerViewModel.kt b/app/src/main/java/com/example/com_us/ui/question/previous/PreviousAnswerViewModel.kt index a3ac535..a9fd0f2 100644 --- a/app/src/main/java/com/example/com_us/ui/question/previous/PreviousAnswerViewModel.kt +++ b/app/src/main/java/com/example/com_us/ui/question/previous/PreviousAnswerViewModel.kt @@ -5,17 +5,31 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.example.com_us.data.default_repository.NetworkError +import com.example.com_us.data.model.question.response.question.ResponseAnswerDetailDto import com.example.com_us.data.model.question.response.question.ResponsePreviousAnswerDto +import com.example.com_us.data.model.question.response.question.ResponseQuestionDto import com.example.com_us.data.repository.QuestionRepository +import com.example.com_us.ui.UiState 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 okhttp3.Response import javax.inject.Inject + + @HiltViewModel class PreviousAnswerViewModel @Inject constructor( private val questionRepository: QuestionRepository ) : ViewModel(){ + // ui 상태 변수 + private val _uiState = MutableStateFlow>(UiState.Initial) + val uiState= _uiState.asStateFlow() + private val _answerPrevious = MutableLiveData() val answerPrevious: LiveData = _answerPrevious @@ -23,10 +37,17 @@ class PreviousAnswerViewModel @Inject constructor( viewModelScope.launch { questionRepository.getPreviousAnswer(questionId) .onSuccess { - _answerPrevious.value = it + _uiState.value = UiState.Success(it) } .onFailure { - Log.d("GET: [ANSWER DETAIL]", it.toString()) + val errorMessage = when(it){ + is NetworkError.NetworkException -> {it.message} + is NetworkError.NullDataError -> "데이터가 없어요" + else -> "알 수 없는 에러가 발생했어요. 다시 시도해주세요!" + } + if (errorMessage != null) { + _uiState.value = UiState.Error(errorMessage) + } } } } diff --git a/app/src/main/java/com/example/com_us/ui/question/result/ResultAfterSignActivity.kt b/app/src/main/java/com/example/com_us/ui/question/result/ResultAfterSignActivity.kt index d73eae5..1ecfb7c 100644 --- a/app/src/main/java/com/example/com_us/ui/question/result/ResultAfterSignActivity.kt +++ b/app/src/main/java/com/example/com_us/ui/question/result/ResultAfterSignActivity.kt @@ -10,6 +10,7 @@ import com.example.com_us.MainActivity import com.example.com_us.R import com.example.com_us.data.model.question.response.question.ResponseAnswerDetailDto import com.example.com_us.databinding.ActivityQuestionResultBinding +import com.example.com_us.ui.question.block.CollectBlockActivity import com.example.com_us.util.QuestionManager import dagger.hilt.android.AndroidEntryPoint @@ -18,6 +19,7 @@ import dagger.hilt.android.AndroidEntryPoint class ResultAfterSignActivity : AppCompatActivity() { private lateinit var binding: ActivityQuestionResultBinding + private lateinit var category : String private var videoPlayCount: MutableLiveData = MutableLiveData(0) @@ -35,6 +37,7 @@ class ResultAfterSignActivity : AppCompatActivity() { if(!QuestionManager.answerDate.isNullOrEmpty()) { binding.textviewResultDate.text = QuestionManager.answerDate } + category = intent.getStringExtra("category").toString() setActionBar() setCompleteButton() @@ -91,7 +94,8 @@ class ResultAfterSignActivity : AppCompatActivity() { private fun setCompleteButton() { binding.buttonResultComplete.setOnClickListener{ QuestionManager.reset() - val intent = Intent(this, MainActivity::class.java) + // 메인 화면으로 이동 + val intent = Intent(this,MainActivity::class.java) startActivity(intent) finish() } diff --git a/app/src/main/java/com/example/com_us/ui/question/result/ResultBeforeSignActivity.kt b/app/src/main/java/com/example/com_us/ui/question/result/ResultBeforeSignActivity.kt index 4b3f354..89296fb 100644 --- a/app/src/main/java/com/example/com_us/ui/question/result/ResultBeforeSignActivity.kt +++ b/app/src/main/java/com/example/com_us/ui/question/result/ResultBeforeSignActivity.kt @@ -1,5 +1,6 @@ package com.example.com_us.ui.question.result +import android.content.Intent import android.net.Uri import android.os.Bundle import android.view.MenuItem @@ -16,7 +17,7 @@ import dagger.hilt.android.AndroidEntryPoint // 답변 선택 시 처음으로 이동하는 화면 (질문, 답변 , 수형 영상, 따라해보기 버튼) @AndroidEntryPoint -class ResultBeforeSignActivity : AppCompatActivity() { +class ResultBeforeSignActivity : AppCompatActivity(){ private lateinit var binding: ActivityQuestionCheckAnswerBinding private val viewModel: ResultViewModel by viewModels() @@ -106,7 +107,14 @@ class ResultBeforeSignActivity : AppCompatActivity() { } private fun moveToFollowAlongDialog() { videoPlayCount.value = -1 - val dialog = SignAnswerDialog.newInstance(question, answer, category) + + viewModel.answerDetail.value + val dialog = viewModel.answerDetail.value?.let { + SignAnswerDialog.newInstance(question, answer, category, + it + ) + } + print(viewModel.answerDetail.value) // 다이얼로그가 뜨면 아래 내용 질문만 희미하게 보이게 하기 binding.textView8.visibility = View.GONE @@ -115,11 +123,12 @@ class ResultBeforeSignActivity : AppCompatActivity() { binding.textviewAnswerDescrp.visibility = View.GONE binding.buttonAnswerFollowalong.visibility = View.GONE binding.videoviewAnswerSign.visibility = View.GONE - dialog.isCancelable = false + dialog?.isCancelable = false - dialog.show(supportFragmentManager, "FollowAlongDialog") + dialog?.show(supportFragmentManager, "FollowAlongDialog") } + // override fun onServerSuccess() { // } // diff --git a/app/src/main/java/com/example/com_us/ui/question/select/SelectAnswerActivity.kt b/app/src/main/java/com/example/com_us/ui/question/select/SelectAnswerActivity.kt index 483fe21..752c247 100644 --- a/app/src/main/java/com/example/com_us/ui/question/select/SelectAnswerActivity.kt +++ b/app/src/main/java/com/example/com_us/ui/question/select/SelectAnswerActivity.kt @@ -9,8 +9,10 @@ import androidx.activity.viewModels import androidx.compose.foundation.layout.Row import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.core.content.ContextCompat +import androidx.lifecycle.lifecycleScope import com.example.com_us.R import com.example.com_us.databinding.ActivityQuestionDetailBinding +import com.example.com_us.ui.UiState import com.example.com_us.util.ColorMatch import com.example.com_us.ui.compose.AnswerOptionList import com.example.com_us.ui.compose.AnswerTypeTag @@ -18,6 +20,7 @@ import com.example.com_us.ui.question.result.ResultBeforeSignActivity import com.example.com_us.ui.question.previous.PreviousAnswerActivity import com.example.com_us.util.QuestionManager import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch // 질문에 대한 답변을 선택하는 화면 @AndroidEntryPoint @@ -57,20 +60,29 @@ class SelectAnswerActivity : AppCompatActivity() { } private fun setQuestionDetail() { - viewModel.questionDetail.observe(this) { - if(it != null) { - binding.constraintQuestionDetail.visibility = View.VISIBLE - binding.textviewDetailQuestion.text = it.question.questionContent - question = it.question.questionContent - category = it.question.category - setQuestionTypeCompose(it.question.answerType) - setQuestionAnswerOptionCompose(it.answerList) - } - else{ - Toast.makeText(this, getString(R.string.question_detail_msg_when_server_failure), Toast.LENGTH_SHORT).show() - finish() - } - } + lifecycleScope.launch { + viewModel.uiState.collect { + if (it is UiState.Success) { + binding.constraintQuestionDetail.visibility = View.VISIBLE + binding.textviewDetailQuestion.text = it.data.question.questionContent + question = it.data.question.questionContent + category = it.data.question.category + setQuestionTypeCompose(it.data.question.answerType) + setQuestionAnswerOptionCompose(it.data.answerList) + } + + if (it is UiState.Error) { + Toast.makeText( + this@SelectAnswerActivity, + it.message, + Toast.LENGTH_SHORT + ).show() + finish() + } + } + + } + } private fun setQuestionTypeCompose(answerType: String) { diff --git a/app/src/main/java/com/example/com_us/ui/question/select/SelectAnswerViewModel.kt b/app/src/main/java/com/example/com_us/ui/question/select/SelectAnswerViewModel.kt index 047c4f0..7a4d179 100644 --- a/app/src/main/java/com/example/com_us/ui/question/select/SelectAnswerViewModel.kt +++ b/app/src/main/java/com/example/com_us/ui/question/select/SelectAnswerViewModel.kt @@ -1,6 +1,5 @@ package com.example.com_us.ui.question.select -import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel @@ -8,7 +7,10 @@ import androidx.lifecycle.viewModelScope import com.example.com_us.data.default_repository.NetworkError import com.example.com_us.data.model.question.response.question.ResponseQuestionDetailDto import com.example.com_us.data.repository.QuestionRepository +import com.example.com_us.ui.UiState import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import javax.inject.Inject @@ -17,6 +19,10 @@ class SelectAnswerViewModel @Inject constructor( private val questionRepository: QuestionRepository ): ViewModel() { + // ui 상태 변수 + private val _uiState = MutableStateFlow>(UiState.Initial) + val uiState: StateFlow> = _uiState + private val _questionDetail = MutableLiveData() val questionDetail: LiveData = _questionDetail @@ -32,18 +38,17 @@ class SelectAnswerViewModel @Inject constructor( viewModelScope.launch { questionRepository.getQuestionDetail(questionId) .onSuccess { - _questionDetail.value = it + _uiState.value = UiState.Success(it) } .onFailure { - when { - it is NetworkError.NetworkException -> { - Log.d("GET: [QUESTION DETAIL]", it.toString()) - - } - it is NetworkError.NullDataError -> { - Log.d("GET: [QUESTION DETAIL]", it.toString()) - } + println(it) + val errorMessage = when(it) { + is NetworkError.ApiError -> { it.message } + is NetworkError.NullDataError -> { "데이터가 없어요" } + is NetworkError.NetworkException -> { "잠시 후에 다시 시도해주세요" } + else -> "알 수 없는 에러가 발생했어요. 다시 시도해주세요!" } + _uiState.value = UiState.Error(errorMessage) } } } diff --git a/app/src/main/java/com/example/com_us/ui/question/sign/SignAnswerDialog.kt b/app/src/main/java/com/example/com_us/ui/question/sign/SignAnswerDialog.kt index 9bc7ed6..3a31e19 100644 --- a/app/src/main/java/com/example/com_us/ui/question/sign/SignAnswerDialog.kt +++ b/app/src/main/java/com/example/com_us/ui/question/sign/SignAnswerDialog.kt @@ -1,55 +1,46 @@ package com.example.com_us.ui.question.sign +import android.content.Context import android.content.Intent import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.net.Uri import android.os.Bundle +import android.util.Printer import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Toast import androidx.fragment.app.DialogFragment import androidx.fragment.app.viewModels import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.viewModelScope import com.example.com_us.data.model.question.response.question.ResponseAnswerDetailDto import com.example.com_us.databinding.DialogQuestionFollowAlongBinding import com.example.com_us.ui.question.block.CollectBlockActivity +import com.example.com_us.ui.question.result.ResultAfterSignActivity import com.example.com_us.util.QuestionManager import dagger.hilt.android.AndroidEntryPoint - - -private const val ARG_PARAM_QUESTION = "paramQuestion" -private const val ARG_PARAM_ANSWER = "paramAnswer" -private const val ARG_PARAM_CATEGORY = "paramCategory" -private const val ARG_PARAM_SIGNDATA = "paramSignData" - - +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlin.math.sign // 수형 따라해보기 화면 @AndroidEntryPoint -class SignAnswerDialog : DialogFragment() { - // TODO: Rename and change types of parameters - private lateinit var paramQuestion: String - private lateinit var paramAnswer: String - private lateinit var paramCategory: String - +class SignAnswerDialog( + private val question : String, + private val answer : String, + private val category : String, + private val signDescription: List, +) : DialogFragment() { private lateinit var signData: List - - private var videoPlayCount: MutableLiveData = MutableLiveData(0) + private var index = 0 private var _binding: DialogQuestionFollowAlongBinding? = null private val binding get() = _binding!! private val viewModel: SignAnswerViewModel by viewModels() - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - arguments?.let { - paramQuestion = it.getString(ARG_PARAM_QUESTION)!! - paramAnswer = it.getString(ARG_PARAM_ANSWER)!! - paramCategory = it.getString(ARG_PARAM_CATEGORY)!! - //paramSignData = it.getSerializable(ARG_PARAM_SIGNDATA) as List - } - } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -58,43 +49,36 @@ class SignAnswerDialog : DialogFragment() { _binding = DialogQuestionFollowAlongBinding.inflate(inflater, container, false) val view = binding.root dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) - signData = QuestionManager.signLanguageInfo - + signData = signDescription return view } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + setSignDetail(0) - binding.linearProgressIndicator.progress = 50 - binding.textviewFollowdialogQuestion.text = paramQuestion - binding.textviewFollowdialogAnswer.text = paramAnswer - - // 완료하기 버튼 클릭 시 - binding.buttonAnswerComplete.setOnClickListener { - val questionId = QuestionManager.questionId - if(questionId > 0 && paramAnswer.isNotEmpty()) viewModel.postAnswer(questionId, paramAnswer) - - setAnswerDetail() - getAnswerDate() - - videoPlayCount.observe(this) { - val intent = Intent(activity, CollectBlockActivity::class.java) - intent.putExtra("category", paramCategory) + binding.btnCompleteWithoutBlock.setOnClickListener { + val intent = Intent(context,ResultAfterSignActivity::class.java) + startActivity(intent) + } + binding.buttonNextStep.setOnClickListener { + if (viewModel.signIndex.value == signData.lastIndex) { + val intent = Intent(context,CollectBlockActivity::class.java) + intent.putExtra("category",category) startActivity(intent) - activity?.finish() - if(it >= 0) setSignDetail(it) } - + else { + index +=1 + viewModel.setSignIndex(index) + setSignDetail(index) + } } - - // - + binding.linearProgressIndicator.progress = 50 + binding.textviewFollowdialogQuestion.text = question } override fun onStart() { super.onStart() - dialog?.window?.setLayout( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT @@ -102,14 +86,9 @@ class SignAnswerDialog : DialogFragment() { } companion object { - fun newInstance(paramQuestion: String, paramAnswer: String, paramCategory: String) = - SignAnswerDialog().apply { - arguments = Bundle().apply { - putString(ARG_PARAM_QUESTION, paramQuestion) - putString(ARG_PARAM_ANSWER, paramAnswer) - putString(ARG_PARAM_CATEGORY, paramCategory) - //putSerializable(ARG_PARAM_SIGNDATA, ArrayList(paramSignData)) - } + fun newInstance(paramQuestion: String, paramAnswer: String, paramCategory: String,signDescription: List) = + SignAnswerDialog(paramQuestion,paramAnswer,paramCategory, signDescription).apply { + arguments = Bundle() } } @@ -118,27 +97,9 @@ class SignAnswerDialog : DialogFragment() { _binding = null } - private fun setAnswerDetail() { - binding.textviewFollowdialogQuestion.text = paramQuestion - var repeatCount = 0 - if(signData.size > 1) { - binding.videoviewFollowdialogSign.setOnCompletionListener { - if(videoPlayCount.value!! >= signData.size-1){ - if(++repeatCount > 3) { - videoPlayCount.value = -1 - repeatCount = 0 - } else { - videoPlayCount.value = 0 - } - } else - videoPlayCount.value = videoPlayCount.value?.plus(1) - - } - } - } - + // todo: 역할이 뭐지? private fun getAnswerDate() { viewModel.resultData.observe(this) { if(it != null) QuestionManager.answerDate = it.answerDate @@ -146,6 +107,11 @@ class SignAnswerDialog : DialogFragment() { } private fun setSignDetail(signIdx: Int) { + if (signIdx > signData.lastIndex) return + if (viewModel.signIndex.value == signData.lastIndex) { + binding.buttonNextStep.text = "완료하기" + binding.btnCompleteWithoutBlock.visibility = View.GONE + } binding.textviewFollowdialogAnswer.text = signData[signIdx].signLanguageName binding.videoviewFollowdialogSign.setVideoURI(Uri.parse(signData[signIdx].signLanguageVideoUrl)) binding.videoviewFollowdialogSign.start() diff --git a/app/src/main/java/com/example/com_us/ui/question/sign/SignAnswerViewModel.kt b/app/src/main/java/com/example/com_us/ui/question/sign/SignAnswerViewModel.kt index 2b1feb2..a5097b0 100644 --- a/app/src/main/java/com/example/com_us/ui/question/sign/SignAnswerViewModel.kt +++ b/app/src/main/java/com/example/com_us/ui/question/sign/SignAnswerViewModel.kt @@ -1,48 +1,54 @@ package com.example.com_us.ui.question.sign -import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.example.com_us.data.default_repository.NetworkError import com.example.com_us.data.repository.QuestionRepository import com.example.com_us.data.model.question.request.RequestAnswerRequest import com.example.com_us.data.model.question.response.question.ResponseAnswerDetailWithDateDto -import com.example.com_us.data.model.question.response.question.ResponseQuestionDto +import com.example.com_us.ui.UiState import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import javax.inject.Inject -// Ui 상태 정의 -sealed class QuestionUiState { - data object Initial : QuestionUiState() - data class Success(val data : List) : QuestionUiState() - data class Error(val message : String) : QuestionUiState() - -} - @HiltViewModel class SignAnswerViewModel @Inject constructor( private val questionRepository: QuestionRepository, ) : ViewModel() { + private val _signIndex = MutableStateFlow(0) + val signIndex = _signIndex.asStateFlow() + private val _uiState = MutableStateFlow>(UiState.Initial) + val uiState= _uiState.asStateFlow() private val _resultData = MutableLiveData() val resultData: LiveData = _resultData - - + fun setSignIndex(index : Int ) { + _signIndex.value = index + } +// todo : fun postAnswer(questionId: Long, answerContent: String){ - var body = RequestAnswerRequest(questionId, answerContent) + val body = RequestAnswerRequest(questionId, answerContent) viewModelScope.launch { questionRepository.postAnswer(body) .onSuccess { - _resultData.value = it + _uiState.value = UiState.Success(it) } .onFailure { - Log.d("POST: [ANSWER]", it.toString()) + val errorMessage = when(it) { + is NetworkError.NetworkException -> { "네트워크 에러가 발생했어요! 잠시 후에 다시 시도해주세에요"} + is NetworkError.ApiError ->{it.message} + is NetworkError.NullDataError -> {"데이터를 준비하고 있어요!"} + else -> { "잠시 후에 다시 시도해주세요"} + } + _uiState.value = UiState.Error(errorMessage) } } } diff --git a/app/src/main/java/com/example/com_us/ui/question/theme/ThemeQuestionListActivity.kt b/app/src/main/java/com/example/com_us/ui/question/theme/ThemeQuestionListActivity.kt index 98cf4c2..6814927 100644 --- a/app/src/main/java/com/example/com_us/ui/question/theme/ThemeQuestionListActivity.kt +++ b/app/src/main/java/com/example/com_us/ui/question/theme/ThemeQuestionListActivity.kt @@ -4,15 +4,21 @@ import android.content.Intent import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.MenuItem +import android.widget.Toast import androidx.activity.viewModels import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.example.com_us.R import com.example.com_us.databinding.ActivityThemeQuestionListBinding +import com.example.com_us.ui.UiState import com.example.com_us.ui.question.select.SelectAnswerActivity import com.example.com_us.ui.compose.QuestionListItem import com.example.com_us.ui.question.list.AllQuestionListViewModel import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch @AndroidEntryPoint class ThemeQuestionListActivity : AppCompatActivity() { @@ -36,25 +42,35 @@ class ThemeQuestionListActivity : AppCompatActivity() { binding.textviewTitle.text = String.format(resources.getString(R.string.theme_question_list_title), themeKor) setActionBar() setComposeList() - - } + private fun setComposeList() { - viewModel.questionListByCate.observe(this) { - binding.composeviewTheme.apply { - setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) - setContent { - LazyColumn { - items(it.size) { idx -> - QuestionListItem(data = it[idx], onClick = { moveToQuestionDetail(it[idx].id) }) + lifecycleScope.launch { + viewModel.uiState.collect { + when (it) { + is UiState.Success -> { + binding.composeviewTheme.apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + LazyColumn { + println(it.data) + items(it.data.size) { idx -> + QuestionListItem( + data = it.data[idx], + onClick = { moveToQuestionDetail(it.data[idx].id) }) + } + } + } } } + else -> { + Toast.makeText(this@ThemeQuestionListActivity,it.toString(),Toast.LENGTH_SHORT).show() + } } } } } - private fun setActionBar() { setSupportActionBar(binding.includeToolbar.toolbar) val actionBar = supportActionBar diff --git a/app/src/main/java/com/example/com_us/ui/question/theme/ThemeQuestionListViewModel.kt b/app/src/main/java/com/example/com_us/ui/question/theme/ThemeQuestionListViewModel.kt index 9ab725e..53d642f 100644 --- a/app/src/main/java/com/example/com_us/ui/question/theme/ThemeQuestionListViewModel.kt +++ b/app/src/main/java/com/example/com_us/ui/question/theme/ThemeQuestionListViewModel.kt @@ -6,15 +6,19 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.example.com_us.R import com.example.com_us.data.default_repository.NetworkError +import com.example.com_us.data.model.question.response.question.ResponsePreviousAnswerDto import com.example.com_us.data.model.question.response.question.ResponseQuestionDto import com.example.com_us.data.repository.QuestionRepository -import com.example.com_us.ui.question.sign.QuestionUiState +import com.example.com_us.ui.UiState 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 ThemeQuestionListViewModel @Inject constructor( private val questionRepository: QuestionRepository @@ -24,6 +28,9 @@ class ThemeQuestionListViewModel @Inject constructor( val questionListByCate: LiveData> = _questionListByCate + // ui 상태 변수 + private val _uiState = MutableStateFlow>>(UiState.Initial) + val uiState = _uiState.asStateFlow() // 선택한 테마의 id private val _selectedThemeId = MutableLiveData().apply { @@ -37,9 +44,8 @@ class ThemeQuestionListViewModel @Inject constructor( fun loadQuestionListByCate(category: String){ viewModelScope.launch { questionRepository.getQuestionListByCate(category) - .onSuccess { - _questionListByCate.value = it + _uiState.value = UiState.Success(it) } .onFailure { val errorMessage = when(it){ @@ -48,7 +54,7 @@ class ThemeQuestionListViewModel @Inject constructor( else -> "알 수 없는 에러가 발생했습니다. 다시 시도해주세요!" } if (errorMessage != null) { - + _uiState.value = UiState.Error(errorMessage) } } } diff --git a/app/src/main/res/layout/dialog_question_follow_along.xml b/app/src/main/res/layout/dialog_question_follow_along.xml index 009eecc..8572a18 100644 --- a/app/src/main/res/layout/dialog_question_follow_along.xml +++ b/app/src/main/res/layout/dialog_question_follow_along.xml @@ -106,7 +106,7 @@ app:layout_constraintStart_toStartOf="parent" />