Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
119 commits
Select commit Hold shift + click to select a range
3e99af1
Modernize build system: Gradle KTS, version catalog, Kotlin 2.3.0, AG…
codebutler Feb 5, 2026
7d2badd
Convert to Kotlin Multiplatform: core modules, abstractions, and app …
codebutler Feb 5, 2026
c6dc704
Convert farebot-card-cepas to Kotlin Multiplatform
codebutler Feb 5, 2026
399d2f5
Convert farebot-card-classic to Kotlin Multiplatform
codebutler Feb 5, 2026
129fdcd
Convert farebot-card-desfire to Kotlin Multiplatform
codebutler Feb 5, 2026
864a3ac
Convert farebot-card-felica to Kotlin Multiplatform and absorb nfc-fe…
codebutler Feb 5, 2026
8d89d91
Convert farebot-card-ultralight to Kotlin Multiplatform
codebutler Feb 5, 2026
f7c113a
Convert farebot-transit-bilhete to Kotlin Multiplatform
codebutler Feb 5, 2026
7fa78f2
Convert farebot-transit-clipper to Kotlin Multiplatform
codebutler Feb 5, 2026
c948d03
Convert farebot-transit-easycard to Kotlin Multiplatform
codebutler Feb 5, 2026
30092ee
Convert farebot-transit-edy to Kotlin Multiplatform
codebutler Feb 5, 2026
9eeb8a6
Convert farebot-transit-ezlink to Kotlin Multiplatform
codebutler Feb 5, 2026
65ecdcf
Convert farebot-transit-hsl to Kotlin Multiplatform
codebutler Feb 5, 2026
2d3ecd2
Convert farebot-transit-kmt to Kotlin Multiplatform
codebutler Feb 5, 2026
54d4254
Convert farebot-transit-manly to Kotlin Multiplatform
codebutler Feb 5, 2026
79149ba
Convert farebot-transit-myki to Kotlin Multiplatform
codebutler Feb 5, 2026
d5bb2cf
Convert farebot-transit-octopus to Kotlin Multiplatform
codebutler Feb 5, 2026
47c8487
Convert farebot-transit-opal to Kotlin Multiplatform
codebutler Feb 5, 2026
e7e804e
Convert farebot-transit-orca to Kotlin Multiplatform
codebutler Feb 5, 2026
6052211
Convert farebot-transit-ovc to Kotlin Multiplatform
codebutler Feb 5, 2026
53f67ec
Convert farebot-transit-seqgo to Kotlin Multiplatform
codebutler Feb 5, 2026
fae776b
Convert farebot-transit-stub to Kotlin Multiplatform
codebutler Feb 5, 2026
89e8adc
Convert farebot-transit-suica to Kotlin Multiplatform
codebutler Feb 5, 2026
5f5ba66
Port iso7816 card protocol from Metrodroid
codebutler Feb 5, 2026
efcbdd1
Port vicinity card protocol from Metrodroid
codebutler Feb 5, 2026
81177ba
Port ksx6924 card protocol from Metrodroid
codebutler Feb 5, 2026
d26f56e
Port china card protocol from Metrodroid
codebutler Feb 5, 2026
4c4e4e2
Add serial-only transit identification module
codebutler Feb 5, 2026
adb980b
Port ERG transit framework from Metrodroid
codebutler Feb 5, 2026
0bab36c
Port Nextfare transit framework from Metrodroid
codebutler Feb 5, 2026
08c5d5d
Port EN1545 European transit framework from Metrodroid
codebutler Feb 5, 2026
abefda7
Port Calypso transit framework from Metrodroid
codebutler Feb 5, 2026
5008365
Port Nextfare Ultralight transit framework from Metrodroid
codebutler Feb 5, 2026
5cd1c79
Port Ultralight transit framework from Metrodroid
codebutler Feb 5, 2026
78c5fc1
Port NDEF tag reader from Metrodroid
codebutler Feb 5, 2026
9319267
Port unknown card handler from Metrodroid
codebutler Feb 5, 2026
0004cc3
Port bip! transit system from Metrodroid (Santiago, Chile)
codebutler Feb 5, 2026
cd07f68
Port Bonobus transit system from Metrodroid (Spain)
codebutler Feb 5, 2026
5f6ec57
Port CharlieCard transit system from Metrodroid (Boston, USA)
codebutler Feb 5, 2026
a0978fa
Port CiFiAl transit system from Metrodroid (France)
codebutler Feb 5, 2026
f1d6b4f
Port Intercard transit system from Metrodroid (Europe)
codebutler Feb 5, 2026
20236f1
Port Kazan Transit transit system from Metrodroid (Kazan, Russia)
codebutler Feb 5, 2026
a2f0423
Port Kyiv Transit transit system from Metrodroid (Kyiv, Ukraine)
codebutler Feb 5, 2026
9eedeef
Port KomuterLink transit system from Metrodroid (Malaysia)
codebutler Feb 5, 2026
87e15a1
Port MagnaCarta transit system from Metrodroid (Europe)
codebutler Feb 5, 2026
d9f6357
Port MetroMoney transit system from Metrodroid (Georgia)
codebutler Feb 5, 2026
500859e
Port MetroQ transit system from Metrodroid (Qatar)
codebutler Feb 5, 2026
fc7a6f3
Port MRT Jakarta transit system from Metrodroid (Jakarta, Indonesia)
codebutler Feb 5, 2026
3592787
Port Otago transit system from Metrodroid (Otago, New Zealand)
codebutler Feb 5, 2026
e5136f4
Port Oyster transit system from Metrodroid (London, UK)
codebutler Feb 5, 2026
e5ce034
Port Pilet transit system from Metrodroid (France)
codebutler Feb 5, 2026
db5ade1
Port Podorozhnik transit system from Metrodroid (St. Petersburg, Russia)
codebutler Feb 5, 2026
817faff
Port Selecta transit system from Metrodroid (Argentina)
codebutler Feb 5, 2026
995a8fb
Port SmartRider transit system from Metrodroid (Perth, Australia)
codebutler Feb 5, 2026
8332de9
Port Snapper transit system from Metrodroid (Wellington, New Zealand)
codebutler Feb 5, 2026
0ddabe3
Port Tampere Transit transit system from Metrodroid (Tampere, Finland)
codebutler Feb 5, 2026
5c6704a
Port TFI Leap transit system from Metrodroid (Ireland)
codebutler Feb 5, 2026
7ac0b0e
Port Touch 'n Go transit system from Metrodroid (Malaysia)
codebutler Feb 5, 2026
55fdd50
Port Waikato transit system from Metrodroid (Waikato, New Zealand)
codebutler Feb 5, 2026
6eac25e
Port Warsaw Transit transit system from Metrodroid (Warsaw, Poland)
codebutler Feb 5, 2026
22a6fc8
Port Zolotaya Korona transit system from Metrodroid (Russia)
codebutler Feb 5, 2026
a2c2f93
Port Yargor transit system from Metrodroid (Russia)
codebutler Feb 5, 2026
c402e9f
Port China Transit transit system from Metrodroid (China)
codebutler Feb 5, 2026
3bcbf6c
Port T-Money transit system from Metrodroid (South Korea)
codebutler Feb 5, 2026
3495b1c
Port Krocap transit system from Metrodroid (France)
codebutler Feb 5, 2026
4f8ae9e
Port Ventra transit system from Metrodroid (Chicago, USA)
codebutler Feb 5, 2026
38eee56
Port Compass transit system from Metrodroid (Vancouver, Canada)
codebutler Feb 5, 2026
dba3f79
Port Troika transit system from Metrodroid (Moscow, Russia)
codebutler Feb 5, 2026
ca4fa30
Port GoTo transit system from Metrodroid (Minneapolis-St. Paul, USA)
codebutler Feb 5, 2026
d011382
Port Umarsh transit system from Metrodroid (Russia)
codebutler Feb 5, 2026
f7554fb
Port Vicinity transit system from Metrodroid (Various)
codebutler Feb 5, 2026
a31cc39
Port Gautrain transit system from Metrodroid (Gauteng, South Africa)
codebutler Feb 5, 2026
340b27a
Port RicaricaMi transit system from Metrodroid (Milan, Italy)
codebutler Feb 5, 2026
ac275f1
Port Hafilat transit system from Metrodroid (Abu Dhabi, UAE)
codebutler Feb 5, 2026
7fcb7da
Port LAX TAP transit system from Metrodroid (Los Angeles, USA)
codebutler Feb 5, 2026
f4576a8
Port CHC Metrocard transit system from Metrodroid (Christchurch, New …
codebutler Feb 5, 2026
9e5610f
Add shared Compose Multiplatform UI module
codebutler Feb 5, 2026
a591916
Add iOS app with CoreNFC
codebutler Feb 5, 2026
66427fc
Add project documentation and tooling
codebutler Feb 5, 2026
29e482e
Add ISO7816 reader infrastructure for China and KSX6924 balance reading
codebutler Feb 5, 2026
2992547
Port Adelaide Metrocard transit system from Metrodroid (Adelaide, Aus…
codebutler Feb 5, 2026
bff117c
Port RKF transit system from Metrodroid (Rejsekort, SLaccess, Västtra…
codebutler Feb 5, 2026
cb8caed
Port NextfareDesfire and TPFCard serialonly transit systems from Metr…
codebutler Feb 6, 2026
b44796c
Add JVM target, improve test infrastructure
codebutler Feb 6, 2026
1ea56aa
Add DESFire and FeliCa support to Flipper NFC parser
codebutler Feb 6, 2026
c24592e
Fix Clipper and EasyCard DESFire file handling
codebutler Feb 6, 2026
ff1ca0d
Rewrite HSL to EN1545 framework with V1/V2/Waltti support
codebutler Feb 6, 2026
cf997f5
Rewrite OVC (OV-chipkaart) to EN1545 framework
codebutler Feb 6, 2026
784bcfd
Implement full Snapper KSX6924 transit parsing (Wellington, NZ)
codebutler Feb 6, 2026
701cd0a
Fix KMT transaction info display and SeqGo system code check
codebutler Feb 6, 2026
6db8d07
Add Metrodroid port audit results (63 PASS, 2 MINOR)
codebutler Feb 6, 2026
203da0e
Add thorough assertions to FlipperIntegrationTest
codebutler Feb 6, 2026
fa3cb96
Dissolve farebot-transit-ultralight and farebot-transit-unknown modules
codebutler Feb 6, 2026
e37393a
Add BER-TLV extra info display to Pilet transit cards
codebutler Feb 6, 2026
4dab3b2
Fix audit findings for Myki, LAX TAP, and SeqGo
codebutler Feb 6, 2026
038cd20
Update audit document with resolved items and architectural notes
codebutler Feb 6, 2026
1292b12
Update AUDIT.md to reflect dissolved ultralight/unknown modules
codebutler Feb 6, 2026
30b4fc1
Rewrite Manly Fast Ferry to use ERG framework
codebutler Feb 6, 2026
c59f51f
Fix test port gaps for Suica, EasyCard, and Myki
codebutler Feb 6, 2026
81ab7b3
Fix Station.Builder dropping all but first MDST line name
codebutler Feb 6, 2026
023f929
Add Troika hybrid card support for Podorozhnik and Strelka
codebutler Feb 6, 2026
9958f90
Remove unused Android legacy code and resources
codebutler Feb 10, 2026
6c3d4eb
Add card images for expanded supported cards catalog
codebutler Feb 10, 2026
65a3314
Consolidate TransitFactoryRegistry into shared module
codebutler Feb 10, 2026
478a963
Refactor CardInfo and TransitRegion models
codebutler Feb 10, 2026
bfdbfa4
Update transit factory CardInfo declarations for StringResource API
codebutler Feb 10, 2026
bd3640c
Expand supported cards catalog and overhaul Help screen
codebutler Feb 10, 2026
b8c407e
Add Cards Map screen for visualizing supported transit systems
codebutler Feb 10, 2026
16f5732
Update README with comprehensive supported cards documentation
codebutler Feb 10, 2026
37415c1
Remove old Android-only TransitFactoryRegistry
codebutler Feb 10, 2026
bab6cf1
Hide Keys menu item on devices without MIFARE Classic support
codebutler Feb 10, 2026
4f67246
Remove Settings screen and AppSettings framework
codebutler Feb 10, 2026
7416697
Remove audit documents
codebutler Feb 10, 2026
a3410f4
Fix iOS map screens: update UIKitView import path and remove unnecess…
codebutler Feb 10, 2026
bb1ede8
Add Makefile and update README with build targets
codebutler Feb 10, 2026
289fd0d
Redesign home screen with tabs, inline map, and improved scan flow
codebutler Feb 10, 2026
cf8fd3e
Rename iosApp to farebot-ios and update Xcode scheme to FareBot
codebutler Feb 10, 2026
6dedaf5
Merge farebot-app-persist into farebot-shared
codebutler Feb 10, 2026
8ba3a9e
Add sample card viewing from Explore tab
codebutler Feb 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,18 @@ local.properties
projectFilesBackup/
project.properties
.gradle
.kotlin
release.keystore
com_crashlytics_export_strings.xml
crashlytics-build.properties
crashlytics.properties

# Xcode
xcuserdata/
*.xcscmblueprint
DerivedData/

metrodroid/
metrodroid-commits/

*.hprof
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +0,0 @@
[submodule "third_party/nfc-felica-lib"]
path = third_party/nfc-felica-lib
url = https://github.com/codebutler/nfc-felica-lib.git
146 changes: 146 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# CLAUDE.md — Project Rules for FareBot

## Project Overview

FareBot is a Kotlin Multiplatform (KMP) Android/iOS app for reading NFC transit cards. It is being ported from/aligned with [Metrodroid](https://github.com/metrodroid/metrodroid).

**Metrodroid source code is in the `metrodroid/` directory in this repo.** Always use this local copy for comparisons and porting — do not fetch from GitHub.

## Critical Rules

### 1. NEVER lose existing features

When refactoring, rewriting, or porting code: **every existing feature must be preserved**. Before modifying a file, understand what it currently does. After modifying it, verify nothing was lost. Do not silently drop functionality — if something must change, say so explicitly.

Common regressions to watch for:
- Missing UI elements (images, buttons, screens)
- Lost navigation paths (menu items, long-press handlers)
- Removed data fields from transit info display
- Broken sample data loading

### 2. No stubs — use serialonly for identification-only systems

Do NOT create stub/skeleton transit implementations that only show a card name and serial number **when Metrodroid has a full implementation available to port**. If Metrodroid has trip parsing, balance reading, subscriptions, or other features for a system, port all of it — never reduce a full implementation to a stub.

For systems where Metrodroid itself only supports identification (card name + serial number) with no further parsing, use `farebot-transit-serialonly/` — matching Metrodroid's `serialonly/` directory. These extend `SerialOnlyTransitInfo` and provide a `Reason` (LOCKED, NOT_STORED, MORE_RESEARCH_NEEDED) explaining why data isn't available.

If a full implementation can't be ported yet (e.g., missing infrastructure framework), don't add the system at all until the dependency is ready.

### 3. Faithful ports from Metrodroid

When porting code from Metrodroid: **do a faithful port**. Do not simplify, abbreviate, or "improve" the logic. Port ALL features, ALL edge cases, ALL constants. After writing each file, diff it against the Metrodroid original to verify nothing was missed.

- `ImmutableByteArray` → `ByteArray`
- `Parcelize`/`Parcelable` → `kotlinx.serialization.Serializable`
- `Localizer.localizeString(R.string.x)` → `stringResource.getString(Res.string.x)`
- `Timestamp`/`TimestampFull`/`Daystamp` → `kotlinx.datetime.Instant`
- `TransitData` → `TransitInfo`
- `CardTransitFactory` → `TransitFactory<CardType, TransitInfoType>`

Do NOT:
- Skip features "for later"
- Change logic unless there's a concrete reason
- Remove constants, enums, or data that exist in the original
- Simplify switch/when statements by dropping cases

### 4. Debug systematically, not speculatively

When something is broken: **add logging and diagnostics first**. Do not guess at fixes. The workflow should be:

1. Add debug logging to understand what's actually happening
2. Read the device console output
3. Identify the root cause from actual data
4. Fix the specific problem
5. Remove debug logging

Do NOT make speculative changes hoping they fix the issue. Each failed guess wastes a round.

### 5. All code in commonMain unless it requires OS APIs

Write all code in `src/commonMain/kotlin/`. Only use `androidMain` or `iosMain` for code that directly interfaces with platform APIs (NFC hardware, file system, UI system dialogs). No Objective-C. Tests use `kotlin.test`.

### 6. Use StringResource for all user-facing strings

All user-facing strings must go through Compose Multiplatform resources:
- Define strings in `src/commonMain/composeResources/values/strings.xml`
- For UI labels in `TransitInfo.getInfo()`, use `ListItem(Res.string.xxx, value)` or `HeaderListItem(Res.string.xxx)` directly
- For dynamic string formatting, use `runBlocking { getString(Res.string.xxx) }`
- Legacy pattern: Pass `StringResource` to factories — still works but not required for new code

Example patterns:
```kotlin
// Preferred for static labels
ListItem(Res.string.card_type, cardType)
HeaderListItem(Res.string.card_details)

// For dynamic values
val formatted = runBlocking { getString(Res.string.balance_format) }
```

Do NOT hardcode English strings in Kotlin files.

### 7. Use MDST for station lookups, not SQLite .db3

Station databases should use the MDST (protobuf) format via `MdstStationLookup`, not SQLite .db3 files with SQLDelight. All MDST files live in `farebot-base/src/commonMain/composeResources/files/` and are accessed via `MdstStationLookup.getStation(dbName, stationId)`.

Example:
```kotlin
val station = MdstStationLookup.getStation("orca", stationId)
station?.stationName // English name
station?.companyName // Operator name
station?.latitude // GPS coordinates (if available)
```

### 8. Verify your own work

After making changes:
- Run `./gradlew allTests` to confirm tests pass
- Run `./gradlew assemble` to confirm the build succeeds
- If you changed UI code, describe what the user should see
- If you ported code, diff against the original source

Do NOT claim work is complete without verification.

### 9. Preserve context across sessions

Key project state is in:
- `/Users/eric/.claude/plans/` — implementation plans (check newest first)
- `/Users/eric/Code/farebot/REMAINING-WORK.md` — tracked remaining work
- Session transcripts in `/Users/eric/.claude/projects/-Users-eric-Code-farebot/`

When continuing from a previous session, read these files to recover context rather than starting from scratch.

## Build Commands

```bash
./gradlew allTests # Run all tests
./gradlew assemble # Full build (Android + iOS frameworks)
./gradlew :farebot-android:assembleDebug # Android only
```

## Module Structure

- `farebot-base/` — Core utilities, MDST reader, ByteArray extensions
- `farebot-card-*/` — Card type implementations (classic, desfire, felica, ultralight, iso7816, cepas, vicinity)
- `farebot-transit-*/` — Transit system implementations (one module per system)
- `farebot-transit-serialonly/` — Identification-only systems (serial number + reason, matches Metrodroid's `serialonly/`)
- `farebot-transit/` — Shared transit abstractions (Trip, Station, TransitInfo, TransitCurrency, etc.)
- `farebot-shared/` — Shared app code, Compose UI, ViewModels
- `farebot-android/` — Android app entry point
- `farebot-ios/` — iOS app (Xcode project)

## Registration Checklist for New Transit Modules

1. Create `farebot-transit-{name}/build.gradle.kts`
2. Add `include(":farebot-transit-{name}")` to `settings.gradle.kts`
3. Add `api(project(":farebot-transit-{name}"))` to `farebot-shared/build.gradle.kts`
4. Add `implementation(project(":farebot-transit-{name}"))` to `farebot-android/build.gradle.kts`
5. Register factory in `TransitFactoryRegistry.kt` (Android)
6. Register factory in `MainViewController.kt` (iOS, non-Classic cards only)
7. Add string resources in `composeResources/values/strings.xml`

## Kotlin/Native Gotchas

- `internal` types cannot be exposed in public APIs (stricter than JVM)
- Constructor parameter names matter — use the exact names the data class defines
- When removing a transitive dependency, add direct `api()` deps for anything that was accessed transitively
44 changes: 44 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
IOS_DEVICE_ID := $(shell xcrun xctrace list devices 2>/dev/null | grep -v Simulator | grep -E '\([0-9A-F-]+\)$$' | grep -v Mac | head -1 | grep -oE '[0-9A-F]{8}-[0-9A-F]{16}')
IOS_APP_PATH = $(shell ls -d ~/Library/Developer/Xcode/DerivedData/FareBot-*/Build/Products/Debug-iphoneos/FareBot.app 2>/dev/null | head -1)

.PHONY: android android-install ios ios-sim ios-install test clean help

## Android

android: ## Build Android debug APK
./gradlew :farebot-android:assembleDebug

android-install: android ## Build and install on connected Android device
adb install -r farebot-android/build/outputs/apk/debug/farebot-android-debug.apk

## iOS

ios: ## Build iOS app for physical device
./gradlew :farebot-shared:linkDebugFrameworkIosArm64
./gradlew :farebot-shared:linkDebugFrameworkIosSimulatorArm64
xcodebuild -project farebot-ios/FareBot.xcodeproj -scheme FareBot \
-destination 'id=$(IOS_DEVICE_ID)' -allowProvisioningUpdates build

ios-sim: ## Build iOS app for simulator
./gradlew :farebot-shared:linkDebugFrameworkIosSimulatorArm64
xcodebuild -project farebot-ios/FareBot.xcodeproj -scheme FareBot \
-destination 'platform=iOS Simulator,name=iPhone 16' build

ios-install: ios ## Build and install on connected iOS device
xcrun devicectl device install app --device $(IOS_DEVICE_ID) "$(IOS_APP_PATH)"

## Tests

test: ## Run all tests
./gradlew allTests -x linkDebugTestIosSimulatorArm64 -x linkDebugTestIosX64

## Utility

clean: ## Clean all build artifacts
./gradlew clean
xcodebuild -project farebot-ios/FareBot.xcodeproj -scheme FareBot clean 2>/dev/null || true

help: ## Show this help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}'

.DEFAULT_GOAL := help
42 changes: 0 additions & 42 deletions README-OVChipkaart.md

This file was deleted.

Loading