Android Studio now bundles JBR 25, which Gradle 8.14's embedded Kotlin
can't parse. Follow Tauri's current template: Gradle 9.6.1, Kotlin 2.2.10,
the builtInKotlin/newDsl/failOnMissingFiles compatibility flags, the
compilerOptions DSL, and ExecOperations in BuildTask (project.exec is gone
in Gradle 9). AGP goes to 9.4.1 rather than the template's 9.3.1.
Release lint vital is turned off: AGP 9.3/9.4's lint crashes analysing the
.gradle.kts scripts (findFirCompiledSymbol), failing every release build.
The env scripts now accept JDK 17 through 25, preferring Studio's JBR.
Check each JDK candidate's major version instead of assuming Android Studio's
JBR is 21, look for Adoptium temurin-{21,17}-jdk (Fedora 44 dropped
java-21-openjdk), and propagate android-env.sh failures to scripts that
source it so fdroid-publish.sh stops before building.
scripts/windows-publish.sh builds the NSIS setup.exe for
x86_64-pc-windows-msvc through cargo-xwin, versioned like the RPM and
APK, and copies it into a folder to serve: the newest three installers,
a stable mtgc-latest-x64-setup.exe, and SHA256SUMS.
scripts/rpm-publish.sh builds the RPM, signs it and the repo metadata
with a dedicated GPG key, and adds it to a dnf repo in a given directory,
keeping the newest three versions for downgrades. The directory holds
nothing secret, so it can be served as is.
Both publish scripts now share the version scheme in
scripts/lib/release-version.sh: <major>.<minor>.<commit count>, plain
enough for RPM, MSI and semver alike. Android's versionCode stays the
commit count.
Fixes the publish scripts failing under set -u when the repo URL
variable isn't set, which is every run after the first. tauri.conf.json
now declares the license and homepage for the package metadata.
fdroidserver refuses a repo_url that doesn't end in /repo, but only at
`fdroid update`, after the APK was already copied in. The leftover APK
then made every retry think that version was published. Check the URL
up front, and take the APK back out if the update fails.
FDROID_REPO_URL now also updates repo_url in an existing repo, instead
of being read only when the repo is created.
scripts/fdroid-publish.sh builds a signed arm64 APK and adds it to an
F-Droid repo in a given directory, creating the app signing key and the
repo on first use. The versionCode is the commit count, so any commit can
ship without a manual version bump.
Release builds are signed when src-tauri/gen/android/keystore.properties
exists and stay unsigned otherwise. The manifest picks up the deep-link
plugin's intent filter, which every build regenerates and was never
committed.
Folder sync left too much to the user's sync tool: platform file access
(the Android SAF plugin), choosing the document, merging conflict copies —
and its merge base could fall out of step with the document (absorbing
conflict copies, "forget", picking a new Android folder), reading every
difference as a deletion.
The new mtgc-server crate stores one versioned library per user and accepts
a write only when it names the version it replaces. It never merges: devices
still do that with merge3, through a new sync cycle over a `Remote` trait in
mtgc-core. The base advances only on knowledge — to what was read, or to a
write the server confirmed — and is stored with the server and account it
belongs to, so a different account starts from an empty ancestor instead of
deleting. The working copy is only rewritten under the app-state lock, and
sync commands now run off the main thread.
Sign-in happens in the browser on the server's pages, via the owner's OIDC
provider and/or local username/password accounts (Argon2id, managed with
`mtgc-server user ...`), and returns a per-device revocable token to the app
by PKCE-protected code: a loopback port on desktop, the mtgc:// scheme on
Android.
The Info tab gains sign-in, the first-sync combine/replace choice, the
account's devices, and JSON export/import; the top bar shows sync state.
Includes a Dockerfile, Compose/Caddy example, and scripts to build and push
${DOCKER_REGISTRY}/mtgc/sync-server.
cargo-mobile2 locates Studio by scanning XDG data dirs for a .desktop entry
whose Name is exactly "Android Studio", then runs its Exec line. A standalone
install writes that name; JetBrains Toolbox writes a versioned one ("Android
Studio Quail 2 2026.1.2 Patch 1"), so the lookup misses and it falls back to
running "Android Studio" as a literal command, failing with an ENOENT that
names every env var except the actual problem.
android-setup.sh now writes a hidden shim entry when no exact-name match
exists, reusing the Exec line Toolbox already generated rather than hardcoding
a path. Idempotent: a later run reports Studio as discoverable and rewrites
nothing.
Only the env helper needs a per-shell variant, since it has to mutate the
calling shell; android-setup.sh is executed rather than sourced and already
works from fish.
The fish version finds NDK/JDK candidates via `find` rather than globs because
fish raises an error on a wildcard matching nothing, and "SDK with no ndk/
directory yet" is a normal state the helper has to report cleanly.
Also switch the bash helper's NDK selection to `find | sort -V` so the two
agree: glob order is lexicographic, which would rank 9.x above 10.x. Verified
both pick the same NDK and JDK on this machine and against a fixture with
9.0.111 / 10.0.222 / 28.2.333 / 30.0.999-beta2 installed.
scripts/android-env.sh exports ANDROID_HOME/NDK_HOME/JAVA_HOME by
auto-detection, so there are no machine-specific paths to keep in sync. It
skips prerelease NDKs when several are installed and prefers Android Studio's
bundled JDK 21, which Gradle accepts.
Also document that Android Studio must be launched via
`cargo tauri android dev --open` rather than by opening src-tauri/gen/android
directly: Studio's Run button delegates the Rust build to
`tauri android android-studio-script`, which reads the dev-server address from
/tmp/<identifier>-server-addr and fails with "missing addr file" if no Tauri
dev session is running. `cargo tauri android build` needs no addr file.
The Rust core was already portable (caller-provided db paths, rustls rather
than openssl) and the shell already had the mobile entry point, so this is
mostly toolchain, provisioning, and platform-gating.
Toolchain:
- scripts/android-setup.sh installs what Android Studio doesn't: SDK
cmdline-tools, a pinned stable NDK, and the Rust Android targets. The NDK is
pinned rather than "latest" because sdkmanager's newest is often a beta
(30.0.15729638 is r30-beta2) -- not something to compile SQLite with. Prefers
Android Studio's bundled JDK 21, since Gradle rejects newer JDKs.
- Track src-tauri/gen/android (excluding build output): AndroidManifest.xml is
hand-edited and Android permissions have no tauri.conf.json equivalent.
- vite: serve on TAURI_DEV_HOST so a device/emulator can reach the dev server.
Card data: downloading streams ~740MB to build a ~73MB db, which is rough on a
phone. Add import_reference_db to adopt a desktop-built file instead, validated
by db::inspect_reference_db first so picking the wrong file is a clean error
rather than a broken install. Installs via copy-then-rename so an interrupted
copy can't leave a partial db.
Ingest hardening (helps desktop too):
- Give ureq connect/read timeouts. Cancellation is only polled per parsed
record, so a stalled socket previously hung forever with no way to cancel.
- Drop the remove_file before rename; POSIX rename already replaces atomically,
and the delete-first window could leave no reference.db at all if the process
was killed -- much likelier on Android.
UI: hide Open/New Library on mobile (scoped storage has no arbitrary-path
equivalent, and SQLite can't open a content:// URI), reported via a cfg!(mobile)
flag on Status rather than user-agent sniffing. Use 100dvh so the soft keyboard
doesn't break the shell.
Verified: 70/70 core tests, desktop build clean, and the aarch64 APK builds
(bundled SQLite C and ring both cross-compile under NDK 28). Not yet run on a
device.
Tauri v2's Linux backend hard-depends on GTK3 + webkit2gtk-4.1; GTK4
support is tracked upstream (tauri#7335, tao#1104, wry#1228) but
unmerged with no ETA. Note this so it isn't re-investigated from
scratch later.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>