Skip to content

Commit 67ba864

Browse files
authored
Expose guest Redwood version to host (#1939)
In order to ensure the guest is fully setup such that we can query this, we have to defer creating the host-side bridge until we have received some changes.
1 parent 8dd7283 commit 67ba864

File tree

15 files changed

+75
-20
lines changed

15 files changed

+75
-20
lines changed

redwood-treehouse-guest/api/android/redwood-treehouse-guest.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ public final class app/cash/redwood/treehouse/StandardAppLifecycle : app/cash/re
22
public synthetic fun <init> (Lapp/cash/redwood/protocol/guest/ProtocolBridge$Factory;Lkotlinx/serialization/json/Json;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
33
public fun close ()V
44
public final fun getFrameClock ()Landroidx/compose/runtime/MonotonicFrameClock;
5+
public fun getGuestProtocolVersion-7jYel6c ()Ljava/lang/String;
56
public fun sendFrame (J)V
67
public fun start (Lapp/cash/redwood/treehouse/AppLifecycle$Host;)V
78
}

redwood-treehouse-guest/api/jvm/redwood-treehouse-guest.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ public final class app/cash/redwood/treehouse/StandardAppLifecycle : app/cash/re
22
public synthetic fun <init> (Lapp/cash/redwood/protocol/guest/ProtocolBridge$Factory;Lkotlinx/serialization/json/Json;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
33
public fun close ()V
44
public final fun getFrameClock ()Landroidx/compose/runtime/MonotonicFrameClock;
5+
public fun getGuestProtocolVersion-7jYel6c ()Ljava/lang/String;
56
public fun sendFrame (J)V
67
public fun start (Lapp/cash/redwood/treehouse/AppLifecycle$Host;)V
78
}

redwood-treehouse-guest/api/redwood-treehouse-guest.klib.api

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,7 @@ final class app.cash.redwood.treehouse/StandardAppLifecycle : app.cash.redwood.t
1212
final fun start(app.cash.redwood.treehouse/AppLifecycle.Host) // app.cash.redwood.treehouse/StandardAppLifecycle.start|start(app.cash.redwood.treehouse.AppLifecycle.Host){}[0]
1313
final val frameClock // app.cash.redwood.treehouse/StandardAppLifecycle.frameClock|{}frameClock[0]
1414
final fun <get-frameClock>(): androidx.compose.runtime/MonotonicFrameClock // app.cash.redwood.treehouse/StandardAppLifecycle.frameClock.<get-frameClock>|<get-frameClock>(){}[0]
15+
final val guestProtocolVersion // app.cash.redwood.treehouse/StandardAppLifecycle.guestProtocolVersion|{}guestProtocolVersion[0]
16+
final fun <get-guestProtocolVersion>(): app.cash.redwood.protocol/RedwoodVersion // app.cash.redwood.treehouse/StandardAppLifecycle.guestProtocolVersion.<get-guestProtocolVersion>|<get-guestProtocolVersion>(){}[0]
1517
}
1618
final fun (app.cash.redwood.treehouse/TreehouseUi).app.cash.redwood.treehouse/asZiplineTreehouseUi(app.cash.redwood.treehouse/StandardAppLifecycle): app.cash.redwood.treehouse/ZiplineTreehouseUi // app.cash.redwood.treehouse/asZiplineTreehouseUi|asZiplineTreehouseUi@app.cash.redwood.treehouse.TreehouseUi(app.cash.redwood.treehouse.StandardAppLifecycle){}[0]

redwood-treehouse-guest/src/commonMain/kotlin/app/cash/redwood/treehouse/StandardAppLifecycle.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import app.cash.redwood.protocol.RedwoodVersion
2323
import app.cash.redwood.protocol.WidgetTag
2424
import app.cash.redwood.protocol.guest.ProtocolBridge
2525
import app.cash.redwood.protocol.guest.ProtocolMismatchHandler
26+
import app.cash.redwood.protocol.guest.guestRedwoodVersion
2627
import app.cash.redwood.treehouse.AppLifecycle.Host
2728
import app.cash.zipline.ZiplineApiMismatchException
2829
import kotlin.coroutines.CoroutineContext
@@ -38,6 +39,9 @@ public class StandardAppLifecycle(
3839
private var started = false
3940
private lateinit var host: Host
4041

42+
override val guestProtocolVersion: RedwoodVersion
43+
get() = guestRedwoodVersion
44+
4145
internal val hostProtocolVersion: RedwoodVersion get() {
4246
return try {
4347
host.hostProtocolVersion

redwood-treehouse-host/src/commonMain/kotlin/app/cash/redwood/treehouse/CodeSession.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package app.cash.redwood.treehouse
1717

18+
import app.cash.redwood.protocol.RedwoodVersion
1819
import kotlin.coroutines.CoroutineContext
1920
import kotlinx.coroutines.CoroutineExceptionHandler
2021
import kotlinx.coroutines.CoroutineScope
@@ -55,6 +56,8 @@ internal abstract class CodeSession<A : AppService>(
5556

5657
abstract val json: Json
5758

59+
abstract val guestProtocolVersion: RedwoodVersion
60+
5861
fun start() {
5962
dispatchers.checkUi()
6063
scope.launch(dispatchers.zipline) {

redwood-treehouse-host/src/commonMain/kotlin/app/cash/redwood/treehouse/TreehouseAppContent.kt

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package app.cash.redwood.treehouse
1818
import app.cash.redwood.protocol.Change
1919
import app.cash.redwood.protocol.Event
2020
import app.cash.redwood.protocol.EventSink
21-
import app.cash.redwood.protocol.RedwoodVersion
2221
import app.cash.redwood.protocol.host.ProtocolBridge
2322
import app.cash.redwood.protocol.host.ProtocolFactory
2423
import app.cash.redwood.ui.OnBackPressedCallback
@@ -313,7 +312,10 @@ private class ViewContentCodeBinding<A : AppService>(
313312
/** Only accessed on [TreehouseDispatchers.ui]. Null before [initView] and after [cancel]. */
314313
private var viewOrNull: TreehouseView<*>? = null
315314

316-
/** Only accessed on [TreehouseDispatchers.ui]. Null before [initView] and after [cancel]. */
315+
/**
316+
* Only accessed on [TreehouseDispatchers.ui].
317+
* Null before [initView]+[receiveChangesOnUiDispatcher] and after [cancel].
318+
*/
317319
private var bridgeOrNull: ProtocolBridge<*>? = null
318320

319321
/** Only accessed on [TreehouseDispatchers.zipline]. */
@@ -351,18 +353,6 @@ private class ViewContentCodeBinding<A : AppService>(
351353

352354
view.saveCallback = this
353355

354-
@Suppress("UNCHECKED_CAST") // We don't have a type parameter for the widget type.
355-
bridgeOrNull = ProtocolBridge(
356-
// TODO Wire through guest version. Wanted this from AppLifecycle but it's bound too late.
357-
guestVersion = RedwoodVersion.Unknown,
358-
container = view.children as Widget.Children<Any>,
359-
factory = view.widgetSystem.widgetFactory(
360-
json = codeSession.json,
361-
protocolMismatchHandler = eventPublisher.widgetProtocolMismatchHandler,
362-
) as ProtocolFactory<Any>,
363-
eventSink = this,
364-
)
365-
366356
// Apply all the changes received before we had a view to apply them to.
367357
while (true) {
368358
val changes = changesAwaitingInitView.removeFirstOrNull() ?: break
@@ -388,14 +378,12 @@ private class ViewContentCodeBinding<A : AppService>(
388378
}
389379

390380
private fun receiveChangesOnUiDispatcher(changes: List<Change>) {
391-
val view = viewOrNull
392-
val bridge = bridgeOrNull
393-
394381
if (canceled) {
395382
return
396383
}
397384

398-
if (view == null || bridge == null) {
385+
val view = viewOrNull
386+
if (view == null) {
399387
if (changesAwaitingInitView.isEmpty()) {
400388
// Unblock coroutines suspended on TreehouseAppContent.awaitContent().
401389
val currentState = stateFlow.value
@@ -414,6 +402,21 @@ private class ViewContentCodeBinding<A : AppService>(
414402
return
415403
}
416404

405+
var bridge = bridgeOrNull
406+
if (bridge == null) {
407+
@Suppress("UNCHECKED_CAST") // We don't have a type parameter for the widget type.
408+
bridge = ProtocolBridge(
409+
guestVersion = codeSession.guestProtocolVersion,
410+
container = view.children as Widget.Children<Any>,
411+
factory = view.widgetSystem.widgetFactory(
412+
json = codeSession.json,
413+
protocolMismatchHandler = eventPublisher.widgetProtocolMismatchHandler,
414+
) as ProtocolFactory<Any>,
415+
eventSink = this,
416+
)
417+
bridgeOrNull = bridge
418+
}
419+
417420
if (changesCount++ == 0) {
418421
view.reset()
419422
codeEventPublisher.onCodeLoaded(view, isInitialLaunch)

redwood-treehouse-host/src/commonMain/kotlin/app/cash/redwood/treehouse/ZiplineCodeSession.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
*/
1616
package app.cash.redwood.treehouse
1717

18+
import app.cash.redwood.protocol.RedwoodVersion
1819
import app.cash.zipline.Zipline
20+
import app.cash.zipline.ZiplineApiMismatchException
1921
import app.cash.zipline.ZiplineScope
2022
import app.cash.zipline.withScope
2123
import kotlinx.coroutines.CoroutineScope
@@ -35,12 +37,22 @@ internal class ZiplineCodeSession<A : AppService>(
3537
appService = appService,
3638
) {
3739
private val ziplineScope = ZiplineScope()
40+
private lateinit var appLifecycle: AppLifecycle
3841

3942
override val json: Json
4043
get() = zipline.json
4144

45+
override val guestProtocolVersion: RedwoodVersion
46+
get() {
47+
return try {
48+
appLifecycle.guestProtocolVersion
49+
} catch (_: ZiplineApiMismatchException) {
50+
RedwoodVersion.Unknown
51+
}
52+
}
53+
4254
override fun ziplineStart() {
43-
val appLifecycle = appService.withScope(ziplineScope).appLifecycle
55+
appLifecycle = appService.withScope(ziplineScope).appLifecycle
4456

4557
val host = RealAppLifecycleHost(
4658
appLifecycle = appLifecycle,

redwood-treehouse-host/src/commonTest/kotlin/app/cash/redwood/treehouse/AbstractFrameClockTest.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package app.cash.redwood.treehouse
1717

18+
import app.cash.redwood.protocol.RedwoodVersion
1819
import app.cash.redwood.treehouse.AppLifecycle.Host
1920
import assertk.all
2021
import assertk.assertThat
@@ -42,6 +43,7 @@ abstract class AbstractFrameClockTest {
4243

4344
val frameTimes = Channel<Long>(Channel.UNLIMITED)
4445
val appLifecycle = object : AppLifecycle {
46+
override val guestProtocolVersion get() = RedwoodVersion.Unknown
4547
override fun start(host: Host) {
4648
}
4749
override fun sendFrame(timeNanos: Long) {

redwood-treehouse-host/src/commonTest/kotlin/app/cash/redwood/treehouse/FakeAppService.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
*/
1616
package app.cash.redwood.treehouse
1717

18+
import app.cash.redwood.protocol.RedwoodVersion
19+
import app.cash.redwood.protocol.host.hostRedwoodVersion
20+
1821
internal class FakeAppService private constructor(
1922
private val name: String,
2023
private val eventLog: EventLog,
@@ -26,6 +29,10 @@ internal class FakeAppService private constructor(
2629
get() = mutableUis.toList()
2730

2831
override val appLifecycle = object : AppLifecycle {
32+
override val guestProtocolVersion: RedwoodVersion
33+
// Use latest host version as the guest version to avoid any compatibility behavior.
34+
get() = hostRedwoodVersion
35+
2936
override fun start(host: AppLifecycle.Host) {
3037
eventLog += "$name.appLifecycle.start()"
3138
}

redwood-treehouse-host/src/commonTest/kotlin/app/cash/redwood/treehouse/FakeCodeSession.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package app.cash.redwood.treehouse
1717

18+
import app.cash.redwood.protocol.RedwoodVersion
19+
import app.cash.redwood.protocol.host.hostRedwoodVersion
1820
import kotlinx.coroutines.CoroutineScope
1921
import kotlinx.serialization.json.Json
2022

@@ -33,6 +35,10 @@ internal class FakeCodeSession(
3335
override val json: Json
3436
get() = Json
3537

38+
override val guestProtocolVersion: RedwoodVersion
39+
// Use latest host version as the guest version to avoid any compatibility behavior.
40+
get() = hostRedwoodVersion
41+
3642
override fun ziplineStart() {
3743
eventLog += "$name.start()"
3844
}

0 commit comments

Comments
 (0)