From 4589f4abc3b19e8650a053519dedad38c46cccd6 Mon Sep 17 00:00:00 2001 From: duffyduck Date: Sat, 3 Oct 2026 12:29:14 +0200 Subject: [PATCH] =?UTF-8?q?feat(android-agent):=20Einblendung=20geht=20weg?= =?UTF-8?q?,=20wenn=20Stefan=20etwas=20tut=20=E2=80=94=20statt=20nach=20fe?= =?UTF-8?q?ster=20Zeit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Eine feste Anzeigedauer ist immer geraten: mal zu kurz zum Lesen, mal steht sie laengst im Weg. `seconds=0` liess die Einblendung bisher einfach stehen. Jetzt heisst seconds=0: bleibt, bis der Nutzer etwas tut. - Fernbedienung: der Accessibility-Dienst bekommt onKeyEvent und raeumt die Einblendung weg. Er gibt die Taste mit `return false` WEITER an den Film — es geht also kein Tastendruck verloren und der Player bleibt bedienbar. Dafuer brauchte der Dienst canRequestFilterKeyEvents + flagRequestFilterKeyEvents. - Touch: FLAG_WATCH_OUTSIDE_TOUCH meldet eine Beruehrung ausserhalb. Die geht dank FLAG_NOT_TOUCHABLE gleichzeitig an die App darunter durch. - Notbremse: nach 10 Minuten verschwindet sie in jedem Fall. Bewusst NICHT der naheliegende Weg (Overlay faengt Tasten selbst ab): dafuer muessten FLAG_NOT_FOCUSABLE und FLAG_NOT_TOUCHABLE weg, und dann nimmt die Einblendung dem laufenden Film JEDE Eingabe, solange sie steht. Genau das soll sie nicht. host_notify beschreibt seconds=0 jetzt als den richtigen Wert fuer alles zum Lesen (Wetter, Listen, Zahlen) und die feste Dauer fuer kurze Hinweise. APK baut durch. Co-Authored-By: Claude Opus 5 (1M context) --- aria-brain/agent.py | 13 +++++- .../ariaagent/AriaAccessibilityService.kt | 23 ++++++++++ .../de/hackersoft/ariaagent/OverlayView.kt | 42 +++++++++++++++++-- .../src/main/res/xml/accessibility_config.xml | 3 +- 4 files changed, 75 insertions(+), 6 deletions(-) diff --git a/aria-brain/agent.py b/aria-brain/agent.py index 8c3f794..b5102bf 100644 --- a/aria-brain/agent.py +++ b/aria-brain/agent.py @@ -1638,7 +1638,18 @@ META_TOOLS = [ "host": {"type": "string", "description": "Host-ID/Name (z.B. 'Fire-TV Schlafzimmer')."}, "text": {"type": "string", "description": "Der anzuzeigende Text."}, "title": {"type": "string", "description": "Ueberschrift (Default 'ARIA')."}, - "seconds": {"type": "integer", "description": "Anzeigedauer (Default 12)."}, + "seconds": { + "type": "integer", + "description": ( + "Anzeigedauer in Sekunden (Default 12). " + "0 = stehen lassen, bis Stefan selbst wegdrueckt " + "(Fernbedienung: beliebige Taste, Handy: daneben " + "tippen). Nimm 0 fuer alles zum LESEN — Wetter, " + "Listen, Zahlen: eine feste Dauer ist da immer " + "geraten und meist zu kurz. Kurze Hinweise waehrend " + "eines Films ruhig mit Dauer." + ), + }, "mode": {"type": "string", "description": "overlay | speech | both"}, "focus": {"type": "string", "description": "Bei Sprachausgabe: 'duck' (laufende Medien werden leiser, Standard) | 'pause' (Film/Musik anhalten) | 'none'."}, "image_path": { diff --git a/host-agent/android/app/src/main/java/de/hackersoft/ariaagent/AriaAccessibilityService.kt b/host-agent/android/app/src/main/java/de/hackersoft/ariaagent/AriaAccessibilityService.kt index 7f69884..8acbd77 100644 --- a/host-agent/android/app/src/main/java/de/hackersoft/ariaagent/AriaAccessibilityService.kt +++ b/host-agent/android/app/src/main/java/de/hackersoft/ariaagent/AriaAccessibilityService.kt @@ -44,6 +44,29 @@ class AriaAccessibilityService : AccessibilityService() { override fun onAccessibilityEvent(event: AccessibilityEvent?) { /* passiv */ } override fun onInterrupt() { /* passiv */ } + /** + * Steht eine Einblendung offen, raeumt sie der naechste Tastendruck weg. + * + * Der Trick: wir geben IMMER `false` zurueck. Damit laeuft die Taste ganz + * normal weiter an den Film/die App — die Einblendung schluckt nichts. Sie + * muss dafuer auch nicht fokussierbar sein, kann also weiterhin ueber einem + * laufenden Video stehen, ohne die Fernbedienung zu blockieren. + * + * Deshalb dieser Weg und nicht "Overlay faengt Tasten selbst ab": ein + * fokussierbares Fenster wuerde der App darunter JEDE Eingabe nehmen, + * solange es steht. + * + * Eine feste Anzeigedauer ist naemlich immer falsch — mal zu kurz zum + * Lesen, mal steht sie ewig im Bild. + */ + override fun onKeyEvent(event: android.view.KeyEvent?): Boolean { + if (event != null && event.action == android.view.KeyEvent.ACTION_DOWN + && OverlayView.isShowing()) { + OverlayView.hide(applicationContext) + } + return false // Taste NICHT verbrauchen + } + /** Strukturierter Baum des aktiven Fensters. Shape: {ok, result:{package,count,nodes[]}}. */ fun dump(): JSONObject { val root = rootInActiveWindow diff --git a/host-agent/android/app/src/main/java/de/hackersoft/ariaagent/OverlayView.kt b/host-agent/android/app/src/main/java/de/hackersoft/ariaagent/OverlayView.kt index 7f8e331..65f49f4 100644 --- a/host-agent/android/app/src/main/java/de/hackersoft/ariaagent/OverlayView.kt +++ b/host-agent/android/app/src/main/java/de/hackersoft/ariaagent/OverlayView.kt @@ -25,13 +25,33 @@ import android.widget.TextView * * Nicht fokussierbar (FLAG_NOT_FOCUSABLE) — die Fernbedienung steuert weiter die * App darunter, die Einblendung schluckt keine Tastendruecke. + * + * WEGBEKOMMEN ohne feste Anzeigedauer (seconds = 0): + * - Fernbedienung: der Accessibility-Dienst sieht den naechsten Tastendruck und + * raeumt die Einblendung weg — er gibt die Taste trotzdem an den Film weiter + * (siehe AriaAccessibilityService.onKeyEvent). + * - Touch: FLAG_WATCH_OUTSIDE_TOUCH meldet uns eine Beruehrung AUSSERHALB der + * Einblendung. Die Beruehrung geht gleichzeitig an die App darunter — es + * geht also kein Tipp verloren. + * - Notbremse: nach HARD_CAP_SEC verschwindet sie in jedem Fall. Ohne das + * koennte eine vergessene Einblendung dauerhaft im Bild stehen. + * + * Eine feste Dauer ist naemlich immer falsch geraten — mal zu kurz zum Lesen, + * mal steht sie laengst im Weg. */ object OverlayView { + /** Auch eine "bleibt stehen"-Einblendung geht spaetestens danach weg. */ + private const val HARD_CAP_SEC = 600 + private val main = Handler(Looper.getMainLooper()) private var root: LinearLayout? = null private var hideRunnable: Runnable? = null + /** Steht gerade eine Einblendung? Der Accessibility-Dienst fragt das, bevor + * er auf einen Tastendruck hin aufraeumt. */ + fun isShowing(): Boolean = root != null + /** Hat der Nutzer "Ueber anderen Apps anzeigen" erlaubt? */ fun canDraw(ctx: Context): Boolean = if (Build.VERSION.SDK_INT >= 23) Settings.canDrawOverlays(ctx) else true @@ -98,6 +118,10 @@ object OverlayView { type, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or + // Meldet uns Beruehrungen AUSSERHALB, ohne sie zu + // schlucken — damit raeumt ein Tipp aufs Video die + // Einblendung weg und steuert trotzdem den Player. + WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, android.graphics.PixelFormat.TRANSLUCENT, ).apply { @@ -107,13 +131,23 @@ object OverlayView { width = (ctx.resources.displayMetrics.widthPixels * 0.42f).toInt() } + // Beruehrung ausserhalb -> wegraeumen. Die Beruehrung selbst + // geht dank FLAG_NOT_TOUCHABLE weiter an die App darunter. + box.setOnTouchListener { _, ev -> + if (ev.action == android.view.MotionEvent.ACTION_OUTSIDE) hideNow(ctx) + false + } + wm.addView(box, lp) root = box - if (seconds > 0) { - hideRunnable = Runnable { hideNow(ctx) } - main.postDelayed(hideRunnable!!, seconds * 1000L) - } + // seconds > 0 : feste Dauer wie gehabt. + // seconds == 0: bleibt stehen, bis der Nutzer etwas tut + // (Taste via Accessibility-Dienst / Tipp daneben) + // — mit Notbremse, damit sie nie haengen bleibt. + val cap = if (seconds > 0) seconds else HARD_CAP_SEC + hideRunnable = Runnable { hideNow(ctx) } + main.postDelayed(hideRunnable!!, cap * 1000L) } catch (_: Exception) { root = null } diff --git a/host-agent/android/app/src/main/res/xml/accessibility_config.xml b/host-agent/android/app/src/main/res/xml/accessibility_config.xml index 5b3c810..2984e15 100644 --- a/host-agent/android/app/src/main/res/xml/accessibility_config.xml +++ b/host-agent/android/app/src/main/res/xml/accessibility_config.xml @@ -2,8 +2,9 @@