diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2d782b4..13a0388 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -103,6 +103,21 @@
android:resource="@xml/accessibility_config" />
+
+
+
+
+
+
+
+
+ (data as? JsonObject)?.get("file_path")
+ ?.let { (it as? JsonPrimitive)?.content }
+ }
+ if (filePath == null) {
+ postResultNotification("Screenshot failed: no file path")
+ restoreTile()
+ return@launch
+ }
+
+ val analyzeResult = visionAnalyze.execute(buildJsonObject {
+ put("file_path", filePath)
+ put("question", "Describe what you see on the screen. What app is open? What is the user looking at?")
+ })
+
+ if (analyzeResult.success) {
+ val analysis = analyzeResult.data?.let { data ->
+ (data as? JsonObject)?.get("analysis")
+ ?.let { (it as? JsonPrimitive)?.content }
+ } ?: "Analysis complete"
+ postResultNotification(analysis)
+ } else {
+ postResultNotification("Analysis failed: ${analyzeResult.error}")
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Tile screenshot+explain failed: ${e.message}")
+ postResultNotification("Error: ${e.message}")
+ } finally {
+ restoreTile()
+ }
+ }
+ }
+
+ override fun onDestroy() {
+ tileScope.cancel()
+ super.onDestroy()
+ }
+
+ private fun updateTileState() {
+ val tile = qsTile ?: return
+ try {
+ val agentLoop = getEntryPoint().agentLoop()
+ when (agentLoop.state.value) {
+ AgentState.IDLE -> {
+ tile.state = Tile.STATE_ACTIVE
+ tile.label = "CellClaw"
+ }
+ AgentState.THINKING -> {
+ tile.state = Tile.STATE_ACTIVE
+ tile.label = "Thinking..."
+ }
+ AgentState.EXECUTING_TOOLS -> {
+ tile.state = Tile.STATE_ACTIVE
+ tile.label = "Working..."
+ }
+ AgentState.WAITING_APPROVAL -> {
+ tile.state = Tile.STATE_ACTIVE
+ tile.label = "Approval"
+ }
+ AgentState.PAUSED -> {
+ tile.state = Tile.STATE_INACTIVE
+ tile.label = "Paused"
+ }
+ AgentState.ERROR -> {
+ tile.state = Tile.STATE_INACTIVE
+ tile.label = "Error"
+ }
+ }
+ } catch (e: Exception) {
+ tile.state = Tile.STATE_INACTIVE
+ tile.label = "CellClaw"
+ }
+ tile.updateTile()
+ }
+
+ private fun restoreTile() {
+ try {
+ updateTileState()
+ } catch (_: Exception) {}
+ }
+
+ private fun postResultNotification(text: String) {
+ val notification = NotificationCompat.Builder(this, CellClawApp.CHANNEL_ALERTS)
+ .setContentTitle("Screen Analysis")
+ .setContentText(text)
+ .setStyle(NotificationCompat.BigTextStyle().bigText(text))
+ .setSmallIcon(R.drawable.ic_notification)
+ .setAutoCancel(true)
+ .build()
+
+ val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
+ manager.notify(TILE_NOTIFICATION_ID, notification)
+ }
+
+ companion object {
+ private const val TAG = "CellClawTile"
+ private const val TILE_NOTIFICATION_ID = 200
+ }
+}
diff --git a/app/src/main/res/drawable/ic_qs_cellclaw.xml b/app/src/main/res/drawable/ic_qs_cellclaw.xml
new file mode 100644
index 0000000..6da962c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_qs_cellclaw.xml
@@ -0,0 +1,11 @@
+
+
+
+