commit 8e74d2221936e1ff03621fcd3276f24d4cd918a8
parent fd9b558d4c2e7b47957d9d549e022cf0240f69e0
Author: Daniel Barlow <dan@telent.net>
Date: Sun, 11 Jan 2026 17:21:15 +0000
Merge remote-tracking branch 'dandroid/main'
Diffstat:
1 file changed, 150 insertions(+), 0 deletions(-)
diff --git a/android-termux-client/shore.py b/android-termux-client/shore.py
@@ -0,0 +1,150 @@
+#!/data/data/com.termux/files/usr/bin/python
+
+import termuxgui as tg
+import time
+import sys
+import socket
+import threading
+import select
+import datetime
+from pathlib import Path
+import os
+
+peer_hostname = None
+peer_thread = None
+bytes_recorded = 0
+
+started = False
+
+hosts = {
+ "eculogical.local" : None,
+ "eculogical2.local" : None,
+ "eculogical3.local" : None
+}
+
+def output_pathname():
+ root = Path(os.getenv("HOME"))
+ dir = root / "storage" / "capital-shore"
+ dir.mkdir(0o777, False, True)
+ name = datetime.datetime.utcfromtimestamp(int(time.time())).isoformat() + ".raw"
+ return dir / name
+
+def recording_thread_main():
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ sock.connect((peer_hostname, 5000))
+ sock.setblocking(0)
+ interval = 500
+ global bytes_recorded
+ bytes_recorded = 0
+
+ with open(output_pathname(), "wb") as outf:
+ while started:
+ print("sending subscribe")
+ b = bytes([interval >> 8, interval & 0xff, 0x54, 0x11, 0, 25] )
+ sock.send(b)
+ timeout = time.time() + 60
+ while started and time.time() < timeout:
+ ready = select.select([sock], [], [], timeout - time.time())
+ if ready[0]:
+ data = sock.recv(4000)
+ bytes_recorded = bytes_recorded + len(data)
+ outf.write(data)
+ outf.flush()
+
+ sock.close()
+ print("recording thread terminated")
+
+
+
+
+ # Intercept the back button. Instead of finishing the Activity, it now sends an event instead
+ # a.interceptbackbutton(True)
+ # Also possible: a = tg.Activity(c, intercept=True)
+
+
+with tg.Connection() as c:
+ # create a new Activity. By default, a new Task as created.
+ a = tg.Activity(c)
+ # you can find the Task under a.t
+
+ def add_host(activity, name, parent):
+ selected = (peer_hostname == name)
+ if selected:
+ label = f"* {name}"
+ else:
+ label = name
+ button = tg.Button(activity, label, parent)
+ button.setlinearlayoutparams(0)
+ button.setheight(tg.View.WRAP_CONTENT)
+ hosts[name] = button
+
+ def choose_hostname(hostname):
+ global peer_hostname
+ if hostname == None:
+ started = False
+ peer_hostname = hostname
+ refresh_buttons()
+
+ def refresh_buttons():
+ for name, widget in hosts.items():
+ if name == peer_hostname:
+ label = f"* {name}"
+ else:
+ label = name
+ widget.settext(label)
+ if peer_hostname == None:
+ start_button.settext("----")
+ else:
+ start_button.settext("STOP" if started else "START")
+
+
+ def start_recording(start_or_stop):
+ global started
+ if start_or_stop and peer_hostname != None:
+ started = True
+ print("starting record")
+ peer_thread = threading.Thread(target=recording_thread_main, daemon = True)
+ peer_thread.start()
+ else:
+ print("stopping record")
+ started = False
+
+ instances = tg.LinearLayout(a)
+
+ add_host(a, "eculogical.local", instances)
+ add_host(a, "eculogical2.local", instances)
+ add_host(a, "eculogical3.local", instances)
+
+ tg.Space(a, instances)
+
+ status = tg.TextView(a, "initializing", instances)
+
+ tg.Space(a, instances)
+
+ buttons = tg.LinearLayout(a, instances, False)
+
+ start_button = tg.Button(a, "(start)", buttons)
+ start_button.setlinearlayoutparams(0)
+ start_button.setheight(tg.View.WRAP_CONTENT)
+ # start_button.setwidth(tg.View.WRAP_CONTENT)
+ start_button.setgravity(1, 1)
+
+ def update_byte_counter():
+ global started
+ while True:
+ label = f"recorded {bytes_recorded} bytes" if started else "not recording"
+ status.settext(label)
+ time.sleep(1)
+
+ threading.Thread(target = update_byte_counter, daemon = True).start()
+
+ for ev in c.events(): # waits for events from the gui
+ if ev.type == tg.Event.destroy:
+ sys.exit()
+ if ev.type == tg.Event.click:
+ for k, v in hosts.items():
+ if v == ev.value["id"]:
+ choose_hostname(k)
+ if ev.value["id"] == start_button:
+ start_recording(not started)
+ refresh_buttons()