how to exit app and end activity only when holding the A and B button on a Venu sq

Hello Everyone,

I am making an app I only plan to use for my personal Venu Sq. I often find myself accidentally clicking B and exiting the app/activity session when doing everyday tasks. I was wondering if there was a way to code the watch app so exiting the app/activity can only happen when the A and B buttons are held for 20 seconds. 

I am having trouble finding references on how to do so in the SDK API docs or from others online. Thank you so much for your help in advance.

  • You won't find doc to do this, as it's really not something you want to do with the multi-buttons and the long press of buttons which do other things.

    What you can do is something like in onBack, return "true" if you are recording and your app won't exit.  Then do something like the top button to present a menu to save or discard the recording, and once the activity is saved/discarded, onBack returns false and your app will exit.

  • jim_m_58 is right that in *general* you don't want to rely on detecting long button holds or button combos, because depending on device functionality or user configuration, your app may be unable to detect certain combos (and the sim doesn't reflect this). e.g. On a five-button watch, an app can't respond to Hold Light because that brings up the controls menu (or even if it can respond to Hold Light, it's not useful, since the controls menu will appear anyway).

    Another example is that very old watches are completely unable to detect long presses in CIQ apps, due to API limitations.

    I don't see the harm if it's just an app for personal use and/or it's an optional feature, assuming it works. I do agree with jim_m_58 that it's probably a better user experience to have some other way of exiting the app, like a menu option.

    For educational purposes, here's an example of a behavior delegate that should do what you want, assuming that the watch doesn't eat your long press inputs. i.e. For your watch, holding the button labelled A in the diagram above opens the controls menu or launches assistance features, and holding B opens settings. I don't know what will happen if the watch sees the user hold both A and B. It *might* work, since my 5-button watch has shortcuts for "Hold Down" and "Hold Up", but holding Down + Up does nothing, which means that it's conceivable that a CIQ app could detect that combo and take some useful action.

    The delegate also has support for detecting a short press of A or B.

    SuperLongPressDelegate.mc

    import Toybox.Lang;
    import Toybox.System;
    import Toybox.Timer;
    import Toybox.WatchUi;
    
    class SuperLongPressDelegate extends WatchUi.BehaviorDelegate {
        var veryLongPressTimer as Timer.Timer;
        function initialize() {
            BehaviorDelegate.initialize();
            veryLongPressTimer = new Timer.Timer();
        }
    
        public function onBack() as Boolean {
            System.println("suppress onBack() default behavior");
            return true;
        }
    
        // The system time (System.getTimer()) when the user started pressing KEY_ENTER.
        // If non-null, KEY_ENTER is currently being pressed.
        // If null, KEY_ENTER is not currently being pressed.
        private var keyEnterStartTime as Number?;
        // Same as keyEnterStartTime, except for KEY_ESC.
        private var keyEscStartTime as Number?;
    
        // Any press duration longer or equal to this is considered a very long press
        private const veryLongPressThreshold = 20 * 1000; // 20 seconds
    
        // Any press duration shorter than this is considered a short press
        private const shortPressThreshold = 2 * 1000; // 2 seconds
    
        private var detectingVeryLongEnterAndEscPress as Boolean = false;
    
        // The documentation says onKey() is called when a key is pressed and released,
        // but experience shows that it's actually called following onKeyPressed()
        // and before onKeyReleased():
        // - Immediately when a key is pressed
        // or
        // - (If a key was held down) about 1 second after the key was initially pressed
        public function onKey(evt as KeyEvent) as Boolean {
            return true;
        }
    
        private function cancelVeryLongPressDetection() as Void {
            detectingVeryLongEnterAndEscPress = false;
            veryLongPressTimer.stop();
        }
    
        public function onKeyPressed(evt as KeyEvent) as Boolean {
            var key = evt.getKey();
    
            System.println("Pressed key: " + key);
    
            if (key == KEY_ENTER) {
                keyEnterStartTime = System.getTimer();
            } else if (key == KEY_ESC) {
                keyEscStartTime = System.getTimer();
            }
    
            if (keyEnterStartTime != null && keyEscStartTime != null) {
                if (!detectingVeryLongEnterAndEscPress) {
                    detectingVeryLongEnterAndEscPress = true;
                    veryLongPressTimer.stop();
                    veryLongPressTimer.start(method(:veryLongPressTimerExpired), veryLongPressThreshold, false);
                }
            }
    
            // Suppress default behavior
            return true;
        }
    
        function veryLongPressTimerExpired() as Void {
            if (keyEnterStartTime != null && keyEscStartTime != null) {
                System.println("TODO: Handle very long press of KEY_ENTER + KEY_ESC");
            }
            detectingVeryLongEnterAndEscPress = false;
            keyEnterStartTime = null;
            keyEscStartTime = null;
    	}
    
    
        public function onKeyReleased(evt as KeyEvent) as Boolean {
            var key = evt.getKey();
    
            System.println("Released key: " + key);
    
            var keyReleaseTime = System.getTimer();
            if (key == KEY_ENTER) {
                if (keyEnterStartTime != null && keyReleaseTime - keyEnterStartTime < shortPressThreshold) {
                    System.println("TODO: handle short press of key " + key);
                }
                keyEnterStartTime = null;
                cancelVeryLongPressDetection();
            } else if (key == KEY_ESC) {
                if (keyEscStartTime != null && keyReleaseTime - keyEscStartTime < shortPressThreshold) {
                    System.println("TODO: handle short press of key " + key);
                }
                keyEscStartTime = null;
                cancelVeryLongPressDetection();
            }
    
            // Suppress default behavior
            return true;
        }
    }

    (Note that you can only have 3 timers per app, and this delegate uses up one of them.)

    I tested this in the simulator using the Input sample, adding SuperLongPressDelegate.mc and changing getInitialView() as follows:

        public function getInitialView() as Array<Views or InputDelegates>? {
            var view = new $.InputView();
            var delegate = new $.InputDelegate(view);
            var delegate2 = new SuperLongPressDelegate();
            // return [view, delegate] as Array<Views or InputDelegates>;
            return [view, delegate2] as Array<Views or InputDelegates>;
        }

    (It would be just as easy to create a device app from scratch and drop in SuperLongPressDelegate.mc)

    You can trigger the A button (KEY_ENTER) by pressing ENTER/RETURN on your keyboard, and the B button (KEY_ESC) by pressing ESC.

    I noticed that the simulator seems to ignore simultaneous keypresses, so you'll have to press one button slightly before the other one. I suggest changing the "very long press threshold" to something a lot shorter than 20 seconds (like 5 seconds), at least for the purposes of testing.

  • By the Way, on a Venu  Sq, pressing both buttons at the same time for just a second or two, takes a screen shot.

  • By the Way, on a Venu  Sq, pressing both buttons at the same time for just a second or two, takes a screen shot.

    Thanks! If that's the case (and the user is unable to disable that functionality), then it's possible that the above example is useless for Venu Sq. (Unless of course the CIQ app can still respond to a very long press of A + B, but at the cost of taking an unwanted screenshot.)

    Another possibility (if implementing a menu is out of the question) would be to respond to a double-click of a button such as B (Back).

    e.g.

    class DoubleClickDelegate extends WatchUi.BehaviorDelegate {
        private const doubleClickThreshold = 2 * 1000; // 2 seconds (adjust to suit your needs)
        private var lastUnhandledKeyEscTime as Number?;
    
        function initialize() {
            BehaviorDelegate.initialize();
        }
    
        public function onBack() as Boolean {
            System.println("suppress onBack() default behavior");
            return true;
        }
    
        public function onKey(evt as KeyEvent) as Boolean {
            return true;
        }
    
        public function onKeyPressed(evt as KeyEvent) as Boolean {
            var key = evt.getKey();
            System.println("Pressed key: " + key);
    
            var keyPressedTime = System.getTimer();
    
            if (key == KEY_ESC) {
                if (lastUnhandledKeyEscTime != null) {
                    if (keyPressedTime - lastUnhandledKeyEscTime < doubleClickThreshold) {
                        System.println("TODO: Handle double-click of KEY_ESC");
                    }
                    lastUnhandledKeyEscTime = null;
                } else {
                    lastUnhandledKeyEscTime = keyPressedTime;
                }
            }
    
            // Suppress default behavior
            return true;
        }
    
        public function onKeyReleased(evt as KeyEvent) as Boolean {
            // Suppress default behavior
            return true;
        }
    }