From 6bc815fcd04a5d3c63aca36a08bd01064a747c99 Mon Sep 17 00:00:00 2001 From: Aleksandr Zhukov Date: Wed, 18 Oct 2023 14:53:43 +0400 Subject: [PATCH] Implement input editing --- .../FillBlanksStepQuizFormDelegate.kt | 34 ++++++- .../dialog/FillBlanksInputDialogFragment.kt | 94 +++++++++++++++++++ .../fragment/FillBlanksQuizFragment.kt | 20 +++- ...> bg_step_quiz_fill_blanks_input_item.xml} | 0 ...step_quiz_fill_blanks_input_text_field.xml | 9 ++ .../res/layout/fragment_fill_blanks_input.xml | 31 ++++++ .../item_step_quiz_fill_blanks_input.xml | 2 +- .../src/main/res/values/dimens.xml | 2 + 8 files changed, 186 insertions(+), 6 deletions(-) create mode 100644 androidHyperskillApp/src/main/java/org/hyperskill/app/android/step_quiz_fill_blanks/dialog/FillBlanksInputDialogFragment.kt rename androidHyperskillApp/src/main/res/drawable/{bg_step_quiz_fill_blanks_input.xml => bg_step_quiz_fill_blanks_input_item.xml} (100%) create mode 100644 androidHyperskillApp/src/main/res/drawable/bg_step_quiz_fill_blanks_input_text_field.xml create mode 100644 androidHyperskillApp/src/main/res/layout/fragment_fill_blanks_input.xml diff --git a/androidHyperskillApp/src/main/java/org/hyperskill/app/android/step_quiz_fill_blanks/delegate/FillBlanksStepQuizFormDelegate.kt b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/step_quiz_fill_blanks/delegate/FillBlanksStepQuizFormDelegate.kt index b4cc114aaf..522a23608e 100644 --- a/androidHyperskillApp/src/main/java/org/hyperskill/app/android/step_quiz_fill_blanks/delegate/FillBlanksStepQuizFormDelegate.kt +++ b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/step_quiz_fill_blanks/delegate/FillBlanksStepQuizFormDelegate.kt @@ -2,10 +2,13 @@ package org.hyperskill.app.android.step_quiz_fill_blanks.delegate import android.widget.TextView import androidx.core.view.updateLayoutParams +import androidx.fragment.app.FragmentManager +import androidx.recyclerview.widget.RecyclerView import com.google.android.flexbox.FlexboxLayoutManager import org.hyperskill.app.android.R import org.hyperskill.app.android.databinding.LayoutStepQuizFillBlanksBindingBinding import org.hyperskill.app.android.step_quiz.view.delegate.StepQuizFormDelegate +import org.hyperskill.app.android.step_quiz_fill_blanks.dialog.FillBlanksInputDialogFragment import org.hyperskill.app.step_quiz.domain.model.submissions.Reply import org.hyperskill.app.step_quiz.presentation.StepQuizFeature import org.hyperskill.app.step_quiz_fill_blanks.model.FillBlanksItem @@ -15,15 +18,19 @@ import org.hyperskill.app.step_quiz_fill_blanks.presentation.FillBlanksResolver import ru.nobird.android.ui.adapterdelegates.dsl.adapterDelegate import ru.nobird.android.ui.adapters.DefaultDelegateAdapter import ru.nobird.android.view.base.ui.extension.setTextIfChanged +import ru.nobird.android.view.base.ui.extension.showIfNotExists +import ru.nobird.app.core.model.mutate class FillBlanksStepQuizFormDelegate( - private val binding: LayoutStepQuizFillBlanksBindingBinding + private val binding: LayoutStepQuizFillBlanksBindingBinding, + private val fragmentManager: FragmentManager, + private val onQuizChanged: (Reply) -> Unit ) : StepQuizFormDelegate { private val fillBlanksAdapter = DefaultDelegateAdapter().apply { addDelegate(textAdapterDelegate()) addDelegate( - inputAdapterDelegate { _, _ -> } + inputAdapterDelegate(::onInputItemClick) ) } @@ -76,6 +83,15 @@ class FillBlanksStepQuizFormDelegate( } ) + fun onInputItemModified(index: Int, text: String) { + fillBlanksAdapter.items = fillBlanksAdapter.items.mutate { + val inputItem = get(index) as FillBlanksItem.Input + set(index, inputItem.copy(inputText = text)) + } + fillBlanksAdapter.notifyItemChanged(index) + onQuizChanged(createReply()) + } + private fun textAdapterDelegate() = adapterDelegate(R.layout.item_step_quiz_fill_blanks_text) { val textView = itemView as TextView @@ -87,14 +103,26 @@ class FillBlanksStepQuizFormDelegate( } } - private fun inputAdapterDelegate(onClick: (Int, String) -> Unit) = + private fun inputAdapterDelegate(onClick: (index: Int, String) -> Unit) = adapterDelegate(R.layout.item_step_quiz_fill_blanks_input) { val textView = itemView as TextView + textView.setOnClickListener { + val position = bindingAdapterPosition + if (position != RecyclerView.NO_POSITION) { + onClick(position, textView.text.toString()) + } + } onBind { inputItem -> textView.setTextIfChanged(inputItem.inputText ?: "") } } + private fun onInputItemClick(index: Int, text: String) { + FillBlanksInputDialogFragment + .newInstance(index, text) + .showIfNotExists(fragmentManager, FillBlanksInputDialogFragment.TAG) + } + private enum class ResolveState { NOT_RESOLVED, RESOLVE_SUCCEED, diff --git a/androidHyperskillApp/src/main/java/org/hyperskill/app/android/step_quiz_fill_blanks/dialog/FillBlanksInputDialogFragment.kt b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/step_quiz_fill_blanks/dialog/FillBlanksInputDialogFragment.kt new file mode 100644 index 0000000000..939e4f019a --- /dev/null +++ b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/step_quiz_fill_blanks/dialog/FillBlanksInputDialogFragment.kt @@ -0,0 +1,94 @@ +package org.hyperskill.app.android.step_quiz_fill_blanks.dialog + +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.inputmethod.EditorInfo +import android.view.inputmethod.InputMethodManager +import by.kirich1409.viewbindingdelegate.viewBinding +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import org.hyperskill.app.android.R +import org.hyperskill.app.android.databinding.FragmentFillBlanksInputBinding +import ru.nobird.android.view.base.ui.extension.argument + +class FillBlanksInputDialogFragment : BottomSheetDialogFragment() { + companion object { + const val TAG: String = "FillBlanksInputDialogFragment" + + private const val ARG_INDEX = "INDEX" + private const val ARG_TEXT = "TEXT" + + fun newInstance( + index: Int, + text: String + ): FillBlanksInputDialogFragment = + FillBlanksInputDialogFragment().apply { + this.index = index + this.text = text + } + } + + private var index: Int by argument() + private var text: String by argument() + + private val fillBlanksInputBinding: FragmentFillBlanksInputBinding by viewBinding( + FragmentFillBlanksInputBinding::bind + ) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setStyle(STYLE_NO_TITLE, R.style.TopCornersRoundedBottomSheetDialog) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? = + inflater.inflate(R.layout.fragment_fill_blanks_input, container, false) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + if (savedInstanceState != null) { + index = savedInstanceState.getInt(ARG_INDEX) + text = savedInstanceState.getString(ARG_TEXT) ?: return + } + with(fillBlanksInputBinding) { + fillBlanksInputField.append(text) + fillBlanksInputField.setOnEditorActionListener { _, actionId, _ -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + super.dismiss() + } + false + } + fillBlanksInputField.post { + fillBlanksInputField.requestFocus() + val inputMethodManager = + requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + inputMethodManager.showSoftInput(fillBlanksInputField, InputMethodManager.SHOW_IMPLICIT) + } + } + } + + override fun onPause() { + (parentFragment as? Callback) + ?.onSyncInputItemWithParent( + index = index, + text = fillBlanksInputBinding.fillBlanksInputField.text.toString() + ) + super.onPause() + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putInt(ARG_INDEX, index) + outState.putString(ARG_TEXT, text) + } + + interface Callback { + fun onSyncInputItemWithParent(index: Int, text: String) + } +} \ No newline at end of file diff --git a/androidHyperskillApp/src/main/java/org/hyperskill/app/android/step_quiz_fill_blanks/fragment/FillBlanksQuizFragment.kt b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/step_quiz_fill_blanks/fragment/FillBlanksQuizFragment.kt index 0acad51beb..39f7922854 100644 --- a/androidHyperskillApp/src/main/java/org/hyperskill/app/android/step_quiz_fill_blanks/fragment/FillBlanksQuizFragment.kt +++ b/androidHyperskillApp/src/main/java/org/hyperskill/app/android/step_quiz_fill_blanks/fragment/FillBlanksQuizFragment.kt @@ -8,10 +8,13 @@ import org.hyperskill.app.android.databinding.LayoutStepQuizFillBlanksBindingBin import org.hyperskill.app.android.step_quiz.view.delegate.StepQuizFormDelegate import org.hyperskill.app.android.step_quiz.view.fragment.DefaultStepQuizFragment import org.hyperskill.app.android.step_quiz_fill_blanks.delegate.FillBlanksStepQuizFormDelegate +import org.hyperskill.app.android.step_quiz_fill_blanks.dialog.FillBlanksInputDialogFragment import org.hyperskill.app.step.domain.model.Step import org.hyperskill.app.step.domain.model.StepRoute -class FillBlanksQuizFragment : DefaultStepQuizFragment() { +class FillBlanksQuizFragment : + DefaultStepQuizFragment(), + FillBlanksInputDialogFragment.Callback { companion object { fun newInstance( @@ -28,6 +31,8 @@ class FillBlanksQuizFragment : DefaultStepQuizFragment() { private val binding: LayoutStepQuizFillBlanksBindingBinding get() = requireNotNull(_binding) + private var fillBlanksStepQuizFormDelegate: FillBlanksStepQuizFormDelegate? = null + override val quizViews: Array get() = arrayOf(binding.stepQuizFillBlanksContainer) override val skeletonView: View @@ -42,10 +47,21 @@ class FillBlanksQuizFragment : DefaultStepQuizFragment() { } override fun createStepQuizFormDelegate(): StepQuizFormDelegate = - FillBlanksStepQuizFormDelegate(binding) + FillBlanksStepQuizFormDelegate( + binding = binding, + fragmentManager = childFragmentManager, + onQuizChanged = ::syncReplyState + ).also { + this.fillBlanksStepQuizFormDelegate = it + } override fun onDestroyView() { super.onDestroyView() _binding = null + fillBlanksStepQuizFormDelegate = null + } + + override fun onSyncInputItemWithParent(index: Int, text: String) { + fillBlanksStepQuizFormDelegate?.onInputItemModified(index, text) } } \ No newline at end of file diff --git a/androidHyperskillApp/src/main/res/drawable/bg_step_quiz_fill_blanks_input.xml b/androidHyperskillApp/src/main/res/drawable/bg_step_quiz_fill_blanks_input_item.xml similarity index 100% rename from androidHyperskillApp/src/main/res/drawable/bg_step_quiz_fill_blanks_input.xml rename to androidHyperskillApp/src/main/res/drawable/bg_step_quiz_fill_blanks_input_item.xml diff --git a/androidHyperskillApp/src/main/res/drawable/bg_step_quiz_fill_blanks_input_text_field.xml b/androidHyperskillApp/src/main/res/drawable/bg_step_quiz_fill_blanks_input_text_field.xml new file mode 100644 index 0000000000..f5768edba4 --- /dev/null +++ b/androidHyperskillApp/src/main/res/drawable/bg_step_quiz_fill_blanks_input_text_field.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/androidHyperskillApp/src/main/res/layout/fragment_fill_blanks_input.xml b/androidHyperskillApp/src/main/res/layout/fragment_fill_blanks_input.xml new file mode 100644 index 0000000000..b77591193d --- /dev/null +++ b/androidHyperskillApp/src/main/res/layout/fragment_fill_blanks_input.xml @@ -0,0 +1,31 @@ + + + + + + \ No newline at end of file diff --git a/androidHyperskillApp/src/main/res/layout/item_step_quiz_fill_blanks_input.xml b/androidHyperskillApp/src/main/res/layout/item_step_quiz_fill_blanks_input.xml index bcc9576a79..527d4cc66d 100644 --- a/androidHyperskillApp/src/main/res/layout/item_step_quiz_fill_blanks_input.xml +++ b/androidHyperskillApp/src/main/res/layout/item_step_quiz_fill_blanks_input.xml @@ -15,7 +15,7 @@ android:lines="1" android:ellipsize="end" android:gravity="center" - android:background="@drawable/bg_step_quiz_fill_blanks_input" + android:background="@drawable/bg_step_quiz_fill_blanks_input_item" android:paddingLeft="@dimen/step_quiz_fill_blanks_padding" android:paddingRight="@dimen/step_quiz_fill_blanks_padding" android:layout_marginTop="@dimen/step_quiz_fill_blanks_item_top_margin" diff --git a/androidHyperskillApp/src/main/res/values/dimens.xml b/androidHyperskillApp/src/main/res/values/dimens.xml index e725fcad44..f06230eade 100644 --- a/androidHyperskillApp/src/main/res/values/dimens.xml +++ b/androidHyperskillApp/src/main/res/values/dimens.xml @@ -85,6 +85,7 @@ 8dp + 8dp 32dp 48dp @@ -92,6 +93,7 @@ 8dp 16dp 8dp + 56dp 48dp