Skip to content

Commit

Permalink
só falta o hide ease
Browse files Browse the repository at this point in the history
  • Loading branch information
BrayanDSO committed Mar 23, 2024
1 parent 1447399 commit 9b81986
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class ReviewerTest : InstrumentedTest() {

@Test
@Flaky(os = OS.ALL, "Fails on CI with timing issues frequently")
fun testCustomSchedulerWithCustomData() {
fun testCustomSchedulerWithCustomData() { // replicar
col.cardStateCustomizer =
"""
states.good.normal.review.easeFactor = 3.0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import android.net.Uri
import android.os.Bundle
import android.view.View
import android.webkit.CookieManager
import android.webkit.JsResult
import android.webkit.WebChromeClient
import android.webkit.WebResourceError
import android.webkit.WebResourceRequest
Expand Down Expand Up @@ -49,6 +50,7 @@ import timber.log.Timber
abstract class CardViewerFragment(@LayoutRes layout: Int) : Fragment(layout) {
protected abstract val viewModel: CardViewerViewModel
protected abstract val webView: WebView
protected open val baseUrl = "http://${AnkiServer.LOCALHOST}/"

@CallSuper
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
Expand Down Expand Up @@ -83,7 +85,7 @@ abstract class CardViewerFragment(@LayoutRes layout: Int) : Fragment(layout) {
domStorageEnabled = true
}
loadDataWithBaseURL(
"http://${AnkiServer.LOCALHOST}/",
baseUrl,
stdHtml(requireContext(), Themes.currentTheme.isNightMode),
"text/html",
null,
Expand Down Expand Up @@ -133,22 +135,7 @@ abstract class CardViewerFragment(@LayoutRes layout: Int) : Fragment(layout) {
}

override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
val urlString = request.url.toString()
if (urlString.startsWith("playsound:")) {
viewModel.playSoundFromUrl(urlString)
return true
}
if (urlString.startsWith("tts-voices:")) {
TtsVoicesDialogFragment().show(childFragmentManager, null)
return true
}
try {
openUrl(request.url)
return true
} catch (_: Exception) {
Timber.w("Could not open url")
}
return false
return this@CardViewerFragment.shouldOverrideUrlLoading(request)
}

override fun onReceivedError(
Expand All @@ -163,8 +150,38 @@ abstract class CardViewerFragment(@LayoutRes layout: Int) : Fragment(layout) {
}
}

protected open fun shouldOverrideUrlLoading(request: WebResourceRequest): Boolean {
val urlString = request.url.toString()
if (urlString.startsWith("playsound:")) {
viewModel.playSoundFromUrl(urlString)
return true
}
if (urlString.startsWith("tts-voices:")) {
TtsVoicesDialogFragment().show(childFragmentManager, null)
return true
}
try {
openUrl(request.url)
return true
} catch (_: Exception) {
Timber.w("Could not open url")
}
return false
}

private fun onCreateWebChromeClient(): WebChromeClient {
return object : WebChromeClient() {

override fun onJsAlert(
view: WebView?,
url: String?,
message: String?,
result: JsResult?
): Boolean {
requireActivity().showSnackbar(message!!)
return super.onJsAlert(view, url, message, result)
}

private lateinit var customView: View

// used for displaying `<video>` in fullscreen.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import android.content.Intent
import android.os.Bundle
import android.view.MenuItem
import android.view.View
import android.webkit.WebResourceRequest
import android.webkit.WebView
import androidx.annotation.StringRes
import androidx.appcompat.view.menu.MenuBuilder
Expand Down Expand Up @@ -54,6 +55,8 @@ class ReviewerFragment :

override val webView: WebView
get() = requireView().findViewById(R.id.webview)
override val baseUrl: String
get() = viewModel.baseUrl()

override val baseSnackbarBuilder: SnackbarBuilder = {
anchorView = this@ReviewerFragment.view?.findViewById(R.id.buttons_area)
Expand Down Expand Up @@ -88,6 +91,12 @@ class ReviewerFragment :
}
}
}

viewModel.statesMutationEval.launchCollect(lifecycleScope) { eval ->
webView.evaluateJavascript(eval) { result ->
viewModel.onStateMutationCallback(result)
}
}
}

// TODO
Expand Down Expand Up @@ -172,6 +181,17 @@ class ReviewerFragment :
}
}

override fun shouldOverrideUrlLoading(request: WebResourceRequest): Boolean {
return if (super.shouldOverrideUrlLoading(request)) {
true
} else if (request.url.toString().startsWith("state-mutation-error:")) {
viewModel.onStateMutationError()
true
} else {
false
}
}

companion object {
fun getIntent(context: Context): Intent {
return CardViewerActivity.getIntent(context, ReviewerFragment::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import androidx.core.text.inSpans
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import anki.frontend.SetSchedulingStatesRequest
import com.ichi2.anki.AGAIN
import com.ichi2.anki.CollectionManager.withCol
import com.ichi2.anki.EASY
Expand All @@ -30,17 +31,23 @@ import com.ichi2.anki.HARD
import com.ichi2.anki.asyncIO
import com.ichi2.anki.cardviewer.SoundPlayer
import com.ichi2.anki.launchCatchingIO
import com.ichi2.anki.pages.AnkiServer
import com.ichi2.anki.pages.PostRequestHandler
import com.ichi2.anki.previewer.CardViewerViewModel
import com.ichi2.anki.reviewer.CardSide
import com.ichi2.libanki.sched.CurrentQueueState
import com.ichi2.libanki.undoableOp
import com.ichi2.libanki.utils.TimeManager
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow

class ReviewerViewModel(soundPlayer: SoundPlayer) : CardViewerViewModel(soundPlayer) {
class ReviewerViewModel(soundPlayer: SoundPlayer) :
CardViewerViewModel(soundPlayer),
PostRequestHandler {

private var queueState: CurrentQueueState? = null
private val stateMutationKey = TimeManager.time.intTimeMS().toString()
var isQueueFinishedFlow = MutableSharedFlow<Boolean>()

val againNextTime: MutableStateFlow<String?> = MutableStateFlow(null)
Expand All @@ -52,6 +59,24 @@ class ReviewerViewModel(soundPlayer: SoundPlayer) : CardViewerViewModel(soundPla
withCol { config.get("estTimes") ?: true }
}

private val server = AnkiServer(this).also { it.start() }
val statesMutationEval = MutableSharedFlow<String>()

/**
* A flag that determines if the SchedulingStates in CurrentQueueState are
* safe to persist in the database when answering a card. This is used to
* ensure that the custom JS scheduler has persisted its SchedulingStates
* back to the Reviewer before we save it to the database. If the custom
* scheduler has not been configured, then it is safe to immediately set
* this to true.
*
* This flag should be set to false when we show the front of the card
* and only set to true once we know the custom scheduler has finished its
* execution, or set to true immediately if the custom scheduler has not
* been configured.
*/
private var statesMutated = true

/* *********************************************************************************************
************************ Public methods: meant to be used by the View **************************
********************************************************************************************* */
Expand All @@ -68,6 +93,8 @@ class ReviewerViewModel(soundPlayer: SoundPlayer) : CardViewerViewModel(soundPla
}
}

fun baseUrl() = server.baseUrl()

fun showAnswer() {
launchCatchingIO {
showAnswerInternal()
Expand All @@ -81,6 +108,18 @@ class ReviewerViewModel(soundPlayer: SoundPlayer) : CardViewerViewModel(soundPla
fun answerGood() = answerCard(GOOD)
fun answerEasy() = answerCard(EASY)

fun onStateMutationCallback(result: String?) {
// eval failed, usually a syntax error
// Note, we get "null" (string) and not null
if (result == "null") {
statesMutated = true
}
}

fun onStateMutationError() {
statesMutated = true
}

/* *********************************************************************************************
*************************************** Internal methods ***************************************
********************************************************************************************* */
Expand All @@ -99,6 +138,11 @@ class ReviewerViewModel(soundPlayer: SoundPlayer) : CardViewerViewModel(soundPla
soundPlayer.playAllSoundsForSide(side)
}

override suspend fun showQuestion() {
super.showQuestion()
runStateMutationHook()
}

private suspend fun updateCurrentCard() {
queueState = withCol {
sched.currentQueueState()
Expand Down Expand Up @@ -127,6 +171,54 @@ class ReviewerViewModel(soundPlayer: SoundPlayer) : CardViewerViewModel(soundPla
easyNextTime.emit(easy)
}

override suspend fun handlePostRequest(uri: String, bytes: ByteArray): ByteArray {
return if (uri.startsWith(AnkiServer.ANKI_PREFIX)) {
when (uri.substring(AnkiServer.ANKI_PREFIX.length)) {
"getSchedulingStatesWithContext" -> getSchedulingStatesWithContext()
"setSchedulingStates" -> setSchedulingStates(bytes)
else -> byteArrayOf()
}
} else {
byteArrayOf()
}
}

private suspend fun runStateMutationHook() {
val state = queueState ?: return
val js = state.customSchedulingJs
if (js.isEmpty()) {
statesMutated = true
return
}
statesMutated = false
statesMutationEval.emit(
"""anki.mutateNextCardStates('$stateMutationKey', async (states, customData, ctx) => {{ $js }})
.catch(err => console.log(err));"""
)
}

private fun getSchedulingStatesWithContext(): ByteArray {
val state = queueState ?: return ByteArray(0)
return state.schedulingStatesWithContext().toBuilder()
.mergeStates(
state.states.toBuilder().mergeCurrent(
state.states.current.toBuilder()
.setCustomData(state.topCard.toBackendCard().customData).build()
).build()
)
.build()
.toByteArray()
}

private fun setSchedulingStates(bytes: ByteArray): ByteArray {
val state = queueState ?: return ByteArray(0)
val req = SetSchedulingStatesRequest.parseFrom(bytes)
if (req.key == stateMutationKey) {
state.states = req.states
}
return ByteArray(0)
}

companion object {
fun factory(soundPlayer: SoundPlayer): ViewModelProvider.Factory {
return viewModelFactory {
Expand Down

0 comments on commit 9b81986

Please sign in to comment.