Move changes to smpltmr

master
sir-indy 2022-05-01 18:02:15 +01:00
parent f60d0f22a2
commit 2e8744ca22
13 changed files with 159 additions and 335 deletions

View File

@ -1 +1,2 @@
0.01: Release 0.01: Release
0.02: Rewrite with new interface

View File

@ -1,21 +1,12 @@
# Simple Timer # Simple Timer
A simple app to set a timer quickly. Simply tab on top/bottom/left/right A simple app to set a timer quickly. Drag or tap on the up and down buttons over the hour, minute or second to set the time.
to select the minutes and tab in the middle of the screen to start/stop
the timer. Note that this timer depends on qalarm.
# Overview
If you open the app, you can simply control the timer
by clicking on top, bottom, left or right of the screen.
If you tab at the middle of the screen, the timer is
started / stopped.
![](description.png)
This app uses the `sched` library, which allows the timer to continue to run in the background when this app is closed.
# Creator # Creator
[David Peer](https://github.com/peerdavid) [David Peer](https://github.com/peerdavid)
[Sir Indy](https://github.com/sir-indy)
# Thanks to... # Thanks to...
Time icon created by <a href="https://www.flaticon.com/free-icons/time" title="time icons">CreativeCons - Flaticon</a> Time icon created by <a href="https://www.flaticon.com/free-icons/time" title="time icons">CreativeCons - Flaticon</a>

View File

@ -1,124 +1,173 @@
/* const secondsToTime = (s) => new Object({h:Math.floor((s/3600) % 24), m:Math.floor((s/60) % 60), s:Math.floor(s % 60)});
* SIMPLE TIMER const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
* function formatTime(s) {
* Creator: David Peer var t = secondsToTime(s);
* Date: 02/2022 if (t.h) {
*/ return t.h + ':' + ("0" + t.m).substr(-2) + ':' + ("0" + t.s).substr(-2);
} else {
return t.m + ':' + ("0" + t.s).substr(-2);
}
}
const timerID = "simpletimer";
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets();
var Layout = require("Layout");
var seconds = 5 * 60; // Default to 5 minutes
var drawTimeout;
var imgArrow = Graphics.createImage(`
x
xxx
xxx
xxxxx
xxxxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
`);
const alarm = require("sched"); const imgPause = atob("GBiBAP+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B/w==");
const imgPlay = atob("GBiBAIAAAOAAAPgAAP4AAP+AAP/gAP/4AP/+AP//gP//4P//+P///v///v//+P//4P//gP/+AP/4AP/gAP+AAP4AAPgAAOAAAIAAAA==");
const TIMER_IDX = "smpltmr"; function onDrag(event) {
const screenWidth = g.getWidth(); Bangle.buzz(20, 0.3);
const screenHeight = g.getHeight(); var diff = -Math.round(event.dy/5);
const cx = parseInt(screenWidth/2); if (event.x < timePickerLayout.hours.w) {
const cy = parseInt(screenHeight/2)-12; diff *= 3600;
var minutes = 5; } else if (event.x > timePickerLayout.mins.x && event.x < timePickerLayout.secs.x) {
var interval; //used for the 1 second interval timer diff *= 60;
function isTimerEnabled(){
var alarmObj = alarm.getAlarm(TIMER_IDX);
if(alarmObj===undefined || !alarmObj.on){
return false;
} }
updateTimePicker(diff);
return true;
} }
function getTimerMin(){ function onTouch(button, xy) {
var alarmObj = alarm.getAlarm(TIMER_IDX); var touchMidpoint = timePickerLayout.hours.y + timePickerLayout.hours.h/2;
return Math.round(alarm.getTimeToAlarm(alarmObj)/(60*1000)); var diff = 0;
if (xy.y > 24 && xy.y < touchMidpoint - 10) {
Bangle.buzz(40, 0.3);
diff = 1;
} else if (xy.y > touchMidpoint + 10 && xy.y < timePickerLayout.btnStart.y) {
Bangle.buzz(40, 0.3);
diff = -1;
} else if (xy.y > timePickerLayout.btnStart.y) {
Bangle.buzz(40, 0.6);
runTimer();
return;
}
if (xy.x < timePickerLayout.hours.w) {
diff *= 3600;
} else if (xy.x > timePickerLayout.mins.x && xy.x < timePickerLayout.secs.x) {
diff *= 60;
}
updateTimePicker(diff);
} }
function setTimer(minutes){ function onButton() {
alarm.setAlarm(TIMER_IDX, { var timeToNext = require("sched").getTimeToAlarm(require("sched").getAlarm(timerID));
// msg : "Simple Timer", g.clearRect(Bangle.appRect);
timer : minutes*60*1000, if (timeToNext != undefined) {
runTimer();
} else {
runTimePicker();
}
}
function updateTimePicker(diff) {
seconds = clamp(seconds + (diff || 0), 0, 24 * 3600 - 1);
var set_time = secondsToTime(seconds);
updateLayoutField(timePickerLayout, 'hours', set_time.h);
updateLayoutField(timePickerLayout, 'mins', set_time.m);
updateLayoutField(timePickerLayout, 'secs', set_time.s);
}
function updateLayoutField(layout, field, value) {
layout.clear(layout[field]);
layout[field].label = value;
layout.render(layout[field]);
}
function updateTimer() {
var timeToNext = require("sched").getTimeToAlarm(require("sched").getAlarm(timerID));
updateLayoutField(timerLayout, 'timer', formatTime(timeToNext / 1000));
queueDraw(1000);
}
function queueDraw(millisecs) {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() {
drawTimeout = undefined;
updateTimer();
}, millisecs - (Date.now() % millisecs));
}
function timerStop() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
seconds = require("sched").getTimeToAlarm(require("sched").getAlarm(timerID)) / 1000;
require("sched").setAlarm(timerID, undefined);
require("sched").reload();
runTimePicker();
}
function runTimePicker() {
g.clearRect(Bangle.appRect);
Bangle.setUI({
mode : "custom",
touch : function(n,e) {onTouch(n,e);},
drag : function(e) {onDrag(e);},
btn : function(n) {onButton();},
}); });
alarm.reload(); timePickerLayout.render();
updateTimePicker();
//timePickerLayout.debug();
} }
function deleteTimer(){ function runTimer() {
alarm.setAlarm(TIMER_IDX, undefined); require("sched").setAlarm(timerID, {
alarm.reload(); vibrate : ".-.-",
hidden: true,
timer : seconds * 1000
});
require("sched").reload();
g.clearRect(Bangle.appRect);
timerLayout.render();
updateTimer();
} }
setWatch(_=>load(), BTN1); var timePickerLayout = new Layout({
function draw(){ type:"v", c: [
g.clear(1); {type:undefined, height:2},
Bangle.drawWidgets(); {type:"h", c: [
{type:"v", width:g.getWidth()/3, c: [
if (interval) { {type:"txt", font:"6x8", label:/*LANG*/"Hours", col:g.theme.fg2},
clearInterval(interval); {type:"img", pad:8, src:imgArrow, col:g.theme.fg2},
} {type:"txt", font:"20%", label:"00", id:"hours", filly:1, fillx:1},
interval = undefined; {type:"img", pad:8, src:imgArrow, col:g.theme.fg2, r:2}
]},
// Write time {type:"v", width:g.getWidth()/3, c: [
g.setFontAlign(0, 0, 0); {type:"txt", font:"6x8", label:/*LANG*/"Minutes", col:g.theme.fg2},
g.setFont("Vector", 32).setFontAlign(0,-1); {type:"img", pad:8, src:imgArrow, col:g.theme.fg2},
{type:"txt", font:"20%", label:"00", id:"mins", filly:1, fillx:1},
var started = isTimerEnabled(); {type:"img", pad:8, src:imgArrow, col:g.theme.fg2, r:2}
var text = minutes + " min."; ]},
if(started){ {type:"v", width:g.getWidth()/3, c: [
var min = getTimerMin(); {type:"txt", font:"6x8", label:/*LANG*/"Seconds", col:g.theme.fg2},
text = min + " min."; {type:"img", pad:8, src:imgArrow, col:g.theme.fg2},
} {type:"txt", font:"20%", label:"00", id:"secs", filly:1, fillx:1},
{type:"img", pad:8, src:imgArrow, col:g.theme.fg2, r:2}
var rectWidth = parseInt(g.stringWidth(text) / 2); ]},
]},
if(started){ {type:"btn", src:imgPlay, id:"btnStart", fillx:1 }
interval = setInterval(draw, 1000); ], filly:1
g.setColor("#ff0000");
} else {
g.setColor(g.theme.fg);
}
g.fillRect(cx-rectWidth-5, cy-5, cx+rectWidth, cy+30);
g.setColor(g.theme.bg);
g.drawString(text, cx, cy);
}
Bangle.on('touch', function(btn, e){
var left = parseInt(g.getWidth() * 0.25);
var right = g.getWidth() - left;
var upper = parseInt(g.getHeight() * 0.25);
var lower = g.getHeight() - upper;
var isLeft = e.x < left;
var isRight = e.x > right;
var isUpper = e.y < upper;
var isLower = e.y > lower;
var isMiddle = !isLeft && !isRight && !isUpper && !isLower;
var started = isTimerEnabled();
if(isRight && !started){
minutes += 1;
Bangle.buzz(40, 0.3);
} else if(isLeft && !started){
minutes -= 1;
Bangle.buzz(40, 0.3);
} else if(isUpper && !started){
minutes += 5;
Bangle.buzz(40, 0.3);
} else if(isLower && !started){
minutes -= 5;
Bangle.buzz(40, 0.3);
} else if(isMiddle) {
if(!started){
setTimer(minutes);
} else {
deleteTimer();
}
Bangle.buzz(80, 0.6);
}
minutes = Math.max(0, minutes);
draw();
}); });
g.reset(); var timerLayout = new Layout({
draw(); type:"v", c: [
{type:"txt", font:"22%", label:"0:00", id:"timer", fillx:1, filly:1 },
{type:"btn", src:imgPause, cb: l=>timerStop(), fillx:1 }
], filly:1
});
onButton();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -2,10 +2,10 @@
"id": "smpltmr", "id": "smpltmr",
"name": "Simple Timer", "name": "Simple Timer",
"shortName": "Simple Timer", "shortName": "Simple Timer",
"version": "0.01", "version": "0.02",
"description": "A very simple app to start a timer.", "description": "A very simple app to start a timer.",
"icon": "app.png", "icon": "app.png",
"tags": "tool", "tags": "tool,alarm,timer",
"dependencies": {"scheduler":"type"}, "dependencies": {"scheduler":"type"},
"supports": ["BANGLEJS2"], "supports": ["BANGLEJS2"],
"screenshots": [{"url":"screenshot.png"}, {"url": "screenshot_2.png"}], "screenshots": [{"url":"screenshot.png"}, {"url": "screenshot_2.png"}],

View File

@ -1 +0,0 @@
0.01: First release

View File

@ -1,12 +0,0 @@
# Simple Timer
TESTING - DO NOT INSTALL, MAY BE BROKEN
Does one thing well. Set a time in hours, minutes and seconds, and alerts you when time is up. Opening the app while the timer is running (or just leaving the app open) shows how much time is left on the timer. This is the part I felt was missing from the Alarms and Timer app.
Drag or tap on the up and down buttons over the hour, minute or second to set the time.
![](timersimple-scr1.png)
![](timersimple-scr2.png)
Written by: [Sir Indy](https://github.com/sir-indy) For support and discussion please post in the [Bangle JS Forum](http://forum.espruino.com/microcosms/1424/)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -1,18 +0,0 @@
{
"id":"timersimple",
"name":"Timer Simple",
"shortName": "Timer Simple",
"version": "0.01",
"description": "Sets a single timer, and tells you how long left.",
"readme": "README.md",
"icon":"icons8-time-span-48.png",
"screenshots": [{"url":"timersimple-scr1.png"},{"url":"timersimple-scr2.png"}],
"tags": "tool,alarm,timer",
"supports": ["BANGLEJS2"],
"dependencies": {"scheduler":"type"},
"allow_emulator": true,
"storage": [
{"name":"timersimple.app.js","url":"timersimple.app.js"},
{"name":"timersimple.img","url":"timersimple.icon.js","evaluate":true}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -1,185 +0,0 @@
const secondsToTime = (s) => new Object({h:Math.floor((s/3600) % 24), m:Math.floor((s/60) % 60), s:Math.floor(s % 60)});
const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
function formatTime(s) {
var t = secondsToTime(s);
if (t.h) {
return t.h + ':' + ("0" + t.m).substr(-2) + ':' + ("0" + t.s).substr(-2);
} else {
return t.m + ':' + ("0" + t.s).substr(-2);
}
}
const timerID = "simpletimer";
Bangle.loadWidgets();
Bangle.drawWidgets();
var Layout = require("Layout");
var seconds = 5 * 60; // Default to 5 minutes
var drawTimeout;
var timerLayout;
var timePickerLayout;
var imgArrow = Graphics.createImage(`
x
xxx
xxx
xxxxx
xxxxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
`);
const imgPause = atob("GBiBAP+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B/w==");
const imgPlay = atob("GBiBAIAAAOAAAPgAAP4AAP+AAP/gAP/4AP/+AP//gP//4P//+P///v///v//+P//4P//gP/+AP/4AP/gAP+AAP4AAPgAAOAAAIAAAA==");
function onDrag(event) {
Bangle.buzz(20, 0.3);
var diff = -Math.round(event.dy/5);
if (event.x < timePickerLayout.hours.w) {
diff *= 3600;
} else if (event.x > timePickerLayout.mins.x && event.x < timePickerLayout.secs.x) {
diff *= 60;
}
updateTimePicker(diff);
}
function onTouch(button, xy) {
var touchMidpoint = timePickerLayout.hours.y + timePickerLayout.hours.h/2;
var diff = 0;
if (xy.y > 24 && xy.y < touchMidpoint - 10) {
Bangle.buzz(40, 0.3);
diff = 1;
} else if (xy.y > touchMidpoint + 10 && xy.y < timePickerLayout.btnStart.y) {
Bangle.buzz(40, 0.3);
diff = -1;
} else if (xy.y > timePickerLayout.btnStart.y) {
Bangle.buzz(40, 0.6);
runTimer();
return;
}
if (xy.x < timePickerLayout.hours.w) {
diff *= 3600;
} else if (xy.x > timePickerLayout.mins.x && xy.x < timePickerLayout.secs.x) {
diff *= 60;
}
updateTimePicker(diff);
}
function updateTimePicker(diff) {
seconds = clamp(seconds + (diff || 0), 0, 24 * 3600 - 1);
var set_time = secondsToTime(seconds);
updateLayoutField(timePickerLayout, 'hours', set_time.h);
updateLayoutField(timePickerLayout, 'mins', set_time.m);
updateLayoutField(timePickerLayout, 'secs', set_time.s);
}
function updateLayoutField(layout, field, value) {
layout.clear(layout[field]);
layout[field].label = value;
layout.render(layout[field]);
}
function updateTimer() {
var timeToNext = require("sched").getTimeToAlarm(require("sched").getAlarm(timerID));
updateLayoutField(timerLayout, 'timer', formatTime(timeToNext / 1000));
//var d = new Date();
//updateLayoutField(timerLayout, 'time', require("locale").time(d,1));
queueDraw(1000);
}
function queueDraw(millisecs) {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() {
drawTimeout = undefined;
updateTimer();
}, millisecs - (Date.now() % millisecs));
}
function timerStop() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
seconds = require("sched").getTimeToAlarm(require("sched").getAlarm(timerID)) / 1000;
require("sched").setAlarm(timerID, undefined);
require("sched").reload();
runTimePicker();
}
var timePickerLayoutCode = {
type:"v", c: [
{type:undefined, height:2},
//{type:"txt", font:"15%", label:"TIMER", id:"title"},
{type:"h", c: [
{type:"v", width:g.getWidth()/3, c: [
{type:"txt", font:"6x8", label:/*LANG*/"Hours", col:g.theme.fg2},
{type:"img", pad:8, src:imgArrow, col:g.theme.fg2},
{type:"txt", font:"20%", label:"00", id:"hours", filly:1, fillx:1},
{type:"img", pad:8, src:imgArrow, col:g.theme.fg2, r:2}
]},
{type:"v", width:g.getWidth()/3, c: [
{type:"txt", font:"6x8", label:/*LANG*/"Minutes", col:g.theme.fg2},
{type:"img", pad:8, src:imgArrow, col:g.theme.fg2},
{type:"txt", font:"20%", label:"00", id:"mins", filly:1, fillx:1},
{type:"img", pad:8, src:imgArrow, col:g.theme.fg2, r:2}
]},
{type:"v", width:g.getWidth()/3, c: [
{type:"txt", font:"6x8", label:/*LANG*/"Seconds", col:g.theme.fg2},
{type:"img", pad:8, src:imgArrow, col:g.theme.fg2},
{type:"txt", font:"20%", label:"00", id:"secs", filly:1, fillx:1},
{type:"img", pad:8, src:imgArrow, col:g.theme.fg2, r:2}
]},
]},
{type:"btn", src:imgPlay, id:"btnStart", fillx:1 }
//{type:"btn", label:'Start', font:"20%", id:"btnStart", fillx:0 }
], filly:1
};
var timerLayoutCode = {
type:"v", c: [
{type:undefined, height:8},
//{type:"txt", font:"6x8", label:/*LANG*/"Timer", id:"title", col:g.theme.fg2},
{type:"txt", font:"22%", label:"0:00", id:"timer", fillx:1, filly:1 },
//{type:"h", c: [
// {type:"txt", font:"6x8", pad:8, label:/*LANG*/"Time Now:", halign:-1, col:g.theme.fg2},
// {type:"txt", font:"6x8", label:"00:00", id:"time", halign:1, col:g.theme.fg2},
//]},
{type:"btn", src:imgPause, cb: l=>timerStop(), fillx:1 }
], filly:1
};
function runTimePicker() {
g.clearRect(Bangle.appRect);
timePickerLayout = new Layout(timePickerLayoutCode);
Bangle.setUI({
mode : "custom",
touch : function(n,e) {onTouch(n,e);},
drag : function(e) {onDrag(e);},
btn : function(n) {runTimer();},
});
timePickerLayout.render();
updateTimePicker();
//timePickerLayout.debug();
}
function runTimer() {
require("sched").setAlarm(timerID, {
vibrate : ".-.-",
hidden: true,
timer : seconds * 1000
});
require("sched").reload();
g.clearRect(Bangle.appRect);
timerLayout = new Layout(timerLayoutCode);
timerLayout.render();
updateTimer();
}
var timeToNext = require("sched").getTimeToAlarm(require("sched").getAlarm(timerID));
if (timeToNext != undefined) {
g.clearRect(Bangle.appRect);
timerLayout = new Layout(timerLayoutCode);
timerLayout.render();
updateTimer();
} else {
runTimePicker();
}

View File

@ -1 +0,0 @@
require("heatshrink").decompress(atob("mEwwcBkmSpIC/ARf//9JkQRMCAIRBAwIRKv4RFpARIz4QCCIdJiREJAAgJCCI0nCI3+BgOJCIs/CI3/9MkyJoIAAxuGp4RJ8gRQ/mSogRDu4RJNwKSEqXfCJPSCImSrYRJ+SkEyVfCJP6CIo1B4wRHUgIREA4MAj4SHCIeUCIP//EAt4RHkQRF//ggIDB+EHCJf/wEAAAQRM/0CoAmCCJf/4VDI5pcCNwoRKNZ4RMUIQRLYowAIYozpRrYRJ+QREqVLCJPSpGSCIdJv5GIyQREpVJfA///mSogRDpNJloRH8mSBwQRDku/CIwMBCIspkmXCAvpkmRCIslAYKkETwMkxIRFkmkyVLNwYJCBwgCDAwyeEAQqSBAwiMEAQwGFBxACDygDBkQOKAX4CD"))