Skip to content
Open
Show file tree
Hide file tree
Changes from all 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