Skip to content

Repository files navigation

It's Android's first OpenOffice Document Reader!

This is an Android frontend for our C++ OpenDocument.core library. Feel free to use it in your own project too, but please don't forget to tell us about it!

Get it on F-Droid Get it on Google Play

More information at https://opendocument.app/ and in the app itself.

Translations

Please help to translate on the https://crowdin.com/project/opendocument

Setup

A JDK and the android SDK, and that is the whole list - ./gradlew assembleProDebug works on a fresh checkout. There is no NDK to install, no python, and no conan: the app compiles no native code of its own, and odrcore arrives as an ordinary maven dependency (app.opendocument:odr-core-android) carrying both halves of its JNI bindings, the java classes and a libodr_jni.so per ABI.

It resolves from maven central rather than github packages on purpose - github packages demands authentication even for a public artifact, which no clean source builder such as f-droid can supply. No credentials are involved anywhere in the build.

Release signing

Debug builds need no setup. Release variants are signed only if the credentials are supplied from outside the repository, as gradle properties in ~/.gradle/gradle.properties or as environment variables:

gradle property environment variable meaning
odr.keystore ODR_KEYSTORE path to the keystore
odr.keystorePassword ODR_KEYSTORE_PASSWORD store password
odr.keyPasswordPro ODR_KEY_PASSWORD_PRO key password, defaults to store
odr.keyPasswordLite ODR_KEY_PASSWORD_LITE key password, defaults to store

Without them bundleProRelease and friends still build, just unsigned.

Releasing

The release workflow builds both signed bundles and uploads them to the Play Store internal track - the same thing the fastlane lanes did from a laptop. It is dispatched by hand, with the version it should build:

gh workflow run release.yml -f version=v4.14.0

Nothing triggers it on a tag. It runs as three jobs:

job what it does
build one gradle run producing all three signed flavors, archived on the run
upload one job per play flavor, handing its bundle to fastlane
record once both landed: tag the commit, draft the GitHub release

Lite and Pro always go out together, and nothing chooses one: they are the same app with ads and tracking switched off. Foss is built in the same run but uploaded nowhere - it is the APK on the GitHub release. That is what keeps a version on a single commit - the one the v* tag names and F-Droid builds.

Internal is the only track it uploads to. Anything wider - closed, open, production - is a promotion in the Play Console, which moves the same bundle and version code that was tested onto the wider track instead of uploading a second one, and is where the release notes get written. It is also where the review that a production release waits on actually happens, so the workflow finishing is not the same as the release being out.

If one flavor's upload fails, press "Re-run failed jobs". Only that upload runs again, against the bundle already built and signed, and record runs behind it. Re-running all jobs is the wrong button: Play refuses a version code it has already accepted, so the half that made it cannot go up twice. Past the roughly 30 days GitHub offers re-runs for, the way out is a new patch version for both flavors.

dry_run builds and signs both flavors without uploading either. It is the only run allowed to go without a version, and the only one leaving neither tag nor draft.

In its first seconds the run also refuses a version that has already gone out and one with no CHANGELOG.md section, which is what the release body is made of. .github/scripts/resolve-version.py and changelog-section.py decide both; run either by hand to see what a dispatch would do.

It needs these repository secrets:

secret contents
ODR_KEYSTORE_BASE64 base64 -i google_play.keystore
ODR_KEYSTORE_PASSWORD store password
ODR_KEY_PASSWORD_PRO key password for the reader-pro alias
ODR_KEY_PASSWORD_LITE key password for the reader alias
GOOGLE_PLAY_SERVICE_ACCOUNT play console service account json key

The service account key goes in as the json file the Play Console hands out, whole and unedited - base64 of it is accepted too, but nothing else is: the workflow checks it is a service_account key before the build starts rather than letting fastlane trip over it once the build is done.

Releasing from a laptop still works: fastlane android deployPro version:v4.8.0 builds and uploads, and takes an optional track: (... track:beta). The version can come from ODR_VERSION instead, but it cannot be left out - see below. That reads the key from fastlane_google_play.json in the repository root, as the Appfile says.

Tags

No tag triggers a build, and none is pushed before one. A tag written up front is a promise the run can fail to keep: v4.9.0's tag push run failed and the upload came from a dispatched run - the same commit that time, which was luck.

Tags are written afterwards instead, in two kinds:

tag who writes it what it means
build/<version> the release workflow, once both flavors are up this commit went to the internal track
v<version> publishing the drafted release this is what shipped

One build tag, not one per flavor: a single run builds both from a single checkout. A half uploaded release gets no tag at all, which is the honest answer - nothing yet could be published from it. A lane run from a laptop leaves none either.

The v* tag is written neither by hand nor by the workflow. record drafts a GitHub release named v<version> at the built commit, carrying the Foss APK - the sideloadable copy every release up to v4.6 has had, now the flavor that links nothing proprietary - a version.json naming the version and its code, and the version's CHANGELOG.md section above GitHub's generated list of pull requests. A draft creates no tag; publishing it does, at exactly that commit:

gh release edit v4.14.0 --draft=false

That is the whole manual step, and it waits because internal is not released: promotion to production, and the review it needs, happen in the Play Console days later. F-Droid tracks this repository through that release, so publishing any earlier would push a version to F-Droid users that Google may never release. version.json is what it reads: the version is nowhere in the tree, so releases/latest/download/version.json is the only place F-Droid can learn a version code from.

Versioning

The version is the release run's version input, and no version number is checked in anywhere. The workflow hands it to gradle as -Podr.version, and app/build.gradle derives both halves of it: v4.8.0 becomes version name 4.8.0 and version code 40800, two digits per part. Every part therefore has to stay below 100, which the build refuses rather than folding 4.100.0 onto the same code as 5.0.0. Nobody bumps it anywhere: a commit on main is not a release, and no number on main can describe one that already went out.

All three parts have to be spelled out. A two-part v4.7 used to be padded to 4.7.0, which meant one build could be tagged under two names, and the tags older than v4.8.0 are in both formats because of it. They are left as they are - a release asset is served from a URL carrying its tag name, and F-Droid rebuilds old versions from those names - so the rule only holds for what is tagged from here on.

Builds handed no version - local ones, PR builds, assembleProDebug - are 0.0.0. Nothing reads it: no code in the app looks at its own version, and only what the release workflow builds ever leaves the machine. Any build can be given a real one anyway, with ./gradlew assembleProRelease -Podr.version=v4.8.0.

Version codes up to 204 were counted by hand in AndroidManifest.xml, which is why the first derived one is a five digit jump. That is one way: the Play Store only ever accepts a code above the last one it saw.

About

It's Android's first OpenOffice Document Reader!

Topics

Resources

Stars

418 stars

Watchers

12 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages