From b9f636d9dbcd7a58df5b23c0eeb9e2a020e02aec Mon Sep 17 00:00:00 2001 From: crazysaem Date: Wed, 8 Dec 2021 16:37:42 +0000 Subject: [PATCH 1/3] Create Pattern Launcher app --- apps.json | 18 +- apps/ptlaunch/ChangeLog | 1 + apps/ptlaunch/README.md | 46 ++++ apps/ptlaunch/add_pattern.png | Bin 0 -> 2642 bytes apps/ptlaunch/app-icon.js | 1 + apps/ptlaunch/app.js | 416 ++++++++++++++++++++++++++++++++++ apps/ptlaunch/app.png | Bin 0 -> 1094 bytes apps/ptlaunch/boot.js | 194 ++++++++++++++++ apps/ptlaunch/main_menu.png | Bin 0 -> 2840 bytes apps/ptlaunch/select_app.png | Bin 0 -> 2551 bytes 10 files changed, 675 insertions(+), 1 deletion(-) create mode 100644 apps/ptlaunch/ChangeLog create mode 100644 apps/ptlaunch/README.md create mode 100644 apps/ptlaunch/add_pattern.png create mode 100644 apps/ptlaunch/app-icon.js create mode 100644 apps/ptlaunch/app.js create mode 100644 apps/ptlaunch/app.png create mode 100644 apps/ptlaunch/boot.js create mode 100644 apps/ptlaunch/main_menu.png create mode 100644 apps/ptlaunch/select_app.png diff --git a/apps.json b/apps.json index e5d6dad8a..89b46e301 100644 --- a/apps.json +++ b/apps.json @@ -4800,6 +4800,22 @@ "screenshots":[ { "url":"screenshot.png" } ] + }, + { + "id": "ptlaunch", + "name": "Pattern Launcher", + "shortName": "Pattern Launcher", + "version": "0.01", + "description": "Directly launch apps from the clock screen with custom patterns.", + "icon": "app.png", + "tags": "tools", + "supports": ["BANGLEJS2"], + "readme": "README.md", + "storage": [ + { "name": "ptlaunch.app.js", "url": "app.js" }, + { "name": "ptlaunch.boot.js", "url": "boot.js" }, + { "name": "ptlaunch.img", "url": "app-icon.js", "evaluate": true } + ], + "data": [{"name":"ptlaunch.patterns.json"}] } - ] diff --git a/apps/ptlaunch/ChangeLog b/apps/ptlaunch/ChangeLog new file mode 100644 index 000000000..4967d3207 --- /dev/null +++ b/apps/ptlaunch/ChangeLog @@ -0,0 +1 @@ +0.01: Initial creation of the pattern launch app \ No newline at end of file diff --git a/apps/ptlaunch/README.md b/apps/ptlaunch/README.md new file mode 100644 index 000000000..a69492782 --- /dev/null +++ b/apps/ptlaunch/README.md @@ -0,0 +1,46 @@ +# Pattern Launcher + +Directly launch apps from the clock screen with custom patterns. + +## Usage + +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. + +## Screenshots and detailed steps + +![](main_menu.png) +![](add_pattern.png) +![](select_app.png) + +From the main menu you can: +- Add a new pattern and link it to an app (first entry) + - 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 + - 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 + - Now select the app you want to launch with the pattern. + - Note, you can bind multiple patterns to the same app. +- Remove linked patterns (second entry) + - To remove a pattern first select "Remove Pattern" + - You will now see a list of apps that have patterns linked to them + - Simply select the app that you want to unlink. This will remove the saved pattern, but not the app itself! + - 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) + - 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 + +## FAQ + +1) Nothing happens when I draw on the clock screen! + +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! + +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! + +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! diff --git a/apps/ptlaunch/add_pattern.png b/apps/ptlaunch/add_pattern.png new file mode 100644 index 0000000000000000000000000000000000000000..c7cc38e82c25b4661bc026ce7dd5901c77fb9f7f GIT binary patch literal 2642 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 a2vKOb@;L{iZz}pE0_SX { + if (DEBUG) { + console.log(JSON.stringify(message)); + } +}; + +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 showMainMenu = () => { + log("loading patterns"); + var storedPatterns = storage.readJSON("ptlaunch.patterns.json", 1) || {}; + + var mainmenu = { + "": { + title: "Pattern Launcher", + }, + "< Back": () => { + log("cancel"); + load(); + }, + "Add Pattern": () => { + log("creating pattern"); + createPattern().then((pattern) => { + log("got pattern"); + log(pattern); + log(pattern.length); + + var confirmPromise = new Promise((resolve) => resolve(true)); + + if (!!storedPatterns[pattern]) { + log("pattern already exists. show confirmation prompt"); + confirmPromise = E.showPrompt("Pattern already exists\nOverwrite?", { + title: "Confirm", + buttons: { Yes: true, No: false }, + }); + } + + confirmPromise.then((confirm) => { + log("confirmPromise resolved: " + confirm); + if (!confirm) { + showMainMenu(); + return; + } + + log("selecting app"); + getSelectedApp().then((app) => { + E.showMessage("Saving..."); + log("got app"); + log("saving pattern"); + + storedPatterns[pattern] = { + app: { name: app.name, src: app.src }, + }; + storage.writeJSON("ptlaunch.patterns.json", storedPatterns); + showMainMenu(); + }); + }); + }); + }, + "Remove Pattern": () => { + log("selecting pattern through app"); + getStoredPatternViaApp(storedPatterns).then((pattern) => { + E.showMessage("Deleting..."); + delete storedPatterns[pattern]; + storage.writeJSON("ptlaunch.patterns.json", storedPatterns); + showMainMenu(); + }); + }, + Settings: () => { + var settings = storedPatterns["settings"] || {}; + + var settingsmenu = { + "": { + title: "Pattern Settings", + }, + "< Back": () => { + log("cancel"); + load(); + }, + }; + + if (settings.lockDisabled) { + settingsmenu["Enable lock"] = () => { + settings.lockDisabled = false; + storedPatterns["settings"] = settings; + Bangle.setOptions({ lockTimeout: 1000 * 30 }); + storage.writeJSON("ptlaunch.patterns.json", storedPatterns); + showMainMenu(); + }; + } else { + settingsmenu["Disable lock"] = () => { + settings.lockDisabled = true; + storedPatterns["settings"] = settings; + storage.writeJSON("ptlaunch.patterns.json", storedPatterns); + Bangle.setOptions({ lockTimeout: 1000 * 60 * 60 * 24 * 365 }); + showMainMenu(); + }; + } + + E.showMenu(settingsmenu); + }, + }; + E.showMenu(mainmenu); +}; + +var drawCircle = (circle) => { + g.fillCircle(circle.x, circle.y, CIRCLE_RADIUS); +}; + +var positions = []; +var createPattern = () => { + return new Promise((resolve) => { + E.showMenu(); + g.clear(); + g.setColor(0, 0, 0); + CIRCLES.forEach((circle) => drawCircle(circle)); + + var pattern = []; + + var isFinished = false; + var finishHandler = () => { + if (pattern.length === 0 || isFinished) { + return; + } + log("Pattern is finished."); + isFinished = true; + Bangle.removeListener("drag", dragHandler); + Bangle.removeListener("tap", finishHandler); + resolve(pattern.join("")); + }; + setWatch(() => finishHandler(), BTN); + setTimeout(() => Bangle.on("tap", finishHandler), 250); + + var dragHandler = (position) => { + positions.push(position); + + debounce().then(() => { + if (isFinished) { + 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.setColor(0, 0, 0); + CIRCLES.forEach((circle) => drawCircle(circle)); + + g.setColor(1, 1, 1); + g.setFontAlign(0, 0); + g.setFont("6x8", 4); + pattern.forEach((circleIndex, patternIndex) => { + var circle = CIRCLES[circleIndex]; + g.drawString(patternIndex + 1, circle.x, circle.y); + }); + var t2 = Date.now(); + log(t2 - t0); + }); + }; + + Bangle.on("drag", dragHandler); + }); +}; + +var getAppList = () => { + var appList = storage + .list(/\.info$/) + .map((appInfoFileName) => { + var appInfo = storage.readJSON(appInfoFileName, 1); + return ( + appInfo && { + name: appInfo.name, + // type: appInfo.type, + // icon: appInfo.icon, + sortorder: appInfo.sortorder, + src: appInfo.src, + } + ); + }) + .filter((app) => app && !!app.src); + appList.sort((a, b) => { + var n = (0 | a.sortorder) - (0 | b.sortorder); + if (n) return n; // do sortorder first + if (a.name < b.name) return -1; + if (a.name > b.name) return 1; + return 0; + }); + + return appList; +}; + +var getSelectedApp = () => { + E.showMessage("Loading apps..."); + return new Promise((resolve) => { + var selectAppMenu = { + "": { + title: "Select App", + }, + "< Cancel": () => { + log("cancel"); + showMainMenu(); + }, + }; + + var appList = getAppList(); + appList.forEach((app) => { + selectAppMenu[app.name] = () => { + log("app selected"); + log(app); + resolve(app); + }; + }); + + E.showMenu(selectAppMenu); + }); +}; + +var getStoredPatternViaApp = (storedPatterns) => { + E.showMessage("Loading patterns..."); + log("getStoredPatternViaApp"); + return new Promise((resolve) => { + var selectPatternMenu = { + "": { + title: "Select App", + }, + "< Cancel": () => { + log("cancel"); + showMainMenu(); + }, + }; + + 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(); + +////// +// lib functions +////// + +var debounceTimeoutId; +var debounce = (delay) => { + if (debounceTimeoutId) { + clearTimeout(debounceTimeoutId); + } + + return new Promise((resolve) => { + debounceTimeoutId = setTimeout(() => { + debounceTimeoutId = undefined; + resolve(); + }, delay || 500); + }); +}; + +var cloneCirclesArray = () => { + var circlesClone = Array(CIRCLES.length); + + for (var i = 0; i < CIRCLES.length; i++) { + circlesClone[i] = CIRCLES[i]; + } + + return circlesClone; +}; diff --git a/apps/ptlaunch/app.png b/apps/ptlaunch/app.png new file mode 100644 index 0000000000000000000000000000000000000000..14ed77f1d36f5f5c0b0c01f7da79c9b736431595 GIT binary patch literal 1094 zcmV-M1iAZ(P)Px#1ZP1_K>z@;j|==^1poj60#Hm;MgRZ*(b3UgUtb^~AOHXWCMG6jWo6sj+s4Mm zSy@>b85t}rENg3P=H}-8{rz@!b_NCp2nYy?iHZCB`(tBc1Ox;N3k#l}o{f!-prD`) z4GrPp;lRMa6B85i^70}gB1lL`aBy(U%*;18Hx(5XPEJmRg@r~&Mi&gxIV`Eqh{v9YmwdU~_7v)S3%FfcGfLqmgugYfY1 z_xJZfK|vlK9!yM30s;b$kB>h;KZ}ct?Ck7*etw6ChiPePTU%SHsi}B)c&n?cmX?;9 znwk#}55mI2U0q$dxw)8_m?|nNP*6~Afhs%z000SaNLh0L01m?d01m?e$8V@)0008b zNkl^bI~=1WhX z4>0d+CfV%H%!YuIagE95^44aS3Z5~`E2}Dr^0}-rZpLA;RxBCDFSDlgjZJY>w!}eY zUmT_48dOn)rOLw@RmN=A*ilspikOeRzbhBjw=ulKrm)+9W4o>jlpMa_tFj{+H=w4N zf_>(*DHxL>ysz8Z(||Tnqa&u!ML=6^#wGk@UiUO0APU7&A_N3#3pT)*Gyz!PML?fu zA6u|sfPlW*OqO!Syn&baQA7N9c|bXUW2iQhqr7h_fd%UD2;+M)!g$AU9383xKGLDA zHjsTfQH51MWk*dV;AE)k#vID^L}!`_osl|f`i#1y0&N%DLC_!C`3U3Ic@SJ&+HDq& zhW#LD7i}W^D;iYTU2;zm#tV3<60B1ZLUvKX2*>R>4Y@mL%9xc9|#0bW_$%OA(e}H4G+hW;V zqJo-C3**b%mo(p#+b<$q&wI469O)HNLi*8wU|qc4-kMqncG(wmpr7b z&}v0Z(6PHeR$*~% { + if (DEBUG) { + console.log(JSON.stringify(message)); + } +}; + +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 storedPatterns; +var positions = []; +var dragHandler = (position) => { + positions.push(position); + + debounce().then(() => { + log(positions.length); + + var circlesClone = cloneCirclesArray(); + var 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); + } + } + } + positions = []; + + pattern = pattern.join(""); + + if (!!pattern) { + if (!!storedPatterns[pattern]) { + var app = storedPatterns[pattern].app; + if (!!app && !!app.src) { + Bangle.removeListener("drag", dragHandler); + load(app.src); + } + } + } + }); +}; + +var debounceTimeoutId; +var debounce = (delay) => { + if (debounceTimeoutId) { + clearTimeout(debounceTimeoutId); + } + + return new Promise((resolve) => { + debounceTimeoutId = setTimeout(() => { + debounceTimeoutId = undefined; + resolve(); + }, delay || 500); + }); +}; + +var cloneCirclesArray = () => { + var circlesClone = Array(CIRCLES.length); + + for (var i = 0; i < CIRCLES.length; i++) { + circlesClone[i] = CIRCLES[i]; + } + + return circlesClone; +}; + +(function () { + var sui = Bangle.setUI; + Bangle.setUI = function (mode, cb) { + sui(mode, cb); + if (!mode) { + Bangle.removeListener("drag", dragHandler); + storedPatterns = {}; + return; + } + if (!mode.startsWith("clock")) { + storedPatterns = {}; + Bangle.removeListener("drag", dragHandler); + return; + } + + var storage = require("Storage"); + storedPatterns = storage.readJSON("ptlaunch.patterns.json", 1) || {}; + if (Object.keys(storedPatterns).length > 0) { + Bangle.on("drag", dragHandler); + if (storedPatterns.settings.lockDisabled) { + Bangle.setOptions({ lockTimeout: 1000 * 60 * 60 * 24 * 365 }); + } + } + }; +})(); diff --git a/apps/ptlaunch/main_menu.png b/apps/ptlaunch/main_menu.png new file mode 100644 index 0000000000000000000000000000000000000000..a4ecebb0f4ba586eba902bf1699e9b5f9dbe683a GIT binary patch literal 2840 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 Date: Wed, 8 Dec 2021 20:44:25 +0000 Subject: [PATCH 2/3] ptlaunch: fix undefined error on boot.js if a pattern was saved but no setting was defined --- apps/ptlaunch/boot.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/ptlaunch/boot.js b/apps/ptlaunch/boot.js index 2440c561f..f30cb2921 100644 --- a/apps/ptlaunch/boot.js +++ b/apps/ptlaunch/boot.js @@ -186,8 +186,10 @@ var cloneCirclesArray = () => { storedPatterns = storage.readJSON("ptlaunch.patterns.json", 1) || {}; if (Object.keys(storedPatterns).length > 0) { Bangle.on("drag", dragHandler); - if (storedPatterns.settings.lockDisabled) { - Bangle.setOptions({ lockTimeout: 1000 * 60 * 60 * 24 * 365 }); + if (storedPatterns.settings) { + if (storedPatterns.settings.lockDisabled) { + Bangle.setOptions({ lockTimeout: 1000 * 60 * 60 * 24 * 365 }); + } } } }; From bbef0ded3f2fccb8e430a80c95d1e9c2ecb5b3b5 Mon Sep 17 00:00:00 2001 From: crazysaem Date: Wed, 8 Dec 2021 20:52:46 +0000 Subject: [PATCH 3/3] ptlaunch: fix eslint errors --- apps/ptlaunch/app.js | 6 +++--- apps/ptlaunch/boot.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/ptlaunch/app.js b/apps/ptlaunch/app.js index 07b471273..8ba1adf81 100644 --- a/apps/ptlaunch/app.js +++ b/apps/ptlaunch/app.js @@ -43,7 +43,7 @@ var showMainMenu = () => { var confirmPromise = new Promise((resolve) => resolve(true)); - if (!!storedPatterns[pattern]) { + if (storedPatterns[pattern]) { log("pattern already exists. show confirmation prompt"); confirmPromise = E.showPrompt("Pattern already exists\nOverwrite?", { title: "Confirm", @@ -360,8 +360,8 @@ var getStoredPatternViaApp = (storedPatterns) => { log(patterns); patterns.forEach((pattern) => { - if (!!pattern) { - if (!!storedPatterns[pattern]) { + if (pattern) { + if (storedPatterns[pattern]) { var app = storedPatterns[pattern].app; if (!!app && !!app.name) { var appName = app.name; diff --git a/apps/ptlaunch/boot.js b/apps/ptlaunch/boot.js index f30cb2921..1433f1700 100644 --- a/apps/ptlaunch/boot.js +++ b/apps/ptlaunch/boot.js @@ -131,8 +131,8 @@ var dragHandler = (position) => { pattern = pattern.join(""); - if (!!pattern) { - if (!!storedPatterns[pattern]) { + if (pattern) { + if (storedPatterns[pattern]) { var app = storedPatterns[pattern].app; if (!!app && !!app.src) { Bangle.removeListener("drag", dragHandler);