diff --git a/AnkiDroid/src/main/assets/scripts/ankidroid.js b/AnkiDroid/src/main/assets/scripts/ankidroid.js
index 9f213874a724..84baa7e585aa 100644
--- a/AnkiDroid/src/main/assets/scripts/ankidroid.js
+++ b/AnkiDroid/src/main/assets/scripts/ankidroid.js
@@ -23,11 +23,12 @@ globalThis.ankidroid.showAllHints = function () {
};
/**
- * @param {InputEvent} event - the oninput event of the type answer
+ * @param {KeyboardEvent} event - the onkeydown event of the type answer
*/
-globalThis.ankidroid.onTypeAnswerInput = function (event) {
- const encodedValue = encodeURIComponent(event.target.value);
- window.location.href = `ankidroid://typeinput/${encodedValue}`;
+globalThis.ankidroid.onTypeAnswerKeyDown = function (event) {
+ if (event.key === "Enter") {
+ window.location.href = `ankidroid://show-answer`;
+ }
};
document.addEventListener("focusin", event => {
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerFragment.kt
index d6c68cec2989..97146928ebe3 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerFragment.kt
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerFragment.kt
@@ -48,7 +48,6 @@ import androidx.core.view.WindowInsetsControllerCompat
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
-import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.FragmentContainerView
import androidx.fragment.app.commit
import androidx.fragment.app.viewModels
@@ -208,7 +207,7 @@ class ReviewerFragment :
}
}
- viewModel.statesMutationEval.collectIn(lifecycleScope) { eval ->
+ viewModel.statesMutationEvalFlow.collectIn(lifecycleScope) { eval ->
webView.evaluateJavascript(eval) {
viewModel.onStateMutationCallback()
}
@@ -288,7 +287,7 @@ class ReviewerFragment :
val typeAnswerContainer = view.findViewById(R.id.type_answer_container)
val typeAnswerEditText =
view.findViewById(R.id.type_answer_edit_text).apply {
- setOnEditorActionListener { editTextView, actionId, _ ->
+ setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
viewModel.onShowAnswer()
return@setOnEditorActionListener true
@@ -303,11 +302,9 @@ class ReviewerFragment :
insetsController.hide(WindowInsetsCompat.Type.ime())
}
}
- addTextChangedListener { editable ->
- viewModel.typedAnswer = editable?.toString() ?: ""
- }
}
+ val isHtmlTypeAnswerEnabled = Prefs.isHtmlTypeAnswerEnabled
lifecycleScope.launch {
val autoFocusTypeAnswer = Prefs.autoFocusTypeAnswer
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
@@ -317,7 +314,7 @@ class ReviewerFragment :
return@collect
}
- if (Prefs.isHtmlTypeAnswerEnabled) {
+ if (isHtmlTypeAnswerEnabled) {
webView.requestFocus()
webView.evaluateJavascript("document.getElementById('typeans').focus();", null)
return@collect
@@ -341,6 +338,22 @@ class ReviewerFragment :
viewModel.onCardUpdatedFlow.flowWithLifecycle(lifecycle).collectIn(lifecycleScope) {
typeAnswerEditText.text = null
}
+
+ viewModel.onTypedAnswerResultFlow
+ .flowWithLifecycle(lifecycle)
+ .collectIn(lifecycleScope) { request ->
+ if (isHtmlTypeAnswerEnabled) {
+ val script = """document.getElementById("typeans").value;"""
+ webView.evaluateJavascript(script) { callback ->
+ // the retuned string comes with surrounding `"`, so remove it once
+ val typedAnswer = callback.removeSurrounding("\"")
+ request.complete(typedAnswer)
+ }
+ } else {
+ val typedAnswer = typeAnswerEditText.text.toString()
+ request.complete(typedAnswer)
+ }
+ }
}
/** Chooses the input type based on whether the expected answer is a number or text */
@@ -719,7 +732,7 @@ class ReviewerFragment :
when (url.host) {
"focusin" -> webviewHasFocus = true
"focusout" -> webviewHasFocus = false
- "typeinput" -> url.path?.substring(1)?.let { viewModel.typedAnswer = it }
+ "show-answer" -> viewModel.onShowAnswer()
}
true
}
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerViewModel.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerViewModel.kt
index cd722fc61234..f2515daa3c10 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerViewModel.kt
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerViewModel.kt
@@ -70,6 +70,7 @@ import kotlinx.coroutines.Deferred
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.withTimeoutOrNull
import org.intellij.lang.annotations.Language
import timber.log.Timber
@@ -96,6 +97,7 @@ class ReviewerViewModel :
val redoLabelFlow = MutableStateFlow(null)
val countsFlow = MutableStateFlow(Counts() to Counts.Queue.NEW)
val typeAnswerFlow = MutableStateFlow(null)
+ val onTypedAnswerResultFlow = MutableSharedFlow>()
val onCardUpdatedFlow = MutableSharedFlow()
val destinationFlow = MutableSharedFlow()
val editNoteTagsFlow = MutableSharedFlow()
@@ -106,11 +108,11 @@ class ReviewerViewModel :
val whiteboardEnabledFlow = MutableStateFlow(false)
val replayVoiceFlow = MutableSharedFlow()
val timeBoxReachedFlow = MutableSharedFlow()
+ val statesMutationEvalFlow = MutableSharedFlow()
override val server: AnkiServer = AnkiServer(this, StudyScreenRepository.getServerPort()).also { it.start() }
private val stateMutationKey = TimeManager.time.intTimeMS().toString()
- val statesMutationEval = MutableSharedFlow()
- var typedAnswer = ""
+ private var typedAnswer = ""
private val autoAdvance = AutoAdvance(this)
private val isHtmlTypeAnswerEnabled = Prefs.isHtmlTypeAnswerEnabled
@@ -185,6 +187,17 @@ class ReviewerViewModel :
while (!statesMutated) {
delay(50)
}
+
+ val typedAnswerResult = CompletableDeferred()
+ if (typeAnswerFlow.value != null) {
+ onTypedAnswerResultFlow.emit(typedAnswerResult)
+ } else {
+ typedAnswerResult.complete("")
+ }
+ typedAnswer = withTimeoutOrNull(1000L) {
+ typedAnswerResult.await()
+ } ?: ""
+
updateNextTimes()
showAnswer()
loadAndPlayMedia(CardSide.ANSWER)
@@ -415,7 +428,7 @@ class ReviewerViewModel :
return
}
statesMutated = false
- statesMutationEval.emit(
+ statesMutationEvalFlow.emit(
"anki.mutateNextCardStates('$stateMutationKey', async (states, customData, ctx) => { $js });",
)
}
@@ -560,7 +573,7 @@ class ReviewerViewModel :
val repl =
"""
-
""".trimIndent()