Slide 1

Slide 1 text

Using Compose Runtime to Create a Client Library Fatih Giris Android Lead @DNB Bank

Slide 2

Slide 2 text

Compose is a UI framework

Slide 3

Slide 3 text

Combination of 7 different libraries

Slide 4

Slide 4 text

• compose.animation • compose.foundation • compose.material • compose.material3 • compose.ui • compose.compiler • compose.runtime

Slide 5

Slide 5 text

• compose.animation • compose.foundation • compose.material • compose.material3 • compose.ui • compose.compiler • compose.runtime 🧐

Slide 6

Slide 6 text

Client libraries

Slide 7

Slide 7 text

Client libraries APIs for use in writing client applications* *https://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.infocenter.dc36065.1570/html/ctlibmig/X55077.htm

Slide 8

Slide 8 text

https://developer.android.com/jetpack/compose/layering

Slide 9

Slide 9 text

https://developer.android.com/jetpack/compose/layering Client Libraries

Slide 10

Slide 10 text

Agenda • Compose Compiler • Compose Runtime • Composer • Slot Table

Slide 11

Slide 11 text

Agenda • Applier • Client Integration • Compose Client Library for PowerPoint

Slide 12

Slide 12 text

Compose Compiler

Slide 13

Slide 13 text

Compose Compiler • Kotlin compiler plugin • Transforms @Composable functions • Targets Compose Runtime

Slide 14

Slide 14 text

@Composable Compose Compiler Transformed code

Slide 15

Slide 15 text

@Composable Compose Compiler Transformed code • Injecting composer • Stability checks • Starting groups • Live Literals …

Slide 16

Slide 16 text

Compose Runtime 💡

Slide 17

Slide 17 text

Compose Runtime 💡 Fundamental building blocks of Compose's programming model and state management https://developer.android.com/jetpack/androidx/releases/compose-runtime

Slide 18

Slide 18 text

Compose Runtime • Composer • Slot table • Applier • Compose node tree

Slide 19

Slide 19 text

Composer

Slide 20

Slide 20 text

Composer • Targeted by Compose Compiler • Bridge between the Composables and Slot Table • Insert, update or end groups & nodes • Remember values • Record the changes

Slide 21

Slide 21 text

@Composable fun Hi() { ... } Compose Compiler Composable called Slot Table @Composable fun Hi(composer) { // Composer starts a group ... }

Slide 22

Slide 22 text

Slot Table

Slide 23

Slide 23 text

Slot Table A linear & in-memory data structure to keep the current state of the composition But how?

Slide 24

Slide 24 text

Groups Slots

Slide 25

Slide 25 text

Groups Slots Turns the linear cache into a tree- like structure* *http://intelligiblebabble.com/compose-from- fi rst-principles Keeps the actual data for the groups

Slide 26

Slide 26 text

https://medium.com/androiddevelopers/under-the-hood-of-jetpack-compose-part-2-of-2-37b2c20c6cdd

Slide 27

Slide 27 text

@Composable fun Hi() { ... } Compose Compiler Composable called Slot Table @Composable fun Hi(composer) { // Composer starts a group ... }

Slide 28

Slide 28 text

@Composable fun Hi() { ... } Compose Compiler Composable called Slot Table Composer records changes Applier @Composable fun Hi(composer) { // Composer starts a group ... }

Slide 29

Slide 29 text

Applier

Slide 30

Slide 30 text

Applier Responsible for applying the tree-based operations that get emitted during a composition* https://developer.android.com/reference/kotlin/androidx/compose/runtime/Applier

Slide 31

Slide 31 text

Applier Responsible for applying the tree-based operations that get emitted during a composition* https://developer.android.com/reference/kotlin/androidx/compose/runtime/Applier Remove Move Insert

Slide 32

Slide 32 text

interface Applier

Slide 33

Slide 33 text

interface Applier abstract class AbstractApplier

Slide 34

Slide 34 text

interface Applier abstract class AbstractApplier

Slide 35

Slide 35 text

@Composable fun Hi() { ... } Compose Compiler Composable called Slot Table Composer records changes Applier Changes applied Compose Node Tree @Composable fun Hi(composer) { // Composer starts a group ... }

Slide 36

Slide 36 text

How is the Compose node tree created ?

Slide 37

Slide 37 text

@Composable fun Hi() { … } @Composable fun Dev(){ … } Emits a node Emits a node Create or update the tree Applier Compose Node Tree

Slide 38

Slide 38 text

How is the Compose node emitted ?

Slide 39

Slide 39 text

How is the Compose node emitted ? ReusableComposeNode ComposeNode

Slide 40

Slide 40 text

https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composables.kt

Slide 41

Slide 41 text

@Composable fun Hi() { … } @Composable fun Dev(){ … } Emits a node Emits a node Create or update the tree Applier Compose Node Tree

Slide 42

Slide 42 text

Emits a node Emits a node Create or update the tree Applier @Composable fun Hi() { ComposeNode(…) } TextNode @Composable fun Dev() { ComposeNode(…) } Compose Node Tree TextNode

Slide 43

Slide 43 text

@Composable fun Hi() { ... } Compose Compiler Composable called Slot Table Composer records changes Applier Changes applied Compose Node Tree Materialize UI @Composable fun Hi(composer) { // Composer starts a group ... }

Slide 44

Slide 44 text

Client Integration

Slide 45

Slide 45 text

Client Integration • Gets the Composable content • Creates a frame clock and Recomposer • Creates the initial Composition • Observes and applies changes from the Snapshot objects • Refreshes the frame

Slide 46

Slide 46 text

Compose Client Library for PowerPoint

Slide 47

Slide 47 text

Compose Client Library for PowerPoint

Slide 48

Slide 48 text

Compose Client Library for PowerPoint • Nodes (Text & Slide) • A custom Applier • Composables for the nodes • A client integration

Slide 49

Slide 49 text

Compose Client Library for PowerPoint • Nodes (Text & Slide) • A custom Applier • Composables for the nodes • A client integration

Slide 50

Slide 50 text

abstract class ComposePPTNode { /** * The children of this node. */ val children = mutableListOf() /** * Renders the current node. */ abstract fun render() } Nodes (Base) ComposePPTNode.kt

Slide 51

Slide 51 text

abstract class ComposePPTNode { /** * The children of this node. */ val children = mutableListOf() /** * Renders the current node. */ abstract fun render() } Nodes (Base) ComposePPTNode.kt

Slide 52

Slide 52 text

abstract class ComposePPTNode { /** * The children of this node. */ val children = mutableListOf() /** * Renders the current node. */ abstract fun render() } Nodes (Base) ComposePPTNode.kt

Slide 53

Slide 53 text

class SlideNode : ComposePPTNode() { override fun render() { children.forEach { it.render() } } } Nodes (Slide) SlideNode.kt

Slide 54

Slide 54 text

class SlideNode : ComposePPTNode() { override fun render() { children.forEach { it.render() } } } Nodes (Slide) SlideNode.kt

Slide 55

Slide 55 text

class TextNode : ComposePPTNode() { var text: String = "" override fun render() { // Render the text with Apache POI createSlideShowAndRenderText(text) } } Nodes (Text) TextNode.kt

Slide 56

Slide 56 text

class TextNode : ComposePPTNode() { var text: String = "" override fun render() { // Render the text with Apache POI createSlideShowAndRenderText(text) } } Nodes (Text) TextNode.kt

Slide 57

Slide 57 text

class TextNode : ComposePPTNode() { var text: String = "" override fun render() { // Render the text with Apache POI createSlideShowAndRenderText(text) } } Nodes (Text) TextNode.kt

Slide 58

Slide 58 text

Compose Client Library for PowerPoint • Nodes (Text & Slide) • A custom Applier • Composables for the nodes • A client integration

Slide 59

Slide 59 text

Compose Client Library for PowerPoint • Nodes (Text & Slide) • A custom Applier • Composables for the nodes • A client integration

Slide 60

Slide 60 text

class ComposePPTApplier( root: ComposePPTNode ) : AbstractApplier(root) { override fun insertTopDown(index: Int, instance: ComposePPTNode) { current.children.add(index, instance) } override fun insertBottomUp(index: Int, instance: ComposePPTNode) { // Ignored as the tree is built top-down. } override fun remove(index: Int, count: Int) { current.children.remove(index, count) } override fun move(from: Int, to: Int, count: Int) { current.children.move(from, to, count) } override fun onClear() { root.children.clear() } } Applier ComposePPTApplier.kt

Slide 61

Slide 61 text

class ComposePPTApplier( root: ComposePPTNode ) : AbstractApplier(root) { override fun insertTopDown(index: Int, instance: ComposePPTNode) { current.children.add(index, instance) } override fun insertBottomUp(index: Int, instance: ComposePPTNode) { // Ignored as the tree is built top-down. } override fun remove(index: Int, count: Int) { current.children.remove(index, count) } override fun move(from: Int, to: Int, count: Int) { current.children.move(from, to, count) Applier

Slide 62

Slide 62 text

class ComposePPTApplier( root: ComposePPTNode ) : AbstractApplier(root) { override fun insertTopDown(index: Int, instance: ComposePPTNode) { current.children.add(index, instance) } override fun insertBottomUp(index: Int, instance: ComposePPTNode) { // Ignored as the tree is built top-down. } override fun remove(index: Int, count: Int) { current.children.remove(index, count) } override fun move(from: Int, to: Int, count: Int) { current.children.move(from, to, count) Applier

Slide 63

Slide 63 text

override fun insertTopDown(index: Int, instance: ComposePPTNode) { current.children.add(index, instance) } override fun insertBottomUp(index: Int, instance: ComposePPTNode) { // Ignored as the tree is built top-down. } override fun remove(index: Int, count: Int) { current.children.remove(index, count) } override fun move(from: Int, to: Int, count: Int) { current.children.move(from, to, count) } override fun onClear() { root.children.clear() } } Applier

Slide 64

Slide 64 text

Compose Client Library for PowerPoint • Nodes (Text & Slide) • A custom Applier • Composables for the nodes • A client integration

Slide 65

Slide 65 text

Compose Client Library for PowerPoint • Nodes (Text & Slide) • A custom Applier • Composables for the nodes • A client integration

Slide 66

Slide 66 text

@Composable fun Slide(content: @Composable () -> Unit) { ComposeNode( factory = ::SlideNode, update = {}, content = content ) } Composables (Slide) Slide.kt

Slide 67

Slide 67 text

@Composable fun Slide(content: @Composable () -> Unit) { ComposeNode( factory = ::SlideNode, update = {}, content = content ) } Composables (Slide) Slide.kt

Slide 68

Slide 68 text

@Composable fun Slide(content: @Composable () -> Unit) { ComposeNode( factory = ::SlideNode, update = {}, content = content ) } Composables (Slide) Slide.kt

Slide 69

Slide 69 text

@Composable fun Slide(content: @Composable () -> Unit) { ComposeNode( factory = ::SlideNode, update = {}, content = content ) } Composables (Slide) Slide.kt

Slide 70

Slide 70 text

@Composable fun Text(text: String) { ComposeNode( factory = ::TextNode ) { set(text) { this.text = it } } } Composables (Text) Text.kt

Slide 71

Slide 71 text

@Composable fun Text(text: String) { ComposeNode( factory = ::TextNode ) { set(text) { this.text = it } } } Composables (Text) Text.kt

Slide 72

Slide 72 text

@Composable fun Text(text: String) { ComposeNode( factory = ::TextNode ) { set(text) { this.text = it } } } Composables (Text) Text.kt

Slide 73

Slide 73 text

@Composable fun Text(text: String) { ComposeNode( factory = ::TextNode ) { set(text) { this.text = it } } } Composables (Text) Text.kt

Slide 74

Slide 74 text

Compose Client Library for PowerPoint • Nodes (Text & Slide) • A custom Applier • Composables for the nodes • A client integration

Slide 75

Slide 75 text

Compose Client Library for PowerPoint • Nodes (Text & Slide) • A custom Applier • Composables for the nodes • A client integration

Slide 76

Slide 76 text

fun runComposePPT( content: @Composable () -> Unit ) = runBlocking { // 🛠 } Client Integration

Slide 77

Slide 77 text

fun runComposePPT( content: @Composable () -> Unit ) = runBlocking { val frameClock = BroadcastFrameClock() } Client Integration

Slide 78

Slide 78 text

fun runComposePPT( content: @Composable () -> Unit ) = runBlocking { val frameClock = BroadcastFrameClock() val recomposer = Recomposer(coroutineContext + frameClock) } Client Integration

Slide 79

Slide 79 text

fun runComposePPT( content: @Composable () -> Unit ) = runBlocking { val frameClock = BroadcastFrameClock() val recomposer = Recomposer(coroutineContext + frameClock) val rootNode = SlideNode() } Client Integration

Slide 80

Slide 80 text

fun runComposePPT( content: @Composable () -> Unit ) = runBlocking { val frameClock = BroadcastFrameClock() val recomposer = Recomposer(coroutineContext + frameClock) val rootNode = SlideNode() // Register an observer for any snapshot object change Snapshot.registerGlobalWriteObserver { // Whenever any snapshot object changes, send an apply noti fi cation // so that Recomposer can trigger the recomposition Snapshot.sendApplyNotifications() } } Client Integration

Slide 81

Slide 81 text

content: @Composable () -> Unit ) = runBlocking { val frameClock = BroadcastFrameClock() val recomposer = Recomposer(coroutineContext + frameClock) val rootNode = SlideNode() // Register an observer for any snapshot object change Snapshot.registerGlobalWriteObserver { // Whenever any snapshot object changes, send an apply noti fi cation // so that Recomposer can trigger the recomposition Snapshot.sendApplyNotifications() } // Create the initial composition val composition = Composition( applier = ComposePPTApplier(rootNode), parent = recomposer ) } Client Integration

Slide 82

Slide 82 text

// Register an observer for any snapshot object change Snapshot.registerGlobalWriteObserver { // Whenever any snapshot object changes, send an apply noti fi cation // so that Recomposer can trigger the recomposition Snapshot.sendApplyNotifications() } // Create the initial composition val composition = Composition( applier = ComposePPTApplier(rootNode), parent = recomposer ) // Need to trigger invoking the composable content composition.setContent(content) } Client Integration

Slide 83

Slide 83 text

// Create the initial composition val composition = Composition( applier = ComposePPTApplier(rootNode), parent = recomposer ) // Need to trigger invoking the composable content composition.setContent(content) // Starting undispatched in order not to send a frame before // the Recomposer registers an awaiter. launch(context = frameClock, start = CoroutineStart.UNDISPATCHED) { recomposer.runRecomposeAndApplyChanges() } } Client Integration

Slide 84

Slide 84 text

composition.setContent(content) // Starting undispatched in order not to send a frame before // the Recomposer registers an awaiter. launch(context = frameClock, start = CoroutineStart.UNDISPATCHED) { recomposer.runRecomposeAndApplyChanges() } launch { // Sends a frame every 100 ms & render it while (true) { frameClock.sendFrame(System.nanoTime()) rootNode.render() // Refresh rate for each frame delay(100L) } } } Client Integration

Slide 85

Slide 85 text

Compose Client Library for PowerPoint • Nodes (Text & Slide) • A custom Applier • Composables for the nodes • A client integration

Slide 86

Slide 86 text

Compose Client Library for PowerPoint • Nodes (Text & Slide) • A custom Applier • Composables for the nodes • A client integration

Slide 87

Slide 87 text

How to use it?

Slide 88

Slide 88 text

fun main() Sample App

Slide 89

Slide 89 text

fun main() { runComposePPT { // Composable content } } Sample App

Slide 90

Slide 90 text

fun main() { runComposePPT { Slide { Text(text = "Hello stranger 👋") } } } Sample App

Slide 91

Slide 91 text

fun main() { runComposePPT { Slide { Text(text = "Hello stranger 👋") } } } Sample App

Slide 92

Slide 92 text

https://github.com/fgiris/composePPT

Slide 93

Slide 93 text

TL;DR

Slide 94

Slide 94 text

TL;DR • Compose Compiler transforms @Composable functions • Composer is the bridge between the Composables and the Slot Table • Slot Table keeps the current state of the composition • Applier is responsible for the tree-based operations for the emitted node

Slide 95

Slide 95 text

TL;DR • Client integration • Creating the initial composition • Getting & setting the composable content • Listening state object changes and sending apply noti fi cations • Waiting for any recomposition • Refreshing the output

Slide 96

Slide 96 text

Thanks!

Slide 97

Slide 97 text

linktr.ee/composeppt