Files
Anitrack/wailsv3-plan.md
T
John O'Keefe 2bddc6a224 docs: add Wails v3 + Android migration plan
Skimmable phased plan for getting AniTrack onto Android for personal
sideload use (no Play Store, no official F-Droid). Written for both the
maintainer and as working context for AI-assisted implementation.

Covers: desktop v3 parity as a mechanical step (main.go, runtime call
mapping, wailsjs import rewrite), per-file Android storage shims
(keyring on desktop vs EncryptedSharedPreferences via
Android.Secure*), the localhost :6734 to anitrack:// deep-link OAuth
redesign with per-provider dashboard changes, and the sideload run/
package commands plus Obtainium updates. Includes guardrails: small
diffs, per-service file ownership, build-tagged platform files only,
runnable check per phase, and open decisions (first provider, desktop
parallel builds, redirect URI registration).
2026-09-12 21:23:14 -04:00

4.7 KiB

AniTrack → Wails v3 + Android plan

Goal: run this same app on my Android phone for personal use. Sideload the APK. No Play Store. No official F-Droid.

Non-goals: no rewrite of AniList / MAL / Simkl logic. No iOS. No store releases.

Big picture (3 steps)

  1. Desktop parity — get v3 building and acting like v2 on Linux.
  2. Android storage — swap where tokens are saved on Android only.
  3. Android login — replace the localhost login callback with a deep link.

Each step must run before moving on. If a step fails, stay on the previous one.

Step 0 — Done on main (already in this branch)

Fixed ignored errors in the 3 login files. Same wallet (ServiceName: AniTrack), same keys, per-file helpers:

  • AniListUserFunctions.go: aniRingReady() + aniRingSet(), keyring.Open error logged, Set errors logged with key name, bad ExpiresIn logged instead of silently kept.
  • MALUserFunctions.go: malRingReady() + malRingSet(), same treatment, including refresh-token saves.
  • SimklUserFunctions.go: simklRingReady() + simklRingSet(), same treatment.
  • Logouts still clear in-memory JWT even if storage is missing. No more nil-pointer panic when storage fails.

Step 1 — Desktop v3 parity (mechanical, no logic changes)

What changes and nothing else:

  • main.go: wails.Run(options.App{Bind: app})application.New(Services: [app]) + Window.New().
  • app.go: move the 4 runtime calls to v3 equivalents — WindowSetTitle, WindowUnminimise/Show, EventsEmit, MessageDialog.
  • Go login files: BrowserOpenURL and MessageDialog imports change from wails/v2/pkg/runtime to v3. Logic stays.
  • Frontend (11 files import wailsjs/go/main/App, 2 import wailsjs/runtime): regenerate with wails3 generate bindings, script-replace the import paths. No Svelte rewrites.
  • wails.json → v3 Taskfile + build/config.yml. Makefile wails buildwails3 task build.

Check: wails3 doctor, then desktop app logs in/out of all 3 services exactly like v2.

Step 2 — Android token storage (per-file shims)

Why: 99designs/keyring is desktop-only (macOS Keychain / WinCred / Linux Secret Service). No Android backend. On Android Open returns “no backend”.

Keep your organization: one file per service, same function names. Only the helper body changes by platform:

  • Desktop files (//go:build !android): keep calling keyring via aniRingSet / malRingSet / simklRingSet.
  • Android files (//go:build android): same helper names, but call application.Android.SecureSet / SecureGet / SecureDelete (backed by EncryptedSharedPreferences).
  • Keys stay the same strings (anilistAccessToken, MyAnimeListRefreshToken, SimklScope, …).

Check: GOOS=android compiles; login persists after app restart on emulator.

Step 3 — Android login callback (the one redesign)

Why: today each login opens the browser then listens on http://localhost:6734/callback (AniList:77, MAL:127, Simkl:71). Android WebView serves assets in-process — no localhost server, no open ports.

New flow per service (same order, different plumbing):

  1. App opens system browser to AniList / MAL / Simkl authorize URL.
  2. Provider redirects to anitrack://callback?code=... instead of http://localhost:6734/callback.
  3. Android activity receives the deep link, hands code to the existing get*AuthorizationToken() function.
  4. Rest is unchanged: save with *RingSet(), same in-memory JWT.

Requires: add anitrack://callback redirect URI in each provider dashboard (AniList, MAL, Simkl), add intent-filter in build/android, keep desktop on localhost.

Start with ONE provider (suggest AniList) to prove the pattern, then copy to the other two.

Step 4 — Run it on my phone

Need: JDK 21, Android SDK 35 + NDK 26.3.x, ANDROID_HOME set. wails3 doctor confirms.

wails3 task android:run          # emulator
wails3 task android:run:device   # my phone via adb, debug APK
wails3 task android:package      # release APK I sideload manually

Updates: drop APKs in GitHub Releases, point Obtainium at it. No stores.

Rules for AI doing this work

  • Small diffs, one step at a time. Never rewrite domain logic to “clean it up”.
  • AniList* stays in AniListUserFunctions.go, MAL in its file, Simkl in its file.
  • New platform code goes in new //go:build files, not edits to shared logic.
  • Every step ends with a runnable check (see “Check” lines). Stop if it fails.
  • Go stays plain Go: services are thin facades over existing funcs. No frameworks, no inheritance.

Open decisions (mine)

  • First Android login: AniList only, or all 3 day one?
  • Keep desktop building in parallel on this branch?
  • New anitrack://callback URIs registered yet in the 3 dashboards?