Files
Cable/android
Stefan Lange-Hegermann 9673bde107 Review: ask on value milestones, not exports
The old gate needed two exports, three days of install age, one prompt per
version and 120 days between prompts. Aptabase shows only 9% of users ever
export and 5% export twice, so `Review Prompt Requested` never fired once in
19 days and the store has a single rating.

Replace the export counter with three milestones -- a fully configured system,
an opened bill of materials, a completed export -- each counted at most once
per install, and ask once two different ones are reached. Recording and asking
are now separate: milestones are booked mid-task, where StoreKit and Play drop
the request, so the ask happens from calm screens only (system overview,
export preview dismissal, share sheet dismissal).

Two fixes that made the old prompt lose its slot for good: the throttle keys
were written before checking for a foreground scene, and the export trigger
fired while the Quick Look sheet was still animating away. Both platforms now
mark the throttle only once the store API really has a review flow to show.

Add a Settings entry that links straight to the store review page. It is never
throttled, and 45% of users open Settings versus 9% who export. New analytics
event `Review Milestone Reached` makes the funnel measurable.

Existing installs keep their legacy export credit and still need a second
milestone before being asked.
2026-08-19 15:07:46 +02:00
..
2026-08-12 12:27:28 +02:00
2026-08-12 12:27:28 +02:00

Cable for Android

A native Kotlin / Jetpack Compose port of the Cable by VoltPlan iOS app — a tool for sizing low-voltage electrical conductors (boats, RVs, off-grid). This module reproduces every feature of the iOS app with an Android-adapted navigation structure and the same Aptabase analytics backend.

Feature parity with iOS

  • Systems — list, create (with name-derived icons), delete, onboarding empty state.
  • System detail — Android bottom-navigation tabs (Overview · Components · Batteries · Chargers), replacing the iOS TabView.
  • Calculator / Loads — voltage/current/power/length sliders with snap-to-common-values and tap-to-edit dialogs, watt⇄ampere mode, duty cycle & daily on-time (advanced), live wire-gauge / voltage-drop / power-loss / fuse results, and a per-load Bill of Materials sheet.
  • Batteries — chemistry picker, nominal voltage, capacity, usable-capacity override, charge & cut-off voltage, temperature range; bank voltage/capacity mismatch warnings.
  • Chargers — power source picker (shore/solar/wind/generator/alternator), input/output voltage, and a charge-output field that toggles between current and power entry.
  • Overview — estimated runtime & charge time with editable goals, BOM completion progress, and loads/batteries/chargers summary cards.
  • Bill of Materials — categorized checklist (components, batteries, cables, fuses, accessories), completion tracking persisted per component, and locale-aware Amazon affiliate / search links.
  • Component Library — VoltPlan PocketBase backend (https://base.voltplan.app) with pagination, multi-language translations, locale-aware affiliate resolution, and Coil-cached remote icons.
  • PDF export — system overview and BOM PDFs via Android PdfDocument, shared through a FileProvider.
  • Localization — English, German, Spanish, French, Dutch (values, values-de/es/fr/nl).
  • Analytics — Aptabase (app key A-SH-4260269603, host https://apta.yuzuhub.com), implemented against the Aptabase ingestion protocol so every iOS event (App Launched, System Created, Tab Changed, Load/Battery/Charger Created/Deleted, BOM Item Tapped, …) is mirrored. The iOS app uses Aptabase, so Aptabase is the source of truth here.

Architecture

  • UI: Jetpack Compose + Material 3, Navigation-Compose.
  • State: ViewModel + StateFlow; auto-save editors mirror the iOS "save on change" behaviour.
  • Persistence: Room (SwiftData → @Entity), all values stored in metric; imperial is a display-time conversion only (UnitSystemSettings via DataStore).
  • Calculation engine: calc/ElectricalCalculations.kt is a direct port (0.017 Ω·mm²/m copper resistivity, 5 % voltage-drop limit, AWG/metric tables).
  • Networking: Retrofit + kotlinx.serialization (PocketBase), OkHttp (Aptabase).

Package root: app.voltplan.cable.

Building

This module needs the Android toolchain (JDK 17 + Android SDK 35). The Gradle wrapper JAR is a binary and is not checked in here, so generate it once (or just open the folder in Android Studio, which does it automatically):

cd android
# Option A: open in Android Studio (Giraffe+) and let it sync.
# Option B: with a system Gradle 8.11+ installed:
gradle wrapper --gradle-version 8.11.1
./gradlew :app:assembleDebug

Create a local.properties pointing at your SDK if Studio doesn't:

sdk.dir=/path/to/Android/sdk

Notes

  • SF Symbol names from the iOS data model are preserved verbatim and translated to Material icons at render time (ui/Symbols.kt), so the two platforms share identical stored data.
  • The in-calculator "saved loads" picker from iOS is intentionally omitted; the VoltPlan component library is the primary picker on Android.