Skip to content

Commit b8566eb

Browse files
committed
feat: proof of concept of postMessage API
1 parent fed9d95 commit b8566eb

33 files changed

Lines changed: 1458 additions & 211 deletions

File tree

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.callstack.brownfield.android.example
22

33
object ReactNativeConstants {
4-
const val MAIN_MODULE_NAME = "RNApp"
4+
const val MAIN_MODULE_NAME = "main"
5+
const val APP_NAME = "Android (Expo)"
56
}

apps/AndroidApp/app/src/main/java/com/callstack/brownfield/android/example/MainActivity.kt

Lines changed: 10 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,22 @@ import androidx.appcompat.app.AppCompatActivity
99
import androidx.compose.foundation.background
1010
import androidx.compose.foundation.layout.Arrangement
1111
import androidx.compose.foundation.layout.Column
12+
import androidx.compose.foundation.layout.Spacer
1213
import androidx.compose.foundation.layout.fillMaxSize
13-
import androidx.compose.foundation.layout.fillMaxWidth
14+
import androidx.compose.foundation.layout.height
1415
import androidx.compose.foundation.layout.padding
1516
import androidx.compose.foundation.shape.RoundedCornerShape
16-
import androidx.compose.material3.Button
17-
import androidx.compose.material3.Card
18-
import androidx.compose.material3.CardDefaults
1917
import androidx.compose.material3.MaterialTheme
2018
import androidx.compose.material3.Scaffold
21-
import androidx.compose.material3.Text
2219
import androidx.compose.runtime.Composable
23-
import androidx.compose.runtime.getValue
24-
import androidx.compose.runtime.mutableStateOf
25-
import androidx.compose.runtime.saveable.rememberSaveable
26-
import androidx.compose.runtime.setValue
2720
import androidx.compose.ui.Alignment
2821
import androidx.compose.ui.Modifier
2922
import androidx.compose.ui.draw.clip
30-
import androidx.compose.ui.text.style.TextAlign
3123
import androidx.compose.ui.tooling.preview.Preview
3224
import androidx.compose.ui.unit.dp
3325
import androidx.fragment.compose.AndroidFragment
26+
import com.callstack.brownfield.android.example.components.GreetingCard
27+
import com.callstack.brownfield.android.example.components.PostMessageCard
3428
import com.callstack.brownfield.android.example.ui.theme.AndroidBrownfieldAppTheme
3529
import com.callstack.reactnativebrownfield.ReactNativeFragment
3630
import com.callstack.reactnativebrownfield.constants.ReactNativeFragmentArgNames
@@ -81,57 +75,22 @@ private fun MainScreen(modifier: Modifier = Modifier) {
8175
horizontalAlignment = Alignment.CenterHorizontally // center top bar content
8276
) {
8377
GreetingCard(
84-
name = "Android",
85-
modifier = Modifier.fillMaxWidth()
78+
name = ReactNativeConstants.APP_NAME,
8679
)
8780

81+
PostMessageCard()
82+
83+
Spacer(modifier = Modifier.height(1.dp))
84+
8885
ReactNativeView(
8986
modifier = Modifier
90-
.fillMaxWidth()
87+
.fillMaxSize()
9188
.clip(RoundedCornerShape(16.dp))
9289
.background(MaterialTheme.colorScheme.surface)
9390
)
9491
}
9592
}
9693

97-
@Composable
98-
fun GreetingCard(
99-
name: String,
100-
modifier: Modifier = Modifier
101-
) {
102-
var counter by rememberSaveable { mutableStateOf(0) }
103-
104-
Card(
105-
modifier = modifier,
106-
shape = RoundedCornerShape(16.dp),
107-
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
108-
) {
109-
Column(
110-
modifier = Modifier
111-
.padding(16.dp)
112-
.fillMaxWidth(),
113-
horizontalAlignment = Alignment.CenterHorizontally,
114-
verticalArrangement = Arrangement.spacedBy(12.dp)
115-
) {
116-
Text(
117-
text = "Hello native $name 👋",
118-
style = MaterialTheme.typography.titleMedium,
119-
textAlign = TextAlign.Center
120-
)
121-
122-
Text(
123-
text = "You clicked the button $counter time${if (counter == 1) "" else "s"}",
124-
textAlign = TextAlign.Center,
125-
style = MaterialTheme.typography.bodyMedium
126-
)
127-
128-
Button(onClick = { counter++ }) {
129-
Text("Increment counter")
130-
}
131-
}
132-
}
133-
}
134-
13594
@Composable
13695
fun ReactNativeView(
13796
modifier: Modifier = Modifier
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.callstack.brownfield.android.example.components
2+
3+
import androidx.compose.foundation.layout.Arrangement
4+
import androidx.compose.foundation.layout.Column
5+
import androidx.compose.foundation.layout.fillMaxWidth
6+
import androidx.compose.foundation.layout.padding
7+
import androidx.compose.material3.Button
8+
import androidx.compose.material3.MaterialTheme
9+
import androidx.compose.material3.Text
10+
import androidx.compose.runtime.Composable
11+
import androidx.compose.runtime.getValue
12+
import androidx.compose.runtime.mutableIntStateOf
13+
import androidx.compose.runtime.saveable.rememberSaveable
14+
import androidx.compose.runtime.setValue
15+
import androidx.compose.ui.Alignment
16+
import androidx.compose.ui.Modifier
17+
import androidx.compose.ui.text.style.TextAlign
18+
import androidx.compose.ui.unit.dp
19+
20+
@Composable
21+
fun GreetingCard(
22+
name: String,
23+
) {
24+
var counter by rememberSaveable { mutableIntStateOf(0) }
25+
26+
MaterialCard {
27+
Column(
28+
modifier = Modifier
29+
.padding(16.dp)
30+
.fillMaxWidth(),
31+
horizontalAlignment = Alignment.CenterHorizontally,
32+
verticalArrangement = Arrangement.spacedBy(12.dp)
33+
) {
34+
Text(
35+
text = "Hello native $name 👋",
36+
style = MaterialTheme.typography.titleMedium,
37+
textAlign = TextAlign.Center
38+
)
39+
40+
Text(
41+
text = "You clicked the button $counter time${if (counter == 1) "" else "s"}",
42+
textAlign = TextAlign.Center,
43+
style = MaterialTheme.typography.bodyMedium
44+
)
45+
46+
Button(onClick = { counter++ }) {
47+
Text("Increment counter")
48+
}
49+
}
50+
}
51+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.callstack.brownfield.android.example.components
2+
3+
import androidx.compose.foundation.shape.RoundedCornerShape
4+
import androidx.compose.material3.Card
5+
import androidx.compose.material3.CardDefaults
6+
import androidx.compose.runtime.Composable
7+
import androidx.compose.ui.Modifier
8+
import androidx.compose.ui.unit.dp
9+
10+
@Composable
11+
fun MaterialCard(
12+
modifier: Modifier = Modifier,
13+
content: @Composable () -> Unit
14+
) {
15+
Card(
16+
modifier = modifier,
17+
shape = RoundedCornerShape(16.dp),
18+
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
19+
) {
20+
content()
21+
}
22+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package com.callstack.brownfield.android.example.components
2+
3+
import android.widget.Toast
4+
import androidx.compose.foundation.layout.Arrangement
5+
import androidx.compose.foundation.layout.Column
6+
import androidx.compose.foundation.layout.Row
7+
import androidx.compose.foundation.layout.fillMaxWidth
8+
import androidx.compose.foundation.layout.padding
9+
import androidx.compose.material3.Button
10+
import androidx.compose.material3.MaterialTheme
11+
import androidx.compose.material3.OutlinedTextField
12+
import androidx.compose.material3.Text
13+
import androidx.compose.runtime.Composable
14+
import androidx.compose.runtime.DisposableEffect
15+
import androidx.compose.runtime.getValue
16+
import androidx.compose.runtime.mutableIntStateOf
17+
import androidx.compose.runtime.mutableStateOf
18+
import androidx.compose.runtime.remember
19+
import androidx.compose.runtime.setValue
20+
import androidx.compose.ui.Alignment
21+
import androidx.compose.ui.Modifier
22+
import androidx.compose.ui.platform.LocalContext
23+
import androidx.compose.ui.unit.dp
24+
import com.callstack.reactnativebrownfield.OnMessageListener
25+
import com.callstack.reactnativebrownfield.ReactNativeBrownfield
26+
import org.json.JSONObject
27+
28+
@Composable
29+
fun PostMessageCard() {
30+
var nextId by remember { mutableIntStateOf(0) }
31+
var draft by remember { mutableStateOf("") }
32+
val lastToast = remember { mutableStateOf<Toast?>(null) }
33+
34+
val context = LocalContext.current
35+
36+
DisposableEffect(Unit) {
37+
val listener = OnMessageListener { raw ->
38+
val text = try {
39+
JSONObject(raw).optString("text", raw)
40+
} catch (_: Exception) {
41+
raw
42+
}
43+
val toast = Toast.makeText(
44+
context,
45+
"Received message from React Native: $text",
46+
Toast.LENGTH_LONG
47+
)
48+
lastToast.value?.cancel() // cancel previous toast if still visible
49+
toast.show()
50+
lastToast.value = toast
51+
}
52+
ReactNativeBrownfield.shared.addMessageListener(listener)
53+
onDispose { ReactNativeBrownfield.shared.removeMessageListener(listener) }
54+
}
55+
56+
MaterialCard {
57+
Column(
58+
modifier = Modifier
59+
.padding(16.dp)
60+
.fillMaxWidth(),
61+
horizontalAlignment = Alignment.CenterHorizontally,
62+
verticalArrangement = Arrangement.spacedBy(12.dp)
63+
) {
64+
Text(
65+
"postMessage",
66+
style = MaterialTheme.typography.titleMedium,
67+
modifier = Modifier
68+
.padding(bottom = 2.dp)
69+
.align(Alignment.CenterHorizontally)
70+
)
71+
72+
Row(
73+
modifier = Modifier.fillMaxWidth(),
74+
verticalAlignment = Alignment.CenterVertically,
75+
horizontalArrangement = Arrangement.spacedBy(8.dp)
76+
) {
77+
78+
OutlinedTextField(
79+
value = draft,
80+
onValueChange = { draft = it },
81+
modifier = Modifier.weight(1f),
82+
placeholder = { Text("Type a message...") },
83+
singleLine = true,
84+
)
85+
Button(onClick = {
86+
val text = draft.ifBlank { "Hello from Android! (#${nextId++})" }
87+
val json = JSONObject().put("text", text).toString()
88+
ReactNativeBrownfield.shared.postMessage(json)
89+
draft = ""
90+
}) {
91+
Text("Send")
92+
}
93+
}
94+
}
95+
}
96+
}

apps/AndroidApp/app/src/vanilla/java/com/callstack/brownfield/android/example/ReactNativeConstants.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ package com.callstack.brownfield.android.example
22

33
object ReactNativeConstants {
44
const val MAIN_MODULE_NAME = "RNApp"
5+
const val APP_NAME = "Android"
56
}

apps/AppleApp/Brownfield Apple App/BrownfieldAppleApp.swift

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,40 @@
11
import BrownfieldLib
22
import Brownie
3-
import SwiftUI
43
import ReactBrownfield
4+
import SwiftUI
55

66
class AppDelegate: NSObject, UIApplicationDelegate {
77
var window: UIWindow?
8-
8+
99
func application(
1010
_ application: UIApplication,
11-
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
11+
didFinishLaunchingWithOptions launchOptions: [UIApplication
12+
.LaunchOptionsKey: Any]? = nil
1213
) -> Bool {
13-
return ReactNativeBrownfield.shared.application(application, didFinishLaunchingWithOptions: launchOptions)
14+
return ReactNativeBrownfield.shared.application(
15+
application,
16+
didFinishLaunchingWithOptions: launchOptions
17+
)
1418
}
1519
}
1620

1721
@main
1822
struct BrownfieldAppleApp: App {
1923
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
20-
24+
2125
init() {
2226
ReactNativeBrownfield.shared.bundle = ReactNativeBundle
2327
ReactNativeBrownfield.shared.startReactNative {
2428
print("React Native has been loaded")
2529
}
2630

27-
#if USE_EXPO_HOST
28-
ReactNativeBrownfield.shared.ensureExpoModulesProvider()
29-
#endif
31+
#if USE_EXPO_HOST
32+
ReactNativeBrownfield.shared.ensureExpoModulesProvider()
33+
#endif
3034

3135
BrownfieldStore.register(initialState)
3236
}
33-
37+
3438
var body: some Scene {
3539
WindowGroup {
3640
ContentView()

0 commit comments

Comments
 (0)