feat(android): add data models, DataStore settings, Application class
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
28
android/app/src/main/AndroidManifest.xml
Normal file
28
android/app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:name=".DroidClawApp"
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/Theme.DroidClaw">
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:theme="@style/Theme.DroidClaw">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<Preferences> 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<String> = context.dataStore.data.map { prefs ->
|
||||||
|
prefs[SettingsKeys.API_KEY] ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val serverUrl: Flow<String> = context.dataStore.data.map { prefs ->
|
||||||
|
prefs[SettingsKeys.SERVER_URL] ?: "wss://localhost:8080"
|
||||||
|
}
|
||||||
|
|
||||||
|
val deviceName: Flow<String> = context.dataStore.data.map { prefs ->
|
||||||
|
prefs[SettingsKeys.DEVICE_NAME] ?: android.os.Build.MODEL
|
||||||
|
}
|
||||||
|
|
||||||
|
val autoConnect: Flow<Boolean> = 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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<AgentStep>,
|
||||||
|
val status: GoalStatus,
|
||||||
|
val timestamp: Long = System.currentTimeMillis()
|
||||||
|
)
|
||||||
@@ -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<UIElement>,
|
||||||
|
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
|
||||||
|
)
|
||||||
@@ -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<Int> = listOf(0, 0),
|
||||||
|
val size: List<Int> = 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
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user