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:
filip
2026-03-11 01:19:17 +01:00
commit fe861cfafa
134 changed files with 19078 additions and 0 deletions

View 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