Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions android/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.gradle/
build/
*.tsbuildinfo
local.properties
*.log
.kotlin/
65 changes: 65 additions & 0 deletions android/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Login with ChatGPT — Android

On-device, serverless Android port of the [Login with ChatGPT](../README.md) SDK.
Users sign in with **their own** ChatGPT subscription via OpenAI's device-code
OAuth flow, and the app calls Codex models billed to that user — no OpenAI API key,
and no backend to host. Tokens live on the device in the Android Keystore.

## Modules

| Module | What it is |
| --- | --- |
| [`lwc-core`](./lwc-core) | Pure Kotlin/JVM engine — device flow, token refresh, JWT parsing, Codex streaming. No Android deps; unit-tested on the JVM. Port of the TS `-core` package. |
| `lwc-android` | Android library — `KeystoreTokenStore` (encrypted at rest), a Custom Tab launcher, and the `LoginWithChatGPT` facade. |
| `sample` | Minimal Compose app: sign-in screen → device code → streamed chat. |

## Build & run

Uses the Gradle wrapper (8.11.1) — the JDK 17 toolchain and Android SDK are picked
up from `JAVA_HOME` / `local.properties`.

On a low-RAM machine (≤8 GB), add these to your user `~/.gradle/gradle.properties`
to keep the build inside one small JVM:

```properties
org.gradle.jvmargs=-Xmx1024m -XX:MaxMetaspaceSize=512m
org.gradle.workers.max=1
kotlin.compiler.execution.strategy=in-process
```

```bash
# Run the pure-JVM unit tests (body normalization + JWT parsing)
./gradlew :lwc-core:test

# Stage-0 spike: full device login + streamed reply against a REAL ChatGPT account
./gradlew :lwc-core:run --args="Say hello in one short sentence."

# Build the sample APK
./gradlew :sample:assembleDebug

# Install + launch on a running emulator/device
./gradlew :sample:installDebug
adb shell am start -n com.opencoredev.loginwithchatgpt.sample/.MainActivity
```

## Using the library in your own app

```kotlin
val lwc = LoginWithChatGPT(context) // Keystore-backed by default

// Sign in
val device = lwc.startDeviceLogin()
lwc.openVerification(device) // Custom Tab; user enters device.userCode
while (lwc.poll(device) is DevicePollResult.Pending) delay(device.interval * 1000L)

// Call a model — billed to the signed-in user's ChatGPT plan
lwc.chat(buildJsonObject { put("model", "gpt-5.5"); put("input", "Hi") })
.collect { delta -> /* append streamed text */ }
```

## Security & status

Tokens are encrypted at rest via Android-Keystore-backed `EncryptedSharedPreferences`
(see [`lwc-core/README`](./lwc-core/README.md) for the trust-boundary notes). This
rides OpenAI's unofficial Codex OAuth client, so it may break if OpenAI changes an
endpoint — every URL/id is overridable through `ChatGPTConfig`.
9 changes: 9 additions & 0 deletions android/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Root build: declare plugin versions once; modules apply them without versions.
plugins {
id("com.android.application") version "8.7.3" apply false
id("com.android.library") version "8.7.3" apply false
id("org.jetbrains.kotlin.android") version "2.0.21" apply false
id("org.jetbrains.kotlin.jvm") version "2.0.21" apply false
id("org.jetbrains.kotlin.plugin.compose") version "2.0.21" apply false
id("org.jetbrains.kotlin.plugin.serialization") version "2.0.21" apply false
}
4 changes: 4 additions & 0 deletions android/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
org.gradle.caching=true
kotlin.code.style=official
android.useAndroidX=true
Binary file added android/gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
9 changes: 9 additions & 0 deletions android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
248 changes: 248 additions & 0 deletions android/gradlew

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading