Skip to content

Latest commit

 

History

88 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

QOwnNotes Mobile

QOwnNotes Mobile is an Android-first, offline-capable Markdown notes application. It preserves key QOwnNotes behavior while synchronizing through the Nextcloud Notes API.

Features

  • Import one or more accounts from the Nextcloud Files Android app through Single Sign-On.
  • Read, search, create, rename, edit, and delete notes while keeping Room as the offline source of truth.
  • Synchronize with Nextcloud Notes API 1.2 or newer using incremental pulls, ETags, conflict-safe updates, and durable pending changes.
  • Long-press notes to select several and move them to the Nextcloud trash bin together.
  • Browse and restore server note versions and remotely trashed notes when the Nextcloud QOwnNotesAPI app is installed.
  • Favorite notes with a star and keep them above other notes, including while offline.
  • Create a note from text shared by another Android application.
  • Render CommonMark and GitHub Flavored Markdown, QOwnNotes task states, wiki links, legacy note:// links, tables, fenced code, and safe remote images.
  • Edit highlighted Markdown source with formatting actions, undo and redo, cursor preservation, and local draft persistence.
  • Find text inside an open note, select and copy rendered text, and adjust the note text size.
  • Detect read-only and QOwnNotes-encrypted notes and fail closed for unsafe HTML, links, images, and filesystem access.
  • Use light and dark themes on Android 9 and newer.

The production application is named QOwnNotes and uses org.qownnotes.mobile. Development builds are named QOwnNotes Dev and use org.qownnotes.mobile.dev, so both can be installed on the same device. See CHANGELOG.md for release details.

Screenshots

Note list Note view Note edit
Note list Note view Note edit

Nextcloud Account Setup

Install the Nextcloud Files Android app, sign in to the desired server there, then choose Add Nextcloud account in QOwnNotes Mobile. The app requires the Nextcloud Notes server app with Notes API 1.2 or newer. Downloaded notes and account metadata remain available offline; credentials stay in Nextcloud's SSO integration and are not copied into the QOwnNotes database.

Use Remove in an account's note list to delete that account reference, its synchronization history, and its cached notes from QOwnNotes Mobile. This does not remove the account from Nextcloud Files or delete notes from the server.

Use New to create a QOwnNotes-compatible note or Edit while viewing a writable note. Drafts are stored locally first and synchronized through the Nextcloud Notes API; updates use the last known ETag to avoid blindly overwriting a concurrent server edit.

Editing Offline

Editing does not wait for Nextcloud. Changes are cached immediately while the app is running and written to Room after a 500 ms pause, when editing finishes, or when the screen stops. A persisted edit remains visible after restarting the app and stays queued if synchronization cannot reach the server. A later edit, manual refresh, or return to the note list tries synchronization again.

Updates use the last known ETag. If the server copy changed in the meantime, the app keeps the local text and marks the note as conflicted instead of overwriting the server. Durable background retry and conflict resolution are not implemented yet. Because the live draft cache is in memory, an abrupt process kill during the 500 ms save window can lose only the newest, not-yet-persisted characters.

NixOS Recipes

Enter The Development Environment

devenv shell

The first invocation downloads JDK 17, the Android SDK, build tools, platform

List the available recipes:

just

Build The Development APK

just build-dev

just build is an alias for just build-dev. Configure local development signing as described below before running either recipe.

The APK is written to:

app/build/outputs/apk/debug/app-debug.apk

Run On The Emulator

Create the project-local emulator once:

just create-avd

Start it in one devenv shell:

just start-emulator

In another devenv shell, install and launch the development app. The recipe waits until Android's package manager is ready:

just deploy-dev

just run is an alias for just deploy-dev.

Run On A Physical Android Device

Enable ADB access in the NixOS configuration and rebuild the system:

{
  programs.adb.enable = true;
  users.users.<username>.extraGroups = [ "adbusers" ];
}

Enable USB debugging on the device, connect it, accept the authorization prompt, and verify the connection:

adb devices

Install and launch the development app:

just deploy-dev

After configuring the release signing variables described below, build and deploy the production application over USB with:

just build-release
just deploy-release

Run JVM Tests

just test

Format Sources

just format
just format-check

Check Dependency Licenses

just license-check

Run Android Device Tests

With an emulator or physical device running:

just device-test

CI runs the same instrumentation suite on an API 36 emulator. Real Nextcloud SSO interoperability remains a manual test because it requires an installed and authenticated Nextcloud Files app.

Run All Host-Side Checks

just check

Build Signed Development And Release Variants

Local development and release builds retrieve signing files from a private Vaultwarden instance. Create one item for each signing identity with these attachments:

Development item:
qownnotes-development.jks
qownnotes-development.env

Release item:
qownnotes-release.jks
qownnotes-release.env

The development dotenv attachment must contain:

ANDROID_DEV_KEYSTORE_PASSWORD=...
ANDROID_DEV_KEY_ALIAS=...
ANDROID_DEV_KEY_PASSWORD=...

The release dotenv attachment must contain:

ANDROID_KEYSTORE_PASSWORD=...
ANDROID_KEY_ALIAS=...
ANDROID_KEY_PASSWORD=...

Configure the Bitwarden CLI for Vaultwarden and log in once:

bw logout
bw config server https://vaultwarden.example.com
bw login
just build-dev
just deploy-dev
just build-release
just deploy-release
just release

When the vault is locked, a signing recipe runs bw unlock --raw and prompts for the master password itself. The resulting BW_SESSION exists only inside the wrapper, so it does not need to be exported manually or stored in shell configuration. An already exported BW_SESSION is still accepted for automation.

The public devenv.nix configuration sets VAULTWARDEN_DEV_SIGNING_ITEM and VAULTWARDEN_SIGNING_ITEM to the item names above. The variables may instead contain item UUIDs and may name the same item when all four attachments are stored together. Attachment names can be overridden locally:

VAULTWARDEN_DEV_KEYSTORE_ATTACHMENT
VAULTWARDEN_DEV_SIGNING_ENV_ATTACHMENT
VAULTWARDEN_KEYSTORE_ATTACHMENT
VAULTWARDEN_SIGNING_ENV_ATTACHMENT

The signing wrapper downloads the selected attachments into a private temporary directory, uses the matching SecretSpec profile to validate and inject the dotenv values, sets the corresponding keystore path, and removes both files when Gradle exits. It never sources the downloaded dotenv file as shell code, and Gradle does not inherit the Vaultwarden session or item configuration.

The reproducible devenv shell provides bw, jq, and secretspec. Outside that shell, install the Bitwarden Password Manager CLI, jq, and SecretSpec separately. GitHub Actions continues using its existing ANDROID_DEV_* and ANDROID_* repository secrets. When either complete signing environment is already present, the wrapper skips Vaultwarden and SecretSpec.

The signed outputs are written to:

app/build/outputs/apk/debug/app-debug.apk
app/build/outputs/apk/release/app-release.apk
app/build/outputs/bundle/release/app-release.aab

CI runs checks and uploads a debug APK for every pushed branch except release, and for pull requests. A push to the release branch instead runs the dedicated release workflow, which reads the committed version, runs the checks, builds signed packages, extracts that version's section from CHANGELOG.md, and publishes an immutable GitHub release tagged v<version>. Before pushing to the release branch, increment both values in version.properties and add the matching changelog section. Configure the following GitHub Actions repository secrets:

ANDROID_KEYSTORE_BASE64
ANDROID_KEYSTORE_PASSWORD
ANDROID_KEY_ALIAS
ANDROID_KEY_PASSWORD

If QOwnNotes Mobile has already been distributed, use its existing release or upload key. Android will not accept updates signed by a replacement key. For a first release, create and configure a key from the repository root with:

mkdir -p .signing
keytool -genkeypair \
  -keystore .signing/qownnotes-release.jks \
  -storetype PKCS12 \
  -alias qownnotes-release \
  -keyalg RSA \
  -keysize 4096 \
  -validity 10000
base64 -w 0 .signing/qownnotes-release.jks | gh secret set ANDROID_KEYSTORE_BASE64
printf '%s' 'qownnotes-release' | gh secret set ANDROID_KEY_ALIAS
gh secret set ANDROID_KEYSTORE_PASSWORD
gh secret set ANDROID_KEY_PASSWORD

The final two commands prompt without putting the passwords in shell history. PKCS12 normally uses the same password for the keystore and key. Store the .jks file and password in a durable password manager backup; losing them can prevent future application updates.

Pushes to main also replace the GitHub prerelease tagged continuous with a signed QOwnNotes Dev APK and its SHA-256 checksum. Use a separate development key so publishing continuous builds does not expose the stable release key to the main workflow. Configure these additional secrets:

ANDROID_DEV_KEYSTORE_BASE64
ANDROID_DEV_KEYSTORE_PASSWORD
ANDROID_DEV_KEY_ALIAS
ANDROID_DEV_KEY_PASSWORD

Create and configure a development key from the repository root with keytool and the GitHub CLI:

mkdir -p .signing
keytool -genkeypair \
  -keystore .signing/qownnotes-development.jks \
  -storetype PKCS12 \
  -alias qownnotes-development \
  -keyalg RSA \
  -keysize 4096 \
  -validity 10000
base64 -w 0 .signing/qownnotes-development.jks | gh secret set ANDROID_DEV_KEYSTORE_BASE64
printf '%s' 'qownnotes-development' | gh secret set ANDROID_DEV_KEY_ALIAS
gh secret set ANDROID_DEV_KEYSTORE_PASSWORD
gh secret set ANDROID_DEV_KEY_PASSWORD

The final two commands prompt for the passwords without putting them in shell history. PKCS12 normally uses the same password for the keystore and key, so enter the password chosen by keytool for both secrets. Keep the .jks file and its password in a secure backup: every continuous APK must use the same key for Android to accept it as an update. The .signing/ directory is ignored by Git.

The Base64 variables contain the binary keystores in a text form GitHub Actions can store. The keystore-password, alias, and key-password variables select and unlock the private key inside each keystore. ANDROID_KEYSTORE_PATH and ANDROID_DEV_KEYSTORE_PATH are local or temporary file paths, not repository secrets. ANDROID_VERSION_CODE is also not a secret: continuous CI sets it to the current Unix timestamp so every development build has a higher Android version code.

Clean Build Outputs

just clean

About

QOwnNotes Android

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages