Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ android {

defaultConfig {
applicationId = "dev.expo.brownfieldintegratedtester"
minSdk = 24
minSdk = 26
targetSdk = 36
versionCode = 1
versionName = "1.0"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package dev.expo.brownfieldintegratedtester

import android.util.Log
import android.widget.Toast
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler
import expo.modules.brownfield.BrownfieldMessage
import expo.modules.brownfield.BrownfieldMessaging
import expo.modules.brownfield.BrownfieldState
import expo.modules.brownfield.Removable
import host.exp.exponent.brownfield.BrownfieldActivity
import java.util.Timer
import kotlin.concurrent.timerTask

open class BrownfieldTestActivity : BrownfieldActivity(), DefaultHardwareBackBtnHandler {
// Listeners
private var messagingListenerId: String? = null
private var stateListener: Removable? = null

// Other test utils
private var messageTimer: Timer? = null
private var messageCounter = 0

fun setupBrownfieldTests() {
// Messaging
messagingListenerId =
BrownfieldMessaging.addListener { message ->
Log.i("BrownfieldTestActivity", "Message from React Native received:")
Log.i("BrownfieldTestActivity", message.toString())
showToast(message)
}

// Shared state
stateListener =
BrownfieldState.subscribe("counter") { state: Any? ->
val count = state as? Double
if (count == null) {
Log.i("BrownfieldTestActivity", "Failed to parse state update as a double")
return@subscribe
}
// Return (synchronize) duplicated value to JS
BrownfieldState.set("counter-duplicated", count * 2)
}

startMessageTimer()
}

override fun onDestroy() {
super.onDestroy()
// Clean up messaging tests
messagingListenerId?.let { BrownfieldMessaging.removeListener(it) }
stopMessageTimer()
// Clean up state tests
stateListener?.remove()
}

private fun startMessageTimer() {
messageTimer =
Timer().apply {
schedule(
timerTask {
sendMessage()
setTime()
},
0,
1000,
)
}
}

private fun stopMessageTimer() {
messageTimer?.cancel()
messageTimer = null
}

private fun showToast(message: BrownfieldMessage) {
val sender = message["sender"] as? String
val nested = message["source"] as? Map<*, *>
val platform = nested?.get("platform") as? String
if (sender != null && platform != null) {
Toast.makeText(this, "$platform($sender)", Toast.LENGTH_LONG).show()
}
}

private fun sendMessage() {
messageCounter++
val nativeMessage =
mapOf(
"source" to mapOf("platform" to "Android"),
"counter" to messageCounter,
"timestamp" to System.currentTimeMillis(),
"array" to listOf("ab", 'c', false, 1, 2.45),
)
BrownfieldMessaging.sendMessage(nativeMessage)
}

private fun setTime() {
val timeString =
java.time.LocalDateTime.now()
.format(java.time.format.DateTimeFormatter.ofPattern("HH:mm:ss"))
BrownfieldState.set("time", mapOf("time" to timeString))
}

override fun invokeDefaultOnBackPressed() {
TODO("Not yet implemented")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,35 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val rootLayout = LinearLayout(this).apply {
orientation = LinearLayout.VERTICAL
gravity = Gravity.CENTER
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val rootLayout =
LinearLayout(this).apply {
orientation = LinearLayout.VERTICAL
gravity = Gravity.CENTER
layoutParams =
ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
)
}

val button = Button(this).apply {
text = "Open React Native app"
backgroundTintList = ContextCompat.getColorStateList(context, R.color.purple_500)
id = R.id.openReactNativeButton
setTextColor(Color.WHITE)
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
val button =
Button(this).apply {
text = "Open React Native app"
backgroundTintList = ContextCompat.getColorStateList(context, R.color.purple_500)
id = R.id.openReactNativeButton
setTextColor(Color.WHITE)
layoutParams =
LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT,
)

setOnClickListener {
startActivity(Intent(context, ReactNativeActivity::class.java))
}
setOnClickListener { startActivity(Intent(context, ReactNativeActivity::class.java)) }
}

rootLayout.addView(button)
setContentView(rootLayout)
}
}
rootLayout.addView(button)
setContentView(rootLayout)
}
}
Original file line number Diff line number Diff line change
@@ -1,77 +1,14 @@
package dev.expo.brownfieldintegratedtester

import android.os.Bundle
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import host.exp.exponent.brownfield.BrownfieldActivity
import host.exp.exponent.brownfield.showReactNativeFragment
import expo.modules.brownfield.BrownfieldMessage
import expo.modules.brownfield.BrownfieldMessaging
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler
import java.util.Timer
import kotlin.concurrent.timerTask

class ReactNativeActivity : BrownfieldActivity(), DefaultHardwareBackBtnHandler {
private var listenerId: String? = null
private var messageTimer: Timer? = null
private var messageCounter = 0

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
showReactNativeFragment()

listenerId =
BrownfieldMessaging.addListener { message ->
println("Message from React Native received:")
println(message)
showToast(message)
}
startMessageTimer()
}

override fun onDestroy() {
super.onDestroy()
listenerId?.let { BrownfieldMessaging.removeListener(it) }
stopMessageTimer()
}

private fun startMessageTimer() {
messageTimer = Timer()
// Schedule: delay 0ms, repeat every 5000ms (5 seconds)
messageTimer?.schedule(timerTask {
sendMessage()
}, 0, 2500)
}

private fun stopMessageTimer() {
messageTimer?.cancel()
messageTimer = null
}

private fun showToast(message: BrownfieldMessage) {
val sender = message["sender"] as? String
val nested = message["source"] as? Map<*, *>
val platform = nested?.get("platform") as? String
if (sender != null && platform != null) {
Toast.makeText(this, "$platform($sender)", Toast.LENGTH_LONG).show()
}
}

private fun sendMessage() {
messageCounter++
val nativeMessage = mapOf(
"source" to mapOf(
"platform" to "Android"
),
"counter" to messageCounter,
"timestamp" to System.currentTimeMillis(),
"array" to listOf("ab", 'c', false, 1, 2.45)
)
BrownfieldMessaging.sendMessage(nativeMessage)
}

override fun invokeDefaultOnBackPressed() {
TODO("Not yet implemented")
}
}
class ReactNativeActivity : BrownfieldTestActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
showReactNativeFragment()
setupBrownfieldTests()
}
}
10 changes: 9 additions & 1 deletion apps/brownfield-tester/expo-app/src/app/apis/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ActionButton } from '@/components';
const Index = () => {
const router = useRouter();

const navigateToScreen = (screen: 'communication' | 'navigation') => {
const navigateToScreen = (screen: 'communication' | 'navigation' | 'state') => {
router.navigate(`/apis/${screen}`);
};

Expand All @@ -29,6 +29,14 @@ const Index = () => {
onPress={() => navigateToScreen('navigation')}
testID="apis-navigation"
/>
<ActionButton
type="link"
icon="database"
title="State"
description="State API"
onPress={() => navigateToScreen('state')}
testID="apis-state"
/>
</SafeAreaView>
);
};
Expand Down
Loading
Loading