{"id":13905587,"url":"https://github.com/getodk/collect","last_synced_at":"2026-04-10T21:23:01.382Z","repository":{"id":35925857,"uuid":"40213809","full_name":"getodk/collect","owner":"getodk","description":"ODK Collect is an Android app for filling out forms. It's been used to collect billions of data points in challenging environments around the world. Contribute and make the world a better place! ✨📋✨","archived":false,"fork":false,"pushed_at":"2026-01-14T14:54:00.000Z","size":106869,"stargazers_count":754,"open_issues_count":317,"forks_count":1413,"subscribers_count":57,"default_branch":"master","last_synced_at":"2026-01-14T18:22:22.480Z","etag":null,"topics":["android","data-collection","global-development","global-health","java","mhealth","mobile-data-collection","odk","social-impact","xforms"],"latest_commit_sha":null,"homepage":"https://docs.getodk.org/collect-intro","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/getodk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"docs/CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2015-08-04T23:21:33.000Z","updated_at":"2026-01-14T14:54:55.000Z","dependencies_parsed_at":"2023-01-16T23:31:06.734Z","dependency_job_id":"0e27172d-5c74-4e70-94a0-403b0fa44121","html_url":"https://github.com/getodk/collect","commit_stats":{"total_commits":12153,"total_committers":191,"mean_commits":63.6282722513089,"dds":0.6896239611618531,"last_synced_commit":"56801e48197b17b054687005f4db83839eea9d87"},"previous_names":[],"tags_count":345,"template":false,"template_full_name":null,"purl":"pkg:github/getodk/collect","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getodk%2Fcollect","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getodk%2Fcollect/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getodk%2Fcollect/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getodk%2Fcollect/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/getodk","download_url":"https://codeload.github.com/getodk/collect/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/getodk%2Fcollect/sbom","scorecard":{"id":43711,"data":{"date":"2025-08-11","repo":{"name":"github.com/getodk/collect","commit":"fb9872ba73f1390f01d3e81800486ce38a02ac24"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":6.2,"checks":[{"name":"Code-Review","score":10,"reason":"all changesets reviewed","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":10,"reason":"30 commit(s) and 24 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Security-Policy","score":10,"reason":"security policy file detected","details":["Info: security policy file detected: SECURITY.md:1","Info: Found linked content: SECURITY.md:1","Info: Found disclosure, vulnerability, and/or timelines in security policy: SECURITY.md:1","Info: Found text in security policy: SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"License","score":9,"reason":"license file detected","details":["Info: project has a license file: LICENSE.md:0","Warn: project license file does not contain an FSF or OSI license."],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Signed-Releases","score":0,"reason":"Project has not signed or included provenance with any releases.","details":["Warn: release artifact v2025.2.3 not signed: https://api.github.com/repos/getodk/collect/releases/238427437","Warn: release artifact v2025.2.2 not signed: https://api.github.com/repos/getodk/collect/releases/236988143","Warn: release artifact v2025.3.0-beta.2 not signed: https://api.github.com/repos/getodk/collect/releases/236988279","Warn: release artifact v2025.3.0-beta.1 not signed: https://api.github.com/repos/getodk/collect/releases/232935474","Warn: release artifact v2025.2.1 not signed: https://api.github.com/repos/getodk/collect/releases/232933714","Warn: release artifact v2025.2.3 does not have provenance: https://api.github.com/repos/getodk/collect/releases/238427437","Warn: release artifact v2025.2.2 does not have provenance: https://api.github.com/repos/getodk/collect/releases/236988143","Warn: release artifact v2025.3.0-beta.2 does not have provenance: https://api.github.com/repos/getodk/collect/releases/236988279","Warn: release artifact v2025.3.0-beta.1 does not have provenance: https://api.github.com/repos/getodk/collect/releases/232935474","Warn: release artifact v2025.2.1 does not have provenance: https://api.github.com/repos/getodk/collect/releases/232933714"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Binary-Artifacts","score":8,"reason":"binaries present in source code","details":["Warn: binary detected: collect_app/libs/bikram-sambat-1.8.0.jar:1","Warn: binary detected: gradle/wrapper/gradle-wrapper.jar:1"],"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-14T22:12:01.522Z","repository_id":35925857,"created_at":"2025-08-14T22:12:01.522Z","updated_at":"2025-08-14T22:12:01.522Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28583644,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-19T19:46:29.903Z","status":"ssl_error","status_checked_at":"2026-01-19T19:45:54.560Z","response_time":67,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","data-collection","global-development","global-health","java","mhealth","mobile-data-collection","odk","social-impact","xforms"],"created_at":"2024-08-06T23:01:19.237Z","updated_at":"2026-04-10T21:23:01.375Z","avatar_url":"https://github.com/getodk.png","language":"Kotlin","readme":"# ODK Collect\n\n![Platform](https://img.shields.io/badge/platform-Android-blue.svg)\n[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n[![Build status](https://circleci.com/gh/getodk/collect.svg?style=shield\u0026circle-token=:circle-token)](https://circleci.com/gh/getodk/collect)\n[![Slack](https://img.shields.io/badge/chat-on%20slack-brightgreen)](https://slack.getodk.org)\n\nODK Collect is an Android app for filling out forms. It is designed to be used in resource-constrained environments with challenges such as unreliable connectivity or power infrastructure. ODK Collect is part the ODK project, a free and open-source set of tools which help organizations author, field, and manage mobile data collection solutions. Learn more about ODK and its history [here](https://getodk.org/) and read about example ODK deployments [here](https://forum.getodk.org/c/showcase).\n\nODK Collect renders forms that are compliant with the [ODK XForms standard](https://getodk.github.io/xforms-spec/), a subset of the [XForms 1.1 standard](https://www.w3.org/TR/xforms/) with some extensions. The form parsing is done by the [JavaRosa library](https://github.com/getodk/javarosa) which Collect includes as a dependency.\n\nPlease note that the `master` branch reflects ongoing development and is not production-ready.\n\n## Table of Contents\n* [Learn more about ODK Collect](#learn-more-about-odk-collect)\n* [Release cycle](#release-cycle)\n* [Downloading builds](#downloading-builds)\n* [Suggesting new features](#suggesting-new-features)\n* Contributing\n  * [Contributing code](#contributing-code)\n  * [Contributing translations](#contributing-translations)\n* Developing\n  * [Setting up your development environment](#setting-up-your-development-environment)\n  * [Testing a form without a server](#testing-a-form-without-a-server)\n  * [Using APIs for local development](#using-apis-for-local-development)\n  * [Debugging JavaRosa](#debugging-javarosa)\n  * [Troubleshooting](#troubleshooting)\n  * [Test devices](#test-devices)\n* [Creating signed releases for Google Play Store](#creating-signed-releases-for-google-play-store)\n\n## Learn more about ODK Collect\n* ODK website: [https://getodk.org](https://getodk.org)\n* ODK Collect usage documentation: [https://docs.getodk.org/collect-intro/](https://docs.getodk.org/collect-intro/)\n* ODK forum: [https://forum.getodk.org](https://forum.getodk.org)\n* ODK developer Slack chat: [https://slack.getodk.org](https://slack.getodk.org)\n\n## Release cycle\n\nThe work to be done is continuously revised and prioritized in [the backlog](https://github.com/orgs/getodk/projects/9/views/8) by the Collect team. The majority of this is influenced by the priorities in [the ODK roadmap](https://getodk.org/roadmap). Releases are planned to happen every 2-3 months (resulting in ~4 releases a year). This goal is to balance the pace of delivery with keeping things stable for users while also minimizing the risk in each release.\n\nSometimes issues will be assigned to core team members before they are actually started (moved to \"in progress\") to make it clear who's going to be working on what.\n\nOnce the majority of high risk or visible work is done for a release, a new beta will then be released to the Play Store by [@lognaturel](https://github.com/lognaturel) and that will be used for regression testing by [@getodk/testers](https://github.com/orgs/getodk/teams/testers). If any problems are found, the release is blocked until we can merge fixes. Regression testing should continue on the original beta build (rather than a new one with fixes) unless problems block the rest of testing. Once the process is complete, [@lognaturel](https://github.com/lognaturel) pushes the releases to the Play Store following [these instructions](#creating-signed-releases-for-google-play-store).\n\nFixes to a previous release should be merged to a \"release\" branch (`v2023.2.x` for example) so as to leave `master` available for the current release's work. If hotfix changes are needed in the current release as well then these can be merged in as a PR after hotfix releases (generally easiest as a single PR for the whole hotfix release). This approach can also be used if work for the next release starts before the current one is out - the next release continues on `master` while the release is on a release branch.\n\nAt the beginning of each release cycle, [@grzesiek2010](https://github.com/grzesiek2010) updates all dependencies that have compatible upgrades available and ensures that the build targets the latest SDK.\n\n## Downloading builds\nPer-commit debug builds can be found on [CircleCI](https://circleci.com/gh/getodk/collect). Login with your GitHub account, click the build you'd like, then find the APK in the Artifacts tab.\n\nIf you are looking to use ODK Collect, we strongly recommend using the [Play Store build](https://play.google.com/store/apps/details?id=org.odk.collect.android). Current and previous production builds can be found in [Releases](https://github.com/getodk/collect/releases).\n\n## Suggesting new features\nWe try to make sure that all issues in the issue tracker are as close to fully specified as possible so that they can be closed by a pull request. Feature suggestions should be described [in the forum Features category](https://forum.getodk.org/c/features) and discussed by the broader user community. Once there is a clear way forward, issues should be filed on the relevant repositories. More controversial features will be discussed as part of the Technical Steering Committee's [roadmapping process](https://github.com/getodk/governance/blob/master/TSC-1/STANDARD-OPERATING-PROCEDURES.md#roadmap).\n\n## Contributing code\nAny and all contributions to the project are welcome. ODK Collect is used across the world primarily by organizations with a social purpose so you can have real impact!\n\nIssues tagged as [good first issue](https://github.com/getodk/collect/labels/good%20first%20issue) should be a good place to start. There are also currently many issues tagged as [needs reproduction](https://github.com/getodk/collect/labels/needs%20reproduction) which need someone to try to reproduce them with the current version of ODK Collect and comment on the issue with their findings.\n\nIf you're ready to contribute code, see [the contribution guide](docs/CONTRIBUTING.md).\n\n## Contributing translations\nIf you know a language other than English, consider contributing translations through [Transifex](https://explore.transifex.com/getodk/collect/).\n\nTranslations are updated right before the first beta for a release and before the release itself. To update translations, download the zip from https://explore.transifex.com/getodk/collect/. The contents of each folder then need to be moved to the Android project folders. A quick script like [the one in this gist](https://gist.github.com/lognaturel/9974fab4e7579fac034511cd4944176b) can help. We currently copy everything from Transifex to minimize manual intervention. Sometimes translation files will only get comment changes. When new languages are updated in Transifex, they need to be added to the script above. Additionally, `ApplicationConstants.TRANSLATIONS_AVAILABLE` needs to be updated. This array provides the choices for the language preference in settings. Ideally the list could be dynamically generated.\n\n## Setting up your development environment\n\n1. Download and install [Git](https://git-scm.com/downloads) and add it to your PATH\n\n1. Download and install [Android Studio](https://developer.android.com/studio/index.html) \n\n1. Fork the collect project ([why and how to fork](https://help.github.com/articles/fork-a-repo/))\n\n1. Clone your fork of the project locally. At the command line:\n\n        git clone https://github.com/YOUR-GITHUB-USERNAME/collect\n\n    If you prefer not to use the command line, you can use Android Studio to create a new project from version control using `https://github.com/YOUR-GITHUB-USERNAME/collect`.\n\n1. Use Android Studio to import the project from its Gradle settings. To run the project, click on the green arrow at the top of the screen.\n\n1. Windows developers: continue configuring Android Studio with the steps in this document: [Developing ODK Collect on Windows](docs/WINDOWS-DEV-SETUP.md).\n\n1. Make sure you can run unit tests by running everything under `collect_app/src/test/java` in Android Studio or on the command line:\n\n    ```\n    ./gradlew testDebug\n    ```\n\n1. Make sure you can run instrumented tests by running everything under `collect_app/src/androidTest/java` in Android Studio or on the command line:\n\n    ```\n    ./gradlew connectedAndroidTest\n    ```\n    **Note:** You can see the emulator setup used on CI in  `.circleci/config.yml`.\n\n## Customizing the development environment\n\n### Changing JVM heap size\n\nYou can customize the heap size that is used for compiling and running tests. Increasing these will most likely speed up compilation and tests on your local machine. The default values are specified in the project's `gradle.properties` and this can be overriden by your user level `gradle.properties` (found in your `GRADLE_USER_HOME` directory). An example `gradle.properties` that would give you a heap size of 4GB (rather than the default 1GB) would look like:\n\n```\norg.gradle.jvmargs=-Xmx4096m\n```\n\n## Testing a form without a server\nWhen you first run Collect, it is set to download forms from [https://demo.getodk.org/](https://demo.getodk.org/), the demo server. You can sometimes verify your changes with those forms but it can also be helpful to put a specific test form on your device. Here are some options for that:\n\n1. The `All question types` form from the default server is [here](https://docs.google.com/spreadsheets/d/1af_Sl8A_L8_EULbhRLHVl8OclCfco09Hq2tqb9CslwQ/edit#gid=0). You can also try [example forms](https://github.com/XLSForm/example-forms) and [test forms](https://github.com/XLSForm/test-forms) or [make your own](https://xlsform.org).\n\n1. Convert the XLSForm (xlsx) to XForm (xml). Use the [ODK website](http://getodk.org/xlsform/) or [XLSForm Offline](https://gumroad.com/l/xlsform-offline) or [pyxform](https://github.com/XLSForm/pyxform).\n\n1. Once you have the XForm, use [adb](https://developer.android.com/studio/command-line/adb.html) to push the form to your device (after [enabling USB debugging](https://www.kingoapp.com/root-tutorials/how-to-enable-usb-debugging-mode-on-android.htm)) or emulator.\n\t```\n\tadb push my_form.xml /sdcard/Android/data/org.odk.collect.android/files/projects/{project-id}/forms\n\t```\n\nIf you are using the demo project, kindly replace `{project_id}` with `DEMO`\n\n4. Launch ODK Collect and tap `+ Start new form`. The new form will be there.\n\nMore information about using Android Debug Bridge with Collect can be found [here](https://docs.getodk.org/collect-adb/).\n\n## Using APIs for local development\n\nCertain functions in ODK Collect depend on cloud services that require API keys or authorization steps to work.  Here are the steps you need to take in order to use these functions in your development builds.\n\n**Google Maps API**: When the \"Google Maps SDK\" option is selected in the \"User interface\" settings, ODK Collect uses the Google Maps API for displaying maps in the geospatial question types (GeoPoint, GeoTrace, and GeoShape).  To enable this API:\n  1. [Get a Google Maps API key](https://developers.google.com/maps/documentation/android-api/signup).  Note that this requires a credit card number, though the card will not be charged immediately; some free API usage is permitted.  You should carefully read the terms before providing a credit card number.\n  1. Edit or create `secrets.properties` and set the `GOOGLE_MAPS_API_KEY` property to your API key.  You should end up with a line that looks like this:\n    ```\n    GOOGLE_MAPS_API_KEY=AIbzvW8e0ub...\n    ```\n\n**Mapbox Maps SDK for Android**: When the \"Mapbox SDK\" option is selected in the \"User interface\" settings, ODK Collect uses the Mapbox SDK for displaying maps in the geospatial question types (GeoPoint, GeoTrace, and GeoShape).  To enable this API:\n  1. [Create a Mapbox account](https://www.mapbox.com/signup/).  Note that signing up with the \"Pay-As-You-Go\" plan does not require a credit card.  Mapbox provides free API usage up to the monthly thresholds documented at [https://www.mapbox.com/pricing](https://www.mapbox.com/pricing).  If your usage exceeds these thresholds, you will receive e-mail with instructions on how to add a credit card for payment; services will remain live until the end of the 30-day billing term, after which the account will be deactivated and will require a credit card to reactivate.\n  2. Find your access token on your [account page](https://account.mapbox.com/) - it should be in \"Tokens\" as \"Default public token\".\n  3. Edit or create `secrets.properties` and set the `MAPBOX_ACCESS_TOKEN` property to your access token.  You should end up with a line that looks like this:\n    ```\n    MAPBOX_ACCESS_TOKEN=pk.eyJk3bumVp4i...\n    ```\n  4. Create a new secret token with the \"DOWNLOADS:READ\" secret scope and then add it to `secrets.properties` as `MAPBOX_DOWNLOADS_TOKEN`.\n\n*Note: Mapbox will not be available as an option in compiled versions of Collect unless you follow the steps above. Mapbox will also not be available on x86 devices as the native libraries are excluded to reduce the APK size. If you need to use an x86 device, you can force the build to include x86 libs by include the `x86Libs` Gradle parameter. For example, to build a debug APK with x86 libs: `./gradlew assembleDebug -Px86Libs`.*\n\n## Debugging JavaRosa\n\nJavaRosa is the form engine that powers Collect. If you want to debug or change that code while running Collect you can deploy it locally with Maven (you'll need `mvn` and `sed` installed):\n\n1. Build and install your changes of JavaRosa (into your local Maven repo):\n\n```bash\n./gradlew publishToMavenLocal\n```\n\n1. Change `const val javarosa = javarosa_online` in `Dependencies.kt` to `const val javarosa = javarosa_local`\n\n## Troubleshooting\n\n#### Error when running Robolectric tests from Android Studio on macOS: `build/intermediates/bundles/debug/AndroidManifest.xml (No such file or directory)`\n\u003e Configure the default JUnit test runner configuration in order to work around a bug where IntelliJ / Android Studio does not set the working directory to the module being tested. This can be accomplished by editing the run configurations, Defaults -\u003e JUnit and changing the working directory value to $MODULE_DIR$.\n\n\u003e Source: [Robolectric Wiki](https://github.com/robolectric/robolectric/wiki/Running-tests-in-Android-Studio#notes-for-mac).\n\n#### Android Studio Error: `SDK location not found. Define location with sdk.dir in the local.properties file or with an ANDROID_HOME environment variable.`\nWhen cloning the project from Android Studio, click \"No\" when prompted to open the `build.gradle` file and then open project.\n\n#### Execution failed for task ':collect_app:transformClassesWithInstantRunForDebug'.\n\nWe have seen this problem happen in both IntelliJ IDEA and Android Studio, and believe it to be due to a bug in the IDE, which we can't fix.  As a workaround, turning off [Instant Run](https://developer.android.com/studio/run/#set-up-ir) will usually avoid this problem. The problem is fixed in Android Studio 3.5 with the new [Apply Changes](https://medium.com/androiddevelopers/android-studio-project-marble-apply-changes-e3048662e8cd) feature.\n\n#### Moving to the main view if user minimizes the app\nIf you build the app on your own using Android Studio `(Build -\u003e Build APK)` and then install it (from an `.apk` file), you might notice this strange behaviour thoroughly described: [#1280](https://github.com/getodk/collect/issues/1280) and [#1142](https://github.com/getodk/collect/issues/1142).\n\nThis problem occurs building other apps as well.\n\n#### gradlew Failure: `FAILURE: Build failed with an exception.`\n\nIf you encounter an error similar to this when running `gradlew`:\n\n```\nFAILURE: Build failed with an exception\n\nWhat went wrong:\nA problem occurred configuring project ':collect_app'.\n\u003e Failed to notify project evaluation listener.\n   \u003e Could not initialize class com.android.sdklib.repository.AndroidSdkHandler\n```\n\nYou may have a mismatch between the embedded Android SDK Java and the JDK installed on your machine. You may wish to set your **JAVA_HOME** environment variable to that SDK. For example, on macOS:\n\n`export JAVA_HOME=\"/Applications/Android\\ Studio.app/Contents/jre/Contents/Home/\"\n`\n\nNote that this change might cause problems with other Java-based applications (e.g., if you uninstall Android Studio).\n\n#### gradlew Failure: `java.lang.NullPointerException (no error message).`\nIf you encounter the `java.lang.NullPointerException (no error message).` when running `gradlew`, please make sure your Java version for this project is Java 17.\n\nThis can be configured under **File \u003e Project Structure** in Android Studio, or by editing `$USER_HOME/.gradle/gradle.properties` to set `org.gradle.java.home=(path to JDK home)` for command-line use.\n\n#### `Unable to resolve artifact: Missing` while running tests\n\nThis is encountered when Robolectric has problems downloading the jars it needs for different Android SDK levels. If you keep running into this you can download the JARs locally and point Robolectric to them by doing:\n\n```\n./download-robolectric-deps.sh\n```\n\n## Test devices\n\nDevices that @getodk/testers have available for testing are as follows:\n\n* Xiaomi Redmi 9T 4GB - Android 10\n* Pixel 7a 8GB - Android 14\n* LG Nexus 5X 2GB - Android 8.1\n* Samsung Galaxy M12 4GB - Android 11\n* Samsung Galaxy M23 4GB - Android 14\n* Xiaomi Redmi 7 3GB - Android 10\n* Pixel 6a 6GB - Android 13\n* Pixel 3a 4GB - Android 12\n* Huawei Y560-L01 1GB - Android 5.1\n\n## Creating signed releases for Google Play Store\nMaintainers keep a folder with a clean checkout of the code and use [jenv.be](https://www.jenv.be) in that folder to ensure compilation with Java 17.\n\n### Release prerequisites:\n\n- a`local.properties` file in the root folder with the following:\n  ```\n  sdk.dir=/path/to/android/sdk\n  ```\n\n- the keystore file and passwords\n\n- a `secrets.properties` file in the root project folder folder with the following:\n  ```\n  // secrets.properties\n  RELEASE_STORE_FILE=/path/to/collect.keystore\n  RELEASE_STORE_PASSWORD=secure-store-password\n  RELEASE_KEY_ALIAS=key-alias\n  RELEASE_KEY_PASSWORD=secure-alias-password\n  ```\n\n- a `google-services.json` file in the `collect_app/src/odkCollectRelease` folder. The contents of the file are similar to the contents of `collect_app/src/google-services.json`.\n\n### Release checklist:\n\n- update translations\n- make sure CI is green for the chosen commit\n- run `./gradlew releaseCheck`. If successful, a signed release will be at `collect_app/build/outputs/apk` (with an old version name)\n- verify a basic \"happy path\": scan a QR code to configure a new project, get a blank form, fill it, open the form map (confirms that the Google Maps key is correct), send form\n- run `./benchmark.sh` with a real device connected to verify performance\n  - To run benchmarks a project will need to be set up in Central with the benchmark forms and app users. The forms and entities needed for that are available [here](https://drive.google.com/drive/folders/1dPLvDY0LhVX-5qTUEs6EDoraDnLpUS0g?usp=drive_link).\n- verify new APK can be installed as update to previous version and that above \"happy path\" works in that case also\n- create and publish scheduled forum post with release description\n- write Play Store release notes, include link to forum post\n- when creating a major release:\n  - Tag the commit for the release (`vX.X.0`)\n  - Run `./create-release.sh \u003clast release version code\u003e \u003crelease tag\u003e`\n- when creating a patch release:\n  - Tag the commit for the patch release (`vX.X.X`)\n  - (If beta has started for next release) Tag the commit for the beta release (`vX.X.X-beta.X`)\n  - Run `./create-release.sh \u003clast release version code\u003e \u003cpatch release tag\u003e \u003cbeta release tag\u003e`\n- when creating a beta release:\n  - Tag the commit for the beta release (`vX.X.X-beta.X`)\n  - Run `./create-release.sh \u003clast release version code\u003e \u003cbeta release tag\u003e`\n- add a release to Github [here](https://github.com/getodk/collect/releases), generate release notes and attach the APK\n- upload APK(s) to Play Store\n  - When creating a hotfix, the beta APK should be uploaded second as it will have a higher version code\n- backup dependencies for the release by downloading the `vX.X.X.tar` artifact from the `create_dependency_backup` job on Circle CI (for the release commit) and then uploading it to [this folder](https://drive.google.com/drive/folders/1_tMKBFLdhzFZF9GKNeob4FbARjdfbtJu?usp=share_link)\n- backup a self signed release APK by downloading the `selfSignedRelease.apk` from the `build_release` job on Circle CI (for the release commit) and then upload to [this folder](https://drive.google.com/drive/folders/1pbbeNaMTziFhtZmedOs0If3BeYu3Ex5x?usp=share_link)\n\n## Compiling a previous release using backed-up dependencies\n\n1. Download the `.tar` for relevant release tag\n2. Extract `.local-m2` into the project directory:\n    ```bash\n    tar -xf maven.tar -C \u003ccollect project directory\u003e\n    ```\n   \nThe project will now be able to fetch dependencies that are no longer available (but were used to compile the release) from the `.local-m2` Maven repo.\n\n","funding_links":[],"categories":["Java","Kotlin"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgetodk%2Fcollect","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgetodk%2Fcollect","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgetodk%2Fcollect/lists"}