Custom Commands
Android: Custom Commands
Overview
On a CUSTOMAPP session with the same CXRLink instance, build payloads with Caps and send via sendCustomCmd, receiving replies in ICustomCmdCbk. Enables bidirectional binary messaging between the phone app and the glasses-side custom app.
CustomView sessions do not support custom commands.
Bidirectional channels
Must match glasses CXRServiceBridge and RenewCXRLSample CustomCmdViewModel:
| Direction | Phone CXR-L | Glasses CXR-S |
|---|---|---|
| Phone → glasses | sendCustomCmd(“rk_custom_client”, Caps) | subscribe(“rk_custom_client”, MsgCallback) |
| Glasses → phone | onCustomCmdResult(“rk_custom_key”, bytes) | sendMessage(“rk_custom_key”, Caps) |
Sample Caps fields (demo)
Phone → glasses (sendCustomCmd payload):
Caps().apply {
write(“rk_custom_key”)
write(“from client click times = $count”)
}
Glasses → phone (sendMessage payload):
Caps().apply {
write(“message”)
write(str)
}
Prerequisites
- Session type is
CUSTOMAPP. - Global
CXRLinkconnected. - Scene building completed: target app running on glasses.
- Link ready (CXR + Bluetooth).
Core APIs (phone)
| Method | Description |
|---|---|
setCXRCustomCmdCbk(ICustomCmdCbk) | Register command callbacks |
sendCustomCmd(key: String, payload: Caps) | Send; pass a Caps instance directly |
ICustomCmdCbk
fun onCustomCmdResult(key: String?, payload: ByteArray?)
Parse with Caps.fromBytes(payload) following the agreed field order.
Caps example (phone)
link.sendCustomCmd(“rk_custom_client”, Caps().apply {
write(“rk_custom_key”)
write(“message from phone”)
})
link.setCXRCustomCmdCbk(object : ICustomCmdCbk {
override fun onCustomCmdResult(key: String?, payload: ByteArray?) {
if (key == “rk_custom_key” && payload != null) {
val caps = Caps.fromBytes(payload)
}
}
})
Agree with the glasses app on channel keys, field order, types, and max packet size.
Constraints
- Send only when scene is ready and link is up.
- Clear Cmd callback on page destroy.
- Reuse the same
CXRLinkfrom CustomApp session creation.
Glasses-side: CXRServiceBridge
After CUSTOMAPP session build, the glasses app subscribes to phone command channels and replies via sendMessage.
Prerequisites
- Phone
appStartsucceeded (onOpenAppResult(true)). - Glasses
MainViewModel(or equivalent) has calledsubscribeon the agreed channel.
Core APIs
| Method | Description |
|---|---|
setStatusListener(StatusListener) | Connection lifecycle |
subscribe(name: String, MsgCallback) | Subscribe to phone channel |
sendMessage(name: String, caps: Caps) | Send reply to phone |
StatusListener (excerpt)
| Callback | Description |
|---|---|
onConnected | Bridge connected |
onDisconnected | Bridge disconnected |
onConnecting | Connecting |
MsgCallback
fun onReceive(name: String?, args: Caps?, bytes: ByteArray?)
Integration example (glasses)
class MainViewModel : ViewModel() {
private val cxrBridge = CXRServiceBridge()
private val clientKey = “rk_custom_client”
private val cmdKey = “rk_custom_key”
private val msgCallback = object : CXRServiceBridge.MsgCallback {
override fun onReceive(name: String?, args: Caps?, bytes: ByteArray?) {
// Update UI from args
}
}
init {
cxrBridge.setStatusListener(/* StatusListener impl */)
cxrBridge.subscribe(clientKey, msgCallback)
}
fun sendMessage(text: String) {
cxrBridge.sendMessage(cmdKey, Caps().apply {
write(“message”)
write(text)
})
}
}
Constraints
- Channel keys, field order, and types must match on both sides.
- Register
subscribebefore relying on phone traffic; release on destroy per SDK guidance. - Glasses do not call
connectseparately.
Key reporting uses the same sendMessage(“rk_custom_key”, …) channel — see Keys and System Broadcasts chapter.
Appendix: reference sample
| Side | Class |
|---|---|
| Phone | CustomCmdViewModel.kt — sendCustomCmd, onCustomCmdResult filters rk_custom_key |
| Glasses | MainViewModel.kt — clientKey / cmdKey, subscribe, sendMessage, parseCaps |