# Android Platform SDK — Implementation & Migration Guide

> **Package:** `ByteLystPlatformSDK` (Kotlin Android library, consumed via Gradle composite build)
> **Location:** `learning_ai_common_plat/packages/kotlin-platform-sdk/`
> **Status:** ✅ Complete — 13 components, 35 JUnit5+MockWebServer tests, all 3 Android apps migrated

---

## 1. Executive Summary

The **Kotlin Platform SDK** (`kotlin-platform-sdk`) mirrors the Swift SDK's 13-component architecture and is consumed by all 3 Android apps via Gradle composite builds. Each app eliminated ~550–1,200 LOC of duplicated platform code.

This document describes the SDK architecture, the completed migrations, and the integration pattern for future apps.

---

## 2. Current State — Duplication Audit

### 2.1 Apps Scanned

| App            | Repo                                | Android Package        | Platform Files                  | Estimated Platform LOC |
| -------------- | ----------------------------------- | ---------------------- | ------------------------------- | ---------------------- |
| **LysnrAI**    | `learning_voice_ai_agent`           | `com.saravana.lysnrai` | 12 files                        | ~1,200                 |
| **ChronoMind** | `learning_ai_clock`                 | `com.chronomind.app`   | 7 files                         | ~800                   |
| **MindLyst**   | `learning_multimodal_memory_agents` | `com.mindlyst.android` | 5 files                         | ~550                   |
| **JarvisJr**   | `learning_ai_jarvis_jr`             | —                      | 0 (Android not started)         | 0                      |
| **PeakPulse**  | `learning_ai_peakpulse`             | —                      | 0 (Android not started)         | 0                      |
| **NomGap**     | `learning_ai_fastgap`               | —                      | React Native (see separate doc) | —                      |

**Total duplicated platform LOC: ~2,550**

### 2.2 Per-Component Duplication

| Component              | LysnrAI                     | ChronoMind                           | MindLyst                       | Diff Between Copies               |
| ---------------------- | --------------------------- | ------------------------------------ | ------------------------------ | --------------------------------- |
| **AuthService**        | ✅ 300+ LOC (OkHttp)        | ✅ 344 LOC (HttpURLConnection, Hilt) | ✅ 232 LOC (HttpURLConnection) | productId, prefs name, DI style   |
| **TelemetryService**   | ❌ Not yet                  | ✅ 178 LOC (Hilt @Singleton)         | ✅ 171 LOC (plain class)       | productId, prefs name, class name |
| **FeatureFlagService** | ✅ 98 LOC (OkHttp, object)  | ✅ 89 LOC (Hilt @Singleton)          | ✅ similar                     | HTTP client, DI style             |
| **KillSwitchService**  | ✅ 64 LOC (OkHttp, object)  | ❌ Not yet                           | ❌ Not yet                     | —                                 |
| **BlobService**        | ✅ 150 LOC (OkHttp, object) | ❌ Not yet                           | ❌ Not yet                     | —                                 |
| **LicenseService**     | ✅ 161 LOC (OkHttp, object) | ❌ N/A                               | ❌ N/A                         | —                                 |
| **AuditLogger**        | ✅ 72 LOC (file-based)      | ❌ Not yet                           | ❌ Not yet                     | —                                 |
| **BiometricAuth**      | ✅ exists                   | ❌ Not yet                           | ❌ Not yet                     | —                                 |
| **CertificatePinning** | ✅ exists                   | ❌ Not yet                           | ❌ Not yet                     | —                                 |
| **PlatformApiClient**  | ❌ (uses OkHttp inline)     | ✅ 182 LOC (HttpURLConnection)       | ✅ PlatformServiceClient       | HTTP client choice                |

### 2.3 Key Differences Between Copies

The duplicated code is **95% identical** with these product-specific differences:

| Dimension        | LysnrAI                            | ChronoMind                     | MindLyst                     |
| ---------------- | ---------------------------------- | ------------------------------ | ---------------------------- |
| Product ID       | `"lysnrai"`                        | `"chronomind"`                 | `"mindlyst"`                 |
| SharedPrefs name | `"lysnrai_auth"`                   | `"chronomind_auth"`            | `"mindlyst_auth"`            |
| Default base URL | `BuildConfig.PLATFORM_SERVICE_URL` | `"https://api.chronomind.app"` | `"http://10.0.2.2:4003/api"` |
| HTTP client      | OkHttp                             | `HttpURLConnection`            | `HttpURLConnection`          |
| DI framework     | None (singleton objects)           | Hilt (`@Singleton @Inject`)    | None (plain class)           |
| Flag caching     | SharedPrefs JSON                   | In-memory only                 | In-memory only               |

---

## 3. Proposed Kotlin Platform SDK

### 3.1 Architecture

Mirror the Swift SDK's component-for-component design:

```
learning_ai_common_plat/packages/kotlin-platform-sdk/
├── build.gradle.kts              # Pure Kotlin library (no Android framework deps in core)
├── src/
│   ├── main/kotlin/com/bytelyst/platform/
│   │   ├── BLPlatformConfig.kt   # Product config data class
│   │   ├── BLPlatformClient.kt   # HTTP client (OkHttp-based)
│   │   ├── BLAuthClient.kt       # Auth operations + token management
│   │   ├── BLTelemetryClient.kt  # Telemetry queue + batch flush
│   │   ├── BLFeatureFlagClient.kt # Flag polling + caching
│   │   ├── BLKillSwitchClient.kt # Kill switch check (fail-open)
│   │   ├── BLBlobClient.kt       # Azure Blob upload via SAS
│   │   ├── BLLicenseClient.kt    # License activation + status
│   │   ├── BLAuditLogger.kt      # Local rotating JSON audit log
│   │   ├── BLBiometricAuth.kt    # BiometricPrompt wrapper
│   │   ├── BLCrashReporter.kt    # Uncaught exception handler
│   │   └── BLSecureStore.kt      # EncryptedSharedPreferences wrapper (Android Keystore)
│   └── test/kotlin/com/bytelyst/platform/
│       ├── BLAuthClientTest.kt
│       ├── BLTelemetryClientTest.kt
│       ├── BLFeatureFlagClientTest.kt
│       └── ...
└── gradle.properties
```

### 3.2 Component Mapping (Swift → Kotlin)

| #   | Swift Component       | Kotlin Component      | Notes                                        |
| --- | --------------------- | --------------------- | -------------------------------------------- |
| 1   | `BLPlatformConfig`    | `BLPlatformConfig`    | Data class, identical structure              |
| 2   | `BLPlatformClient`    | `BLPlatformClient`    | OkHttp-based (not HttpURLConnection)         |
| 3   | `BLKeychain`          | `BLSecureStore`       | EncryptedSharedPreferences (AndroidKeystore) |
| 4   | `BLTelemetryClient`   | `BLTelemetryClient`   | Coroutine-based flush, same event schema     |
| 5   | `BLAuthClient`        | `BLAuthClient`        | StateFlow-based auth state                   |
| 6   | `BLFeatureFlagClient` | `BLFeatureFlagClient` | Coroutine polling + SharedPrefs cache        |
| 7   | `BLSyncEngine`        | `BLSyncEngine`        | Generic sync with adapter interface          |
| 8   | `BLBlobClient`        | `BLBlobClient`        | OkHttp multipart upload                      |
| 9   | `BLKillSwitchClient`  | `BLKillSwitchClient`  | Fail-open, same API                          |
| 10  | `BLLicenseClient`     | `BLLicenseClient`     | URL-encode key, same API                     |
| 11  | `BLBiometricAuth`     | `BLBiometricAuth`     | AndroidX BiometricPrompt                     |
| 12  | `BLCrashReporter`     | `BLCrashReporter`     | Thread.setDefaultUncaughtExceptionHandler    |
| 13  | `BLAuditLogger`       | `BLAuditLogger`       | File-based rotating log (same as Swift)      |

### 3.3 Design Decisions

| Decision           | Choice                                     | Rationale                                                                                    |
| ------------------ | ------------------------------------------ | -------------------------------------------------------------------------------------------- |
| **HTTP client**    | OkHttp                                     | Already used by LysnrAI; standard for Android; supports interceptors, timeouts, cert pinning |
| **Serialization**  | kotlinx.serialization                      | Already used by all 3 apps; compile-time safe                                                |
| **Concurrency**    | Kotlin Coroutines                          | Already used by all 3 apps; StateFlow for reactive state                                     |
| **DI approach**    | Constructor injection (no Hilt dependency) | SDK must be DI-framework agnostic; apps wire via Hilt/Koin/manual                            |
| **Secure storage** | EncryptedSharedPreferences                 | Standard Android Keystore-backed storage; replaces plain SharedPreferences for tokens        |
| **Min SDK**        | API 26 (Android 8.0)                       | Matches all existing apps                                                                    |
| **Distribution**   | Composite Gradle build or `includeBuild()` | Similar to Swift Package local reference; no Maven Central needed                            |

### 3.4 BLPlatformConfig (Core)

```kotlin
package com.bytelyst.platform

data class BLPlatformConfig(
    /** Product identifier (e.g., "chronomind", "lysnrai", "mindlyst") */
    val productId: String,
    /** Platform-service base URL (e.g., "http://localhost:4003/api") */
    val baseUrl: String,
    /** Platform string for telemetry (e.g., "android", "wear_os") */
    val platform: String = "android",
    /** Channel string for telemetry (e.g., "native", "keyboard") */
    val channel: String = "native",
    /** Application ID (e.g., "com.chronomind.app") */
    val applicationId: String,
)
```

### 3.5 App Wrapper Pattern (Mirrors iOS)

Each Android app creates thin wrappers:

```kotlin
// di/PlatformModule.kt (Hilt example)
@Module
@InstallIn(SingletonComponent::class)
object PlatformModule {
    @Provides @Singleton
    fun providePlatformConfig(): BLPlatformConfig = BLPlatformConfig(
        productId = "chronomind",
        baseUrl = BuildConfig.PLATFORM_SERVICE_URL,
        applicationId = BuildConfig.APPLICATION_ID,
    )

    @Provides @Singleton
    fun provideAuthClient(config: BLPlatformConfig, context: @ApplicationContext Context): BLAuthClient =
        BLAuthClient(config, BLSecureStore(context, config.applicationId))

    @Provides @Singleton
    fun provideTelemetryClient(config: BLPlatformConfig, context: @ApplicationContext Context): BLTelemetryClient =
        BLTelemetryClient(config, context)

    @Provides @Singleton
    fun provideFeatureFlagClient(config: BLPlatformConfig): BLFeatureFlagClient =
        BLFeatureFlagClient(config)
}
```

For apps without Hilt (MindLyst uses Koin):

```kotlin
// di/PlatformModule.kt (Koin example)
val platformModule = module {
    single { BLPlatformConfig(productId = "mindlyst", baseUrl = "...", applicationId = "com.mindlyst.android") }
    single { BLSecureStore(androidContext(), get<BLPlatformConfig>().applicationId) }
    single { BLAuthClient(get(), get()) }
    single { BLTelemetryClient(get(), androidContext()) }
    single { BLFeatureFlagClient(get()) }
}
```

---

## 4. Migration Plan

### Phase 1: Create SDK — ✅ Complete

SDK created at `packages/kotlin-platform-sdk/` with 13 components:

- `BLPlatformConfig`, `BLPlatformClient` (OkHttp), `BLSecureStore` (EncryptedSharedPreferences)
- `BLAuthClient` (full auth + StateFlow), `BLTelemetryClient` (batched + SharedPrefs queue)
- `BLFeatureFlagClient` (polling + cache), `BLKillSwitchClient` (fail-open)
- `BLBlobClient` (SAS upload), `BLLicenseClient` (URL-encoded keys)
- `BLAuditLogger` (rotating JSONL), `BLBiometricAuth` (AndroidX BiometricPrompt)
- `BLCrashReporter` (UncaughtExceptionHandler), `BLSyncEngine` (pull-merge-push)

**Tests:** 5 test files, 35 JUnit5+MockWebServer tests (BLPlatformConfigTest, BLPlatformClientTest, BLFeatureFlagClientTest, BLKillSwitchClientTest, BLLicenseClientTest).

### Phase 2: Migrate ChronoMind — ✅ Complete (Hilt DI)

| File                                                     | What Changed                                                |
| -------------------------------------------------------- | ----------------------------------------------------------- |
| `settings.gradle.kts`                                    | Added `includeBuild` for kotlin-platform-sdk                |
| `platform/Config.kt`                                     | **New** — wraps `BLPlatformConfig` with app-specific values |
| `platform/PlatformModule.kt`                             | **New** — Hilt module providing all SDK clients             |
| `AppModule.kt`                                           | Removed old TelemetryService + FeatureFlagService providers |
| `TimerViewModel.kt`                                      | `TelemetryService` → `BLTelemetryClient`                    |
| `MainActivity.kt`, `LoginScreen.kt`, `SettingsScreen.kt` | `AuthService` → `BLAuthClient`                              |

### Phase 3: Migrate MindLyst — ✅ Complete (Koin DI)

| File                         | What Changed                                               |
| ---------------------------- | ---------------------------------------------------------- |
| `settings.gradle.kts`        | Added `includeBuild` for kotlin-platform-sdk               |
| `platform/Config.kt`         | **New** — wraps `BLPlatformConfig` with dev prefs fallback |
| `platform/PlatformModule.kt` | **New** — Koin module providing all SDK clients            |
| `MainActivity.kt`            | `AuthService` → `BLAuthClient` (Koin inject)               |
| `LoginScreen.kt`             | `AuthService` → `BLAuthClient` (state, login, register)    |

### Phase 4: Migrate LysnrAI — ✅ Complete (Hilt DI, partial)

| File                                  | What Changed                                                               |
| ------------------------------------- | -------------------------------------------------------------------------- |
| `settings.gradle.kts`                 | Added `includeBuild` for kotlin-platform-sdk                               |
| `platform/Config.kt`                  | **New** — wraps `BLPlatformConfig` using `BuildConfig` values              |
| `platform/PlatformModule.kt`          | **New** — Hilt module providing Telemetry, FeatureFlag, KillSwitch clients |
| `ui/navigation/LysnrNavEntryPoint.kt` | **New** — Hilt EntryPoint for Composable injection                         |
| `ui/navigation/Navigation.kt`         | `KillSwitchService` + `FeatureFlagService` singletons → SDK clients        |

> **Note:** LysnrAI's `AuthViewModel` remains app-specific (DataStore, keyboard bridging, CloudSync integration). It was intentionally not replaced by `BLAuthClient`.

### Phase 5: Extended Components — ✅ Complete (shipped with Phase 1)

All 13 components were implemented in the initial SDK creation, including BLBlobClient, BLLicenseClient, BLAuditLogger, BLBiometricAuth, BLCrashReporter, and BLSyncEngine.

---

## 5. Gradle Integration

### 5.1 SDK build.gradle.kts

```kotlin
// packages/kotlin-platform-sdk/build.gradle.kts
plugins {
    id("com.android.library")
    kotlin("android") version "2.1.0"
    kotlin("plugin.serialization") version "2.1.0"
}

android {
    namespace = "com.bytelyst.platform"
    compileSdk = 35
    defaultConfig { minSdk = 26 }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }
}

group = "com.bytelyst.platform"
version = "0.1.0"

dependencies {
    implementation("com.squareup.okhttp3:okhttp:4.12.0")
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")

    // Android-specific (SecureStore needs Keystore-backed prefs, BiometricAuth needs AndroidX)
    implementation("androidx.security:security-crypto:1.0.0")
    implementation("androidx.biometric:biometric:1.1.0")
    implementation("androidx.core:core-ktx:1.15.0")

    testImplementation(kotlin("test"))
    testImplementation("com.squareup.okhttp3:mockwebserver:4.12.0")
    testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0")
    testImplementation("org.robolectric:robolectric:4.14.1")
}
```

> **Why `com.android.library` instead of `kotlin("jvm")`?** Several SDK components require
> `android.content.Context` (SecureStore, TelemetryClient, BiometricAuth). A pure JVM module
> cannot access Android framework classes. If you need a pure-Kotlin subset (e.g., for KMP),
> split into `kotlin-platform-core` (JVM: PlatformConfig, models, HTTP client) and
> `kotlin-platform-android` (Android library: SecureStore, BiometricAuth, TelemetryClient).

### 5.2 Consumer App settings.gradle.kts

```kotlin
// android/settings.gradle.kts
includeBuild("../../learning_ai_common_plat/packages/kotlin-platform-sdk") {
    dependencySubstitution {
        substitute(module("com.bytelyst.platform:kotlin-platform-sdk"))
            .using(project(":"))
    }
}
```

### 5.3 Consumer App build.gradle.kts

```kotlin
// android/app/build.gradle.kts
dependencies {
    implementation("com.bytelyst.platform:kotlin-platform-sdk")
    // ... app-specific deps
}
```

---

## 6. Testing Strategy

| Level           | Tool                       | What to Test                                                     |
| --------------- | -------------------------- | ---------------------------------------------------------------- |
| **Unit**        | JUnit5 + MockWebServer     | HTTP requests, JSON serialization, token management, queue logic |
| **Integration** | Robolectric                | EncryptedSharedPreferences, BiometricPrompt                      |
| **E2E**         | Espresso (in consumer app) | Login flow, telemetry flush, flag polling                        |

**Target:** 60+ tests in the SDK (matching Swift SDK's coverage aspirations).

---

## 7. Completion Summary

| Phase                | Status  | Deliverable                                                             |
| -------------------- | ------- | ----------------------------------------------------------------------- |
| SDK creation         | ✅ Done | 13 components, 35 tests                                                 |
| ChronoMind migration | ✅ Done | Hilt DI, AuthService+TelemetryService+FeatureFlagService replaced       |
| MindLyst migration   | ✅ Done | Koin DI, AuthService replaced                                           |
| LysnrAI migration    | ✅ Done | Hilt DI, KillSwitch+FeatureFlag+Telemetry replaced (AuthViewModel kept) |
| Extended components  | ✅ Done | All 13 components shipped in initial SDK                                |

---

## 8. Future Android Apps

Once the SDK exists, new Android apps (JarvisJr Phase 4, PeakPulse future) get platform infrastructure for free:

1. Add `includeBuild()` in settings.gradle.kts
2. Add `implementation("com.bytelyst.platform:kotlin-platform-sdk")` in build.gradle.kts
3. Create DI wiring (Hilt `@Module` or Koin `module {}`)
4. Focus 100% on app-specific business logic

**Estimated setup time for a new app:** 30 minutes (vs. 2-3 days copying + adapting platform code today).

---

## 9. Design Token Integration

The `@bytelyst/design-tokens` package already generates:

- `tokens.css` (web)
- `tokens.ts` (web)
- `MindLystTokens.kt` (Kotlin)
- `MindLystTheme.swift` (Swift)

**Action needed:** Extend the token generator to produce per-product Kotlin token files:

- `ChronoMindTokens.kt`
- `LysnrAITokens.kt`
- `PeakPulseTokens.kt`
- `JarvisJrTokens.kt`

Or create a single `BLDesignTokens.kt` with product-specific color palette selection at runtime via `BLPlatformConfig.productId`.

---

## 10. Risk Mitigation

| Risk                                       | Mitigation                                                                           |
| ------------------------------------------ | ------------------------------------------------------------------------------------ |
| **OkHttp version conflict**                | SDK declares OkHttp as `api` dependency; consumers use same version                  |
| **Hilt vs Koin**                           | SDK uses constructor injection (no DI annotations); each app wires its own way       |
| **EncryptedSharedPreferences on API < 23** | `minSdk = 26` across all apps; not an issue                                          |
| **Breaking changes**                       | Semantic versioning; test in ChronoMind first (cleanest Hilt DI structure)           |
| **Gradle composite build issues**          | Document exact `includeBuild()` + `dependencySubstitution` setup                     |
| **CI builds without sibling repo**         | Pack SDK into `.aar` tarball for CI (same pattern as Swift Package + docker-prep.sh) |
