diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..0f4c0ac
--- /dev/null
+++ b/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/java/com/thisux/droidclaw/DroidClawApp.kt b/android/app/src/main/java/com/thisux/droidclaw/DroidClawApp.kt
new file mode 100644
index 0000000..31752b6
--- /dev/null
+++ b/android/app/src/main/java/com/thisux/droidclaw/DroidClawApp.kt
@@ -0,0 +1,14 @@
+package com.thisux.droidclaw
+
+import android.app.Application
+import com.thisux.droidclaw.data.SettingsStore
+
+class DroidClawApp : Application() {
+ lateinit var settingsStore: SettingsStore
+ private set
+
+ override fun onCreate() {
+ super.onCreate()
+ settingsStore = SettingsStore(this)
+ }
+}
diff --git a/android/app/src/main/java/com/thisux/droidclaw/data/SettingsStore.kt b/android/app/src/main/java/com/thisux/droidclaw/data/SettingsStore.kt
new file mode 100644
index 0000000..f9da10b
--- /dev/null
+++ b/android/app/src/main/java/com/thisux/droidclaw/data/SettingsStore.kt
@@ -0,0 +1,55 @@
+package com.thisux.droidclaw.data
+
+import android.content.Context
+import androidx.datastore.core.DataStore
+import androidx.datastore.preferences.core.Preferences
+import androidx.datastore.preferences.core.booleanPreferencesKey
+import androidx.datastore.preferences.core.edit
+import androidx.datastore.preferences.core.stringPreferencesKey
+import androidx.datastore.preferences.preferencesDataStore
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+val Context.dataStore: DataStore by preferencesDataStore(name = "settings")
+
+object SettingsKeys {
+ val API_KEY = stringPreferencesKey("api_key")
+ val SERVER_URL = stringPreferencesKey("server_url")
+ val DEVICE_NAME = stringPreferencesKey("device_name")
+ val AUTO_CONNECT = booleanPreferencesKey("auto_connect")
+}
+
+class SettingsStore(private val context: Context) {
+
+ val apiKey: Flow = context.dataStore.data.map { prefs ->
+ prefs[SettingsKeys.API_KEY] ?: ""
+ }
+
+ val serverUrl: Flow = context.dataStore.data.map { prefs ->
+ prefs[SettingsKeys.SERVER_URL] ?: "wss://localhost:8080"
+ }
+
+ val deviceName: Flow = context.dataStore.data.map { prefs ->
+ prefs[SettingsKeys.DEVICE_NAME] ?: android.os.Build.MODEL
+ }
+
+ val autoConnect: Flow = context.dataStore.data.map { prefs ->
+ prefs[SettingsKeys.AUTO_CONNECT] ?: false
+ }
+
+ suspend fun setApiKey(value: String) {
+ context.dataStore.edit { it[SettingsKeys.API_KEY] = value }
+ }
+
+ suspend fun setServerUrl(value: String) {
+ context.dataStore.edit { it[SettingsKeys.SERVER_URL] = value }
+ }
+
+ suspend fun setDeviceName(value: String) {
+ context.dataStore.edit { it[SettingsKeys.DEVICE_NAME] = value }
+ }
+
+ suspend fun setAutoConnect(value: Boolean) {
+ context.dataStore.edit { it[SettingsKeys.AUTO_CONNECT] = value }
+ }
+}
diff --git a/android/app/src/main/java/com/thisux/droidclaw/model/AppState.kt b/android/app/src/main/java/com/thisux/droidclaw/model/AppState.kt
new file mode 100644
index 0000000..bfa1001
--- /dev/null
+++ b/android/app/src/main/java/com/thisux/droidclaw/model/AppState.kt
@@ -0,0 +1,30 @@
+package com.thisux.droidclaw.model
+
+enum class ConnectionState {
+ Disconnected,
+ Connecting,
+ Connected,
+ Error
+}
+
+enum class GoalStatus {
+ Idle,
+ Running,
+ Completed,
+ Failed
+}
+
+data class AgentStep(
+ val step: Int,
+ val action: String,
+ val reasoning: String,
+ val timestamp: Long = System.currentTimeMillis()
+)
+
+data class GoalSession(
+ val sessionId: String,
+ val goal: String,
+ val steps: List,
+ val status: GoalStatus,
+ val timestamp: Long = System.currentTimeMillis()
+)
diff --git a/android/app/src/main/java/com/thisux/droidclaw/model/Protocol.kt b/android/app/src/main/java/com/thisux/droidclaw/model/Protocol.kt
new file mode 100644
index 0000000..8d1d4f8
--- /dev/null
+++ b/android/app/src/main/java/com/thisux/droidclaw/model/Protocol.kt
@@ -0,0 +1,75 @@
+package com.thisux.droidclaw.model
+
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.json.JsonObject
+
+@Serializable
+data class AuthMessage(
+ val type: String = "auth",
+ val apiKey: String,
+ val deviceInfo: DeviceInfoMsg? = null
+)
+
+@Serializable
+data class DeviceInfoMsg(
+ val model: String,
+ val androidVersion: String,
+ val screenWidth: Int,
+ val screenHeight: Int
+)
+
+@Serializable
+data class ScreenResponse(
+ val type: String = "screen",
+ val requestId: String,
+ val elements: List,
+ val screenshot: String? = null,
+ val packageName: String? = null
+)
+
+@Serializable
+data class ResultResponse(
+ val type: String = "result",
+ val requestId: String,
+ val success: Boolean,
+ val error: String? = null,
+ val data: String? = null
+)
+
+@Serializable
+data class GoalMessage(
+ val type: String = "goal",
+ val text: String
+)
+
+@Serializable
+data class PongMessage(
+ val type: String = "pong"
+)
+
+@Serializable
+data class ServerMessage(
+ val type: String,
+ val requestId: String? = null,
+ val deviceId: String? = null,
+ val message: String? = null,
+ val sessionId: String? = null,
+ val goal: String? = null,
+ val success: Boolean? = null,
+ val stepsUsed: Int? = null,
+ val step: Int? = null,
+ val action: JsonObject? = null,
+ val reasoning: String? = null,
+ val screenHash: String? = null,
+ val x: Int? = null,
+ val y: Int? = null,
+ val x1: Int? = null,
+ val y1: Int? = null,
+ val x2: Int? = null,
+ val y2: Int? = null,
+ val duration: Int? = null,
+ val text: String? = null,
+ val packageName: String? = null,
+ val url: String? = null,
+ val code: Int? = null
+)
diff --git a/android/app/src/main/java/com/thisux/droidclaw/model/UIElement.kt b/android/app/src/main/java/com/thisux/droidclaw/model/UIElement.kt
new file mode 100644
index 0000000..68ac65d
--- /dev/null
+++ b/android/app/src/main/java/com/thisux/droidclaw/model/UIElement.kt
@@ -0,0 +1,26 @@
+package com.thisux.droidclaw.model
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class UIElement(
+ val id: String = "",
+ val text: String = "",
+ val type: String = "",
+ val bounds: String = "",
+ val center: List = listOf(0, 0),
+ val size: List = listOf(0, 0),
+ val clickable: Boolean = false,
+ val editable: Boolean = false,
+ val enabled: Boolean = false,
+ val checked: Boolean = false,
+ val focused: Boolean = false,
+ val selected: Boolean = false,
+ val scrollable: Boolean = false,
+ val longClickable: Boolean = false,
+ val password: Boolean = false,
+ val hint: String = "",
+ val action: String = "read",
+ val parent: String = "",
+ val depth: Int = 0
+)