How can I update watch app UI in realtime when receiving messages from Android app

Former Member
Former Member
Hello fellow Garmin developers,

I have been trying to develop a direct messaging communication setup over BLE between my Android App and my connectIQ app (on Garmin Forerunner 230, SDK version 1.3.x). The goal here is that the Android app is collecting some data, and then pushing it to the watch app to display. For the moment, I am sending data (1 int value) every second.

Below is the code on my mailbox listener:
function initialize() {
AppBase.initialize();
Sys.println("app-initialize()");

mailMethod = method(:onMail);
Comm.setMailboxListener(mailMethod);
}

function onMail(mailIter) {
var mail = mailIter.next();

while(mail!=null) {
Sys.println("app-onMail: received - "+mail);

message = mail.toString();
Ui.requestUpdate();
mail = mailIter.next();
}

Comm.emptyMailbox();
}


Assuming the messages from Android are 1, 2, 3, 4, 5: I would like the app to do update the UI as the messages are read, like this:
app-onMail: received - 1
//update the UI
app-onMail: received - 2
//update the UI
app-onMail: received - 3
//update the UI
app-onMail: received - 4
//update the UI
app-onMail: received - 5
//update the UI


Instead, this happens
app-onMail: received - 1
app-onMail: received - 2
app-onMail: received - 3
app-onMail: received - 4
app-onMail: received - 5
//update the UI
//update the UI
//update the UI
//update the UI
//update the UI


How can I update the UI in real time as I receive message from the phone app?

Thanks in advance for your responses!
  • How is onMail() being called? Looks like it may only be called when all the messages are already there.
  • Former Member
    Former Member over 8 years ago
    How is onMail() being called? Looks like it may only be called when all the messages are already there.


    I set up the MailboxListener the regular way:
    function initialize() {
    AppBase.initialize();
    Sys.println("app-initialize()");

    mailMethod = method(:onMail);
    Comm.setMailboxListener(mailMethod);
    }


    Is there a faster way of getting the messages?
  • I looked back and your code and changed my post as I saw you were doing the Ui.requestUpdate(). It looks like onMail() is only being called when all 5 messages have been received. Ui.requestUpdate() doesn't call onUpdate(), but queues the request.
  • Former Member
    Former Member over 8 years ago
    I looked back and your code and changed my post as I saw you were doing the Ui.requestUpdate(). It looks like onMail() is only being called when all 5 messages have been received. Ui.requestUpdate() doesn't call onUpdate(), but queues the request.


    I thought so as well. I have 2 classes: App.mc (handles messages) and View.mc (handles UI updates). As far as I can tell, the API only allows for UI ops after completing the tasks in App.mc.
  • What happens if you only send the message every 5-10 seconds? To the updates occur after each message?
  • Former Member
    Former Member over 8 years ago
    When I send messages every 5 secs, the updates occur after each message (there is only one message in the mailbox). When I tried a 3sec interval, the mailbox would sometimes have 2-3 messages.

    This seems to be the fastest way for me to update the UI with the latest message, although I notice a 3-4sec delay from the time Android sends the message to the time the watch receives it. Communication seems to be a lot faster on other watch platforms.

    Thanks for the help!
  • Are you seeing this in the sim or on a real watch and phone with BT? The two environments could be different.
  • Former Member
    Former Member over 8 years ago
    Working with a real watch (FR 230) and a physical Android device with the Android ConnectIQ SDK.
  • As has been pointed out, the problem is not all that complex. The framework polls to see if there are mail messages. If there are any, it invokes your application onMail() callback. Your callback consumes each message from the queue, and repeatedly sets a flag that indicates the UI needs to update. After your call returns, the framework checks the flag to see if the UI needs to be updated, and if so it calls onUpdate() for the active view..

    If you want to visualize every message as they come in, you'd need to maintain a queue of mail items (or just a counter) and then handle the mail items between draws. Something like this...

    class MyApp extends App.AppBase
    {
    hidden var _M_messages;
    hidden var _M_count;

    function initialize() {
    AppBase.initialize();
    _M_messages = new [10];
    _M_count = 0;
    }

    function getInitialView() {
    return [ new MyView() ];
    }

    function onStart(params) {
    Comm.setMailboxListener(self.method(:onMail));
    }

    function onStop(params) {
    Comm.setMailboxListener(null);
    }

    function onMail(mailIter) {

    var mail = mailIter.next();
    while (mail != null) {

    // only track up to 10 messages
    if (_M_count < 10) {
    _M_messages[_M_count] = mail;
    ++_M_count;
    }
    else {
    break;
    }

    mail = mailIter.next();
    }

    Comm.emptyMailbox();

    startProcessingMessages();
    }


    hidden function startProcessingMessages() {
    if (_M_timer == null) {
    _M_timer = new Timer.Timer();
    _M_timer.start(self.method(:processOneMessage), 250, true);
    }
    }

    hidden function stopProcessingMessages() {
    if (_M_timer != null) {
    _M_timer.stop();
    _M_timer = null;
    }
    }

    function getMessageCount() {
    return _M_messages;
    }

    function processOneMessage() {
    if (_M_count != 0) {
    --_M_count;
    var mail = _M_messages[_M_count];
    _M_messages[_M_count] = null;

    // process the message here

    Ui.requestUpdate();

    if (_M_count == 0) {
    stopProcessingMessages();
    }
    }
    }
    }

    class MyView extends Ui.View
    {
    hidden var _M_app;

    function initialize(app) {
    View.initialize();
    _M_app = app;
    }

    function onUpdate(dc) {

    var mailMessages = _M_app.getMessageCount();

    // draw the number of mail messages
    }
    }
  • Former Member
    Former Member over 8 years ago
    Hi TRAVIS.VITEK,

    That solution worked for me, thanks for the sample code!