diff --git a/apps/runplus/ChangeLog b/apps/runplus/ChangeLog index 8e9b7385b..4016a2463 100644 --- a/apps/runplus/ChangeLog +++ b/apps/runplus/ChangeLog @@ -26,3 +26,4 @@ Write to correct settings file, fixing settings not working. 0.23: Minor code improvements 0.24: Add indicators for lock,gps and pulse to karvonen screen 0.25: Fix step count bug when runs are resumed after a long time +0.26: Add ability to zoom in on a single stat by tapping it diff --git a/apps/runplus/README.md b/apps/runplus/README.md index ddcf34cb7..d930b55e5 100644 --- a/apps/runplus/README.md +++ b/apps/runplus/README.md @@ -4,6 +4,8 @@ Displays distance, time, steps, cadence, pace and heart rate for runners. Based It requires the input of your minimum and maximum heart rate in the settings for the app to work. You can come back back to the initial run screen anytime by swimping left. To use it, start the app and press the middle button so that the red STOP in the bottom right turns to a green `RUN`. +To focus on a single stat, tap on the stat and it will take up the full screen. Tap again to return to the main screen. + ## Display 1st screen * `DIST` - the distance travelled based on the GPS (if you have a GPS lock). diff --git a/apps/runplus/app.js b/apps/runplus/app.js index 6a9d39346..3054ffa0c 100644 --- a/apps/runplus/app.js +++ b/apps/runplus/app.js @@ -1,5 +1,5 @@ let runInterval; -let karvonenActive = false; +let screen = "main"; // main | karvonen | menu | zoom // Run interface wrapped in a function const ExStats = require("exstats"); let B2 = process.env.HWVERSION===2; @@ -7,9 +7,10 @@ let Layout = require("Layout"); let locale = require("locale"); let fontHeading = "6x8:2"; let fontValue = B2 ? "6x15:2" : "6x8:3"; +let zoomFont = "12x20:3"; +let zoomFontSmall = "12x20:2"; let headingCol = "#888"; let fixCount = 0; -let isMenuDisplayed = false; const wu = require("widget_utils"); g.reset().clear(); @@ -52,11 +53,16 @@ function setStatus(running) { layout.button.label = running ? "STOP" : "START"; layout.status.label = running ? "RUN" : "STOP"; layout.status.bgCol = running ? "#0f0" : "#f00"; - layout.render(); + if (screen === "main") layout.render(); } // Called to start/stop running function onStartStop() { + if (screen === "karvonen") { + // start/stop on the karvonen screen reverts us to the main screen + setScreen("main"); + } + var running = !exs.state.active; var shouldResume = false; var promise = Promise.resolve(); @@ -64,10 +70,10 @@ function onStartStop() { if (running && exs.state.duration > 10000) { // if more than 10 seconds of duration, ask if we should resume? promise = promise. then(() => { - isMenuDisplayed = true; + screen = "menu"; return E.showPrompt("Resume run?",{title:"Run"}); }).then(r => { - isMenuDisplayed=false; + screen = "main"; layout.setUI(); // grab our input handling again layout.forgetLazyState(); layout.render(); @@ -80,11 +86,11 @@ function onStartStop() { // an overwrite before we start tracking exstats if (settings.record && WIDGETS["recorder"]) { if (running) { - isMenuDisplayed = true; + screen = "menu"; promise = promise. then(() => WIDGETS["recorder"].setRecording(true, { force : shouldResume?"append":undefined })). then(() => { - isMenuDisplayed = false; + screen = "main"; layout.setUI(); // grab our input handling again layout.forgetLazyState(); layout.render(); @@ -99,7 +105,7 @@ function onStartStop() { promise.then(() => { if (running) { if (shouldResume) - exs.resume() + exs.resume(); else exs.start(); } else { @@ -111,23 +117,68 @@ function onStartStop() { }); } +function zoom(statID) { + if (screen !== "main") return; + + setScreen("zoom"); + + const onTouch = () => { + Bangle.removeListener("touch", onTouch); + Bangle.removeListener("twist", onTwist); + stat.removeListener("changed", draw); + setScreen("main"); + }; + Bangle.on("touch", onTouch); // queued after layout's touchHandler (otherwise we'd be removed then instantly re-zoomed) + + const onTwist = () => { + Bangle.setLCDPower(1); + }; + Bangle.on("twist", onTwist); + + const draw = stat => { + const R = Bangle.appRect; + + g.reset() + .clearRect(R) + .setFontAlign(0, 0); + + layout.render(layout.bottom); + + const value = exs.state.active ? stat.getString() : "____"; + + g + .setFont(stat.title.length > 5 ? zoomFontSmall : zoomFont) + .setColor(headingCol) + .drawString(stat.title.toUpperCase(), R.x+R.w/2, R.y+R.h/3) + .setColor(g.theme.fg) + .drawString(value, R.x+R.w/2, R.y+R.h*2/3); + }; + layout.lazy = false; // restored when we go back to "main" + + const stat = exs.stats[statID]; + stat.on("changed", draw); + draw(stat); +} + let lc = []; // Load stats in pair by pair for (let i=0;ilayout[e.id].label = e.getString()); if (sb) sb.on('changed', e=>layout[e.id].label = e.getString()); } // At the bottom put time/GPS state/etc -lc.push({ type:"h", filly:1, c:[ +lc.push({ type:"h", id:"bottom", filly:1, c:[ {type:"txt", font:fontHeading, label:"GPS", id:"gps", fillx:1, bgCol:"#f00" }, {type:"txt", font:fontHeading, label:"00:00", id:"clock", fillx:1, bgCol:g.theme.fg, col:g.theme.bg }, {type:"txt", font:fontHeading, label:"---", id:"status", fillx:1 } @@ -135,7 +186,7 @@ lc.push({ type:"h", filly:1, c:[ // Now calculate the layout let layout = new Layout( { type:"v", c: lc -},{lazy:true, btns:[{ label:"---", cb: (()=>{if (karvonenActive) {run();} onStartStop();}), id:"button"}]}); +},{lazy:true, btns:[{ label:"---", cb: onStartStop, id:"button"}]}); delete lc; setStatus(exs.state.active); layout.render(); @@ -165,47 +216,49 @@ Bangle.on("GPS", function(fix) { } }); -// run() function used to start updating traditional run ui -function run() { - require("runplus_karvonen").stop(); - karvonenActive = false; - wu.show(); - Bangle.drawWidgets(); - g.reset().clearRect(Bangle.appRect); - layout.lazy = false; - layout.render(); - layout.lazy = true; - // We always call ourselves once a second to update - if (!runInterval){ - runInterval = setInterval(function() { - layout.clock.label = locale.time(new Date(),1); - if (!isMenuDisplayed) layout.render(); - }, 1000); +function setScreen(to) { + if (screen === "karvonen") { + require("runplus_karvonen").stop(); + wu.show(); + Bangle.drawWidgets(); } -} -run(); -/////////////////////////////////////////////// -// Karvonen -/////////////////////////////////////////////// - -function karvonen(){ - // stop updating and drawing the traditional run app UI if (runInterval) clearInterval(runInterval); runInterval = undefined; g.reset().clearRect(Bangle.appRect); - require("runplus_karvonen").start(settings.HRM, exs.stats.bpm); - karvonenActive = true; + + screen = to; + switch (screen) { + case "main": + layout.lazy = false; + layout.render(); + layout.lazy = true; + // We always call ourselves once a second to update + if (!runInterval){ + runInterval = setInterval(function() { + layout.clock.label = locale.time(new Date(),1); + if (screen !== "menu") layout.render(); + }, 1000); + } + break; + + case "karvonen": + require("runplus_karvonen").start(settings.HRM, exs.stats.bpm); + break; + } } // Define the function to go back and forth between the different UI's function swipeHandler(LR,_) { - if (!isMenuDisplayed){ - if (LR==-1 && karvonenActive) - run(); - if (LR==1 && !karvonenActive) - karvonen(); + if (screen !== "menu"){ + if (LR < 0 && screen == "karvonen") + setScreen("main"); + if (LR > 0 && screen !== "karvonen") + setScreen("karvonen"); // stop updating and drawing the traditional run app UI } } + +setScreen("main"); + // Listen for swipes with the swipeHandler Bangle.on("swipe", swipeHandler); diff --git a/apps/runplus/metadata.json b/apps/runplus/metadata.json index 415194f7a..67e44cf2a 100644 --- a/apps/runplus/metadata.json +++ b/apps/runplus/metadata.json @@ -1,8 +1,8 @@ { "id": "runplus", "name": "Run+", - "version": "0.25", - "description": "Displays distance, time, steps, cadence, pace and more for runners. Based on the Run app, but extended with additional screen for heart rate interval training.", + "version": "0.26", + "description": "Displays distance, time, steps, cadence, pace and more for runners. Based on the Run app, but extended with additional screens for heart rate interval training and individual stat focus.", "icon": "app.png", "tags": "run,running,fitness,outdoors,gps,karvonen,karvonnen", "supports": ["BANGLEJS2"],