Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

convert synchronous js api to asynchronous and remove js interface #14564

Merged
merged 10 commits into from
Dec 7, 2023
1 change: 1 addition & 0 deletions AnkiDroid/src/main/assets/card_template.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<script src="/assets/jquery.min.js"> </script>
<script src="/assets/scripts/card.js" type="text/javascript"> </script>
<script src="/assets/reviewer_extras_bundle.js"> </script>
<script src="/assets/scripts/js-api.js" type="text/javascript"> </script>
</head>
<body class="::class::">
<div id="content">
Expand Down
47 changes: 0 additions & 47 deletions AnkiDroid/src/main/assets/scripts/card.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,53 +108,6 @@ function reloadPage() {
window.location.href = "signal:reload_card_html";
}

// Mark current card
function ankiMarkCard() {
window.location.href = "signal:mark_current_card";
}

/* Toggle flag on card from AnkiDroid Webview using JavaScript
Possible values: "none", "red", "orange", "green", "blue"
See AnkiDroid Manual for Usage
*/
function ankiToggleFlag(flag) {
var flagVal = Number.isInteger(flag);

if (flagVal) {
switch (flag) {
case 0:
window.location.href = "signal:flag_none";
break;
case 1:
window.location.href = "signal:flag_red";
break;
case 2:
window.location.href = "signal:flag_orange";
break;
case 3:
window.location.href = "signal:flag_green";
break;
case 4:
window.location.href = "signal:flag_blue";
break;
case 5:
window.location.href = "signal:flag_pink";
break;
case 6:
window.location.href = "signal:flag_turquoise";
break;
case 7:
window.location.href = "signal:flag_purple";
break;
default:
console.log("No Flag Found");
break;
}
} else {
window.location.href = "signal:flag_" + flag;
}
}

// Show toast using js
function ankiShowToast(message) {
var msg = encodeURI(message);
Expand Down
120 changes: 120 additions & 0 deletions AnkiDroid/src/main/assets/scripts/js-api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* AnkiDroid JavaScript API
* Version: 0.0.2
*/

/**
* jsApiList
*
* name: method name
* value: endpoint
*/
const jsApiList = {
ankiGetNewCardCount: "newCardCount",
ankiGetLrnCardCount: "lrnCardCount",
ankiGetRevCardCount: "revCardCount",
ankiGetETA: "eta",
ankiGetCardMark: "cardMark",
ankiGetCardFlag: "cardFlag",
ankiGetNextTime1: "nextTime1",
ankiGetNextTime2: "nextTime2",
ankiGetNextTime3: "nextTime3",
ankiGetNextTime4: "nextTime4",
ankiGetCardReps: "cardReps",
ankiGetCardInterval: "cardInterval",
ankiGetCardFactor: "cardFactor",
ankiGetCardMod: "cardMod",
ankiGetCardId: "cardId",
ankiGetCardNid: "cardNid",
ankiGetCardType: "cardType",
ankiGetCardDid: "cardDid",
ankiGetCardLeft: "cardLeft",
ankiGetCardODid: "cardODid",
ankiGetCardODue: "cardODue",
ankiGetCardQueue: "cardQueue",
ankiGetCardLapses: "cardLapses",
ankiGetCardDue: "cardDue",
ankiIsInFullscreen: "isInFullscreen",
ankiIsTopbarShown: "isTopbarShown",
ankiIsInNightMode: "isInNightMode",
ankiIsDisplayingAnswer: "isDisplayingAnswer",
ankiGetDeckName: "deckName",
ankiIsActiveNetworkMetered: "isActiveNetworkMetered",
ankiTtsFieldModifierIsAvailable: "ttsFieldModifierIsAvailable",
ankiTtsIsSpeaking: "ttsIsSpeaking",
ankiTtsStop: "ttsStop",
ankiBuryCard: "buryCard",
ankiBuryNote: "buryNote",
ankiSuspendCard: "suspendCard",
ankiSuspendNote: "suspendNote",
ankiAddTagToCard: "addTagToCard",
ankiResetProgress: "resetProgress",
ankiMarkCard: "markCard",
ankiToggleFlag: "toggleFlag",
ankiSearchCard: "searchCard",
ankiSearchCardWithCallback: "searchCardWithCallback",
Comment on lines +54 to +55
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should rethink the names here:

  • searchCard opens the browser
  • searchCardWithCallback performs a search and returns results

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

During implementation of api, only searchCard are implemented but later user wanted result in reviewer also, so used call back, poor naming choice, it can be solved in separate PR, with better naming, because we are upgrading api, so it can also be upgraded.

ankiTtsSpeak: "ttsSpeak",
ankiTtsSetLanguage: "ttsSetLanguage",
ankiTtsSetPitch: "ttsSetPitch",
ankiTtsSetSpeechRate: "ttsSetSpeechRate",
ankiEnableHorizontalScrollbar: "enableHorizontalScrollbar",
ankiEnableVerticalScrollbar: "enableVerticalScrollbar",
ankiSetCardDue: "setCardDue",
};

class AnkiDroidJS {
constructor({ developer, version }) {
this.developer = developer;
this.version = version;
this.handleRequest(`init`);
}

static init({ developer, version }) {
return new AnkiDroidJS({ developer, version });
}

handleRequest = async (endpoint, data) => {
const url = `/jsapi/${endpoint}`;
try {
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
developer: this.developer,
version: this.version,
data,
}),
});

if (!response.ok) {
throw new Error("Failed to make the request");
}

const responseData = await response.text();
if (endpoint.includes("nextTime") || endpoint.includes("deckName")) {
return responseData;
}
return JSON.parse(responseData);
} catch (error) {
console.error("Request error:", error);
throw error;
}
};
}

Object.keys(jsApiList).forEach(method => {
if (method === "ankiTtsSpeak") {
AnkiDroidJS.prototype[method] = async function (text, queueMode = 0) {
const endpoint = jsApiList[method];
const data = JSON.stringify({ text, queueMode });
return await this.handleRequest(endpoint, data);
};
return;
}
AnkiDroidJS.prototype[method] = async function (data) {
const endpoint = jsApiList[method];
return await this.handleRequest(endpoint, data);
};
});
62 changes: 5 additions & 57 deletions AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1053,7 +1053,6 @@ abstract class AbstractFlashcardViewer :

// Javascript interface for calling AnkiDroid functions in webview, see card.js
mAnkiDroidJsAPI = javaScriptFunction()
webView.addJavascriptInterface(mAnkiDroidJsAPI!!, "AnkiDroidJS")

// enable dom storage so that sessionStorage & localStorage can be used in webview
webView.settings.domStorageEnabled = true
Expand Down Expand Up @@ -1313,9 +1312,6 @@ abstract class AbstractFlashcardViewer :

open fun displayCardQuestion() {
displayCardQuestion(false)

// js api initialisation / reset
mAnkiDroidJsAPI!!.init()
}

private fun displayCardQuestion(reload: Boolean) {
Expand Down Expand Up @@ -2295,58 +2291,6 @@ abstract class AbstractFlashcardViewer :
redrawCard()
return true
}
// mark card using javascript
if (url.startsWith("signal:mark_current_card")) {
if (!mAnkiDroidJsAPI!!.isInit(AnkiDroidJsAPIConstants.MARK_CARD, AnkiDroidJsAPIConstants.ankiJsErrorCodeMarkCard)) {
return true
}
executeCommand(ViewerCommand.MARK)
return true
}
// flag card (blue, green, orange, red) using javascript from AnkiDroid webview
if (url.startsWith("signal:flag_")) {
if (!mAnkiDroidJsAPI!!.isInit(AnkiDroidJsAPIConstants.TOGGLE_FLAG, AnkiDroidJsAPIConstants.ankiJsErrorCodeFlagCard)) {
return true
}
return when (url.replaceFirst("signal:flag_".toRegex(), "")) {
"none" -> {
executeCommand(ViewerCommand.UNSET_FLAG)
true
}
"red" -> {
executeCommand(ViewerCommand.TOGGLE_FLAG_RED)
true
}
"orange" -> {
executeCommand(ViewerCommand.TOGGLE_FLAG_ORANGE)
true
}
"green" -> {
executeCommand(ViewerCommand.TOGGLE_FLAG_GREEN)
true
}
"blue" -> {
executeCommand(ViewerCommand.TOGGLE_FLAG_BLUE)
true
}
"pink" -> {
executeCommand(ViewerCommand.TOGGLE_FLAG_PINK)
true
}
"turquoise" -> {
executeCommand(ViewerCommand.TOGGLE_FLAG_TURQUOISE)
true
}
"purple" -> {
executeCommand(ViewerCommand.TOGGLE_FLAG_PURPLE)
true
}
else -> {
Timber.d("No such Flag found.")
true
}
}
}

// Show toast using JS
if (url.startsWith("signal:anki_show_toast:")) {
Expand Down Expand Up @@ -2555,7 +2499,7 @@ abstract class AbstractFlashcardViewer :
}
}

open fun javaScriptFunction(): AnkiDroidJsAPI? {
open fun javaScriptFunction(): AnkiDroidJsAPI {
return AnkiDroidJsAPI(this)
}

Expand All @@ -2565,6 +2509,10 @@ abstract class AbstractFlashcardViewer :
refreshIfRequired()
}

open fun getCardDataForJsApi(): AnkiDroidJsAPI.CardDataForJsApi {
return AnkiDroidJsAPI.CardDataForJsApi()
}

companion object {
/**
* Result codes that are returned when this activity finishes.
Expand Down
Loading
Loading