Merge pull request #1158 from crazysaem/ptlaunch

Pattern launcher: improve pattern detection and pattern drawing
pull/1162/head^2
Gordon Williams 2022-01-04 09:48:51 +00:00 committed by GitHub
commit 5105a7e454
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 125 additions and 319 deletions

View File

@ -4861,7 +4861,7 @@
"id": "ptlaunch", "id": "ptlaunch",
"name": "Pattern Launcher", "name": "Pattern Launcher",
"shortName": "Pattern Launcher", "shortName": "Pattern Launcher",
"version": "0.11", "version": "0.13",
"description": "Directly launch apps from the clock screen with custom patterns.", "description": "Directly launch apps from the clock screen with custom patterns.",
"icon": "app.png", "icon": "app.png",
"screenshots": [{"url":"manage_patterns_light.png"}], "screenshots": [{"url":"manage_patterns_light.png"}],

View File

@ -3,3 +3,5 @@
0.03: Make tap to confirm new pattern more reliable. Also allow for easier creation of single circle patterns. 0.03: Make tap to confirm new pattern more reliable. Also allow for easier creation of single circle patterns.
0.10: Improve the management of existing patterns: Draw the linked pattern on the left hand side of the app name within a scroller, similar to the default launcher. Slighlty clean up the code to make it less horrible. 0.10: Improve the management of existing patterns: Draw the linked pattern on the left hand side of the app name within a scroller, similar to the default launcher. Slighlty clean up the code to make it less horrible.
0.11: Respect theme colors. Fix: Do not pollute global space with internal variables ans functions in boot.js 0.11: Respect theme colors. Fix: Do not pollute global space with internal variables ans functions in boot.js
0.12: Improve pattern detection code readability by PaddeK http://forum.espruino.com/profiles/117930/
0.13: Improve pattern rendering by HughB http://forum.espruino.com/profiles/167235/

View File

@ -29,12 +29,13 @@ Then launch the linked apps directly from the clock screen by simply drawing the
## Detailed Steps ## Detailed Steps
From the main menu you can: From the main menu you can:
- Add a new pattern and link it to an app (first entry) - Add a new pattern and link it to an app (first entry)
- To create a new pattern first select "Add Pattern" - To create a new pattern first select "Add Pattern"
- Now draw any pattern you like, this will later launch the linked app from the clock screen - Now draw any pattern you like, this will later launch the linked app from the clock screen
- You can also draw a single-circle pattern (meaning a single tap on one circle) instead of drawing a 'complex' pattern - You can also draw a single-circle pattern (meaning a single tap on one circle) instead of drawing a 'complex' pattern
- If you don't like the pattern, simply re-draw it. The previous pattern will be discarded. - If you don't like the pattern, simply re-draw it. The previous pattern will be discarded.
- If you are happy with the pattern tap on screen or press the button to continue - If you are happy with the pattern press the button to continue
- Now select the app you want to launch with the pattern. - Now select the app you want to launch with the pattern.
- Note, you can bind multiple patterns to the same app. - Note, you can bind multiple patterns to the same app.
- Manage created patterns (second entry) - Manage created patterns (second entry)
@ -47,14 +48,22 @@ From the main menu you can:
## FAQ ## FAQ
1) Nothing happens when I draw on the clock screen! 1. Nothing happens when I draw on the clock screen!
Please double-check if you actually have a pattern linked to an app. Please double-check if you actually have a pattern linked to an app.
2) I have a pattern linked to an app and still nothing happens when I draw on the clock screen! 2. I have a pattern linked to an app and still nothing happens when I draw on the clock screen!
Make sure the watch is unlocked before you start drawing. If this bothers you, you can permanently disable the watch-lock from within the Pattern Launcher app (via the Settings). Make sure the watch is unlocked before you start drawing. If this bothers you, you can permanently disable the watch-lock from within the Pattern Launcher app (via the Settings).
3) I have done all that and still nothing happens! 3. I have done all that and still nothing happens!
Please note that drawing on the clock screen will not visually show the pattern you drew. It will start the app as soon as the pattern was recognized - this might take 1 or 2 seconds! If still nothing happens, that might be a bug, sorry! Please note that drawing on the clock screen will not visually show the pattern you drew. It will start the app as soon as the pattern was recognized - this might take 1 or 2 seconds! If still nothing happens, that might be a bug, sorry!
## Authors
Initial creation: [crazysaem](https://github.com/crazysaem)
Improve pattern detection code readability: [PaddeK](http://forum.espruino.com/profiles/117930/)
Improve pattern rendering: [HughB](http://forum.espruino.com/profiles/167235/)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -114,7 +114,6 @@ var showMainMenu = () => {
E.showMenu(mainmenu); E.showMenu(mainmenu);
}; };
var positions = [];
var recognizeAndDrawPattern = () => { var recognizeAndDrawPattern = () => {
return new Promise((resolve) => { return new Promise((resolve) => {
E.showMenu(); E.showMenu();
@ -135,150 +134,55 @@ var recognizeAndDrawPattern = () => {
resolve(pattern.join("")); resolve(pattern.join(""));
}; };
setWatch(() => finishHandler(), BTN); setWatch(() => finishHandler(), BTN);
setTimeout(() => Bangle.on("tap", finishHandler), 250); // setTimeout(() => Bangle.on("tap", finishHandler), 250);
positions = []; var positions = [];
var getPattern = (positions) => {
var circles = [
{ x: 25, y: 25, i: 0 },
{ x: 87, y: 25, i: 1 },
{ x: 150, y: 25, i: 2 },
{ x: 25, y: 87, i: 3 },
{ x: 87, y: 87, i: 4 },
{ x: 150, y: 87, i: 5 },
{ x: 25, y: 150, i: 6 },
{ x: 87, y: 150, i: 7 },
{ x: 150, y: 150, i: 8 },
];
return positions.reduce((pattern, p, i, arr) => {
var idx = circles.findIndex((c) => {
var dx = p.x > c.x ? p.x - c.x : c.x - p.x;
if (dx > CIRCLE_RADIUS) {
return false;
}
var dy = p.y > c.y ? p.y - c.y : c.y - p.y;
if (dy > CIRCLE_RADIUS) {
return false;
}
if (dx + dy <= CIRCLE_RADIUS) {
return true;
}
return dx * dx + dy * dy <= CIRCLE_RADIUS_2;
});
if (idx >= 0) {
pattern += circles[idx].i;
circles.splice(idx, 1);
}
if (circles.length === 0) {
arr.splice(1);
}
return pattern;
}, "");
};
var dragHandler = (position) => { var dragHandler = (position) => {
log(position);
positions.push(position); positions.push(position);
if (position.b === 0 || positions.length >= 200) {
debounce().then(() => { pattern = getPattern(positions).split("");
if (isFinished) {
return;
}
// This might actually be a 'tap' event.
// Use this check in addition to the actual tap handler to make it more reliable
if (pattern.length > 0 && positions.length === 2) {
if (
positions[0].x === positions[1].x &&
positions[0].y === positions[1].y
) {
finishHandler();
positions = [];
return;
}
}
E.showMessage("Calculating...");
var t0 = Date.now();
log(positions.length);
var circlesClone = cloneCirclesArray();
pattern = [];
var step = Math.floor(positions.length / 100) + 1;
var p, a, b, circle;
for (var i = 0; i < positions.length; i += step) {
p = positions[i];
circle = circlesClone[0];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circlesClone.splice(0, 1);
}
}
circle = circlesClone[1];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circlesClone.splice(1, 1);
}
}
circle = circlesClone[2];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circlesClone.splice(2, 1);
}
}
circle = circlesClone[3];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circlesClone.splice(3, 1);
}
}
circle = circlesClone[4];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circlesClone.splice(4, 1);
}
}
circle = circlesClone[5];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circlesClone.splice(5, 1);
}
}
circle = circlesClone[6];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circlesClone.splice(6, 1);
}
}
circle = circlesClone[7];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circlesClone.splice(7, 1);
}
}
circle = circlesClone[8];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circlesClone.splice(8, 1);
}
}
}
var tx = Date.now();
log(tx - t0);
positions = [];
var t1 = Date.now();
log(t1 - t0);
log("pattern:");
log(pattern);
log("redrawing");
g.clear(); g.clear();
drawCirclesWithPattern(pattern); drawCirclesWithPattern(pattern);
}); positions = [];
}
}; };
Bangle.on("drag", dragHandler); Bangle.on("drag", dragHandler);
}); });
}; };
@ -488,7 +392,8 @@ var drawCircle = (circle, drawBuffer, scale) => {
log("drawing circle"); log("drawing circle");
log({ x: x, y: y, r: r }); log({ x: x, y: y, r: r });
drawBuffer.drawCircle(x, y, r); drawBuffer.setColor(0);
drawBuffer.fillCircle(x, y, r);
}; };
var cachedCirclesDrawings = {}; var cachedCirclesDrawings = {};
@ -533,8 +438,11 @@ var drawCirclesWithPattern = (pattern, options) => {
{ msb: true } { msb: true }
); );
CIRCLES.forEach((circle) => drawCircle(circle, drawBuffer, scale)); drawBuffer.setColor(1);
drawBuffer.fillRect(0, 0, drawBuffer.getWidth(), drawBuffer.getHeight());
CIRCLES.forEach((circle) => drawCircle(circle, drawBuffer, scale));
drawBuffer.setColor(1);
drawBuffer.setFontAlign(0, 0); drawBuffer.setFontAlign(0, 0);
drawBuffer.setFont("Vector", 40 * scale); drawBuffer.setFont("Vector", 40 * scale);
pattern.forEach((circleIndex, patternIndex) => { pattern.forEach((circleIndex, patternIndex) => {
@ -545,12 +453,12 @@ var drawCirclesWithPattern = (pattern, options) => {
circle.y * scale circle.y * scale
); );
}); });
image = { image = {
width: drawBuffer.getWidth(), width: drawBuffer.getWidth(),
height: drawBuffer.getHeight(), height: drawBuffer.getHeight(),
bpp: 1, bpp: 1,
buffer: drawBuffer.buffer, buffer: drawBuffer.buffer,
palette: new Uint16Array([g.theme.fg, g.theme.bg], 0, 1),
}; };
if (enableCaching) { if (enableCaching) {
@ -563,16 +471,6 @@ var drawCirclesWithPattern = (pattern, options) => {
g.drawImage(image, offset.x, offset.y); g.drawImage(image, offset.x, offset.y);
}; };
var cloneCirclesArray = () => {
var circlesClone = Array(CIRCLES.length);
for (var i = 0; i < CIRCLES.length; i++) {
circlesClone[i] = CIRCLES[i];
}
return circlesClone;
};
////// //////
// misc lib functions // misc lib functions
////// //////
@ -583,20 +481,6 @@ var log = (message) => {
} }
}; };
var debounceTimeoutId;
var debounce = (delay) => {
if (debounceTimeoutId) {
clearTimeout(debounceTimeoutId);
}
return new Promise((resolve) => {
debounceTimeoutId = setTimeout(() => {
debounceTimeoutId = undefined;
resolve();
}, delay || 500);
});
};
////// //////
// run main function // run main function
////// //////

View File

@ -7,16 +7,10 @@
}; };
var storedPatterns; var storedPatterns;
var positions = [];
var dragHandler = (position) => {
positions.push(position);
debounce().then(() => {
log(positions.length);
var CIRCLE_RADIUS = 25; var CIRCLE_RADIUS = 25;
var CIRCLE_RADIUS_2 = CIRCLE_RADIUS * CIRCLE_RADIUS; var CIRCLE_RADIUS_2 = Math.pow(CIRCLE_RADIUS, 2);
var positions = [];
var getPattern = (positions) => {
var circles = [ var circles = [
{ x: 25, y: 25, i: 0 }, { x: 25, y: 25, i: 0 },
{ x: 87, y: 25, i: 1 }, { x: 87, y: 25, i: 1 },
@ -28,107 +22,36 @@
{ x: 87, y: 150, i: 7 }, { x: 87, y: 150, i: 7 },
{ x: 150, y: 150, i: 8 }, { x: 150, y: 150, i: 8 },
]; ];
var pattern = []; return positions.reduce((pattern, p, i, arr) => {
var idx = circles.findIndex((c) => {
var step = Math.floor(positions.length / 100) + 1; var dx = p.x > c.x ? p.x - c.x : c.x - p.x;
if (dx > CIRCLE_RADIUS) {
var p, a, b, circle; return false;
for (var i = 0; i < positions.length; i += step) {
p = positions[i];
circle = circles[0];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(0, 1);
} }
var dy = p.y > c.y ? p.y - c.y : c.y - p.y;
if (dy > CIRCLE_RADIUS) {
return false;
} }
if (dx + dy <= CIRCLE_RADIUS) {
circle = circles[1]; return true;
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(1, 1);
} }
return dx * dx + dy * dy <= CIRCLE_RADIUS_2;
});
if (idx >= 0) {
pattern += circles[idx].i;
circles.splice(idx, 1);
} }
if (circles.length === 0) {
circle = circles[2]; arr.splice(1);
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(2, 1);
} }
} return pattern;
}, "");
circle = circles[3]; };
if (circle) { var dragHandler = (position) => {
a = p.x - circle.x; positions.push(position);
b = p.y - circle.y; if (position.b === 0 || positions.length >= 200) {
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) { var pattern = getPattern(positions);
pattern.push(circle.i); log(pattern);
circles.splice(3, 1);
}
}
circle = circles[4];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(4, 1);
}
}
circle = circles[5];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(5, 1);
}
}
circle = circles[6];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(6, 1);
}
}
circle = circles[7];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(7, 1);
}
}
circle = circles[8];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(8, 1);
}
}
}
positions = [];
pattern = pattern.join("");
if (pattern) { if (pattern) {
if (storedPatterns[pattern]) { if (storedPatterns[pattern]) {
@ -145,21 +68,9 @@
} }
} }
} }
});
};
var debounceTimeoutId; positions = [];
var debounce = (delay) => {
if (debounceTimeoutId) {
clearTimeout(debounceTimeoutId);
} }
return new Promise((resolve) => {
debounceTimeoutId = setTimeout(() => {
debounceTimeoutId = undefined;
resolve();
}, delay || 500);
});
}; };
var sui = Bangle.setUI; var sui = Bangle.setUI;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 941 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB