[CIQBUG] Session.stop causes future key press to be lost

This is a weird one.

The issue is that if a ActivityRecording.Session is stopped and no user input is received (key presses in the case of this example, but taps and swipes are known to be affected) before pushing a new view (or menu, or whatever), the first user input after the Ui.pushView() has no effect. Note that I cannot reproduce this problem on the simulator. I have reproduced it on the 920XT w/ 3.20 firmware, and I've been told the problem exists for the VivoActive as well (not sure of firmware version).

The following test case walks you through several scenarios, like a wizard, interacting with the Session and Ui.pushView(). In the cases that the session is modified and the view is pushed, you'll notice that the first up/down key press has no effect on the key press count.

I'm fairly certain that this issue relates to this one I filed the other day. My gut tells me that the session object creates some sort of drawable that hooks into the rendering system (I'm betting that it is the lap screen that appears automatically when Session.addLap() is called). For some reason that drawable is always resetting the draw colors (which it should not do), and it is somehow causing input to be consumed.

using Toybox.Application as App;
using Toybox.ActivityRecording as Arc;
using Toybox.Graphics as Gfx;
using Toybox.System as Sys;
using Toybox.WatchUi as Ui;

class Delegate2 extends Ui.InputDelegate
{
hidden var view;
hidden var count;

function initialize(view) {
self.view = view;
setCount(0);
}

function onKey(evt) {
var key = evt.getKey();
if (key == Ui.KEY_ESC) {
Ui.popView(Ui.SLIDE_IMMEDIATE);
}
else if (key == Ui.KEY_ENTER) {
Ui.popView(Ui.SLIDE_IMMEDIATE);
}
else {
setCount(count + 1);
}

return true;
}

hidden function setCount(n) {
count = n;
view.setText("Up/Down Presses " + n.toString());
}
}

class Delegate1 extends Ui.InputDelegate
{
hidden var view;
hidden var activity;
hidden var state;

function initialize(view) {
self.view = view;
activity = null;
state = 0;
}

function showMenu() {
var next = new TestView();
Ui.pushView(next, new Delegate2(next), Ui.SLIDE_IMMEDIATE);

view.setText("Press Enter\nto Start");
}

function onKey(evt) {
var key = evt.getKey();
if (key == Ui.KEY_ENTER) {
Sys.println("state="+state);

if (state == 0) {
// menu is normal
showMenu();
view.setText("Press Enter\nto create session");
}
else if (state == 1) {
activity = Arc.createSession({
:sport => Arc.SPORT_GENERIC,
:subSport => Arc.SUB_SPORT_GENERIC,
:name => "testing"
});

view.setText("Created.\nPress Enter\nto show menu");
}
else if (state == 2) {
// menu is black, but behaves normally
showMenu();
view.setText("Created.\nPress Enter\nto record");
}
else if (state == 3) {
activity.start();
view.setText("Recording.\nPress Enter\nto stop");
}
else if (state == 4) {
activity.stop();
view.setText("Paused.\nPress Enter\nto show menu");
}
else if (state == 5) {
// menu is black, but behaves normally
showMenu();
view.setText("Created.\nPress Enter\nto resume and\nshow menu");
}
else if (state == 6) {
activity.start();
// menu is black, but requires one key input before it behaves normally
showMenu();
view.setText("Recording.\nPress Enter\nto stop and\nshow menu");
}
else if (state == 7) {
activity.stop();
// menu is black, but requires one key input before it behaves normally
showMenu();
view.setText("Paused.\nPress Enter\nto discard");
}
else if (state == 8) {
activity.discard();
view.setText("Discarded.\nPress Enter\nto deallocate");
}
else if (state == 9) {
activity = null;
view.setText("Deallocated.\nPress Enter\nto exit");
}
else {
Ui.popView(Ui.SLIDE_IMMEDIATE);
}

++state;
}

return true;
}
}

class TestView extends Ui.View
{
var text;

function initialize() {
text = "Press Enter\nTo Start";
}

function setText(text) {
self.text = text;
Ui.requestUpdate();
}

function onUpdate(dc) {
dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_WHITE);
dc.clear();

var cx = dc.getWidth() / 2;
var cy = dc.getHeight() / 2;

dc.setColor(Gfx.COLOR_BLACK, Gfx.COLOR_TRANSPARENT);
dc.drawText(cx, cy, Gfx.FONT_SMALL,
text,
Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER);
}
}

class Test_ToGeoStringApp extends App.AppBase
{
function getInitialView() {
var view = new TestView();
var delegate = new Delegate1(view);

return [ view, delegate ];
}
}
  • Not that it matters, but if someone could confirm that the Session is indeed producing a drawable that is responsible for this bad behavior, that would be awesome.

    Travis
  • Thanks Travis! We'll look into this. I'll make sure someone answers your question about the Session producing a drawable.
  • I spent all morning creating a test case for what I thought was a new bug, but apparently it is the same as the one above. This test case is simpler and shows that the first key press after calling start() or stop() is lost. Previously I had just thought it was stop(), but I was mistaken.

    This test case also has a stub Session class that will be used instead of Activity.Session if you change the conditional on line 52. As expected, the problem goes away when using the stub.

    using Toybox.ActivityRecording as Arc;
    using Toybox.Application as App;
    using Toybox.Graphics as Gfx;
    using Toybox.System as Sys;
    using Toybox.WatchUi as Ui;

    class Session
    {
    hidden var mRecording;

    function initialize() {
    mRecording = false;
    }

    function start() {
    mRecording = true;
    }

    function isRecording() {
    return mRecording;
    }

    function addLap() {
    }

    function stop() {
    mRecording = false;
    }

    function save() {
    }

    function discard() {
    }
    }

    class Delegate extends Ui.InputDelegate {
    hidden var mView;
    hidden var mActivity;

    function initialize(view) {
    mView = view;
    mActivity = null;
    }

    function onKey(evt) {
    var key = evt.getKey();
    if (key == Ui.KEY_ENTER) {

    if (mActivity == null) {

    if (1) {
    mActivity = Arc.createSession({
    :sport => Arc.SPORT_SPORT_GENERIC,
    :subSport => Arc.SUB_SPORT_GENERIC,
    :name => "testing"
    });
    }
    else {
    mActivity = new Session();
    }
    }

    if (mActivity.isRecording()) {
    mActivity.stop();
    mView.setColor(Gfx.COLOR_RED);
    }
    else {
    mActivity.start();
    mView.setColor(Gfx.COLOR_GREEN);
    }
    }
    else if (key == Ui.KEY_ESC) {

    if (mActivity == null) {
    Ui.popView(Ui.SLIDE_IMMEDIATE);
    }
    else if (mActivity.isRecording()) {
    mActivity.addLap();
    mView.setColor(Gfx.COLOR_WHITE);
    }
    else {
    mActivity.discard();
    Ui.popView(Ui.SLIDE_IMMEDIATE);
    }
    }
    else {
    mView.setColor(Gfx.COLOR_WHITE);
    }

    return true;
    }
    }

    class View extends Ui.View {
    hidden var mPresses;
    hidden var mColor;

    function initialize() {
    mPresses = 0;
    mColor = Gfx.COLOR_WHITE;
    }

    function onUpdate(dc) {
    dc.setColor(Gfx.COLOR_WHITE, Gfx.COLOR_BLACK);
    dc.clear();

    var cx = dc.getWidth() / 2;
    var cy = dc.getHeight() / 2;

    dc.setColor(mColor, Gfx.COLOR_TRANSPARENT);
    dc.drawText(cx, cy, Gfx.FONT_NUMBER_THAI_HOT,
    mPresses.toString(),
    Gfx.TEXT_JUSTIFY_CENTER | Gfx.TEXT_JUSTIFY_VCENTER);
    }

    function setColor(color) {
    mPresses += 1;
    mColor = color;

    Ui.requestUpdate();
    }
    }

    class App extends App.AppBase {

    function getInitialView() {
    var view = new View();
    var delegate = new Delegate(view);
    return [ view, delegate ];
    }
    }
  • Thanks--I've included this additional info on the ticket for investigation.
  • Garmin - Just a reminder on this bug.

    It's still present in CIQ 1.1.3 on a VivoActive with FW 3.10. I haven't checked the latest on a 920XT.
  • FYI, this issue just came up in our sprint planning today, so it's out of the backlog to be looked at more closely.
  • I'm not sure if in affected by this bug.. But I'm getting some lost key press after a session is stopped and I present the save/discard/resume menu, up down keys can still be used (so is swiping on a VA) but pressing enter / tapping the screen to either save/discard or resume will NOT have any effect.

    Is there any updates on this bug?

    Travis - what do u mean by "stub"?
  • Travis - what do u mean by "stub"?

    A bit of code used to simulate a complete working implementation. The Session class looks just like Toybox.Recording.Session in terms of its interface, but it doesn't actually do any session recording. https://en.wikipedia.org/wiki/Method_stub

    Travis