Initial commit: Kecalek Android client
Complete Android client for encrypted chat platform. 78+ Kotlin files: crypto (X3DH, Double Ratchet, AES-GCM, Ed25519, X25519, RSA-PSS), network (TCP/TLS, 50 endpoints), Hilt DI, Room+SQLCipher DB, Jetpack Compose UI with Catppuccin Mocha theme. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
341
specs/agent-a-gradle-setup.md
Normal file
341
specs/agent-a-gradle-setup.md
Normal file
@@ -0,0 +1,341 @@
|
||||
# Agent A: Gradle + Project Setup
|
||||
|
||||
## Phase: 0 (Scaffolding)
|
||||
## Priority: FIRST — blocks all other agents
|
||||
|
||||
## Context
|
||||
You are setting up an Android project for "Kecalek" — an end-to-end encrypted chat application.
|
||||
The app uses Signal Protocol (X3DH + Double Ratchet + Sender Keys) for encryption.
|
||||
This is a Kotlin-first project with Jetpack Compose UI.
|
||||
|
||||
## Task
|
||||
Create the complete Android project structure with Gradle build files, manifest, and empty package directories.
|
||||
|
||||
## Files to Create
|
||||
|
||||
### Root Level
|
||||
```
|
||||
android/
|
||||
├── build.gradle.kts (project-level)
|
||||
├── settings.gradle.kts
|
||||
├── gradle.properties
|
||||
├── gradle/
|
||||
│ └── wrapper/
|
||||
│ ├── gradle-wrapper.jar
|
||||
│ └── gradle-wrapper.properties
|
||||
├── gradlew
|
||||
├── gradlew.bat
|
||||
└── app/
|
||||
├── build.gradle.kts (app-level)
|
||||
└── src/
|
||||
├── main/
|
||||
│ ├── AndroidManifest.xml
|
||||
│ ├── java/com/kecalek/chat/ (package dirs)
|
||||
│ └── res/
|
||||
│ ├── values/
|
||||
│ │ ├── strings.xml
|
||||
│ │ ├── themes.xml
|
||||
│ │ └── colors.xml
|
||||
│ ├── mipmap-hdpi/
|
||||
│ ├── mipmap-mdpi/
|
||||
│ ├── mipmap-xhdpi/
|
||||
│ ├── mipmap-xxhdpi/
|
||||
│ └── mipmap-xxxhdpi/
|
||||
└── test/
|
||||
└── java/com/kecalek/chat/
|
||||
```
|
||||
|
||||
### Project-Level build.gradle.kts
|
||||
```kotlin
|
||||
plugins {
|
||||
id("com.android.application") version "8.2.2" apply false
|
||||
id("org.jetbrains.kotlin.android") version "1.9.22" apply false
|
||||
id("com.google.dagger.hilt.android") version "2.50" apply false
|
||||
id("com.google.devtools.ksp") version "1.9.22-1.0.17" apply false
|
||||
}
|
||||
```
|
||||
|
||||
### settings.gradle.kts
|
||||
```kotlin
|
||||
pluginManagement {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
dependencyResolution {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
rootProject.name = "Kecalek"
|
||||
include(":app")
|
||||
```
|
||||
|
||||
### App-Level build.gradle.kts
|
||||
```kotlin
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("com.google.dagger.hilt.android")
|
||||
id("com.google.devtools.ksp")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.kecalek.chat"
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.kecalek.chat"
|
||||
minSdk = 26
|
||||
targetSdk = 34
|
||||
versionCode = 1
|
||||
versionName = "0.8.5"
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = true
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.5.8"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Compose BOM
|
||||
val composeBom = platform("androidx.compose:compose-bom:2024.02.00")
|
||||
implementation(composeBom)
|
||||
implementation("androidx.compose.ui:ui")
|
||||
implementation("androidx.compose.ui:ui-graphics")
|
||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||
implementation("androidx.compose.material3:material3")
|
||||
implementation("androidx.compose.material:material-icons-extended")
|
||||
implementation("androidx.activity:activity-compose:1.8.2")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.7.0")
|
||||
implementation("androidx.navigation:navigation-compose:2.7.7")
|
||||
|
||||
// Hilt DI
|
||||
implementation("com.google.dagger:hilt-android:2.50")
|
||||
ksp("com.google.dagger:hilt-compiler:2.50")
|
||||
implementation("androidx.hilt:hilt-navigation-compose:1.1.0")
|
||||
|
||||
// Room + SQLCipher
|
||||
implementation("androidx.room:room-runtime:2.6.1")
|
||||
implementation("androidx.room:room-ktx:2.6.1")
|
||||
ksp("androidx.room:room-compiler:2.6.1")
|
||||
implementation("net.zetetic:android-database-sqlcipher:4.5.4")
|
||||
implementation("androidx.sqlite:sqlite-ktx:2.4.0")
|
||||
|
||||
// Crypto: Tink + Bouncy Castle
|
||||
implementation("com.google.crypto.tink:tink-android:1.12.0")
|
||||
implementation("org.bouncycastle:bcprov-jdk18on:1.77")
|
||||
implementation("org.bouncycastle:bcpkix-jdk18on:1.77")
|
||||
|
||||
// Image loading
|
||||
implementation("io.coil-kt:coil-compose:2.5.0")
|
||||
|
||||
// QR code
|
||||
implementation("com.google.zxing:core:3.5.3")
|
||||
implementation("com.journeyapps:zxing-android-embedded:4.3.0")
|
||||
|
||||
// Camera (for QR scanning)
|
||||
implementation("androidx.camera:camera-camera2:1.3.1")
|
||||
implementation("androidx.camera:camera-lifecycle:1.3.1")
|
||||
implementation("androidx.camera:camera-view:1.3.1")
|
||||
|
||||
// Biometric
|
||||
implementation("androidx.biometric:biometric:1.1.0")
|
||||
|
||||
// DataStore (encrypted preferences)
|
||||
implementation("androidx.datastore:datastore-preferences:1.0.0")
|
||||
implementation("androidx.security:security-crypto:1.1.0-alpha06")
|
||||
|
||||
// Coroutines
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
|
||||
|
||||
// JSON
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
|
||||
|
||||
// Testing
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3")
|
||||
androidTestImplementation(composeBom)
|
||||
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
|
||||
debugImplementation("androidx.compose.ui:ui-tooling")
|
||||
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
||||
}
|
||||
```
|
||||
|
||||
### AndroidManifest.xml
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="32" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||
|
||||
<application
|
||||
android:name=".KecalekApp"
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Kecalek"
|
||||
android:networkSecurityConfig="@xml/network_security_config">
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.Kecalek">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
```
|
||||
|
||||
### gradle.properties
|
||||
```properties
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
android.useAndroidX=true
|
||||
kotlin.code.style=official
|
||||
android.nonTransitiveRClass=true
|
||||
```
|
||||
|
||||
### res/values/strings.xml
|
||||
```xml
|
||||
<resources>
|
||||
<string name="app_name">Kecalek</string>
|
||||
</resources>
|
||||
```
|
||||
|
||||
### res/xml/network_security_config.xml
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config cleartextTrafficPermitted="false">
|
||||
<trust-anchors>
|
||||
<certificates src="system" />
|
||||
</trust-anchors>
|
||||
</base-config>
|
||||
<!-- Debug only: allow cleartext for local dev -->
|
||||
<debug-overrides>
|
||||
<trust-anchors>
|
||||
<certificates src="user" />
|
||||
</trust-anchors>
|
||||
</debug-overrides>
|
||||
</network-security-config>
|
||||
```
|
||||
|
||||
## Package Directory Structure
|
||||
Create these empty directories under `app/src/main/java/com/kecalek/chat/`:
|
||||
```
|
||||
di/
|
||||
crypto/
|
||||
network/
|
||||
core/
|
||||
data/
|
||||
data/local/
|
||||
data/local/dao/
|
||||
data/local/entity/
|
||||
data/model/
|
||||
data/repository/
|
||||
ui/
|
||||
ui/theme/
|
||||
ui/navigation/
|
||||
ui/auth/
|
||||
ui/conversations/
|
||||
ui/chat/
|
||||
ui/groups/
|
||||
ui/profile/
|
||||
ui/verification/
|
||||
ui/devices/
|
||||
ui/components/
|
||||
util/
|
||||
```
|
||||
|
||||
## Placeholder Files
|
||||
Create these minimal placeholder files:
|
||||
|
||||
### KecalekApp.kt
|
||||
```kotlin
|
||||
package com.kecalek.chat
|
||||
|
||||
import android.app.Application
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
|
||||
@HiltAndroidApp
|
||||
class KecalekApp : Application()
|
||||
```
|
||||
|
||||
### MainActivity.kt
|
||||
```kotlin
|
||||
package com.kecalek.chat
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent {
|
||||
// TODO: NavGraph entry point
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Constraints
|
||||
- Min SDK 26 (Android 8.0)
|
||||
- Target SDK 34 (Android 14)
|
||||
- Kotlin 1.9.22
|
||||
- Compose BOM 2024.02.00
|
||||
- Java 17 compatibility
|
||||
- Do NOT add any business logic
|
||||
- Do NOT create any UI components beyond the placeholder MainActivity
|
||||
- All `TODO` comments should be brief and descriptive
|
||||
|
||||
## DO NOT
|
||||
- Implement any cryptographic operations
|
||||
- Add any UI screens or composables
|
||||
- Add business logic or ViewModels
|
||||
- Modify any files outside the android/ directory
|
||||
Reference in New Issue
Block a user