Files
Kordant/tasks/shieldai-unified-restructure/37-android-api-client.md
2026-05-25 12:23:23 -04:00

6.9 KiB

37. Android App — API Client, tRPC Bridge, and Offline Support

meta: id: shieldai-unified-restructure-37 feature: shieldai-unified-restructure priority: P1 depends_on: [shieldai-unified-restructure-34, shieldai-unified-restructure-35, shieldai-unified-restructure-36] tags: [android, kotlin, api, networking, offline, mobile]

objective:

  • Build the API client layer for the Android app that communicates with the unified monolith's tRPC endpoints. Use a thin HTTP bridge approach with Retrofit or Ktor Client, plus offline support with Room caching and WorkManager request queuing.

deliverables:

  • android/app/src/main/java/com/shieldai/android/data/remote/ — Remote data layer:
    • TRPCApiService.kt — Retrofit interface or Ktor client defining all tRPC endpoints:
      • @POST("api/trpc/user.me"), @POST("api/trpc/darkwatch.getWatchlist"), etc.
      • Request/response wrappers for tRPC batch format
    • TRPCResponse.kt / TRPCError.kt — Data classes for tRPC response/error parsing
    • AuthInterceptor.kt — OkHttp interceptor injecting JWT from EncryptedSharedPreferences
    • ErrorHandler.kt — Centralized error handling with retry logic
  • android/app/src/main/java/com/shieldai/android/data/local/ — Local data layer:
    • ShieldAIDatabase.kt — Room database with entities:
      • UserEntity, SubscriptionEntity, WatchlistItemEntity, ExposureEntity, AlertEntity, VoiceEnrollmentEntity, VoiceAnalysisEntity, SpamRuleEntity, PropertyEntity, RemovalRequestEntity, BrokerListingEntity
    • DAO interfaces for each entity with CRUD operations
    • CacheManager.kt — TTL-based cache logic using Room
  • android/app/src/main/java/com/shieldai/android/data/repository/ — Repository layer:
    • UserRepository.kt, DarkWatchRepository.kt, VoicePrintRepository.kt, etc.
    • Each repository: remote fetch → cache to Room → return flow/coroutine
    • RepositoryModule.kt — Hilt module for DI
  • android/app/src/main/java/com/shieldai/android/data/model/ — Data models:
    • User.kt, Subscription.kt, WatchlistItem.kt, Exposure.kt, Alert.kt, etc.
    • All models as Kotlin data classes with Parcelable where needed
    • Enum classes matching backend enums
  • android/app/src/main/java/com/shieldai/android/data/sync/ — Offline support:
    • OfflineWorker.kt — WorkManager worker for retrying queued requests
    • PendingRequestDao.kt — Room table for queued mutations
    • SyncManager.kt — Manages queue, triggers WorkManager on connectivity restore
  • android/app/src/main/java/com/shieldai/android/di/ — Dependency injection:
    • NetworkModule.kt — Retrofit/OkHttp setup
    • DatabaseModule.kt — Room database provider
    • RepositoryModule.kt — Repository bindings

steps:

  1. Add dependencies to build.gradle.kts:
    • Retrofit: com.squareup.retrofit2:retrofit, converter-gson or converter-kotlinx-serialization
    • OkHttp: logging-interceptor
    • Room: androidx.room:room-runtime, room-ktx, room-compiler
    • WorkManager: androidx.work:work-runtime-ktx
    • Hilt: com.google.dagger:hilt-android, hilt-compiler
    • Kotlinx Serialization: org.jetbrains.kotlinx:kotlinx-serialization-json
  2. Create TRPCApiService.kt:
    • Define POST endpoints for each tRPC procedure
    • Request body: {"0": {"json": { ... }}} (tRPC batch format for single call)
    • Response: TRPCResponse<T> with result.data field
    • Use Kotlinx Serialization for JSON parsing
  3. Create data models:
    • Kotlin data classes with @Serializable annotation
    • Enum classes with @Serializable and custom serializers if needed
    • Date parsing using kotlinx.datetime or java.time.Instant
  4. Create Room database:
    • ShieldAIDatabase abstract class extending RoomDatabase
    • Entity classes with @Entity, @PrimaryKey, @ColumnInfo
    • DAO interfaces with @Dao, @Query, @Insert, @Update, @Delete
    • Type converters for complex types (enums, dates, JSON)
  5. Create repositories:
    • Each repository has remoteDataSource (API service) and localDataSource (DAO)
    • getData(): check cache first, if stale or missing → fetch remote → save to cache → return
    • Return Flow<T> or suspend functions with Result<T>
  6. Create offline support:
    • PendingRequest entity: endpoint, method, body, timestamp, retryCount
    • SyncManager: add request to pending queue when offline
    • OfflineWorker: periodic WorkManager task that processes pending requests
    • Network monitor using ConnectivityManager to trigger immediate sync
  7. Create DI modules:
    • NetworkModule: provide Retrofit, OkHttpClient with auth interceptor and logging
    • DatabaseModule: provide ShieldAIDatabase singleton
    • RepositoryModule: bind repository interfaces to implementations
  8. Test all layers with mocked dependencies.

steps:

  • Unit: Retrofit service creates correct HTTP requests
  • Unit: Room DAO inserts and retrieves entities correctly
  • Unit: Repository returns cached data when offline
  • Unit: SyncManager queues requests when network unavailable
  • Integration: API client successfully calls user.me against local dev server

acceptance_criteria:

  • Retrofit/Ktor client makes authenticated HTTP requests to tRPC endpoints
  • tRPC response format is correctly parsed into Kotlin data classes
  • Room database caches API responses with TTL-based invalidation
  • Offline mutations are queued and retried when connectivity restored
  • All common API procedures have type-safe Kotlin wrappers
  • Network errors trigger retry with exponential backoff
  • Repository layer provides clean abstraction over remote and local data
  • Hilt dependency injection is configured for all layers
  • API configuration supports different environments (dev, staging, prod)

validation:

  • Point API client to local dev server (http://10.0.2.2:3000 for emulator)
  • Call user.me() and verify response parsed into User model
  • Disconnect network, attempt a mutation, verify it queues in Room
  • Reconnect network, verify WorkManager processes queued request
  • Verify cache hit by calling same endpoint twice with network disabled
  • Run ./gradlew test for unit tests

notes:

  • Retrofit with Kotlinx Serialization is the recommended stack. Gson works too but Kotlinx Serialization has better null safety.
  • For tRPC, the Android client cannot use tRPC's type-safe client directly. The HTTP bridge is the pragmatic approach.
  • The tRPC batch link sends multiple procedures in one HTTP request. For simplicity, use single-procedure requests.
  • Room database schema should mirror the Drizzle schema closely, but can be simplified (fewer columns) for caching purposes.
  • WorkManager requires androidx.work:work-runtime-ktx. It handles background execution reliably across Android versions.
  • For network monitoring, ConnectivityManager.registerDefaultNetworkCallback is the modern approach (API 24+).
  • Use Result<T> or sealed classes (Success, Error, Loading) for API state management in ViewModels.