Merge pull request #2743 from bobrippling/widhid-exception

widhid: avoid clashing with other swipe/drag handlers
pull/2758/head
Gordon Williams 2023-05-15 09:59:47 +01:00 committed by GitHub
commit ced3783ae5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 18 additions and 105 deletions

3
apps/widhid/ChangeLog Normal file
View File

@ -0,0 +1,3 @@
0.01: New widget - music control via a swipe
0.02: Improve interactivity - avoid responding to swipes when a menu or
launcher is active.

View File

@ -13,6 +13,7 @@ Swipe down to enable - note the icon changes from blue to orange, indicating it'
All other watch interaction is disabled for 3 seconds, to prevent clashing taps/drags - this period is extended as you continue to alter the volume, play/pause and jump between tracks.
Requires espruino firmware > 2v17 to avoid event handler clashes.
# Setup / Technical details

View File

@ -2,7 +2,7 @@
"id": "widhid",
"name": "Bluetooth Music Swipe Control Widget",
"shortName": "BLE Swipe Widget",
"version": "0.01",
"version": "0.02",
"description": "Based on Swipe Bluetooth Music Controls (based on Bluetooth Music Controls). Swipe down to enable, then swipe up/down for volume, left/right for previous and next and tap for play/pause. Enable HID in settings, pair with your phone/computer, then use this widget to control music from your watch!",
"icon": "icon.png",
"readme": "README.md",

View File

@ -11,16 +11,13 @@
var activeTimeout;
var waitForRelease = true;
var onSwipe = (function (_lr, ud) {
if (Bangle.CLKINFO_FOCUS)
return;
if (!activeTimeout && ud > 0) {
if (ud > 0 && !activeTimeout && !Bangle.CLKINFO_FOCUS) {
listen();
Bangle.buzz(20);
}
});
var onDrag = (function (e) {
if (Bangle.CLKINFO_FOCUS)
return;
E.stopEventPropagation && E.stopEventPropagation();
if (e.b === 0) {
var wasDragging = dragging;
dragging = false;
@ -82,9 +79,9 @@
var listen = function () {
var wasActive = !!activeTimeout;
if (!wasActive) {
suspendOthers();
waitForRelease = true;
Bangle.on("drag", onDrag);
Bangle["#ondrag"] = [onDrag].concat(Bangle["#ondrag"].filter(function (f) { return f !== onDrag; }));
redraw();
}
if (activeTimeout)
@ -92,7 +89,6 @@
activeTimeout = setTimeout(function () {
activeTimeout = undefined;
Bangle.removeListener("drag", onDrag);
resumeOthers();
redraw();
}, 3000);
};
@ -131,46 +127,4 @@
var toggle = function () { return sendHid(0x10); };
var up = function () { return sendHid(0x40); };
var down = function () { return sendHid(0x80); };
var touchEvents = {
tap: null,
gesture: null,
aiGesture: null,
swipe: null,
touch: null,
drag: null,
stroke: null,
};
var suspendOthers = function () {
for (var event in touchEvents) {
var handlers = Bangle["#on".concat(event)];
if (!handlers)
continue;
var newEvents = void 0;
if (handlers instanceof Array)
newEvents = handlers.slice();
else
newEvents = [handlers];
for (var _i = 0, newEvents_1 = newEvents; _i < newEvents_1.length; _i++) {
var handler = newEvents_1[_i];
Bangle.removeListener(event, handler);
}
touchEvents[event] = newEvents;
}
};
var resumeOthers = function () {
for (var event in touchEvents) {
var handlers = touchEvents[event];
touchEvents[event] = null;
if (handlers)
for (var _i = 0, handlers_1 = handlers; _i < handlers_1.length; _i++) {
var handler = handlers_1[_i];
try {
Bangle.on(event, handler);
}
catch (e) {
console.log("couldn't restore \"".concat(event, "\" handler:"), e);
}
}
}
};
})();

View File

@ -14,16 +14,16 @@
let waitForRelease = true;
const onSwipe = ((_lr, ud) => {
if((Bangle as BangleExt).CLKINFO_FOCUS) return;
if(!activeTimeout && ud! > 0){
// do these checks in order of cheapness
if(ud! > 0 && !activeTimeout && !(Bangle as BangleExt).CLKINFO_FOCUS){
listen();
Bangle.buzz(20);
}
}) satisfies SwipeCallback;
const onDrag = (e => {
if((Bangle as BangleExt).CLKINFO_FOCUS) return;
// Espruino/35c8cb9be11
(E as any).stopEventPropagation && (E as any).stopEventPropagation();
if(e.b === 0){
// released
@ -81,9 +81,14 @@
const listen = () => {
const wasActive = !!activeTimeout;
if(!wasActive){
suspendOthers();
waitForRelease = true; // wait for first touch up before accepting gestures
Bangle.on("drag", onDrag);
// move our drag to the start of the event listener array
(Bangle as any)["#ondrag"] = [onDrag].concat(
(Bangle as any)["#ondrag"].filter((f: unknown) => f !== onDrag)
);
redraw();
}
@ -92,7 +97,6 @@
activeTimeout = undefined;
Bangle.removeListener("drag", onDrag);
resumeOthers();
redraw();
}, 3000);
@ -147,53 +151,4 @@
const toggle = () => /*DEBUG ? console.log("toggle") : */ sendHid(0x10);
const up = () => /*DEBUG ? console.log("up") : */ sendHid(0x40);
const down = () => /*DEBUG ? console.log("down") : */ sendHid(0x80);
// similarly to the lightswitch app, we tangle with the listener arrays to
// disable event handlers
type Handler = () => void;
const touchEvents: {
[key: string]: null | Handler[]
} = {
tap: null,
gesture: null,
aiGesture: null,
swipe: null,
touch: null,
drag: null,
stroke: null,
};
const suspendOthers = () => {
for(const event in touchEvents){
const handlers: Handler[] | Handler | undefined
= (Bangle as any)[`#on${event}`];
if(!handlers) continue;
let newEvents;
if(handlers instanceof Array)
newEvents = handlers.slice();
else
newEvents = [handlers /* single fn */];
for(const handler of newEvents)
Bangle.removeListener(event, handler);
touchEvents[event] = newEvents;
}
};
const resumeOthers = () => {
for(const event in touchEvents){
const handlers = touchEvents[event];
touchEvents[event] = null;
if(handlers)
for(const handler of handlers)
try{
Bangle.on(event as any, handler);
}catch(e){
console.log(`couldn't restore "${event}" handler:`, e);
}
}
};
})()