Starter display: boundary v0 client, readout, mark button, CI
Connects to svc-gw over one WebSocket, heartbeats, treats timeout_ms of silence as a dead link, and retries commands under the same id until acked. Shows link state, signals with stale/null/missing told apart, the raw stream, a mock banner on the surface, and a Mark button that stamps the recording. Android: keeps the screen on and binds the process to the USB NCM network when the gateway is on it. CI mirrors android-stuff's host-mode runner setup. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<!-- requestNetwork for the USB (NCM) Ethernet link to the Orin -->
|
||||
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
|
||||
<application
|
||||
android:label="display"
|
||||
android:label="Yukart"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<activity
|
||||
|
||||
@@ -1,5 +1,83 @@
|
||||
package dev.yukart.display
|
||||
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkRequest
|
||||
import android.os.Bundle
|
||||
import android.view.WindowManager
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
|
||||
class MainActivity : FlutterActivity()
|
||||
/**
|
||||
* Two things Dart cannot do on its own:
|
||||
*
|
||||
* - Keep the screen on. An idle tablet suspends about a minute after screen-off and
|
||||
* then answers nothing, while the Orin still sees the USB link up.
|
||||
* - Route over the USB link. It has no internet, so Android never makes it the
|
||||
* default network while WiFi or cellular is up, and ordinary sockets go elsewhere.
|
||||
* When asked, the whole process is bound to the Ethernet (NCM) network while it
|
||||
* exists, and unbound when it goes.
|
||||
*/
|
||||
class MainActivity : FlutterActivity() {
|
||||
private lateinit var channel: MethodChannel
|
||||
private lateinit var cm: ConnectivityManager
|
||||
private var ethernet: Network? = null
|
||||
private var wantBinding = false
|
||||
|
||||
private val callback = object : ConnectivityManager.NetworkCallback() {
|
||||
override fun onAvailable(network: Network) {
|
||||
ethernet = network
|
||||
apply()
|
||||
}
|
||||
|
||||
override fun onLost(network: Network) {
|
||||
if (ethernet == network) {
|
||||
ethernet = null
|
||||
apply()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
}
|
||||
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
cm = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "yukart/net")
|
||||
channel.setMethodCallHandler { call, result ->
|
||||
when (call.method) {
|
||||
"bindEthernet" -> {
|
||||
wantBinding = call.arguments as? Boolean ?: false
|
||||
apply()
|
||||
result.success(ethernet != null)
|
||||
}
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
// NET_CAPABILITY_INTERNET is in the default request; the USB link lacks it.
|
||||
val request = NetworkRequest.Builder()
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
|
||||
.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||
.build()
|
||||
cm.requestNetwork(request, callback)
|
||||
}
|
||||
|
||||
private fun apply() {
|
||||
val target = if (wantBinding) ethernet else null
|
||||
cm.bindProcessToNetwork(target)
|
||||
val up = ethernet != null
|
||||
runOnUiThread { channel.invokeMethod("ethernet", mapOf("up" to up, "bound" to (target != null))) }
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
runCatching { cm.unregisterNetworkCallback(callback) }
|
||||
cm.bindProcessToNetwork(null)
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user