ptlaunch: 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.
parent
854d2f9ade
commit
31a4d05cb3
|
|
@ -4829,7 +4829,7 @@
|
||||||
"id": "ptlaunch",
|
"id": "ptlaunch",
|
||||||
"name": "Pattern Launcher",
|
"name": "Pattern Launcher",
|
||||||
"shortName": "Pattern Launcher",
|
"shortName": "Pattern Launcher",
|
||||||
"version": "0.03",
|
"version": "0.10",
|
||||||
"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",
|
||||||
"tags": "tools",
|
"tags": "tools",
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
0.01: Initial creation of the pattern launch app
|
0.01: Initial creation of the pattern launch app
|
||||||
0.02: Turn on lcd when launching an app if the lock screen was disabled in the settings
|
0.02: Turn on lcd when launching an app if the lock screen was disabled in the settings
|
||||||
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.
|
||||||
|
|
@ -8,12 +8,19 @@ Create patterns and link them to apps in the Pattern Launcher app.
|
||||||
|
|
||||||
Then launch the linked apps directly from the clock screen by simply drawing the desired pattern.
|
Then launch the linked apps directly from the clock screen by simply drawing the desired pattern.
|
||||||
|
|
||||||
## Screenshots and detailed steps
|
## Add Pattern Screenshots
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
|
## Manage Pattern Screenshots
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
## 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"
|
||||||
|
|
@ -23,11 +30,10 @@ From the main menu you can:
|
||||||
- If you are happy with the pattern tap on screen or press the button to continue
|
- If you are happy with the pattern tap on screen or 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.
|
||||||
- Remove linked patterns (second entry)
|
- Manage created patterns (second entry)
|
||||||
- To remove a pattern first select "Remove Pattern"
|
- To manage your patterns first select "Manage Patterns"
|
||||||
- You will now see a list of apps that have patterns linked to them
|
- You will now see a scrollabe list of patterns + linked apps
|
||||||
- Simply select the app that you want to unlink. This will remove the saved pattern, but not the app itself!
|
- If you want to deletion a pattern (and unlink the app) simply tap on it, and confirm the deletion
|
||||||
- Note, that you can not actually preview the patterns. This makes removing patterns that are linked to the same app annoying. sorry!
|
|
||||||
- Disable the lock screen on the clock screen from the settings (third entry)
|
- Disable the lock screen on the clock screen from the settings (third entry)
|
||||||
- To launch the app from the pattern on the clock screen the watch must be unlocked.
|
- To launch the app from the pattern on the clock screen the watch must be unlocked.
|
||||||
- If this annoys you, you can disable the lock on the clock screen from the setting here
|
- If this annoys you, you can disable the lock on the clock screen from the setting here
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
var storage = require("Storage");
|
|
||||||
|
|
||||||
var DEBUG = false;
|
var DEBUG = false;
|
||||||
var log = (message) => {
|
var log = (message) => {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
|
|
@ -7,20 +5,7 @@ var log = (message) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var CIRCLE_RADIUS = 25;
|
var storage = require("Storage");
|
||||||
var CIRCLE_RADIUS_2 = CIRCLE_RADIUS * CIRCLE_RADIUS;
|
|
||||||
|
|
||||||
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 },
|
|
||||||
];
|
|
||||||
|
|
||||||
var showMainMenu = () => {
|
var showMainMenu = () => {
|
||||||
log("loading patterns");
|
log("loading patterns");
|
||||||
|
|
@ -73,17 +58,32 @@ var showMainMenu = () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
"Remove Pattern": () => {
|
"Manage Patterns": () => {
|
||||||
log("selecting pattern through app");
|
log("selecting pattern through app");
|
||||||
getStoredPatternViaApp(storedPatterns).then((pattern) => {
|
showScrollerContainingAppsWithPatterns().then((selected) => {
|
||||||
|
var pattern = selected.pattern;
|
||||||
|
var appName = selected.appName;
|
||||||
|
if (pattern === "back") {
|
||||||
|
showMainMenu();
|
||||||
|
} else {
|
||||||
|
E.showPrompt(appName + "\n\npattern:\n" + pattern, {
|
||||||
|
title: "Delete?",
|
||||||
|
buttons: { Yes: true, No: false },
|
||||||
|
}).then((confirm) => {
|
||||||
|
if (confirm) {
|
||||||
E.showMessage("Deleting...");
|
E.showMessage("Deleting...");
|
||||||
delete storedPatterns[pattern];
|
delete storedPatterns[pattern];
|
||||||
storage.writeJSON("ptlaunch.patterns.json", storedPatterns);
|
storage.writeJSON("ptlaunch.patterns.json", storedPatterns);
|
||||||
showMainMenu();
|
showMainMenu();
|
||||||
|
} else {
|
||||||
|
showMainMenu();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
Settings: () => {
|
Settings: () => {
|
||||||
var settings = storedPatterns["settings"] || {};
|
var settings = storedPatterns.settings || {};
|
||||||
|
|
||||||
var settingsmenu = {
|
var settingsmenu = {
|
||||||
"": {
|
"": {
|
||||||
|
|
@ -98,7 +98,7 @@ var showMainMenu = () => {
|
||||||
if (settings.lockDisabled) {
|
if (settings.lockDisabled) {
|
||||||
settingsmenu["Enable lock"] = () => {
|
settingsmenu["Enable lock"] = () => {
|
||||||
settings.lockDisabled = false;
|
settings.lockDisabled = false;
|
||||||
storedPatterns["settings"] = settings;
|
storedPatterns.settings = settings;
|
||||||
Bangle.setOptions({ lockTimeout: 1000 * 30 });
|
Bangle.setOptions({ lockTimeout: 1000 * 30 });
|
||||||
storage.writeJSON("ptlaunch.patterns.json", storedPatterns);
|
storage.writeJSON("ptlaunch.patterns.json", storedPatterns);
|
||||||
showMainMenu();
|
showMainMenu();
|
||||||
|
|
@ -106,7 +106,7 @@ var showMainMenu = () => {
|
||||||
} else {
|
} else {
|
||||||
settingsmenu["Disable lock"] = () => {
|
settingsmenu["Disable lock"] = () => {
|
||||||
settings.lockDisabled = true;
|
settings.lockDisabled = true;
|
||||||
storedPatterns["settings"] = settings;
|
storedPatterns.settings = settings;
|
||||||
storage.writeJSON("ptlaunch.patterns.json", storedPatterns);
|
storage.writeJSON("ptlaunch.patterns.json", storedPatterns);
|
||||||
Bangle.setOptions({ lockTimeout: 1000 * 60 * 60 * 24 * 365 });
|
Bangle.setOptions({ lockTimeout: 1000 * 60 * 60 * 24 * 365 });
|
||||||
showMainMenu();
|
showMainMenu();
|
||||||
|
|
@ -119,9 +119,7 @@ var showMainMenu = () => {
|
||||||
E.showMenu(mainmenu);
|
E.showMenu(mainmenu);
|
||||||
};
|
};
|
||||||
|
|
||||||
var drawCircle = (circle) => {
|
showMainMenu();
|
||||||
g.fillCircle(circle.x, circle.y, CIRCLE_RADIUS);
|
|
||||||
};
|
|
||||||
|
|
||||||
var positions = [];
|
var positions = [];
|
||||||
var createPattern = () => {
|
var createPattern = () => {
|
||||||
|
|
@ -160,7 +158,10 @@ var createPattern = () => {
|
||||||
// This might actually be a 'tap' event.
|
// This might actually be a 'tap' event.
|
||||||
// Use this check in addition to the actual tap handler to make it more reliable
|
// Use this check in addition to the actual tap handler to make it more reliable
|
||||||
if (pattern.length > 0 && positions.length === 2) {
|
if (pattern.length > 0 && positions.length === 2) {
|
||||||
if (positions[0].x === positions[1].x && positions[0].y === positions[1].y) {
|
if (
|
||||||
|
positions[0].x === positions[1].x &&
|
||||||
|
positions[0].y === positions[1].y
|
||||||
|
) {
|
||||||
finishHandler();
|
finishHandler();
|
||||||
positions = [];
|
positions = [];
|
||||||
return;
|
return;
|
||||||
|
|
@ -354,54 +355,217 @@ var getSelectedApp = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var getStoredPatternViaApp = (storedPatterns) => {
|
//////
|
||||||
E.showMessage("Loading patterns...");
|
// manage pattern related variables and functions
|
||||||
log("getStoredPatternViaApp");
|
// - draws all saved patterns and their linked app names
|
||||||
|
// - uses the scroller to allow the user to browse through them
|
||||||
|
//////
|
||||||
|
|
||||||
|
var scrollerFont = g.getFonts().includes("12x20") ? "12x20" : "6x8:2";
|
||||||
|
|
||||||
|
var drawBackButton = (r) => {
|
||||||
|
g.clearRect(r.x, r.y, r.x + r.w - 1, r.y + r.h - 1);
|
||||||
|
g.setFont(scrollerFont)
|
||||||
|
.setFontAlign(-1, 0)
|
||||||
|
.drawString("< Back", 64, r.y + 32);
|
||||||
|
};
|
||||||
|
|
||||||
|
var drawAppWithPattern = (i, r, storedPatterns) => {
|
||||||
|
log("draw app with pattern");
|
||||||
|
log({ i: i, r: r, storedPatterns: storedPatterns });
|
||||||
|
var storedPattern = storedPatterns[i];
|
||||||
|
var pattern = storedPattern.pattern;
|
||||||
|
var app = storedPattern.app;
|
||||||
|
|
||||||
|
g.clearRect(r.x, r.y, r.x + r.w - 1, r.y + r.h - 1);
|
||||||
|
|
||||||
|
g.drawLine(r.x, r.y, 176, r.y);
|
||||||
|
|
||||||
|
drawCirclesWithPattern(pattern, 0.33, { x: 1, y: 3 + r.y });
|
||||||
|
|
||||||
|
g.setColor(0, 0, 0);
|
||||||
|
var appName = g.wrapString(app.name, g.getWidth() - 64).join("\n");
|
||||||
|
g.setFont(scrollerFont)
|
||||||
|
.setFontAlign(-1, 0)
|
||||||
|
.drawString(appName, 64, r.y + 32);
|
||||||
|
};
|
||||||
|
|
||||||
|
var showScrollerContainingAppsWithPatterns = () => {
|
||||||
|
var storedPatternsArray = getStoredPatternsArray();
|
||||||
|
log("drawing scroller for stored patterns");
|
||||||
|
log(storedPatternsArray);
|
||||||
|
log(storedPatternsArray.length);
|
||||||
|
|
||||||
|
g.clear();
|
||||||
|
|
||||||
|
var c = Math.max(storedPatternsArray.length + 1, 3);
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
var selectPatternMenu = {
|
E.showScroller({
|
||||||
"": {
|
h: 64,
|
||||||
title: "Select App",
|
c: c,
|
||||||
|
draw: (i, r) => {
|
||||||
|
log("draw");
|
||||||
|
log({ i: i, r: r });
|
||||||
|
if (i <= 0) {
|
||||||
|
drawBackButton(r);
|
||||||
|
} else if (i <= storedPatternsArray.length) {
|
||||||
|
drawAppWithPattern(i - 1, r, storedPatternsArray);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"< Cancel": () => {
|
select: (i) => {
|
||||||
log("cancel");
|
log("selected: " + i);
|
||||||
showMainMenu();
|
var pattern = "back";
|
||||||
|
var appName = "";
|
||||||
|
if (i > 0) {
|
||||||
|
var storedPattern = storedPatternsArray[i - 1];
|
||||||
|
pattern = storedPattern.pattern.join("");
|
||||||
|
appName = storedPattern.app.name;
|
||||||
|
}
|
||||||
|
resolve({ pattern: pattern, appName: appName });
|
||||||
},
|
},
|
||||||
};
|
|
||||||
|
|
||||||
log(storedPatterns);
|
|
||||||
var patterns = Object.keys(storedPatterns);
|
|
||||||
log(patterns);
|
|
||||||
|
|
||||||
patterns.forEach((pattern) => {
|
|
||||||
if (pattern) {
|
|
||||||
if (storedPatterns[pattern]) {
|
|
||||||
var app = storedPatterns[pattern].app;
|
|
||||||
if (!!app && !!app.name) {
|
|
||||||
var appName = app.name;
|
|
||||||
var i = 0;
|
|
||||||
while (appName in selectPatternMenu[app.name]) {
|
|
||||||
appName = app.name + i;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
selectPatternMenu[appName] = () => {
|
|
||||||
log("pattern via app selected");
|
|
||||||
log(pattern);
|
|
||||||
log(app);
|
|
||||||
resolve(pattern);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
E.showMenu(selectPatternMenu);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
showMainMenu();
|
//////
|
||||||
|
// storage related functions:
|
||||||
|
// - stored patterns
|
||||||
|
// - stored settings
|
||||||
|
//////
|
||||||
|
|
||||||
|
var getStoredPatternsMap = () => {
|
||||||
|
log("loading stored patterns map");
|
||||||
|
var storedPatternsMap = storage.readJSON("ptlaunch.patterns.json", 1) || {};
|
||||||
|
delete storedPatternsMap.settings;
|
||||||
|
log(storedPatternsMap);
|
||||||
|
return storedPatternsMap;
|
||||||
|
};
|
||||||
|
|
||||||
|
var getStoredPatternsArray = () => {
|
||||||
|
var storedPatternsMap = getStoredPatternsMap();
|
||||||
|
log("converting stored patterns map to array");
|
||||||
|
var patterns = Object.keys(storedPatternsMap);
|
||||||
|
var storedPatternsArray = [];
|
||||||
|
for (var i = 0; i < patterns.length; i++) {
|
||||||
|
var pattern = "" + patterns[i];
|
||||||
|
storedPatternsArray.push({
|
||||||
|
pattern: pattern
|
||||||
|
.split("")
|
||||||
|
.map((circleIndex) => parseInt(circleIndex, 10)),
|
||||||
|
app: storedPatternsMap[pattern].app,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
log(storedPatternsArray);
|
||||||
|
return storedPatternsArray;
|
||||||
|
};
|
||||||
|
|
||||||
//////
|
//////
|
||||||
// lib functions
|
// circle related variables and functions:
|
||||||
|
// - the circle array itself
|
||||||
|
// - the radius and the squared radius of the circles
|
||||||
|
// - circle draw function
|
||||||
|
//////
|
||||||
|
|
||||||
|
var CIRCLE_RADIUS = 25;
|
||||||
|
var CIRCLE_RADIUS_2 = CIRCLE_RADIUS * CIRCLE_RADIUS;
|
||||||
|
|
||||||
|
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 },
|
||||||
|
];
|
||||||
|
|
||||||
|
var drawCircle = (circle, drawBuffer, scale) => {
|
||||||
|
if (!drawBuffer) {
|
||||||
|
drawBuffer = g;
|
||||||
|
}
|
||||||
|
if (!scale) {
|
||||||
|
scale = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var x = circle.x * scale;
|
||||||
|
var y = circle.y * scale;
|
||||||
|
var r = CIRCLE_RADIUS * scale;
|
||||||
|
|
||||||
|
log("drawing circle");
|
||||||
|
log({ x: x, y: y, r: r });
|
||||||
|
|
||||||
|
drawBuffer.fillCircle(x, y, r);
|
||||||
|
};
|
||||||
|
|
||||||
|
var cachedCirclesDrawings = {};
|
||||||
|
var drawCirclesWithPattern = (pattern, scale, offset) => {
|
||||||
|
if (!pattern || pattern.length === 0) {
|
||||||
|
pattern = [];
|
||||||
|
}
|
||||||
|
if (!scale) {
|
||||||
|
scale = 1;
|
||||||
|
}
|
||||||
|
if (!offset) {
|
||||||
|
offset = { x: 0, y: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
log("drawing circles with pattern, scale and offset");
|
||||||
|
log(pattern);
|
||||||
|
log(scale);
|
||||||
|
log(offset);
|
||||||
|
|
||||||
|
// cache drawn patterns. especially useful for the manage pattern menu
|
||||||
|
var image = cachedCirclesDrawings[pattern.join("")];
|
||||||
|
if (!image) {
|
||||||
|
var drawBuffer = Graphics.createArrayBuffer(
|
||||||
|
g.getWidth() * scale,
|
||||||
|
g.getHeight() * scale,
|
||||||
|
1,
|
||||||
|
{ msb: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
drawBuffer.setColor(1);
|
||||||
|
CIRCLES.forEach((circle) => drawCircle(circle, drawBuffer, scale));
|
||||||
|
|
||||||
|
drawBuffer.setColor(0);
|
||||||
|
drawBuffer.setFontAlign(0, 0);
|
||||||
|
drawBuffer.setFont("6x8", 4 * scale);
|
||||||
|
pattern.forEach((circleIndex, patternIndex) => {
|
||||||
|
var circle = CIRCLES[circleIndex];
|
||||||
|
drawBuffer.drawString(
|
||||||
|
patternIndex + 1,
|
||||||
|
circle.x * scale,
|
||||||
|
circle.y * scale
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
image = {
|
||||||
|
width: drawBuffer.getWidth(),
|
||||||
|
height: drawBuffer.getHeight(),
|
||||||
|
bpp: 1,
|
||||||
|
buffer: drawBuffer.buffer,
|
||||||
|
};
|
||||||
|
cachedCirclesDrawings[pattern.join("")] = image;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
//////
|
//////
|
||||||
|
|
||||||
var debounceTimeoutId;
|
var debounceTimeoutId;
|
||||||
|
|
@ -417,13 +581,3 @@ var debounce = (delay) => {
|
||||||
}, delay || 500);
|
}, delay || 500);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var cloneCirclesArray = () => {
|
|
||||||
var circlesClone = Array(CIRCLES.length);
|
|
||||||
|
|
||||||
for (var i = 0; i < CIRCLES.length; i++) {
|
|
||||||
circlesClone[i] = CIRCLES[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return circlesClone;
|
|
||||||
};
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
Loading…
Reference in New Issue