Skip to content

[Feature] JavaScript Addons Support#7958

Closed
krmanik wants to merge 22 commits intoankidroid:masterfrom
krmanik:addons
Closed

[Feature] JavaScript Addons Support#7958
krmanik wants to merge 22 commits intoankidroid:masterfrom
krmanik:addons

Conversation

@krmanik
Copy link
Copy Markdown
Member

@krmanik krmanik commented Dec 26, 2020

Pull Request template

Purpose / Description

Using AnkiDroid JS API a deck can be extended to implement features like progress bar or custom card layouts. But it requires user to manually add the html/css/js to card templates. This is implementation for a way to inject code to all deck templates when addons get activated.

Fixes

Fixes #7959

Approach

  1. Download addons from https://www.npmjs.com/search?q=keywords:ankidroid-js-addon inside the app
  2. In app there are implementation to check for valid addons for AnkiDroid
    It will check following string in latest package.json
{
    "name": "ankidroid-js-addon-progress-bar",
    "version": "1.0.0",
    "author": "https://github.com/infinyte7",
    "homepage": "https://github.com/infinyte7/Anki-Custom-Card-Layout",
    "ankidroid_js_api": "0.0.1",
    "addon_type": "reviewer",
    "keywords": ["ankidroid-js-addon"]
}

There isValidPackageJson boolean function will check for above values if present then the addons is valid.

  1. The enabled addons' index.js content wrapped in <script> tag will be added to fillFlashCard() in AbstractFlashcardViewer.java

There are two types of addon reviewer and note_editor. In this PR, reviewer addon is implemented.

Idea for note_editor addon:
The javascript function can be called using https://github.com/evgenyneu/js-evaluator-for-android/#call-a-javascript-function . So, in note editor multiple buttons will get added to toolbar with tag containing addons' name. When the toolbar button pressed, it will take selected text from FieldEditText and pass to jsEvaluate java function. The jsEvaluate java function will get tag of clicked toolbar button and open js file with name equal to tag of button and pass the selected text to addon's function implementation. The addon's js function will return the manipulated string to FieldEditText in note editor. Even if other implementation can be done without selected text as defined in js file in addon's folder. This will implemented in separated PR.

How Has This Been Tested?

Tested on emulator

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration (SDK version(s), emulator or physical, etc)

Learning (optional, can help others)

https://developer.android.com/guide/topics/ui/layout/recyclerview

Checklist

Please, go through these checks before submitting the PR.

  • [ x ] You have not changed whitespace unnecessarily (it makes diffs hard to read)
  • [ x ] You have a descriptive commit message with a short title (first line, max 50 chars).
  • [ x ] Your code follows the style of the project (e.g. never omit braces in if statements)
  • [ x ] You have commented your code, particularly in hard-to-understand areas
  • [ x ] You have performed a self-review of your own code
  • [ x ] UI changes: include screenshots of all affected screens (in particular showing any new or changed strings)

@krmanik
Copy link
Copy Markdown
Member Author

krmanik commented Dec 26, 2020

This is first commit and need help in implementing way to store the activated addons status.

@krmanik krmanik marked this pull request as draft December 26, 2020 04:35
@codecov
Copy link
Copy Markdown

codecov bot commented Dec 26, 2020

Codecov Report

Merging #7958 (c6b1ac9) into master (3cca3b1) will decrease coverage by 0.41%.
The diff coverage is 6.97%.

❗ Current head c6b1ac9 differs from pull request most recent head f1bb756. Consider uploading reports for the commit f1bb756 to get more accurate results
Impacted file tree graph

@@             Coverage Diff              @@
##             master    #7958      +/-   ##
============================================
- Coverage     37.11%   36.70%   -0.42%     
+ Complexity     3854     3791      -63     
============================================
  Files           361      358       -3     
  Lines         36326    36209     -117     
  Branches       4798     4778      -20     
============================================
- Hits          13484    13291     -193     
- Misses        21310    21409      +99     
+ Partials       1532     1509      -23     
Impacted Files Coverage Δ Complexity Δ
...Droid/src/main/java/com/ichi2/anki/AddonModel.java 0.00% <0.00%> (ø) 0.00 <0.00> (?)
...id/src/main/java/com/ichi2/anki/AddonsAdapter.java 0.00% <0.00%> (ø) 0.00 <0.00> (?)
.../java/com/ichi2/anki/NavigationDrawerActivity.java 30.28% <0.00%> (-0.71%) 13.00 <0.00> (ø)
...id/src/main/java/com/ichi2/anki/AddonsBrowser.java 3.21% <3.21%> (ø) 2.00 <2.00> (?)
...n/java/com/ichi2/anki/AbstractFlashcardViewer.java 38.87% <41.46%> (-0.72%) 168.00 <2.00> (-1.00)
...kiDroid/src/main/java/com/ichi2/utils/MapUtil.java 0.00% <0.00%> (-50.00%) 0.00% <0.00%> (-2.00%)
.../src/main/java/com/ichi2/anki/web/HttpFetcher.java 0.00% <0.00%> (-16.42%) 0.00% <0.00%> (-2.00%)
.../java/com/ichi2/utils/DatabaseChangeDecorator.java 42.68% <0.00%> (-10.98%) 20.00% <0.00%> (-6.00%)
...roid/src/main/java/com/ichi2/ui/FixedEditText.java 44.82% <0.00%> (-9.46%) 5.00% <0.00%> (-2.00%)
...d/src/main/java/com/ichi2/utils/ClipboardUtil.java 0.00% <0.00%> (-8.34%) 0.00% <0.00%> (-3.00%)
... and 94 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 3cca3b1...f1bb756. Read the comment docs.

@Arthur-Milchior
Copy link
Copy Markdown
Member

I really like the general idea.
Given that there is no monkey patching, I hope that this will ensure that add-ons live together better in ankidroid than in anki (even if it became better in anki the two last years)
I hope and fear it should be cool to have a place to list all add-ons, maybe vouch them, and a way to update easily. But I guess that's not necessary to consider all of this immediately.

A small note about screen capture. I believe you could easily improve it by turning on android's preference option to display where you click/touch. This way, it'll become easier to follow what you are doing.

@krmanik
Copy link
Copy Markdown
Member Author

krmanik commented Dec 26, 2020

This will greatly help in introducing some features of Anki addons that can be implemented using JavaScript for AnkiDroid using JS API. For example Card info during review. But that need to added to all decks manully. Using feature in this PR, the content that needs to be added to reviewer will have done once.

I will implement the feature if file with .jsaddon extension valid for addons or not using their manifest.json file.

Also there will be some place where user can search and find AnkiDroid JS addons like https://addon-docs.ankiweb.net/

@krmanik
Copy link
Copy Markdown
Member Author

krmanik commented Dec 28, 2020

@krmanik
Copy link
Copy Markdown
Member Author

krmanik commented Dec 28, 2020

This is second commit to this PR.
The addons containing {{Deck}} or {{Subdeck}} needs to converted to respective deckname. And similar transformation to be done if addons contains {{anything}}

@krmanik
Copy link
Copy Markdown
Member Author

krmanik commented Dec 29, 2020

@krmanik krmanik marked this pull request as ready for review December 29, 2020 13:31
@krmanik
Copy link
Copy Markdown
Member Author

krmanik commented Dec 29, 2020

For importing addon, addon must contain two files.

content.html
manifest.json

content.html is file which get added to webview and manifest.json used for getting id.

The id in manifest.json and folder name of addon must be same.

The structure of js_addon_progress_bar_1234578

js_addon_progress_bar_1234578
↳ content.css
↳ content.html
↳ content.js
↳ manifest.json

In this case addon folder name and id both are js_addon_progress_bar_1234578.

The folder js_addon_progress_bar_1234578 and files zipped with .jsaddon extension.

For testing I attached two jsaddons

js_addons_progress_bar_12345678.zip

js_addons_top_bar_12345678.zip

Change the extension from zip to jsaddon.

@mikehardy
Copy link
Copy Markdown
Member

I'm sorry I haven't given this a real scan yet, but I wonder if it we could use the standard package.json format and thus rely on npmjs.com for distribution etc 🤔 ?

@krmanik
Copy link
Copy Markdown
Member Author

krmanik commented Dec 29, 2020

I haven't implemented any packages for npmjs so I will study the distribution. The format can be updated to package.json but for distribution I will read the documentation and then try to implement.

Distribution from npmjs will be better because there will no need for Anki Ecosystem to view and manage those files. That totally rely on developer. I will view the documentation of npmjs for better implementation. :)

@mikehardy
Copy link
Copy Markdown
Member

the package.json format is definitely the most well known. It defines an entry point for the package, naming (and npmjs.com will enforce unique names :-)) plus I think you can tag on npmjs.com with keywords so we could do a search or something for "ankidroid-js-addon" or something if we make a convention for that. It can also define homepages and version and dependencies (regular ones, devDependencies, peerDependencies) etc.

For our purposes we likely won't be using anything but the most basic keywords in the package.json format but it keeps us using standard things which is nice

@krmanik
Copy link
Copy Markdown
Member Author

krmanik commented Dec 29, 2020

I understood it. I will change the manifest.json to package.json and will use useful and needed keywords from package.json.

@krmanik
Copy link
Copy Markdown
Member Author

krmanik commented Dec 30, 2020

Implementation

  1. Published addon as npm package to npmjs
    e.g. https://www.npmjs.com/package/ankidroid-js-addon-progress-bar

  2. In AnkiDroid downloading latest package info as json file from
    http://registry.npmjs.org/{ file name from edit text }/latest
    e.g. http://registry.npmjs.org/ankidroid-js-addon-progress-bar/latest

    I haven't found way to download npm package directly.
    https://stackoverflow.com/questions/50933603/download-npm-dist-package-without-having-to-install-npm

  3. Reading json file and getting tarball link to download the package

  4. The npm package file downloded with .tgz
    e.g. ankidroid-js-addon-progress-bar.tgz

  5. Decompressing the tar file to addons folder using https://github.com/thrau/jarchivelib

  6. In AbstractFlashcardViewer.java, the index.js read from addons/ankidroid-js.../package/index.js and added to reviewer

Demo

@krmanik
Copy link
Copy Markdown
Member Author

krmanik commented Dec 30, 2020

This is outdated now.

For importing addon, addon must contain two files.

content.html
manifest.json

content.html is file which get added to webview and manifest.json used for getting id.

The id in manifest.json and folder name of addon must be same.

The structure of js_addon_progress_bar_1234578

js_addon_progress_bar_1234578
↳ content.css
↳ content.html
↳ content.js
↳ manifest.json

In this case addon folder name and id both are js_addon_progress_bar_1234578.

The folder js_addon_progress_bar_1234578 and files zipped with .jsaddon extension.

For testing I attached two jsaddons

js_addons_progress_bar_12345678.zip

js_addons_top_bar_12345678.zip

Change the extension from zip to jsaddon.

@mikehardy
Copy link
Copy Markdown
Member

I have still been booked up completely on some contract work but every time I see one of your animated gifs it is 🤤 ! this looks very powerful

@krmanik
Copy link
Copy Markdown
Member Author

krmanik commented Dec 30, 2020

The information need for contract is stored in package.json file. Email, ankidroid_api, author
If ankidroid_api does not match api in AnkiDroid then .tgz file will not downloaded. So checking email, ankidroid_api, keywords in package.json will stop from downloading other npmjs package.

I think, gifs is better than written description for explaining workflow of the PR. As it changes ui element also. :)

@krmanik
Copy link
Copy Markdown
Member Author

krmanik commented Dec 30, 2020

Also I think that one file in wiki should list all the addons that has been added to npmjs by a developer like wiki/Third-Party-Apps. New addons can be listed by the addon developer.

@mikehardy
Copy link
Copy Markdown
Member

Is there way to do "discovery by convention" with a search powered by someone else?
What I mean concretely is: what if we had a convention of specific tags/keywords you should use when you upload to npmjs.com, then for JS addons we had a wiki page that had the npmjs.com search for those keywords in it, and we used the same search in the app?

That way all the search/discovery would be offloaded to npmjs.com and add on authors? zero maintenance burden unless npmjs.com changed their URL query interface...

@krmanik
Copy link
Copy Markdown
Member Author

krmanik commented Dec 30, 2020

I found this url useful.
https://www.npmjs.com/search?q=keywords:ankidroid-js-addon

In app we may add option for Get more addons.

  1. It will prompt for opening link in browser. (In wiki a line should be mentioned for addon developer to add keyword for discoverability, like ankidroid-js-addon)
    https://www.npmjs.com/search?q=keywords:ankidroid-js-addon

  2. Users view the listed addons on npmjs and select any addon

  3. Copy their npm i ankidroid-js-addon-... and paste it in AnkiDroid->Addons

@krmanik
Copy link
Copy Markdown
Member Author

krmanik commented Dec 31, 2020

Implementation

  1. It will download latest info of addon file
  2. Check for following info if present in .json file
name
version 
author
ankidroid_js_api
homepage
keywords

If in latest json file, ankidroid_js_api is equal to 0.0.1 (current js api version) and keywords is equals to ankidroid-js-addon then get tarball link for downloading addon.tgz file and extract then copy it to AnkiDroid/addons folder.

https://github.com/ankidroid/Anki-Android/pull/7958/files#diff-e71d85656c47c1c4a8f0135d9b62eb6db857881841574c79522848f29725386dR348-R375

Also added option to Get more addons. It will open url in browser with url:
https://www.npmjs.com/search?q=keywords:ankidroid-js-addon

@krmanik
Copy link
Copy Markdown
Member Author

krmanik commented Jan 2, 2021

Implementation

  1. Added addon type, reviewer and note_editor
  2. The content of addon with reviewer type will get added to reviewer from js file wrapped in <script> tag
  3. The addon with note_editor type to be implemented.

Idea:
The javascript function can be called using https://github.com/evgenyneu/js-evaluator-for-android/#call-a-javascript-function . So, in note editor multiple buttons will get added to toolbar with tag containing addons' name. When the toolbar button pressed, it will take selected text from FieldEditText and pass to jsEvaluate java function. The jsEvaluate java function will get tag of clicked toolbar button and open js file with name equal to tag of button and pass the selected text to addon's function implementation. The addon's js function will return the manipulated string to FieldEditText in note editor. Even if other implementation can be done without selected text as defined in js file in addon's folder. This will implemented in separated PR.

@krmanik krmanik marked this pull request as draft January 2, 2021 16:13
@krmanik krmanik mentioned this pull request Jan 11, 2021
@krmanik
Copy link
Copy Markdown
Member Author

krmanik commented Mar 14, 2021

Update 1: Change addon info popup layout

Update 2: Remove prefs when addon delete

Update 3: Added a new addon for button and card counts to npmjs

@krmanik krmanik marked this pull request as ready for review March 19, 2021 09:27
krmanik added 2 commits April 1, 2021 21:49
- Options in advance settings
- Layout for showing message when no addons added
- Open npmjs.org url in AnkiDroid
- comments added
@krmanik
Copy link
Copy Markdown
Member Author

krmanik commented Apr 1, 2021

  1. JS Addons in Advance settings
  2. Layout for showing message to download addons when no addons added
  3. Open npmjs.org in app

Copy link
Copy Markdown
Member

@Arthur-Milchior Arthur-Milchior left a comment

Choose a reason for hiding this comment

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

Are 22 commits really useful ? Hard to review from scratch right now. For example, I'd like "first commit" to explain a little bit more what I'm reading. I don't know NPM at all, I don't really know JS, so I need commit message to guide me through what's going on to be able to review this

@@ -0,0 +1,7 @@
package com.ichi2.anki;

public class AddonsBrowserTest extends RobolectricTest {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What's the point of an empty class?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

To pass build of project.

@krmanik
Copy link
Copy Markdown
Member Author

krmanik commented Apr 2, 2021

I think it is not good idea to review from First commit because there are lot of changes done.
Should I squash all commit in to one?

@krmanik
Copy link
Copy Markdown
Member Author

krmanik commented Apr 2, 2021

I am creating new PR for this with detailed commit message.

@krmanik krmanik closed this Apr 2, 2021
@Arthur-Milchior
Copy link
Copy Markdown
Member

Squashing everything will not really help me. I can not really read 1157 new lines of code and check that I'm okay with all of them. It's particularly hard because I don't know anything about JS and NPM and other tool you use.

It is possible that you actually can not split this into smaller commits that all makes sens by themselves; it sometime happens and makes things hard. However, ideally, what I would love to see is a story. You make a first commit that compiles, pass tests, with a commit message that explains to me and future reader why you do it, why it is useful. Then you add another part of the code etc...
Using git rebase -i (for interactive) you usually can change history, modify a commit to correct what was added in it.
Of course, it works best if you do it progressively through the history of the PR; rewritting all of this as a consistent story later requires more thought process.

@krmanik
Copy link
Copy Markdown
Member Author

krmanik commented Apr 2, 2021

I have created separate PR because creating is easy and better than rewriting history. :)
Also I have tried to explain the working of js addons idea.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] JavaScript Addons support for AnkiDroid

3 participants