-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Development Guide
We always welcome any contributors, and you can start contributing immediately if you desire so. Feel free to join our Discord: #dev-ankidroid to join the community, or if you need help with the below. We are all volunteers.
This page contains information on how you can contribute back to the AnkiDroid project. You do not have to read all of it, just come back when you need an answer to a commonly asked question.
- Getting Started
-
Source code
- Android Studio
- AnkiDroid source code overview: Where to find what
- Issues to get started with
- Submit Improvements
- Git workflow
- Initial setup (one time)
- Making a new pull request
- Dealing with merge conflicts
- Running automated tests
- Compiling from the command line
- Adding translations
- Handling translations
- Making parallel builds
- Anki database structure
- Branching Model
- Localization Administration
- Code Coverage
- Developer options
- Other development tools
- Checking database modifications
- To do from time to time
Feel free to join our Discord Server: Anki to join our community. This is shared between the Anki Desktop app and Mobile apps
A few relevant channels are:
-
#mobile-apps
- Help for using AnkiDroid and AnkiMobile -
#dev-ankidroid
- General AnkiDroid Development discussions -
#ankidroid-gsoc
- Discussions/introductions related to onboarding and the Google Summer of Code program -
#help
- General Anki help
If it's unclear exactly what needs to be done, or how to do it, please don't hesitate to ask for clarification or help, ensuring that you skim our guides beforehand to see if your question has already been asked.
If you are a new developer looking to contribute something to AnkiDroid, please take a look and see if there's anything that you'd like to work on in the issue tracker.
The "Good First Issue" label has been added to any tasks that seem like a good way to get started working with the codebase.
If you are looking to make a lasting impact on the project from the get-go, issues with the label "Help Wanted" are tasks that we'd really like to have done but don't yet have time to do ourselves.
Please let us know that you're working on an open issue and let us assign it to you, this ensures that two people aren't working on the same issue, and ensures that all effort is valuable to the project. If an issue hasn't seen activity in a couple of weeks, feel free to ping the assignee to see if they're working on it, or ask a maintainer to reassign the issue to you.
If you can't work on an issue any more, please post another message to let us know.
If it's unclear exactly what needs to be done, or how to do it, please don't hesitate to ask for clarification or help!
Adding code documentation is helpful, and easy to get started with. This includes adding Javadocs to classes, members, and methods, and adding annotations such as @CheckResult
. It also include cleaning nullability typing, removing ?
in type when it's clear from context that types can not actually be null (e.g. the argument is always used with !!
)
Increasing test coverage is extremely welcomed, as it will considerably improve the future of the codebase.
If you want to create your own feature, please post a feature request via an issue so that the core contributors can confirm whether it would be accepted.
First, register here on GitHub, and follow the instructions on GitHub on the Anki-Android repository to fork and clone the code. If you want to be notified about each new improvement/bugfix, please subscribe to the commits feed for the main branch.
To ensure that users do not update the dependency or the Gradle plugin on their own, you can provide clear instructions and guidance in your documentation. Here's a revised version of your instructions that emphasizes this point:
The next step is to install Android Studio and the Android SDK. Open Android Studio and choose "Open Project," then select the folder where you earlier cloned the Github repository (we will refer to this folder as %AnkiDroidRoot%
).
On opening the project, it should start to build, and eventually, it may prompt you to install missing SDK components. Please do not update any dependencies or the Gradle plugin manually. This is taken care of by the project maintainers and automated bots to ensure compatibility and stability.
If prompted, install the following missing SDK components one by one:
- Android SDK Build-tools and Android SDK platform (ensure the version matches the "compileSdk" in the project's build.gradle file).
- Android Support Repository (latest version)
- Android Support Library (latest version)
After installing these dependencies as prompted, the project should build successfully. If you encounter any issues related to dependencies or the Gradle plugin, please report them to the project maintainers/ (discord)[https://discord.gg/GWgJAEst].
By following these instructions and avoiding manual updates, you'll help ensure a stable and reliable development environment for the project.
Provided you are able to use hardware acceleration for the Android emulators, setting up an emulator is a very effective method for testing code and running the connected android tests. If possible, verify your work periodically against both the newest version of the emulator available and the oldest version we support that you can run (at this time API18 is the oldest emulator easy to run though it is possible to run all the way down to API15 on linux at least with some effort).
In order to run a custom build of AnkiDroid on your device or attach the debugger, you'll need to connect your device over the adb protocol. Open your Android device, and enable developer options and USB debugging
. Finally when you connect your device to your computer over USB, it should get recognized by Android Studio and you should start to see output from it in the logcat section of Android studio.
If you don't want to test on your actual collection, you just have to rename the AnkiDroid
folder to any other name, such as AnkiDroidBack
and restart AnkiDroid. Since AnkiDroid won't find an AnkiDroid
folder, it will create one and assume it's a new collection. Note however that it will still have your AnkiWeb login and some preference settings. When you are done testing, you can rename this folder AnkiDroidBack
to AnkiDroid
.
Connect your device to your computer, or setup an emulator, then select Run -> Run 'AnkiDroid'
from the menu in Android Studio. This will compile a version of AnkiDroid suitable for testing (i.e. signed with a debug key), and pop up a window where you can select the device or emulator that you want to install and run the code on. See the main Android developer documentation for more detailed information.
Make sure that your Run Configuration for the AnkiDroid Project is set to use the APK from the app bundle instead of the Default APK (Run -> Edit Configurations -> Deploy (select 'APK from app bundle')
.
An apk file signed with a standard "debug" key will be generated named "AnkiDroid-debug.apk"
in:
%AnkiDroidRoot%/AnkiDroid/build/outputs/apk/
-
/AnkiDroid/src/main/
is the root directory for the main project (%root
) -
%root/java/com/ichi2/anki/
is the directory containing the main AnkiDroid source code. -
%root/java/com/ichi2/anki/DeckPicker.kt
is the code which runs when starting AnkiDroid from the Android launcher. -
%root/java/com/ichi2/libanki/
contains the underlying Anki code which is common to all Anki clients. It is ported directly from the Anki Desktop python code into Java.- Because
libanki
code comes upstream from the common Anki code, edit it only when necessary. Keeping the code consistent with the common core makes it easier to integrate future upgrades.
- Because
-
%root/assets/
contains the flashcard.css and HTML templates included with each flash card -
%root/res/
contains the Android resource XML files for the project. -
%root/res/values/
contains app strings, whiteboard colors, and a basic HTML template for flashcards. -
%root/res/layout/
contains the GUI layouts for most screens. -
%root/res/drawable-****/
contains the icons used throughout the app at various resolutions.
Once you have improved the code, commit it and send a pull request to AnkiDroid Github Repository. It will then be accepted after the code has been reviewed, and the enhanced application will be available on the Android Market on the next release. See the branching model section if you are unsure which branch to push to.
If you have trouble with Git, you can paste the changed files as text to the forum or github issue tracker.
Git can be a bit complicated to use in the beginning. This section describes the workflow that we recommend for regular contributors. The following assumes that you're using either linux, or the linux subsystem for Windows. We also assume you're using SSH to authenticate with github (highly recommended) and that you've already forked the AnkiDroid repository on github.
First let's set up our local repository
git clone git@github.com:GITHUB_USERNAME/Anki-Android.git AnkiDroid
cd AnkiDroid
git remote add upstream https://github.com/ankidroid/Anki-Android.git
Now if want to make a new change to the code base, we create a new 'feature branch' based off the latest version of the main branch:
git checkout main
git pull upstream main
git checkout -b my-feature-branch
# make your changes to the source code
git push origin HEAD
On navigating to the main AnkiDroid repository, github will now pop up a message asking you if you want to create a new pull request based on the branch that you just pushed.
If changes are made to the AnkiDroid repository that conflict with the changes in your pull request in-between creating the feature branch and your changes getting merged into the main repository, it may be necessary to rebase your code:
git checkout main
git pull upstream main
git checkout my-feature-branch # or just "git checkout -" to save typing
git rebase main
# it may be necessary to resolve merge conflicts here
# if you need to update the existing pull request, you should do a 'force' push
git push origin HEAD -f
There are two kinds of test in AnkiDroid. Unit test and on-device integration test. The second one needs a connected device or an emulated device. You can run both test set simultaneously from the command line with
./gradlew jacocoTestReport
Note that running ./gradlew jacocoTestReport
will run the unit tests, and then run a connected check on every currently running emulator and connected device. If you have a series of emulators that contain NEW
in their name (that is, if you run emulator -list-avds
you see NEW
in the AVD name) then you may appreciate scripts that will start and stop all your NEW emulators at once - https://github.com/ankidroid/Anki-Android/blob/main/tools/quality-check/start_all_emulators.sh and https://github.com/ankidroid/Anki-Android/blob/main/tools/quality-check/stop_all_emulators.sh will do this for you.
To run tests on Apple Silicon devices, you need to download and setup latest release of arm64 dylib file Anki-Android-Backend. You can either use the script tools/setup-anki-backend.sh or do manual setup:
Run the following command from project root and follow the instructions.
sh tools/setup-anki-backend.sh
- Download the latest release of arm64 dylib file Anki-Android-Backend
- Place it on your system somewhere accessible [I made a .anki folder in $HOME and placed it there]
- Add these two environment variables into .zshrc if you use zshell, .bashrc if bash etc.
export ANKIDROID_BACKEND_PATH={Path to dylib file}
export ANKIDROID_BACKEND_VERSION={Latest release version}
Please log out and log back in again, then start Android Studio again for the changes to take effect. You might get a permission denied error when running tests for first time, click on the right top ?
icon (ref) and follow the steps in order to run .dylib file.
If you get java.io.FileNotFoundException
, try using full path. Eg: '/Users/<username>/<path to dylib file>'
There are unit tests defined in the AnkiDroid/src/test
directory, with an extendable test superclass available using the Robolectric framework to make standard Android services available, including sqlite so you can operate on Anki Collections in your tests - each Collection created on the fly prior to each test method and deleted afterwards for isolation. You can run these tests by selecting them directly from AndroidStudio for individual tests or all tests from one file, or you can run them from the command line and generate a coverage report to verify the effect of your testing from the command line using:
./gradlew jacocoUnitTestReport
Afterwards you should find the coverage report in %AnkiDroidRoot%/AnkiDroid/build/reports/jacoco/jacocoUnitTestReport/html/index.html
Change com.ichi2.anki.NoteEditorTest
with test class name.
./gradlew AnkiDroid:testPlayDebugUnitTest --tests com.ichi2.anki.NoteEditorTest
Afterwards you should find the coverage report in %AnkiDroidRoot%/AnkiDroid/build/reports/tests/testPlayDebugUnitTest/index.html
In addition to the unit tests, several integration tests are defined in the AnkiDroid/src/androidTest
folder. You can run the tests from within Android Studio by simply right clicking on the test and running it against the chosen connected Android device (be sure to choose the icon with the Android symbol if there are multiple options shown), or from the command line against all connected devices at once using
./gradlew jacocoAndroidTestReport
After this you should find a coverage report that integrates unit and integration test execution in %AnkiDroidRoot%/AnkiDroid/build/reports/jacoco/jacocoTestReport/html/index.html
Note: Some of the connected tests involve the deletion of models, which will force a full-sync, so it's not recommended to try running the tests on your main device.
AnkiDroid defines custom lint rules to enforce code conventions and ensure that bugs are not introduced. We run these for every pull request on the Code Quality Checks / Lint Release (pull_request)
check.
To run these manually, open a terminal (either in Android Studio, or in %AnkiDroidRoot%
) and execute:
./gradlew lintRelease
AnkiDroid uses Ktlint for ensuring coding style and standard for Kotlin files. Every pull request runs the Ktlint check.
To run it manually, open a terminal (either in Android Studio, or in %AnkiDroidRoot%
) and execute:
./gradlew ktlintCheck
To format Kotlin files, run the following command in the terminal:
./gradlew ktlintFormat
Ensure that you run this command for formatting before sending a pull request.
If tests do not behave as expected, you can replace ./gradlew
by ./gradlew clean
to clean the directory before running test.
If on-device tests are slow or fail, it maybe because of conflict with your ankidroid collection. To avoid them, you can simply rename the "ankidroid" folder to "ankidroid_back" on your device when running test. You can then rename "ankidroid_back" to "ankidroid" when you are done
If you have the Android SDK installed, you should be able to compile from the command line even without installing Android Studio.
Windows: Open a command prompt in %AnkiDroidRoot%
and type:
gradlew.bat assembleDebug
Linux & Mac: Open a terminal in %AnkiDroidRoot%
and type:
./gradlew assembleDebug
An apk file signed with a standard "debug" key will be generated named "AnkiDroid-debug.apk"
in:
%AnkiDroidRoot%/AnkiDroid/build/outputs/apk/
AnkiDroid uses string resources for all translatable strings. These are managed by a platform called CrowdIn and Pontoon
<string name="example_key">Example value</string>
- Determine the correct resource XML file to edit (Guide) and expand it in Android Studio
- Add the string to the first file in the list (
/values
) and no other file. - Reference the string using the appropriate function
- Normally use
context.getString(R.string.example_key)
, some functions directly accept a string ID (@StringRes int
). - If the item is a plural, see Google's documentation for handling plurals
- Normally use
- Ignore any errors stating that these are missing translations after adding the string. If it compiles, it's fine. Maintainers will handle generating the strings after the pull request is completed.
- Take a screenshot of the string in context and add it to the Pull Request
- After the change is merged, a maintainer should add this screenshot to the platform to help translators with context
Example of the correct file to modify
- Avoid ending a string with a full stop unless there are multiple sentences
- Plurals should use
<plurals
. Only defineone
andother
for English as CrowdIn handles other plural types - Keys names should be lowercase using underscores
- Values should be unique in English. If the values are not unique, a
comment=""
attribute must be added to explain the differences to our translators. This is enforced by lint.
As described in the contributing wiki, AnkiDroid localization is done through the Crowdin and Pontoon platform. Developers should basically ignore all resource folders that have non-English locales. Edit the English strings only, and one of the project owners will handle the syncing of the translations. The process works as follows:
- Developers can freely add, delete, or modify strings in English to the resources folder (
values/
) and commit to git.- Renaming translation keys should be avoided if at all possible if the use is the same as it causes re-translation (appears as a delete-old/add-new to translators).
- If you update the value of an existing key it will not be marked as needing translation. Existing translations are preserved. If you need to change a string significantly, meaning it needs re-translation, you must use a new key and remove the old key.
- A project owner will run a script that pushes those changes to the crowdin and pontoon platform .
- Translators will see any new untranslated strings on crowdin and pontoon and submit their translations
- Before release a project owner will run another script which pulls all the current translations from crowdin and pontoon and overwrites the existing files
- These new files are then committed and pushed to GitHub
If you want to run several different versions of AnkiDroid side by side (e.g. as described in the FAQ on using profiles), you need to edit the package ID (from com.ichi2.anki) in the following places so that every version of AnkiDroid that you install has a unique ID:
-
applicationId
inAnkiDroid/build.gradle
-
applicationIdSuffix
inAnkiDroid/build.gradle
There are some convenient gradle command arguments that automates this process, which you can run from a shell from the top level AnkiDroid directory as follows:
./gradlew assemblePlayRelease -PcustomSuffix="suffix" -PcustomName="New app name"
See the separate wiki page for a description of the database structure
See the Release Procedure Wiki Page
Updating the main strings from Git to Crowdin is a pretty delicate thing. Uploading an empty string.xml for instance would delete all translations. And uploading changed strings delete as well all translations. This is the desired behavior in most cases, but when just some English typos are corrected this shouldn't destroy all translations.
In this case, it's necessary to:
- rebuild a download package at first (option "r" in script [currently broken])
- download all translations (update-translations.py)
- upload the changed strings
- reupload the translations (option "t" and language "all").
In march 2021, only 36% of the code were covered. That is that all of our test only runs 36% of the lines written. Erroneous change in the remaining 64% of lines would not be detected automatically and would impact user experience. One of the best way to improve long-term evolution of the code base would be to increase the coverage. You can go to https://codecov.io/gh/ankidroid/Anki-Android/tree/main/AnkiDroid/src/main/java/com/ichi2 and find functions and part of codes which are not yet tested, and write test for them.
Not 100% of the code have to be covered. In particular, it is useless to cover part of the code that should be rewritten, or ported to rust. Use your own judgement or discuss with maintainers if you have got a doubt.
Coverage can also be generated on your computer. This is mostly useful if you want to check that you correctly covered the code you expected to cover. You can do it by running tests with ./gradlew jacocoTestReport
and looking at the report on %AnkiDroidRoot%/AnkiDroid/build/reports/jacoco/jacocoUnitTestReport/html/index.html
in your browser
Starting on 2.16alpha66, there is a settings category called Developer options
. It suits some options aimed at developers and advanced users, and therefore may not be translated. Developer options is always shown on debug builds and are hidden on release versions.
To enable developer options on release versions, you should:
- Go to
Settings > About
- Tap AnkiDroid's logo 6 times
- Confirm
A tool like "SQLite Database Browser" is very useful to understand how your "collection.anki2" file is made, and to test SQL queries. To install it on Ubuntu:
sudo apt-add-repository ppa:linuxgndu/sqlitebrowser
sudo apt-get update
sudo apt-get install sqlitebrowser
Binaries for Windows and Mac can be found here.
If you are maintaining the project, you should monitor the dependabot PRs that come in against the "dependency-updates" branch.
The flow for dependency updates is a circle
- dependabot makes PRs to the dependency-updates branch
- if they pass CI and look good, a maintainer will merge them to the dependency branch
- periodically a maintainer will make a PR to the main development branch from the dependency-updates branch (now with a few dependency updates, most likely)
- if the PR to the main development branch passes, typically it will be squash-merged to main development branch
- the maintainer will take a local clone of the dependency-updates branch, rebase it to the main development branch
- the locally up-to-date dependency updates branch will be force-pushed back to the github dependency-updates branch
- the cycle repeats: dependabot makes PRs to the dependency-updates branch, etc
If dependabot needs configuration changes (to ignore a dependency or a version), the configuration should be done in the .github/dependabot.yaml
file, not via any UI interaction. The difference is that the YAML config is reviewable text while the UI modifies some opaque database and thus is not manageable or reviewable.
Sometimes you need to look at the HTML and javascript generated by AnkiDroid as it displays cards. There are two ways to do this, either by looking at the raw HTML as a file or by watching it live on the device
First you'll need to enable the HTML/Javascript Debugging
setting. If you are using a version before 2.16alpha77, you can find it on Settings - Advanced
, otherwise it's on Settings - Developer options
. To know how to find the Developer options category, see here
Then:
To look at the HTML & JavaScript live you can use Chrome WebView Remote debugging. This also works on the chromium-based Edge.
- Ensure that AnkiDroid's setting: Advanced - HTML/Javascript Debugging is enabled.
- Enable USB Debugging via the Android System Menu
- Connect your PC and phone via USB cable and approve the connection to your computer
- Open AnkiDroid and review the card you want to inspect
- Using Chrome on the same PC, browse to:
chrome://inspect
and you should see the WebView on your phone/emulator [for Edge, open:edge://inspect/#devices
] - Click
Inspect
for that WebView and you'll get a full Chrome remote debugging console. From this remote console, you can send JavaScript commands to AnkiDroid and edit HTML/CSS.
To look at the HTML as a file, open the card you want to inspect in the reviewer, and at the same time browse to /sdcard/AnkiDroid
- you should see card.html
. The file will be overwritten fresh every time a question or answer is displayed in the reviewer.
card.html
can be copied to your PC for inspection. You should also consider copying the collection.media
folder to help diagnose missing media errors
https://github.com/liriliri/eruda
This can be used inside the AnkiDroid to view console log like Chrome dev tools.
- Add following lines to Front/Back of card templates
<script src="https://cdn.jsdelivr.net/npm/eruda"></script>
<script>eruda.init();</script>
- Save the templates and open deck again.
- At bottom right corner, there will be button to open console log
- View Demo
Many browsers support a "custom search engine" function. These save time while developing.
Instructions to add a Search Engine to Chrome: https://zapier.com/blog/add-search-engine-to-chrome/
keyword | url | Description |
---|---|---|
issue | https://github.com/ankidroid/Anki-Android/issues/%s | Jump to Issue/PR |
commit | https://github.com/ankidroid/Anki-Android/commit/%s | Jump to commit hash |
gc | https://github.com/ankidroid/Anki-Android/search?q=%22Originally+reported+on+Google+Code+with+ID+%s%22&type=issues | Jump to Google Code Issue ID (rare, somtimes documented in source code) |
acra | https://couchdb.ankidroid.org/acralyzer/_design/acralyzer/index.html#/reports-browser/user/%s | Search for ACRA user UUID |
wiki | https://github.com/ankidroid/Anki-Android/search?q=%s&type=wikis | Search GitHub Wiki for keyword |
acrarep | https://couchdb.ankidroid.org/acralyzer/_design/acralyzer/index.html#/report-details/%s | Go to ACRA report (rare) |
Usage:
- F6/Select Search Bar
- Type in keyword
- Tab
- Type in search term
- Enter
On Ubuntu Linux:
- Install sqlite3 as above and meld: sudo apt-get install sqlite3 meld.
- Make sure my desktop and android have about the same clock time.
- Import the same shared deck to both.
- Perform the same review sequence on both at the same time.
- Copy the modified decks for comparison.
- Run the following command and check that times are not too different, and that are no other differences:
echo .dump | sqlite3 desktop_collection.anki2 > desktop.dump
echo .dump | sqlite3 android_collection.anki2 > android.dump
diff desktop.dump android.dump
In addition to maintaining the issues tracker, here are a few things that someone should perform once in a while, maybe every few months or so.
- Check that all files mention the GNU-GPL license.
- Add it to those who don't.
- From AnkiDroid's top directory, run
./tools/update-localizations.py
. - To build a new package on Crowdin, the script needs the Crowdin API key. Ask on the mailing list and we will send it to you.
- Commit and push.
- Check whether the versions are AnkiDroid's latest release. If not, contact the person responsible for this Market.
- Look for new alternative markets (especially in non-English languages) and upload there (please update the Wiki then).