What I Watch #1 - F-Droid
Following Google’s decision to lock Android out of the open-source, I’ve decided not only to make an Android app but also to publish it on F-Droid. It will be all for nothing? As I write this post, there are 96 days to go until that fateful moment, so only time will tell.
So, first thing first: my app is called What I Watch (repo here) and helps you keep track of the series you’re currently watching. Not only TV series because it uses the TvMaze API, that is a sort of community driven things for what I’ve understood, there are also anime and other stuff but it would have ben too long to put in as short description for the store page.
Enough with the preamble, let’s get to the point. To publish your app on FDroid you need to:
- have an open source app
- fork the
fdroiddatarepo from GitLab - add a
ymlfile with the istruction to build your app - make a merge request to add you app to the FDroid repo
Now that I’ve managed it, it’s not a difficult task. But I’m not a super-talented developer like you readers, so I struggled to figure out what to do. So thank you, Linsui, for babay-sit me through the whole process (I couldn’t find any social media links or anything else on your profile, so I can’t mention you directly, but I really do thank you so much).
First steps
As said, the first thin you need is the URL to the git repo of your app. The repo could be anywhere on the internet but it must be open source and reachable from the FDroid servers.
Then create an account on GitLab and fork the fdroiddata repository. Now you can clone the repo on your PC, you’ll have something like this:
git clone https://gitlab.com/YOUR_USERNAME/fdroiddata.git
Now in the metadata folder create a yml file named as your Andorid app unique name. It will be something like com.example.whatever.yml. Inside this file we need to put the instuctions to the FDroid server on how to reach our repo and how to build the app.
Publish a Flutter app
I’m not quite sure how to go about this, so first I’ll provide an example of the YML file based on my experience, and then I’ll list, in the order they come to mind, the things I’ve discovered along the way.
Categories:
- List of categories
- Your app belongs to
License: Your app license (es: AGPL-3.0-or-later)
AuthorName: Your name
SourceCode: https://your-git-repo-url
IssueTracker: https://your-issue-traker-url
AutoName: your app name
RepoType: git
Repo: https://your-git-repo-url
Builds:
- versionName: 0.1.1
versionCode: 11
commit: your-commit-sha
output: build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk
srclibs:
- flutter@stable
rm:
- linux
prebuild:
- flutterVersion=$(cat .flutter-version)
- '[[ $flutterVersion ]]'
- git -C $$flutter$$ checkout -f $flutterVersion
- export PUB_CACHE=$(pwd)/.pub-cache
- $$flutter$$/bin/flutter config --no-analytics
- $$flutter$$/bin/flutter pub get
scanignore:
- .pub-cache
build:
- export PUB_CACHE=$(pwd)/.pub-cache
- $$flutter$$/bin/flutter build apk --release --split-per-abi --target-platform=android-arm
- versionName: 0.1.1
versionCode: 12
commit: your-commit-sha
output: build/app/outputs/flutter-apk/app-arm64-v8a-release.apk
srclibs:
- flutter@stable
rm:
- linux
prebuild:
- flutterVersion=$(cat .flutter-version)
- '[[ $flutterVersion ]]'
- git -C $$flutter$$ checkout -f $flutterVersion
- export PUB_CACHE=$(pwd)/.pub-cache
- $$flutter$$/bin/flutter config --no-analytics
- $$flutter$$/bin/flutter pub get
scanignore:
- .pub-cache
build:
- export PUB_CACHE=$(pwd)/.pub-cache
- $$flutter$$/bin/flutter build apk --release --split-per-abi --target-platform=android-arm64
- versionName: 0.1.1
versionCode: 13
commit: your-commit-sha
output: build/app/outputs/flutter-apk/app-x86_64-release.apk
srclibs:
- flutter@stable
rm:
- linux
prebuild:
- flutterVersion=$(cat .flutter-version)
- '[[ $flutterVersion ]]'
- git -C $$flutter$$ checkout -f $flutterVersion
- export PUB_CACHE=$(pwd)/.pub-cache
- $$flutter$$/bin/flutter config --no-analytics
- $$flutter$$/bin/flutter pub get
scanignore:
- .pub-cache
build:
- export PUB_CACHE=$(pwd)/.pub-cache
- $$flutter$$/bin/flutter build apk --release --split-per-abi --target-platform=android-x64
AutoUpdateMode: Version
UpdateCheckMode: Tags
VercodeOperation:
- '%c * 10 + 1'
- '%c * 10 + 2'
- '%c * 10 + 3'
UpdateCheckData: pubspec.yaml|version:\s.+\+(\d+)|.|version:\s(.+)\+
CurrentVersion: 0.1.1
CurrentVersionCode: 13
ABI Split & VercodeOperation
This is the thing that annoyed me the most, so it’s only natural that it’s the first thing that comes to mind.
Once the build process is complete, the FDroid pipeline expects to receive three APKs: arm, arm64, and x86_64. These three files must follow a naming convention for the version code that differs from Flutter’s default one. You must therefore modify the version in your pubspec.yaml, change the Gradle file to build the APKs with the correct naming convention and include the same logic in the YML file.
So in the pubspec.yaml you have to add the version for the FDroid’s pipeline after the version you are using with a +:
version: '0.1.1+1'
In the android/app/build.gradle.kts you have to add this to map the APKs versions:
import com.android.build.gradle.internal.api.ApkVariantOutputImpl
[...]
val abiCodes = mapOf("armeabi-v7a" to 1, "arm64-v8a" to 2, "x86_64" to 3)
android.applicationVariants.configureEach {
val variant = this
variant.outputs.forEach { output ->
val abiVersionCode = abiCodes[output.filters.find { it.filterType == "ABI" }?.identifier]
if (abiVersionCode != null) {
(output as ApkVariantOutputImpl).versionCodeOverride = variant.versionCode * 10 + abiVersionCode
}
}
}
And in the yml, as you can see above, three entry in the Build section and the VercodeOperation section to tell the FDroid server how to build and what to expect.
So, for version 0.1.1+1 of the app, we will have version codes 11, 12, and 13; for version 0.1.2+2, 21, 22, and 23; and so on.
Fastlane
This is a folder we need to create in the root directory of your repository to generate descriptions, previews, icons, etc., for the store page. You can find more details here.
.flutter-version
Fdroid doesn’t like to use the latest version of Flutter just because it’s cool, so we need to create a .flutter-version file in the project root containing the version of Flutter to be used in the build, and then reference it in the yml file.
Take a look at this line in the YML file:
- flutterVersion=$(cat .flutter-version)
Antifeatures
F-Droid have a exaustive explantation of what an antifeature is here. Just see if something in your app matches and add it to a list at the topof your .yml file.
AntiFeatures:
NonFreeNet:
en-US: Data is sourced from 3rd party public API.
Commit SHA
I don’t remember why I’ve written this down for the the post but in the commit: section of the yml you need to put the sha of the commit. It’s like obvious, right?
rm:
In the yml file, under the Builds section, there is a subsection labeled rm:. This section tells F-Droid to delete certain files after cloning the Git repository. It is used to free up space on the server by removing items that are not needed for the build process. In my case, I delete the linux folder because it is not required for the Android version of the app.
Merge, Forrest! Merge!
Once your yml file is ready you can push the file in your forked repo, a build pipeline should start. If all is on the green side you can proceed to make a merge request. The naming of the request for a new app is “New App: app name”, in the merge form you also need to complete the template provided with the correct info.
Aaaand that’s all for now. Ciao!
Comments
You can use your Mastodon or other fediverse account to comment on this article by replying to the associated post . Note that older Fediverse posts are commonly deleted to conserve server resources, so the linked post may be broken if this article is sufficiently old.