-
Notifications
You must be signed in to change notification settings - Fork 271
Migrate to Kotlin DSL, Compose, and modern Android stack #209
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
codebutler
wants to merge
37
commits into
master
Choose a base branch
from
claude/update-android-dependencies-87n3T
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
- Gradle: 5.4.1 → 8.11.1 - AGP: 3.5.0-alpha13 → 8.7.3 - Kotlin: 1.3.31 → 2.1.0 - compileSdk/targetSdk: 28 → 35, minSdk: 16 → 21 - Java compatibility: 7 → 17 - AutoDispose: 0.8.0 → 1.4.0 - AutoValue: 1.6.5 → 1.11.0 - Dagger: 2.22.1 → 2.52 - Room: 2.1.0-alpha07 → 2.6.1 - Material: 28.0.0 → 1.12.0 - Play Services Maps: 16.1.0 → 19.0.0 - Guava: 27.1-android → 33.3.1-android Migrated to AGP 8.x namespace convention, removed Fabric/Crashlytics (replaced with android.util.Log), fixed Kotlin 2.x nullability and API changes, excluded old support-v4 transitive dependency from rxbroadcast, and added android:exported for Android 12+ target compatibility. https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
….serialization - Replace AutoValue with Kotlin data classes across all card and transit modules - Replace Guava collections with Kotlin stdlib (ImmutableMap/List -> Map/List) - Migrate UI from Magellan/RxJava/Views to Jetpack Compose with Navigation - Replace Dagger 2 with Hilt for dependency injection - Replace GSON with kotlinx.serialization for JSON serialization - Add @serializable annotations to all data classes - Add @JvmStatic to companion object methods for Java interop - Remove old View-based screens, adapters, layouts, and menus - Remove RxJava, AutoDispose, KotterKnife, and other legacy dependencies https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
- Convert all 65 remaining Java source files to Kotlin (excluding third_party) - Remove @JvmStatic annotations from 89 files (no Java callers remain) - Convert interfaces (CardSerializer, CardKeys, RawCard, CardPersister, etc.) - Convert enums (CardType), abstract classes (Trip, Refill, TransitInfo, etc.) - Convert utility classes (ByteArray, ByteUtils, IOUtils, etc.) - Convert tag readers, protocols, and transit factories to Kotlin - Only third_party/nfc-felica-lib Java files remain (external library) https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
- Remove nfc-felica-lib git submodule, absorb source directly into project - Convert all 15 Java files to Kotlin (FeliCaLib, FeliCaTag, NfcTag, etc.) - Add kotlin-android plugin and kotlinStdlib dependency to build.gradle - Zero Java source files remain in the entire project https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
- Resolve ByteArray naming conflict with kotlin.ByteArray using FQN - Fix nullability mismatches (ByteArray? parameters, nullable returns) - Correct Byte vs Int type mismatches in FeliCaLib and FeliCaLiteTag - Add missing override modifiers and remove final method overrides - Add @contextual annotation for FareBotUiTree serialization https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
- Fix Card subclass property overrides (override val instead of override fun) - Fix nullable safe calls across all transit factories and tag readers - Fix platform declaration clashes (val + fun with same JVM signature) - Fix property access vs function call mismatches throughout codebase - Migrate Room and Hilt from kapt to KSP for Kotlin 2.1.0 compatibility - Update Dagger/Hilt from 2.52 to 2.56.2 for Kotlin metadata support - Add KSP plugin (2.1.0-1.0.29) to build configuration - Add @contextual annotations for kotlinx.serialization on abstract types - Remove unnecessary @serializable from EasyCardTransitInfo - Add missing string resources and fix drawable references - Fix IOUtils.kt uninitialized variable errors - Fix NfcTag, FeliCaTag, and response class compilation issues https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
farebot-base, farebot-card, farebot-transit, and farebot-app-persist had appcompat declared but never imported anything from it. Replaced with the lightweight androidx.annotation dependency in farebot-base (needed for @stringres). https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
- Replace android.content.res.Resources with StringResource interface across all library modules (farebot-base, farebot-card*, farebot-transit*) - Create AndroidStringResource adapter in farebot-app to bridge Android Context.getString() to the platform-agnostic StringResource interface - Replace android.text.TextUtils with Kotlin stdlib equivalents (isNullOrEmpty, joinToString) - Migrate from Room to SQLDelight for KMP-compatible persistence - Replace Room entities/DAOs with SQLDelight .sq schema files - Update DbCardPersister and DbCardKeysPersister for SQLDelight APIs - Remove Room dependencies, add SQLDelight 2.0.2 with Android driver - Remove @stringres annotation dependency from farebot-base - Update FareBotUiTree and UiTreeBuilder to use StringResource https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
- Change all 25 library modules from com.android.library + kotlin-android to org.jetbrains.kotlin.multiplatform + com.android.library - Restructure source directories: pure Kotlin code to src/commonMain/kotlin/, Android-specific code (NFC, SQLite, etc.) to src/androidMain/kotlin/ - Move Android resources and manifests to src/androidMain/ - Move SQLDelight .sq files to src/commonMain/sqldelight/ - Add Compose Multiplatform plugin (1.7.3) to root build.gradle classpath - Add SQLDelight runtime dependency for commonMain - Split SeqGoUtil into SeqGoDateUtil (commonMain) for pure date parsing - Remove android.util.Log from OctopusTransitInfo for multiplatform compat - Move SuicaTrip to androidMain due to SQLite dependency chain - Configure androidTarget jvmTarget globally in root build.gradle - Keep nfc-felica-lib as Android-only library (NFC is inherently platform-specific) https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
…onMain, and port nfc-felica-lib to KMP - Convert all build.gradle files to build.gradle.kts (Kotlin DSL) - Add gradle/libs.versions.toml version catalog for centralized dependency management - Create settings.gradle.kts with dependencyResolutionManagement - Use buildscript/classpath in root for plugin resolution compatibility - Remove all JVM dependencies from commonMain source sets: - Replace java.util.Date/Calendar with kotlinx-datetime Instant - Replace NumberFormat/Currency with pure Kotlin CurrencyFormatter - Replace java.lang.Long/Integer helpers with Kotlin stdlib - Remove ArrayUtils, Charsets, IOUtils from commonMain (moved to androidMain where needed) - Port nfc-felica-lib to KMP: - Split into commonMain (constants, data classes) and androidMain (NFC operations) - Remove Parcelable from IDm/PMm, serialize via byte arrays - Fix compilation errors in TagReader files (Date -> Clock.System.now()) - Fix sample card files to use Instant instead of Date - Build passes: assembleDebug succeeds https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
Replace Android R.string references with Compose Multiplatform Res.string across all KMP library modules. Copy strings.xml files from androidMain/res to commonMain/composeResources for all locales. Update StringResource interface to use CMP StringResource type instead of Android int resource IDs. https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
Replace Android-specific DBUtil/SQLiteDatabase station lookups with SQLDelight-generated type-safe queries. Add .sq schema files for felica, OVC, and SEQ Go station databases. Create expect/actual BundledDatabaseDriverFactory for opening pre-bundled .db3 files. Remove legacy DBUtil, FelicaDBUtil, OVChipDBUtil, and SeqGoDBUtil. https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
Create common NFC technology interfaces (NfcTechnology, CardTransceiver, ClassicTechnology, UltralightTechnology, NfcFTechnology) in commonMain with Android wrapper implementations. Move DesfireProtocol and CEPASProtocol to commonMain using CardTransceiver interface. Refactor all TagReaders to use common technology interfaces instead of Android NFC API types directly. https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
- Add iosX64, iosArm64, iosSimulatorArm64 targets to all 26 KMP modules - Replace expect/actual DateFormatting with pure Kotlin implementation using kotlinx-datetime - Add iOS BundledDatabaseDriverFactory actual using NativeSqliteDriver - Create farebot-shared module with Compose Multiplatform UI and iOS framework export - Add iOS app entry point using ComposeUIViewController - Add XcodeGen project configuration for iOS app - Add SQLDelight native driver dependency for iOS modules https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
Restructured to emphasize verifying the proxy before building, since Gradle aggressively caches dependency resolution failures. Added troubleshooting sections for the most common failure modes: stale Gradle metadata caches, dead/restarted proxy, DNS failures, and the --refresh-dependencies footgun. https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
Document health check endpoint, JWT credential refresh behavior, and retry logic now available in the proxy script. https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
Previously lived at /tmp/local_proxy2.py which is ephemeral. Checking it in makes the CLAUDE.md setup instructions self-contained. Updated all references in CLAUDE.md to point to tools/local_proxy.py. https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
- Create iOS NFC implementations (IosCardTransceiver, IosNfcFTechnology, IosUltralightTechnology) using Core NFC with dispatch_semaphore bridging - Add shared Compose Multiplatform screens in farebot-shared (Home, Card, CardAdvanced, History, Keys, Help) with state-driven architecture - Add shared UI state models (HomeUiState, CardUiState, HistoryUiState, KeysUiState, CardAdvancedUiState, TransactionItem, SupportedCardInfo) - Add platform abstractions (PlatformActions, NfcStatus) and shared theme - Add Compose Multiplatform string resources in composeResources - Rewire Android app screens as thin wrappers that delegate to shared Compose screens while keeping Android ViewModels and Hilt DI - Update MainActivity to use shared FareBotTheme https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
- Move Screen route definitions from farebot-app to farebot-shared - Add navigation-compose dependency to farebot-shared for KMP navigation - Replace custom stack-based navigation in App.kt with Jetpack NavHost - Update MainActivity to import Screen routes from shared module - Remove FareBotNavigation.kt (routes now live in shared module) https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
Clarifies the module's role as the Android-specific application entry point, distinct from farebot-shared (the KMP library) and a future iosApp Xcode project. https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
- Create shared TransitFactoryRegistry in farebot-shared using CardType enum as key instead of JVM-specific Class<> references - Remove JVM-specific getParentClass() from Card.kt (commonMain) - Create iOS tag readers for DESFire, CEPAS, Ultralight, and FeliCa in each card module's iosMain, reusing common protocol code - Create IosNfcScanner wrapping Core NFC's NFCTagReaderSession with automatic tag type detection and dispatch to readers - Create IosPlatformActions with openUrl, clipboard, share support - Wire FareBotApp with CardScanner and TransitFactoryRegistry params - Wire MainViewController with real iOS services (scanner, registry, platform actions, supported card list) - Update Android TransitFactoryRegistry to use shared version - Make NfcDataConversions public for cross-module iOS access iOS supports: Orca, Clipper, HSL, Opal, Myki (DESFire); Edy, Octopus, KMT (FeliCa); EZ-Link (CEPAS). MIFARE Classic cards (OVChip, SeqGo, etc.) are excluded due to iOS hardware limitations. https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
Move SuicaTransitFactory, SuicaTrip, and SuicaUtil from androidMain to commonMain, replacing all JVM-specific APIs with multiplatform equivalents: - Context parameter → FelicaStationsDb (injected directly) - java.util.Locale → expect/actual getSystemLanguage() - android.util.Log → silent exception handling - java.text.NumberFormat → CurrencyFormatter - Integer.toHexString() → Int.toString(16) - String.format() → Kotlin string templates - java.util collections → Kotlin stdlib Add DefaultStringResource and SystemLocale expect/actual to farebot-base. Register Suica factory in both Android and iOS transit registries. https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
Migrate from Hilt (Android-only) to Koin (KMP) for dependency injection, enabling fully cross-platform ViewModels, navigation, and DI. Android and iOS now both use the shared FareBotApp composable with koinViewModel(). - Add Koin 4.0.3 dependencies, remove Hilt/Dagger from all build files - Create shared ViewModels: Home, Card, History, Keys (commonMain) - Create shared Koin module (SharedModule) and platform modules - Rewrite FareBotApp with koinViewModel() and koinInject() - Create AndroidCardScanner wrapping NfcStream + TagReaderFactory - Update IosNfcScanner to Flow-based CardScanner interface - Update iOS MainViewController with Koin initialization - Simplify MainActivity to use shared FareBotApp composable - Remove 8 Android screen wrappers, 8 Android ViewModels - Remove FareBotApplicationModule (Hilt), CardStream, ExportHelper - Add AndroidPlatformActions, shared NavDataHolder https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
Move platform-dependent code to commonMain so iOS has full functionality: - Move custom ByteArray class from androidMain to commonMain, replacing Android Parcelable/Base64 with kotlin.io.encoding.Base64 - Move FareBotSerializersModule to shared commonMain, dropping the java.util.Date serializer (card models already use kotlinx.datetime.Instant) - Move KotlinxCardSerializer to shared commonMain for cross-platform card serialization/deserialization - Register CardSerializer in iOS Koin module - Fix iOS BundledDatabaseDriverFactory to use real schema for app databases (farebot.db) and no-op schema only for pre-populated bundled databases - Bundle felica_stations.db3 in iOS app resources - Call initKoin() from Swift app entry point before UI initialization - Link FareBotKit framework as dependency in project.yml https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
…functions Delete the custom com.codebutler.farebot.base.util.ByteArray immutable wrapper class and NativeByteArray typealias. Replace all usages across 63 files with plain kotlin.ByteArray plus extension functions (hex(), toBase64(), decodeBase64()) in a new ByteArrayExt.kt file. Key changes: - Remove .bytes() unwrap calls (no longer needed) - Remove ByteArray.create() / ByteArray() wrapping calls - Remove kotlin.ByteArray qualified types (no more shadowing) - Remove FareBotByteArray alias in CEPAS module - Update FareBotSerializersModule to serialize kotlin.ByteArray as base64 - Add hex() import where ByteArray.hex() extension is used https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
Delete the following dead code: - IOUtils.kt: Apache Commons IO copy, unused by any code - Charsets.kt: Apache Commons charset constants, only used by IOUtils - ErrorUtils.kt: Legacy Android error dialogs, unused after Compose migration - ArrayUtil.kt: Array helpers (indexOf, contains, etc.), replaced by Kotlin stdlib - FinderUtil.kt: Collection find/filter helpers, replaced by Kotlin stdlib - IPredicate.kt: Predicate interface, replaced by Kotlin function types Also remove the now-unnecessary checkstyle exclusions for the deleted Java-origin files in build.gradle.kts. https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
- Kotlin 2.1.0 → 2.1.20 - AGP 8.7.3 → 8.8.0 - KSP 2.1.0-1.0.29 → 2.1.20-2.0.1 - Compose Multiplatform 1.7.3 → 1.8.0 (iOS now stable) - SQLDelight 2.0.2 → 2.1.0 - kotlinx-serialization 1.7.3 → 1.8.0 - kotlinx-coroutines 1.9.0 → 1.10.1 - kotlinx-datetime 0.6.1 → 0.6.2 - Compose BOM 2024.12.01 → 2025.01.01 - AndroidX Lifecycle 2.8.7 → 2.9.0 - AndroidX Navigation 2.8.5 → 2.9.0 - AndroidX Activity 1.9.3 → 1.10.0 - Koin 4.0.3 → 4.1.1 - Guava 33.3.1 → 33.4.0 - Checkstyle 10.20.1 → 10.21.0 - Gradle 8.11.1 → 8.12 https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
- Add TripMap composable entry with station location display - Add AddKey composable entry with form UI and AddKeyViewModel - Add shared Settings screen (used by iOS, Android keeps legacy prefs) - Implement platform file picker/export via PlatformActions - iOS: use UIActivityViewController for share instead of clipboard - iOS: show auto-dismissing UIAlertController for toast - Register file picker launcher in Android MainActivity https://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG
… iOS app
Dependency upgrades:
- Kotlin 2.1.20 -> 2.3.0, KSP 2.1.20 -> 2.3.0
- AGP 8.8.0 -> 9.0.0 (with new android-kotlin-multiplatform-library plugin)
- Compose Multiplatform 1.8.0 -> 1.10.0, Compose BOM 2025.01.01 -> 2026.01.01
- SQLDelight 2.1.0 -> 2.2.1, Serialization 1.8.0 -> 1.10.0
- Lifecycle 2.9.0 -> 2.9.6, Navigation 2.9.0 -> 2.9.1
- Coroutines 1.10.1 -> 1.10.2, Datetime 0.6.2 -> 0.7.1
- Gradle wrapper updated, minSdk raised to 23
- Removed crashing gradle-versions-plugin
- Switched lifecycle/navigation to JetBrains KMP artifacts
KMP/iOS compilation fixes:
- Replace Java APIs in commonMain with Kotlin equivalents: Math.ceil,
Integer.valueOf, System.arraycopy, String.format, System.currentTimeMillis,
java.util.Comparator, @JvmField, String(ByteArray), toByteArray()
- Fix CoreNFC Kotlin/Native interop: protocol types (NFCMiFareTagProtocol,
NFCFeliCaTagProtocol), method names (pollingWithSystemCode,
readWithoutEncryptionWithServiceCodeList, beginSession), enum constants
(NFCFeliCaPollingTimeSlotMax1), callback parameter types (Int -> Long)
- Fix Navigation KMP API: getString -> savedstate.read { getStringOrNull }
- Add ExperimentalForeignApi opt-in to BundledDatabaseDriverFactory
- Move nfc-felica-lib dependency from androidMain to commonMain
iOS app fixes:
- Add CADisableMinimumFrameDurationOnPhone to Info.plist (Compose requirement)
- Add -lsqlite3 linker flag for SQLite support
- Fix openUrl using async openURL API instead of deprecated synchronous one
- Fix About URL (github.com -> github.io)
- Add PRODUCT_NAME, code signing, and development team to project.yml
- Add NFC usage description to Info.plist
Supported Cards screen restoration:
- Move card images from Android drawables to Compose multiplatform resources
- Restore card images, preview/experimental labels, and card layout in HelpScreen
- Consolidate supported cards list into shared ALL_SUPPORTED_CARDS constant
- Fix Supported Cards home button navigating to NFC scan instead of help
Other UI fixes:
- Fix blank Preferences screen on iOS by implementing supportsLaunchFromBackground
with NSUserDefaults persistence
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Implement complete shared UI layer with Compose Multiplatform screens (Home, Card, History, Keys, Help, AddKey, TripMap, SupportedCards), viewmodels, drawable resources, sample card data, analytics abstraction, and platform-specific adapters for Android and iOS. Update DeSFIRe protocol, NFC scanner, database driver, and transit factory registry. Co-Authored-By: Claude Opus 4.5 <[email protected]>
Merge useful parts of third_party/nfc-felica-lib into farebot-card-felica as clean multiplatform Kotlin. Introduce FeliCaTagAdapter interface with platform-specific implementations (AndroidFeliCaTagAdapter using NfcF, IosFeliCaTagAdapter using Core NFC) and a shared FeliCaReader algorithm. Migrate IDm, PMm, constants, and utilities into the farebot-card-felica module. Update all transit module imports (Suica, Edy, KMT, Octopus). Delete the entire nfc-felica-lib third-party module. Co-Authored-By: Claude Opus 4.5 <[email protected]>
Update libs.versions.toml with latest dependency versions. Configure iOS project with NFC entitlements, update Info.plist, and sync Xcode project. Co-Authored-By: Claude Opus 4.5 <[email protected]>
Co-Authored-By: Claude Opus 4.5 <[email protected]>
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
This is a major modernization of the FareBot build system and Android codebase. The project has been migrated from Groovy-based Gradle to Kotlin DSL, updated to use Jetpack Compose for UI, and upgraded all dependencies to current versions. The build now uses Koin for dependency injection instead of Dagger, and Kotlinx Serialization instead of Gson.
Key Changes
Build System & Dependencies
build.gradle→build.gradle.kts(Kotlin DSL)dependencies.gradleand inlined dependency managementDependency Injection
AndroidModule.ktwith all Android-specific bindingsFareBotApplicationto use Koin'sstartKoin()DaggerFareBotApplicationComponentSerialization
KotlinxCardKeysSerializerfor card key serializationInstantinstead ofjava.util.DateUI & Platform
AndroidPlatformActionsfor platform-specific operations (clipboard, file picker, NFC settings, etc.)StringResourceabstraction instead of AndroidContextNfcStreamto use Kotlin Flow for reactive tag eventsManifest & Configuration
android:exported="true"to activities (required for API 31+)farebot-android/build.gradle.ktswith modern Android DSLDocumentation
CLAUDE.mdwith proxy configuration guide for authenticated HTTP proxy environmentsCleanup
.gitmodules(nfc-felica-lib submodule).kotlinto.gitignoreNotable Implementation Details
CLAUDE.mddocuments a sophisticated local proxy setup for environments behind authenticated HTTP proxies, with health checks and credential rotation supporthttps://claude.ai/code/session_016GBryRuV3PUzuQhuW2gArG