gioser

sergio/humanoid

HTML 25Markdown 1TOML 10 Sutichaykuna

consola: tap en la notificación abre la sesión exacta

El evento trae shuma_session (el ULID que el daemon siembra en el PTY).
La notificación lo lleva como extra; MainActivity (singleTask + onNewIntent)
lo enruta a ConsolaViewModel.openFromNotification, que se adjunta a ese
terminal exacto.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

03a89d0dbea564c35342da35ed27147cc3ccb1f0 · ruwaq Sergio · (4 killa ñawpaq) · tayta 27b083421 · kay commitpi sach'ata qhaway

Semántico sayk'uy

Mana ima sutichaypas t'ikrakurqanchu: qillqalla, yachachiylla utaq willaylla t'ikrakurqan.

3 qillqakuna · +59 −6

diff --git a/consola/src/main/java/com/rizoma/consola/ConsolaViewModel.kt b/consola/src/main/java/com/rizoma/consola/ConsolaViewModel.kt
index 5b88c2e..a30ea06 100644
--- a/consola/src/main/java/com/rizoma/consola/ConsolaViewModel.kt
+++ b/consola/src/main/java/com/rizoma/consola/ConsolaViewModel.kt
@@ -155,6 +155,30 @@ class ConsolaViewModel(app: Application) : AndroidViewModel(app) {
         }
     }
 
+    /**
+     * Abre la sesión exacta referida por una notificación (tap). Si ya tenemos
+     * su metadata en la lista, se adjunta con ella; si no, se adjunta por id
+     * (el daemon solo necesita el ULID) y refresca la lista en paralelo.
+     */
+    fun openFromNotification(sessionId: String) {
+        val existing = sessions.find { it.id == sessionId }
+        if (existing != null) {
+            attach(existing)
+            return
+        }
+        startTerminal { svc ->
+            svc.openTerminal(
+                session = sessionId,
+                program = "",
+                args = emptyList(),
+                cwd = ".",
+                label = "claude",
+                title = "claude",
+            )
+        }
+        refresh()
+    }
+
     /** Se adjunta a una sesión existente. */
     fun attach(session: PtySession) {
         startTerminal { svc ->
diff --git a/consola/src/main/java/com/rizoma/consola/MainActivity.kt b/consola/src/main/java/com/rizoma/consola/MainActivity.kt
index c9b762a..3404b04 100644
--- a/consola/src/main/java/com/rizoma/consola/MainActivity.kt
+++ b/consola/src/main/java/com/rizoma/consola/MainActivity.kt
@@ -1,12 +1,14 @@
 package com.rizoma.consola
 
 import android.Manifest
+import android.content.Intent
 import android.content.pm.PackageManager
 import android.os.Build
 import android.os.Bundle
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
 import androidx.activity.result.contract.ActivityResultContracts
+import androidx.activity.viewModels
 import androidx.core.content.ContextCompat
 import androidx.core.view.WindowCompat
 import com.rizoma.consola.ui.ConsolaApp
@@ -14,6 +16,10 @@ import com.rizoma.consola.ui.theme.ConsolaTheme
 
 class MainActivity : ComponentActivity() {
 
+    // Mismo VM que crea ConsolaApp (ambos resuelven al store de la Activity),
+    // para poder enrutar el deep-link de la notificación a la sesión exacta.
+    private val vm: ConsolaViewModel by viewModels()
+
     // Permiso para mostrar la notificación persistente del servicio (Android 13+).
     private val pedirNotificaciones =
         registerForActivityResult(ActivityResultContracts.RequestPermission()) { /* opcional: la sesión sigue viva sin ella */ }
@@ -22,17 +28,34 @@ class MainActivity : ComponentActivity() {
         super.onCreate(savedInstanceState)
         WindowCompat.setDecorFitsSystemWindows(window, false)
         pedirPermisoNotificaciones()
+        abrirSesionDeNotificacion(intent)
         setContent {
             ConsolaTheme {
-                ConsolaApp()
+                ConsolaApp(vm)
             }
         }
     }
 
+    override fun onNewIntent(intent: Intent) {
+        super.onNewIntent(intent)
+        setIntent(intent)
+        abrirSesionDeNotificacion(intent)
+    }
+
+    /** Si la notificación trae una sesión, se adjunta a ese terminal. */
+    private fun abrirSesionDeNotificacion(intent: Intent?) {
+        val id = intent?.getStringExtra(EXTRA_SESSION)?.takeIf { it.isNotBlank() } ?: return
+        vm.openFromNotification(id)
+    }
+
     private fun pedirPermisoNotificaciones() {
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return
         val concedido = ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) ==
             PackageManager.PERMISSION_GRANTED
         if (!concedido) pedirNotificaciones.launch(Manifest.permission.POST_NOTIFICATIONS)
     }
+
+    companion object {
+        const val EXTRA_SESSION = "session_id"
+    }
 }
diff --git a/consola/src/main/java/com/rizoma/consola/service/ConsolaService.kt b/consola/src/main/java/com/rizoma/consola/service/ConsolaService.kt
index c0f9bec..245a503 100644
--- a/consola/src/main/java/com/rizoma/consola/service/ConsolaService.kt
+++ b/consola/src/main/java/com/rizoma/consola/service/ConsolaService.kt
@@ -169,6 +169,7 @@ class ConsolaService : Service() {
         val kind = o.optString("kind")
         val cwd = o.optString("cwd")
         val msg = o.optString("message")
+        val session = o.optString("shuma_session").ifBlank { null }
         val dir = cwd.substringAfterLast('/').ifBlank { cwd }
         val (title, text) = when (kind) {
             "Notification" ->
@@ -178,15 +179,20 @@ class ConsolaService : Service() {
             else ->
                 kind.ifBlank { "Claude" } to msg.ifBlank { dir }
         }
-        notifyAlert(title, text)
+        notifyAlert(title, text, session)
     }
 
-    private fun notifyAlert(title: String, text: String) {
+    private fun notifyAlert(title: String, text: String, sessionId: String?) {
         val mgr = getSystemService(NotificationManager::class.java)
+        // Al tocar: abre la app y, si el evento trae la sesión exacta, se
+        // adjunta a ESE terminal (SHUMA_SESSION sembrado por el daemon).
+        val intent = Intent(this, MainActivity::class.java)
+            .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
+        if (sessionId != null) intent.putExtra(MainActivity.EXTRA_SESSION, sessionId)
         val open = PendingIntent.getActivity(
-            this, 2,
-            Intent(this, MainActivity::class.java).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
-            PendingIntent.FLAG_IMMUTABLE,
+            this, sessionId?.hashCode() ?: 2,
+            intent,
+            PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
         )
         val n = NotificationCompat.Builder(this, ALERT_CHANNEL_ID)
             .setSmallIcon(R.drawable.ic_consola)