-
Notifications
You must be signed in to change notification settings - Fork 1
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
Porting card info #23
base: main
Are you sure you want to change the base?
Conversation
If it works like in iOS, you would modify the shouldIntercept routine to check if the .html/.js file is requested, and return the data/content type in the response of that function. If file:// is causing issues, you could try a custom scheme like ankischeme://card-info.html. You'd tell the webview to load that URL, and then should be able to feed it data via shouldIntercept. When shouldIntercept receives the request for i18nResources, it needs to feed the received data to the i18n_resources method, and return the result. Also note you probably can't just copy the js from the latest desktop version, as AnkiDroid is using an older backend (something like Anki 2.1.35?). The frontend html/js should match the version of the backend being used. And if David has not exposed i18nResources in the separate ankidroid backend module, it would need to be updated to make that method available before you'd be able to call it in AnkiDroid. |
Thanks, I will check and try to push some updates to backend or wait till it gets updated. |
This is how you might start on it: diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.kt b/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.kt
index 7eb988d67..ed308b3ed 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.kt
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.kt
@@ -387,8 +387,14 @@ open class Reviewer : AbstractFlashcardViewer() {
showResetCardDialog()
}
R.id.action_mark_card -> {
- Timber.i("Reviewer:: Mark button pressed")
- onMark(mCurrentCard)
+ Timber.i("Card Viewer:: Card Info")
+ // openCardInfo()
+ val intent = Intent(this, AnkiWebview::class.java)
+ intent.putExtra("cardId", mCurrentCard!!.id)
+ startActivityWithoutAnimation(intent)
+// access card info screen via 'mark card' action, since card info is not available by default
+// Timber.i("Reviewer:: Mark button pressed")
+// onMark(mCurrentCard)
}
R.id.action_replay -> {
Timber.i("Reviewer:: Replay audio button pressed (from menu)")
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/pages/AnkiWebview.kt b/AnkiDroid/src/main/java/com/ichi2/anki/pages/AnkiWebview.kt
index 95e15f1a5..f4b9ae6a7 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/pages/AnkiWebview.kt
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/pages/AnkiWebview.kt
@@ -3,8 +3,14 @@ package com.ichi2.anki.pages
import android.os.Bundle
import android.webkit.*
import com.ichi2.anki.AnkiActivity
+import com.ichi2.anki.AnkiDroidApp
import com.ichi2.anki.R
+import com.ichi2.libanki.Collection
+import com.ichi2.libanki.backend.RustDroidBackend
+import com.ichi2.libanki.backend.RustDroidV16Backend
import timber.log.Timber
+import java.io.ByteArrayInputStream
+import java.io.InputStream
class AnkiWebview : AnkiActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
@@ -12,13 +18,14 @@ class AnkiWebview : AnkiActivity() {
setContentView(R.layout.anki_webview)
val webview: WebView = findViewById(R.id.anki_wb)
webview.settings.javaScriptEnabled = true
- webview.settings.allowFileAccess = true
webview.webChromeClient = WebChromeClient()
- webview.webViewClient = AnkiWebChromeClient()
- webview.loadUrl("file:///android_asset/pages/card-info.html")
+ // passing col in here is presumably the wrong way to do this
+ webview.webViewClient = AnkiWebChromeClient(col)
+ webview.loadUrl("https://127.0.0.1/card-info.html")
}
- class AnkiWebChromeClient : WebViewClient() {
+ class AnkiWebChromeClient(val col: Collection) : WebViewClient() {
+
override fun onLoadResource(view: WebView?, url: String?) {
if (url.equals("file:///_anki/i18nResources")) {
view?.loadUrl("file:///android_asset/i18nResources")
@@ -30,8 +37,32 @@ class AnkiWebview : AnkiActivity() {
view: WebView?,
request: WebResourceRequest?
): WebResourceResponse? {
- Timber.d("request %d", request?.method)
- return super.shouldInterceptRequest(view, request)
+ val streamResponse = { data: InputStream, mime: String ->
+ WebResourceResponse(
+ mime, "utf-8", 200, "OK",
+ HashMap(), data
+ )
+ }
+ val fileResponse = { path: String, mime: String ->
+ view?.context?.assets?.open("pages$path")?.let { streamResponse(it, mime) }
+ }
+
+ return request?.url?.let { url ->
+ Timber.i("************** request %s", url)
+ when (val path = url.path) {
+ "/card-info.html" -> fileResponse(path, "text/html")
+ "/card-info.css" -> fileResponse(path, "text/css")
+ "/card-info.js" -> fileResponse(path, "text/javascript")
+ "/_anki/i18nResources" -> {
+ val data = ByteArrayInputStream(col.backend.i18nResources().toByteArray())
+ streamResponse(data, "application/binary")
+ }
+ else -> {
+ Timber.i("************** ignore %s", url.path)
+ null
+ }
+ }
+ } ?: super.shouldInterceptRequest(view, request)
}
}
}
diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/backend/DroidBackend.kt b/AnkiDroid/src/main/java/com/ichi2/libanki/backend/DroidBackend.kt
index df84e952c..3d58b6197 100644
--- a/AnkiDroid/src/main/java/com/ichi2/libanki/backend/DroidBackend.kt
+++ b/AnkiDroid/src/main/java/com/ichi2/libanki/backend/DroidBackend.kt
@@ -19,6 +19,7 @@ import BackendProto.Backend.ExtractAVTagsOut
import BackendProto.Backend.RenderCardOut
import android.content.Context
import androidx.annotation.VisibleForTesting
+import com.google.protobuf.ByteString
import com.ichi2.libanki.Collection
import com.ichi2.libanki.DB
import com.ichi2.libanki.DeckConfig
@@ -85,4 +86,7 @@ interface DroidBackend {
@Throws(BackendNotSupportedException::class)
fun renderCardForTemplateManager(templateRenderContext: TemplateRenderContext): RenderCardOut
+
+ @Throws(BackendNotSupportedException::class)
+ fun i18nResources(): ByteString
}
diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/backend/JavaDroidBackend.java b/AnkiDroid/src/main/java/com/ichi2/libanki/backend/JavaDroidBackend.java
index 8e9cd11d9..4f0bcbae9 100644
--- a/AnkiDroid/src/main/java/com/ichi2/libanki/backend/JavaDroidBackend.java
+++ b/AnkiDroid/src/main/java/com/ichi2/libanki/backend/JavaDroidBackend.java
@@ -18,6 +18,7 @@ package com.ichi2.libanki.backend;
import android.content.Context;
+import com.google.protobuf.ByteString;
import com.ichi2.libanki.Collection;
import com.ichi2.libanki.DB;
import com.ichi2.libanki.TemplateManager;
@@ -108,4 +109,11 @@ public class JavaDroidBackend implements DroidBackend {
public @NonNull Backend.RenderCardOut renderCardForTemplateManager(@NonNull TemplateManager.TemplateRenderContext templateRenderContext) throws BackendNotSupportedException {
throw new BackendNotSupportedException();
}
+
+
+ @NonNull
+ @Override
+ public ByteString i18nResources() throws BackendNotSupportedException {
+ throw new BackendNotSupportedException();
+ }
}
diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/backend/RustDroidBackend.kt b/AnkiDroid/src/main/java/com/ichi2/libanki/backend/RustDroidBackend.kt
index 7bf98cd3a..072e98026 100644
--- a/AnkiDroid/src/main/java/com/ichi2/libanki/backend/RustDroidBackend.kt
+++ b/AnkiDroid/src/main/java/com/ichi2/libanki/backend/RustDroidBackend.kt
@@ -19,6 +19,7 @@ package com.ichi2.libanki.backend
import BackendProto.Backend.ExtractAVTagsOut
import BackendProto.Backend.RenderCardOut
import android.content.Context
+import com.google.protobuf.ByteString
import com.ichi2.libanki.Collection
import com.ichi2.libanki.DB
import com.ichi2.libanki.TemplateManager.TemplateRenderContext
@@ -94,6 +95,8 @@ open class RustDroidBackend(
override fun renderCardForTemplateManager(templateRenderContext: TemplateRenderContext): RenderCardOut {
throw BackendNotSupportedException()
}
+
+ override fun i18nResources() = backend.backend.i18nResources().json
companion object {
const val UNUSED_VALUE = 0 It won't work at the moment though, because the backend is on 2.1.34, and the Svelte card info screen was introduced in 2.1.50. A custom scheme proved problematic - fetch() didn't support it, and relative links were not resolved. So we use standard https URLs, but intercept the requests so a server is not required. |
For graphs.html, it worked. I have added backend code for One questions for this line, I passed empty string and 100 for days. What will be the possible parameters? (I will check code to understand the required params) override fun graphData(): ByteString = backend.backend.graphs("", 100).toByteString() |
In that version of Anki, those two params (configured via the bar at the top) were sent in the POST body as json, so shouldIntercept() will need to extract the JSON from the request, and then pass the two values into graphData(). |
Thanks, I understood it, I try it. |
The language is currently hard-coded to English in backendv1impl:ensureBackend(). Night mode is set by requesting graphs.html#night instead of just graphs.html |
Thanks, I understood this. |
To enable krmanik#23 Also requires ankidroid/Anki-Android-Backend@4c3eea4 or similar
To enable krmanik#23 Also requires ankidroid/Anki-Android-Backend@4c3eea4 or similar
To enable krmanik#23 Also requires ankidroid/Anki-Android-Backend@4c3eea4 or similar
To enable krmanik#23 Also requires ankidroid/Anki-Android-Backend@4c3eea4 or similar
To enable krmanik#23 Also requires ankidroid/Anki-Android-Backend@4c3eea4 or similar
To enable krmanik#23 Also requires ankidroid/Anki-Android-Backend@4c3eea4 or similar
To enable krmanik#23 Also requires ankidroid/Anki-Android-Backend@4c3eea4 or similar
To enable krmanik#23 Also requires ankidroid/Anki-Android-Backend@4c3eea4 or similar
To enable krmanik#23 Also requires ankidroid/Anki-Android-Backend@4c3eea4 or similar
To enable krmanik#23 Also requires ankidroid/Anki-Android-Backend@4c3eea4 or similar
To enable krmanik#23 Also requires ankidroid/Anki-Android-Backend@4c3eea4 or similar
To enable krmanik#23 Also requires ankidroid/Anki-Android-Backend@4c3eea4 or similar
To enable krmanik#23 Also requires ankidroid/Anki-Android-Backend@4c3eea4 or similar
To enable krmanik#23 Also requires ankidroid/Anki-Android-Backend@4c3eea4 or similar
To enable krmanik#23 Also requires ankidroid/Anki-Android-Backend@4c3eea4 or similar
streamResponse(data, "application/binary") | ||
} | ||
"/_anki/graphs" -> { | ||
val data = ByteArrayInputStream(col.backend.graphs("deck:current", 365).toByteArray()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should be able to use graphsRaw() now, and pass it the bytes from the request body.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I am implementing it.
streamResponse(data, "application/binary") | ||
} | ||
"/_anki/getGraphPreferences" -> { | ||
val data = ByteArrayInputStream(col.backend.getGraphPreferences().toByteArray()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getGraphPreferencesRaw()
"/graphs.js" -> fileResponse(path, "text/javascript") | ||
"/_anki/i18nResources" -> { | ||
val byteArray: ByteArray = "en-US".toByteArray() | ||
val data = ByteArrayInputStream(col.backend.i18nResourcesRaw(byteArray)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pass i18nResourcesRaw the bytes from the request body
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, all should be col.xxx now, not col.backend.xxx
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
col.newBackend will give you a CollectionV16 which should have those properties
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks
After building The graphs page assets loaded from aar files stored in web directory. This is result of graphs stats, but I didn't understand args for |
it does seem like the local webserver is the way to go, to be clear, I'm not against that, I appreciate doing quick proof-of-concepts (of, lack-of-proof-of-concepts? 😅 ) on the other styles to quickly demonstrate why localhost appears best |
I will create PR for it to upstream when implementation complete. |
To keep things neat, GUI code should avoid calling the backend directly, so we'll need to add two small methods to BackendImportExport.kt that pass the request on to the backend, like is done in BackendStats.kt |
If you look at the desktop code, it calls this after the document has finished loading:
|
I have added code for it in libanki.
Thanks, In AnkiDroid side, due to scope storage policy extra work needed to make it work for getting |
To enable krmanik#23 Also requires ankidroid/Anki-Android-Backend@4c3eea4 or similar
What will be the approach for changing i18n languages? I checked the code in export async function setupI18n(args: { modules: ModuleName[] }): Promise<void> {
const resources = await i18n.i18nResources(I18n.I18nResourcesRequest.create(args));
const json = JSON.parse(new TextDecoder().decode(resources.json));
const newBundles: FluentBundle[] = [];
for (const res in json.resources) {
const text = json.resources[res];
const lang = json.langs[res];
const bundle = new FluentBundle([lang, "en-US"]);
const resource = new FluentResource(text);
bundle.addResource(resource);
newBundles.push(bundle);
}
setBundles(newBundles);
langs = json.langs;
document.dir = direction();
} |
The active language is set at AnkiDroid startup ( |
These screen are ported In Anki codebase the
def do_import(self, data: bytes) -> None:
request = ImportCsvRequest()
request.ParseFromString(data)
self._on_accepted(request)
super().reject() In current AnkiDroid codebase I have done following Anki-Android/AnkiDroid/src/main/java/com/ichi2/utils/ImportUtils.kt Lines 195 to 199 in 8fd90bf
Anki-Android/AnkiDroid/src/main/java/com/ichi2/anki/pages/AnkiPagesWebview.kt Lines 72 to 77 in 8fd90bf
|
The way I handle this in AnkiMobile is to have each screen subclass a base handler. Each page/subclass declares the endpoints it requires, and can handle special behavior like closing the displayed page. |
I will try to implement the handler class. |
This introduces a dependency to NanoHTTPD, a java server library Used krmanik#23 as base of this commit
This introduces a dependency to NanoHTTPD, a java server library Used krmanik#23 as base of this commit
This introduces a dependency to NanoHTTPD, a java server library Used krmanik#23 as base of this commit
This introduces a dependency to NanoHTTPD, a java server library Used krmanik#23 as base of this commit
This introduces a dependency to NanoHTTPD, a java server library Used krmanik#23 as base of this commit
This introduces a dependency to NanoHTTPD, a java server library Used krmanik#23 as base of this commit
This introduces a dependency to NanoHTTPD, a java server library Used krmanik#23 as base of this commit
This introduces a dependency to NanoHTTPD, a java server library Used krmanik#23 as base of this commit
Pull Request template
Purpose / Description
Trying to port the card info
Fixes
Approach
ts
web
lib
How Has This Been Tested?
Currently getting errors, fixing errors
Learning (optional, can help others)
Describe the research stage
Links to blog posts, patterns, libraries or addons used to solve this problem
Checklist
Please, go through these checks before submitting the PR.
if
statements)