Add authenticated WiFi transport with BLE fallback

This commit is contained in:
2026-10-01 14:32:18 +08:00
parent 071f06d365
commit f011d8357d
55 changed files with 6327 additions and 29 deletions
@@ -22,6 +22,9 @@ class FixtureActivity : ComponentActivity() {
override fun onDestroy() { controller.disconnect(); scope.cancel(); super.onDestroy() }
}
class FixturePlatform : MobilePlatform {
var wifiEnabled = false
val fixtureWifi = FixtureWifi()
override fun wifi(): WifiPlatform? = if (wifiEnabled) fixtureWifi else null
override val model = "模拟测试手机"
private val saved = mutableMapOf<String, String>()
val calls = mutableListOf<String>()
@@ -56,7 +59,9 @@ private class FixtureLink(val platform: FixturePlatform) : DeviceLink {
val method = request.getValue("method").jsonPrimitive.content
platform.calls += method
val result = when (method) {
"session.open" -> """{"device_id":"00000000-0000-4000-8000-000000000001","device_name":"模拟小屏幕","short_id":"00000000","protocol_major":1,"capabilities":["status","frame","library","library_progressive","settings","wifi","wifi_scan"]}"""
"session.open" -> """{"device_id":"00000000-0000-4000-8000-000000000001","device_name":"模拟小屏幕","short_id":"00000000","protocol_major":1,"capabilities":["status","frame","library","library_progressive","settings","wifi","wifi_scan"${if (platform.wifiEnabled) ",\"wifi_transport\"" else ""}]}"""
"transport.offer" -> """{"device_id":"00000000-0000-4000-8000-000000000001","channel_id":"00000000000000000000000000000000"}"""
"transport.close" -> """{"closed":true}"""
"session.ping" -> """{"alive":true}"""
"status.get" -> """{"state":{"animation_playback":{"active":false}}}"""
"frame.get" -> """{"frame_revision":"fixture","data_base64":"fixture-image"}"""
@@ -77,3 +82,33 @@ private class FixtureLink(val platform: FixturePlatform) : DeviceLink {
override suspend fun receive() = messages.receive()
override fun close() { messages.close() }
}
class FixtureWifi : WifiPlatform {
override val state = kotlinx.coroutines.flow.MutableStateFlow(WifiNetworkState(1, true))
override fun start() {}
override fun stop() {}
override fun crypto(offer: JsonObject) = object : SecureChannel {
override val hello = byteArrayOf(1)
override fun finish(serverHello: ByteArray) {}
override fun encrypt(payload: ByteArray) = payload
override fun decrypt(record: ByteArray) = record
}
override suspend fun connect(offer: JsonObject): DeviceLink = object : DeviceLink {
override val mtu = 65536
val replies = Channel<ByteArray>(Channel.UNLIMITED)
var opened = false
override suspend fun send(fragment: ByteArray) {
if (!opened) {
opened = true
replies.send("""{"device_id":"00000000-0000-4000-8000-000000000001","ready":true}""".encodeToByteArray())
} else {
val request = Protocol.json.parseToJsonElement(fragment.decodeToString()).jsonObject
replies.send(buildJsonObject { put("id", request.getValue("id")); put("ok", true)
put("result", buildJsonObject { put("state", buildJsonObject {}); put("alive", true) })
}.toString().encodeToByteArray())
}
}
override suspend fun receive() = replies.receive()
override fun close() { replies.close() }
}
}
@@ -7,6 +7,19 @@ import org.junit.Test
import org.junit.Assert.*
class IsolatedUiTest {
@Test fun wifiTransport() {
ui.runOnIdle { ui.activity.platform.wifiEnabled = true }
connect()
ui.waitUntil(5_000) { ui.activity.controller.state.value.transport == "wifi" }
ui.onNodeWithText("连接状态:WiFi", substring = false).assertExists()
ui.onNodeWithText("连接状态:WiFi · 已连接", substring = true).assertExists()
ui.runOnIdle { ui.activity.platform.fixtureWifi.state.value = org.qimiaoscreen.core.WifiNetworkState(2, false, "未允许局域网访问,仍可使用蓝牙") }
ui.waitUntil(5_000) { ui.activity.controller.state.value.transport == "ble" }
ui.onNodeWithText("允许局域网访问").performScrollTo().assertExists()
ui.onNodeWithText("连接状态:蓝牙", substring = false).performScrollTo().assertExists()
ui.runOnIdle { ui.activity.controller.disconnect() }
ui.onNodeWithText("连接状态:蓝牙", substring = false).assertDoesNotExist()
}
@get:Rule val ui = createAndroidComposeRule<FixtureActivity>()
private fun click(text: String) { ui.onNodeWithText(text).performClick() }
private fun connect() {
@@ -24,6 +37,7 @@ class IsolatedUiTest {
ui.onNodeWithTag("controller_error").assertDoesNotExist()
ui.runOnIdle { ui.activity.platform.scanError = false }
connect()
ui.onNodeWithText("连接状态:蓝牙", substring = false).assertExists()
ui.onNodeWithTag("connected_scan_disconnect").performScrollTo().assertIsEnabled().performClick()
ui.waitUntil(5_000) { ui.activity.controller.state.value.identity == null }
}