Table of Contents
- Development Environment
- Android Studio
- Tools
- Currently used SDK versions
- Docker
- Codium (Visual Studio Code)
- Getting the code
- Build flavors and types
- Running tests
- Building and installing the Gadgetbridge apk
- Short Introduction to Gadgetbridge's Source Code
- Short introduction to git and the related workflow
- Forking the repository
- Cloning your forked copy
- Master branch
- Creating a branch
- Committing into the branch
- Pushing your local branch to your remote on the server
- Switching branches
- Seeing changes between branches
- Syncing with the Gadgetbridge project
- Rebasing on top of the master
- Resetting the master to the upstream
- Squashing commits via git rebase
- Force pushing
- Testing a pull request
- Save a snapshot without creating a commit in your working branch
- An UNDO for git
Development Environment
Android Studio
The core team uses Debian GNU/Linux + Android
Studio, latest version. It is
also available for Windows and Mac, but we never used that. You can use any
Android device connected to USB to run your code straight from Android Studio
on the device. Be aware that you cannot install your own built version
(mainDebug
) and F-Droid's at the same time due to the conflict in Pebble
provider, but you can use the mainNopebble
or banglejsDebug
build types
for that, see Build flavors and types. You can
export and import your database, though.
Tools
You need to install
- a Java Development Kit (JDK)
- git
- adb (Android Debug Bridge, included in Android Studio)
On Ubuntu, you can use following commands
sudo apt-get install openjdk-17-jdk git adb
Currently used SDK versions
(as of October 2023)
- compileSdkVersion 33
- minSdkVersion 21
- targetSdkVersion 33
Frequent question is: The minSdkVersion is rather low... ?
Do not upgrade the SDK, including the minSdkVersion, we still would like to keep support for Android 5.0 (Lollipop). Another problem with upgrading is that since we have a lot of deep integration into the Android system (to make sure that calls, notifications, media... can be controlled, displayed and so on from a bluetooth tethered device), updating the SDK needs a lot of testing on many devices and Android versions, since every time we have bumped the SDK it caused (a lot of hidden) problems for us. Another problem is that we cannot downgrade the SDK anymore once it has been bumped.
Another frequent question: What about Kotlin or Dart&Flutter?
Kotlin could be nice but this project started long before Kotlin was added as Android supported alternative to the Java compiler. Rewrite is unfortunately not what we are planning on. Making APK larger by adding Kotlin for some parts is also not preferred.
Flutter is not a great choice from a software freedom perspective: https://github.com/flutter/flutter/issues/104219
Docker
There are also docker images available with all required tools inside.
To start a container with USB connection to a phone, execute e.g.
host $ docker run -it --rm --device=/dev/bus --net=host -v ${PWD}:/src androidsdk/android-30:latest bash
container # cd /src
This mounts the current directory into the container under /src
and then allows to build and install the results (see below).
Codium (Visual Studio Code)
This has limitations but can be a way to develop too. You need to install Codium (i do not think you would go for the VSCode with telemetry...).
Required plugins that must be installed:
- Android by adelphes ( https://open-vsx.org/extension/adelphes/android-dev-ext ), this provides the build/debug/logcat functions.
- Language Support for Java(TM) by Red Hat ( https://open-vsx.org/vscode/item?itemName=redhat.java ), provides the code navigation with F12.
Follow this article to create a Launch config: https://www.linkedin.com/pulse/building-android-apps-using-microsofts-vs-code-instead-saamer-mansoor/ , make sure when Opening a folder to open the actuall root of the project, not the ./app as suggested in the article.
- for debugging, open the Logcat console by Ctrl+Shift+P → Android: View Logcat.
Getting the code
git clone https://codeberg.org/Freeyourgadget/Gadgetbridge.git
(after you did that once, you can use git pull
to get the newest Gadgetbridge code)
Alternatively you can use Android Studio to clone the Gadgetbridge repository.
Build flavors and types
Product Flavors
Main
There is the main
flavor which is used for normal day to day coding, F-droid
releases, for Nightly releases and so on.
Banglejs
Then there is the banglejs
flavor. This particular custom flavor makes use of
some extra features, together with added permission for networking. Do note
that this permission is not enabled for our normal Gadgetbridge releases and is
only available on the Bangle.js specific build, as the Bangle.js watch is built
around their online application repository and it requires internet
connectivity for proper function. We believe that this cooperation with the
Bangle project is very useful for both sides and that communities around both
Bangle.js and Gadgetbridge can benefit from this connection. We can already see
some contributions that help to clarify for example requests for permissions,
making Gadgetbridge more user friendly during the initial onboarding
experience. Bangle implementation also got new communication intents to be
able to share data from/to other Android apps.
Build Types
There are several build types:
release
- for releases (F-droid...)debug
- for developing and local testingnightly
- for nightly release in our own F-droid repo. It cannot be installed if you already have either the Gadgetbridge or the Pebble app installed, due to a conflict in the Pebble provider.nopebble
- for nightly release in our own F-droid repo. This version has the Pebble provider renamed to prevent conflicts, so some Pebble related integrations will not work, but it can be installed alongside existing Gadgetbridge installation.
Running tests
All tests:
./gradlew test
Running just one specific tests:
./gradlew :app:testBangleDebugUnitTest :app:testMainDebugUnitTest
Or run just one particular test class:
./gradlew :app:testMainDebugUnitTest --tests nodomain.freeyourgadget.gadgetbridge.test.FitProTests
Building and installing the Gadgetbridge apk
If you only want to compile the code, you can simply execute
./gradlew assembleMainDebug
or install it:
./gradlew installMainDebug
Android Studio does all this automatically when you press the Run
or Debug
button, you may have to open the root directory of the repo for the
configuration to be loaded.

Short Introduction to Gadgetbridge's Source Code
Important Classes
- PebbleSupport (see class hierarchy)
- PebbleProtocol (pebble communication protocol)
- PebbleIOThread (background thread for pebble communication)
- MiBandSupport (see class hierarchy)
- BtLEQueue (communication queue for Bluetooth LE devices)
- DeviceCommunicationService (Android service channeling the communication with devices)
- DeviceService ("client side API" of DeviceCommunicationService, see GBApplication#deviceService())
- GBDevice (generic, device-unspecific)
- DeviceCoordinator (see class hierarchy)
- ControlCenterv2 (main activity)
Examples
Short article demonstrating adding a simple database/UI feature: Adding battery info screen to Gadgetbridge.
Overview
All the details about the communication/protocol with a concrete device (Pebble, Mi Band, ...) is inside the "Concrete Device Impl." component, that is, the concrete implementations of the DeviceSupport
interface. Only the DeviceCommunicationService
has access to those -- clients (typically Activities) talk to the DeviceService
interface in order to communicate with the devices.
Bluetooth Error Codes
Logging
We use slf4j
for logging, so just use LoggerFactory.getLogger(Your.class)
and log away. The output will be written to the Android Log (so you can get it with logcat or Android Studio) as well as to the file /sdcard/Android/data/nodomain.freeyourgadget.gadgetbridge/files/gadgetbridge.log
. File logging needs to be enabled in Gadgetbridge's preferences, first.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
...
private static final Logger LOG = LoggerFactory.getLogger(Your.class);
...
LOG.error("Error accessing database", e);`
GBDevice is cached in activities
Most activities receive the GBDevice via Intent during activity invocation. Be aware that the device in the activity is a copy of the device and thus it might not have the same updated information. So if you for example subscribe to device state updates and then want to get current data from the device (for example device.getBatteryLevel()
, make sure to update the activities local copy of GBDevice:
BroadcastReceiver commandReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
LOG.debug("device receiver received " + intent.getAction());
if (intent.getAction().equals(GBDevice.ACTION_DEVICE_CHANGED)) {
GBDevice newDevice = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
if (newDevice.equals(device)) {
device = newDevice; // ← update local copy of the GBDevice
//do your stuff here...
}
}
}
};
Information Display
Use one of the nodomain.freeyourgadget.gadgetbridge.util.GB#toast()
methods to display information to the user.
- the toast when given an exception also logs warnings and errors, so with the toast you not have to use LOG afterwards.
- can safely be called from a background thread
import nodomain.freeyourgadget.gadgetbridge.util.GB;
...
GB.toast("My toast message", Toast.LENGTH_SHORT, GB.ERROR, e);

Database
We use greenDAO
for database access. See nodomain.freeyourgadget.gadgetbridge.daogen.GBDaoGenerator
for entity definition and generation. Do note that we use greenDAO in version 2, the official greenDAO documentation already mentions version 3.
To add a column to a database, simply add a new field to a particular class in nodomain.freeyourgadget.gadgetbridge.daogen.GBDaoGenerator
, then build the project, which will trigger generating of corresponding ...dao.class files. Also, make sure to set a new schema version Schema schema = new Schema(xx
... and prepare a migration file in src/main/java/nodomain/freeyourgadget/gadgetbridge/database/schema/
.
Icons
All icons should be provided as vector drawables, do not use PNGs anymore. If you are drawing the original design in SVG, make sure to export as regular uncompressed SVG, because Android Studio handles these files better. Then, import it to Android Studio via right click in Project panel → New → Vector Asset → Local file. Then, use Avocado optimizer for Android VectorDrawable (VD) and AnimatedVectorDrawable (AVD) xml files. Avocado rewrites the VectorDrawable using the smallest number of s and s possible, reducing their file sizes and making them faster to parse and draw at runtime.
Device icons
For device icons (ic_device_xxx, ic_device_xxx_disabled), start from an existing icon's SVG source - you can use the Galaxy Buds icon, here is the source for the normal state and here for the disabled state. Modify this SVG (remove the buds and draw the device you need), save, then import into Android Studio as described above, then optimize with avocado. Then, in Android Studio, change the dimensions inside the XML file to this:
android:width="45sp"
android:height="45sp"
android:viewportWidth="30"
android:viewportHeight="30"
and if you want to optimize it even further, change the strokeWidth
to remove unnecessary precision, for example from strokeWidth="0.498675"
to strokeWidth=0.5"
. Look at the other device icons for examples.
Colors
The colors.xml
defines colors which are then used throughout the app. See also styles.xml
.
Colors of windows, text...
Color | Name | |
---|---|---|
![]() |
#ff3d00 |
primary_light |
![]() |
#dd2c00 |
primarydark_light |
![]() |
#ff3d00 |
primary_dark |
![]() |
#dd2c00 |
primarydark_dark |
![]() |
#0091ea |
accent |
![]() |
#000000 |
primarytext_light |
![]() |
#ffffff |
primarytext_dark |
![]() |
#ff808080 |
secondarytext |
![]() |
#ffd0d0d0 |
tertiarytext_light |
![]() |
#ff606060 |
tertiarytext_dark |
![]() |
#000000 |
tertiarytext_black |
![]() |
#1f000000 |
divider |
Colors used in charts
Color | Name | |
---|---|---|
![]() |
#ffab40 |
chart_heartrate |
![]() |
#8B0000 |
chart_heartrate_alternative |
![]() |
#fadab1 |
chart_heartrate_fill |
![]() |
#0071b7 |
chart_deep_sleep_light |
![]() |
#4c5aff |
chart_deep_sleep_dark |
![]() |
#46acea |
chart_light_sleep_light |
![]() |
#b6bfff |
chart_light_sleep_dark |
![]() |
#60bd6d |
chart_activity_light |
![]() |
#59b22c |
chart_activity_dark |
![]() |
#545254 |
chart_not_worn_light |
![]() |
#d8d9d8 |
chart_not_worn_dark |
Colors used in tables
Color | Name | |
---|---|---|
![]() |
#FFEDEDED |
alternate_row_background_light |
![]() |
#545254 |
alternate_row_background_dark |
Preferences
Preferences that are not specific to the user's device but are for the whole application are in Prefs prefs = GBApplication.getPrefs();
.
User's device specific preferences - that is, each devices own preferences - go into SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress());
One should try to re-use existing preferences. When adding new preferences, these should be made generic so they can be re-used, rather then being vendor or device specific, if it is not required. So for example RGB color
settings can change color on a watch, led, fm transmitter or a wireless ear buds.
Complete rundown of steps of adding a feature
The Adding battery info screen to Gadgetbridge blog post is a friendly documentation of the steps needed to add a new feature to Gadgetbridge and it touches on several important parts - adding a new database table, hooking up device bluetooth events, storing data, adding a chart screen and so on.
Translations
Do not add translations by editing the language variants of strings.xml directly as this creates merge conflicts between Codeberg git repo and Weblate git repo. Always use Weblate, as per info in the wiki.
Copyright
All source files should contain the relevant copyright header. As an example, for java files created specifically for Gadgetbridge:
/* Copyright (C) 2023 John Smith
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
A real name is not mandatory - a nickname can be used.
For source code copied or adapted from other opensource projects, the relevant copyright header should be kept, not violating that project's license.
Short introduction to git and the related workflow
Git can be intimidating and being familiar with it takes some getting use to. There are numerous resources on the internet, like the Dangit, Git!?! or Git documentation, Git from the bottom up, Things I wish everyone knew about Git.
Here is a short, opinionated, by no means comprehensive guide to the typical steps that are needed when working with Gadgetbridge code and repo. Use this as a simple guide but do make sure to read-up on git more in other places, in documentation and so on. If you spot an issue, please edit it too, to help other to be more confident and comfortable when using git.
Forking the repository
Initial step: fork the Gadgetbridge repo on Codeberg to have your own repo. You do this by using the Fork
button on the Gadgetbridge repo page. This will create your copy of the repo in Codeberg, under your username
. The username will be unique to you, so in this steps, username
indicates a Codeberg and you must replace it with your username if you copy/paste these commands. As this copy is sitting on the remote Codeberg server, we will refer to it as a remote
or as origin
. In order to work with the code, you will need to make a local copy via cloning:
Cloning your forked copy
To get the code to your computer, you must clone the repo.
git clone https://codeberg.org/username/Gadgetbridge.git
This will create local
copy of the repository. The remote
copy will be named origin
, while the official Gadgetbridge repository is registered in your cloned repo as upstream
.
Master branch
The main branch of the Gadgetbridge repo is called master
.
Creating a branch
When adding a feature for later merge/pull request, you typically create a branch. You do a branch and do not do this in the master, because you will typically like to keep the master as is, in order to be able to have it to follow the upstream
's master in Gadgetbridge repo. You can either add a branch before you start:
git checkout -b new-branch
Or you can first make some changes, and only then make the branch, for example like this:
- do some edits
git add ./path_to_the_changed file(s)
git checkout -b new-branch
Committing into the branch
Your changes are now being stored into your my-new-branch
by committing:
git commit
Pushing your local branch to your remote on the server
git push origin new-branch
Switching branches
As long as all your changes are committed, you can switch between different branches, like this:
git checkout master
in order to perhaps see how things are in the master branch and then you can go back to your new-branch git checkout new-branch
.
Seeing changes between branches
You can see diffs between your new-branch and the master: git diff master
You can also get the master
version of a file you edited, to roll it back to the "original" state: git checkout master ./path to a file
Syncing with the Gadgetbridge project
This is all cool, but while you work on your thing, the Gadgetbridge project is moving along and you must stay synced to it. You do this by switching to the master branch and pulling the remote changes:
git checkout master
git pull upstream master
This updates your local master to be the same as upstream.
Rebasing on top of the master
You must also ensure, that your branch is actually based on the master. You do this by rebasing on top of the master:
- Switch to the master and make sure it is up to date with upstream:
git pull upstream master
- Switch back to your branch:
git checkout my-branch
- Then "rebase" it on top of the remote master:
git rebase upstream/master
Resetting the master to the upstream
Sometimes, you mess things up badly and want to make sure that your local master
is really the same as the upstream master
. This can be done by using the destructive reset
command of git. This will cause local data loss, so be sure to know why you do this.
- You switch to your master:
git checkout master
- Remove all unadded files:
git clean -f -d
- And then reset it to upstream:
git reset --hard upstream/master --
Squashing commits via git rebase
The git rebase
command is very power full and allows you to do many things, like remove, re-order or squash commits, edit the commit message and so on. Read-up about it in the documentation. One of the things it can do is to allow you to selectively squash commits. This can be done in an interactive way by using the -i
option and choosing a commit where you want to start. As the action of using the rebase
still makes a commit, so somewhat counter intuitively you must choose "one commit before the start" of your commits:
git rebase -i one-commit-before-the-start-of-your-commits
In the text editor that is opened for you, you leave the first line intact and edit the pick
word in front of the commits on the following lines. For example by changing the pick
to squash
(or to s
), this commit will be squashed to the one above it. As mentioned, leave the first line intact. Save and close the file, which will invoke the squashing.
Force pushing
As the above-mentioned rebase actions overwrite git history, if you have previously pushed to your remote, you must force push now. You should not do this if this is for example a master branch and you share the repo with other people, because this breaks things for them (again, read-up about it). But for your work and/or while working alone in a dedicated branch, this is OK. You will also do this quite a bit if this branch is used as a pull/merge request.
git push -f origin new-branch
Testing a pull request
When people submit a pull request (PR), you can clone it and test it quite easily: git pull upstream pull/1234/head:branch_name
. This will clone a PR number 1234
into a local branch called branch_name
. Now the branch is local and you can work with it like with any other branch.
Save a snapshot without creating a commit in your working branch
Do a git stash
, followed by a git stash apply
. This will create a named commit which can be accessed or checked out at any time back. List all stashed with git stash list
.
An UNDO for git
As long as you added and commited your files and did not accidentally erased the .git
folder, you can mostly recover all your commits even if you for example squashed or removed them or removed a complete branch. Use git reflog
, (short for "reference logs") to get a list of previous commits and changes. Here you can note the commit hashes and get them back for example by cherry-picking: git cherry-pick commit_hash
.
General
- Home
- FAQ
- ReadMe
- Configuration
- Notifications
- ChangeLog
- Widget
- Weather
- Data Backup
- Pairing
- Find phone
- Music info
- Permissions Explained
- Firmware Update
- Automation via Intents
Sports/Activities
- Sports Activities Workouts
- Activity Sessions List
- Activity and Sleep Charts
- Heartrate measurement
- Integrating Sports Tracking apps with Gadgetbridge Sports Activities/Workouts
Smart Device Related
- Bangle.js
- Casio devices
- FitPro
- Fossil Hybrid HR
- Garmin devices
- HPlus
- Huami devices
- Amazfit Active
- Amazfit Active Edge
- Amazfit Balance
- Amazfit Band 5
- Amazfit Band 7
- Amazfit Bip
- Amazfit Bip Lite
- Amazfit Bip S
- Amazfit Bip U
- Amazfit Bip 3 Pro
- Amazfit Bip 5
- Amazfit Cheetah
- Amazfit Cheetah Pro
- Amazfit Cor
- Amazfit Cor 2
- Amazfit Falcon
- Amazfit GTR
- Amazfit GTR 3
- Amazfit GTR 3 Pro
- Amazfit GTR 4
- Amazfit GTR Mini
- Amazfit GTS
- Amazfit GTS 3
- Amazfit GTS 4
- Amazfit GTS 4 Mini
- Amazfit Neo
- Amazfit T-Rex
- Amazfit T-Rex 2
- Amazfit T-Rex Ultra
- Mi Band 1
- Mi Band 2
- Mi Band 3
- Mi Band 4
- Mi Band 5
- Mi Band 6
- Mi Band 7
- MyKronoz ZeTime
- Pebble
- PineTime
- Sony Wena 3
- SMA
- WithingsSteel
Wireless Earbuds
Others
- iTag Keyring trackers
- Nut Keyring trackers
- UM25 USB Voltage meter
- VESC BLDC controller VESC
- Flipper Zero Multi-tool Device for Geeks
- Roidmi Roidmi/Mojietu FM Trans.
- Vibratissimo Private toy
- Shell Racing Toy RC cars
- Femometer Vinca II
Full list of supported devices
Development
- How to Release
- Developer Documentation
- BT Protocol Reverse Engineering
- Support for a new Device
- New Device Tutorial
- Translating Gadgetbridge
- OpenTracks-API
- Intent-API
Feature Discussion
FAQ