forked from FOSS/BangleApps
Merge branch 'master' of github.com:espruino/BangleApps
commit
30519272cf
18
apps.json
18
apps.json
|
@ -2001,5 +2001,23 @@
|
||||||
{"name":"espruinoctrl.app.js"},
|
{"name":"espruinoctrl.app.js"},
|
||||||
{"name":"espruinoctrl.img","url":"app-icon.js","evaluate":true}
|
{"name":"espruinoctrl.img","url":"app-icon.js","evaluate":true}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{ "id": "multiclock",
|
||||||
|
"name": "Multi Clock",
|
||||||
|
"icon": "multiclock.png",
|
||||||
|
"version":"0.06",
|
||||||
|
"description": "Clock with multiple faces - Big, Analogue, Digital, Text.\n Switch between faces with BT1 & BTN3",
|
||||||
|
"readme": "README.md",
|
||||||
|
"tags": "clock",
|
||||||
|
"type":"clock",
|
||||||
|
"allow_emulator":false,
|
||||||
|
"storage": [
|
||||||
|
{"name":"multiclock.app.js","url":"clock.min.js"},
|
||||||
|
{"name":"big.face.js","url":"big.min.js"},
|
||||||
|
{"name":"ana.face.js","url":"ana.min.js"},
|
||||||
|
{"name":"digi.face.js","url":"digi.min.js"},
|
||||||
|
{"name":"txt.face.js","url":"txt.min.js"},
|
||||||
|
{"name":"multiclock.img","url":"multiclock-icon.js","evaluate":true}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
0.01: New App!
|
||||||
|
0.02: Separate *.face.js files for faces
|
||||||
|
0.03: Renaming
|
||||||
|
0.04: Bug Fixes
|
||||||
|
0.05: Add README
|
||||||
|
0.06: Add txt clock
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
# Multiclock
|
||||||
|
|
||||||
|
This is a clock app that supports multiple clock faces. The user can switch between faces while retaining widget state which makes the switch fast and preserves state such as bluetooth connections. Currently there are four clock faces as shown below. To my eye, these faces look better when widgets are hidden using **widviz**.
|
||||||
|
|
||||||
|
### Analog Clock Face
|
||||||
|

|
||||||
|
|
||||||
|
### Digital Clock Face
|
||||||
|

|
||||||
|
|
||||||
|
### Big Digit Clock Face
|
||||||
|

|
||||||
|
|
||||||
|
### Text Clock Face
|
||||||
|

|
||||||
|
|
||||||
|
## Controls
|
||||||
|
Clock faces are kept in a circular list.
|
||||||
|
|
||||||
|
*BTN1* - switches to the next clock face.
|
||||||
|
|
||||||
|
*BTN2* - switches to the app launcher.
|
||||||
|
|
||||||
|
*BTN3* - switches to the previous clock face.
|
||||||
|
|
||||||
|
## Adding a new face
|
||||||
|
Clock faces are described in javascript storage files named `name.face.js`. For example, the Analog Clock Face is described in `ana.face.js`. These files have the following structure:
|
||||||
|
|
||||||
|
```
|
||||||
|
(() => {
|
||||||
|
function getFace(){
|
||||||
|
function onSecond(){
|
||||||
|
//draw digits, hands etc
|
||||||
|
}
|
||||||
|
function drawAll(){
|
||||||
|
//draw background + initial state of digits, hands etc
|
||||||
|
}
|
||||||
|
return {init:drawAll, tick:onSecond};
|
||||||
|
}
|
||||||
|
return getFace;
|
||||||
|
})();
|
||||||
|
```
|
||||||
|
For those familiar with the structure of widgets, this is similar, however, there is an additional level of function nesting. This means that although faces are loaded when the clock app starts running they are not instantiated until their `getFace` function is called, i.e. memory is not allocated to structures such as image buffer arrays declared in `getFace` until the face is selected. Consequently, adding a face does not require a lot of extra memory.
|
||||||
|
|
||||||
|
The app at start up loads all files `*.face.js`. The simplest way of adding a face is thus to load it into `Storage` using the WebIDE. Similarly, to remove an unwanted face, simply delete it from `Storage` using the WebIDE.
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
Please report bugs etc. by raising an issue [here](https://github.com/jeffmer/JeffsBangleAppsDev).
|
|
@ -0,0 +1,72 @@
|
||||||
|
(() => {
|
||||||
|
|
||||||
|
function getFace(){
|
||||||
|
|
||||||
|
const p = Math.PI/2;
|
||||||
|
const PRad = Math.PI/180;
|
||||||
|
|
||||||
|
function seconds(angle, r) {
|
||||||
|
const a = angle*PRad;
|
||||||
|
const x = 120+Math.sin(a)*r;
|
||||||
|
const y = 134-Math.cos(a)*r;
|
||||||
|
if (angle % 90 == 0) {
|
||||||
|
g.setColor(0,1,1);
|
||||||
|
g.fillRect(x-6,y-6,x+6,y+6);
|
||||||
|
} else if (angle % 30 == 0){
|
||||||
|
g.setColor(0,1,1);
|
||||||
|
g.fillRect(x-4,y-4,x+4,y+4);
|
||||||
|
} else {
|
||||||
|
g.setColor(1,1,1);
|
||||||
|
g.fillRect(x-1,y-1,x+1,y+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hand(angle, r1,r2, r3) {
|
||||||
|
const a = angle*PRad;
|
||||||
|
g.fillPoly([
|
||||||
|
120+Math.sin(a)*r1,
|
||||||
|
134-Math.cos(a)*r1,
|
||||||
|
120+Math.sin(a+p)*r3,
|
||||||
|
134-Math.cos(a+p)*r3,
|
||||||
|
120+Math.sin(a)*r2,
|
||||||
|
134-Math.cos(a)*r2,
|
||||||
|
120+Math.sin(a-p)*r3,
|
||||||
|
134-Math.cos(a-p)*r3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var minuteDate;
|
||||||
|
var secondDate;
|
||||||
|
|
||||||
|
function onSecond() {
|
||||||
|
g.setColor(0,0,0);
|
||||||
|
hand(360*secondDate.getSeconds()/60, -5, 90, 3);
|
||||||
|
if (secondDate.getSeconds() === 0) {
|
||||||
|
hand(360*(minuteDate.getHours() + (minuteDate.getMinutes()/60))/12, -16, 60, 7);
|
||||||
|
hand(360*minuteDate.getMinutes()/60, -16, 86, 7);
|
||||||
|
minuteDate = new Date();
|
||||||
|
}
|
||||||
|
g.setColor(1,1,1);
|
||||||
|
hand(360*(minuteDate.getHours() + (minuteDate.getMinutes()/60))/12, -16, 60, 7);
|
||||||
|
hand(360*minuteDate.getMinutes()/60, -16, 86, 7);
|
||||||
|
g.setColor(0,1,1);
|
||||||
|
secondDate = new Date();
|
||||||
|
hand(360*secondDate.getSeconds()/60, -5, 90, 3);
|
||||||
|
g.setColor(0,0,0);
|
||||||
|
g.fillCircle(120,134,2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawAll() {
|
||||||
|
secondDate = minuteDate = new Date();
|
||||||
|
// draw seconds
|
||||||
|
g.setColor(1,1,1);
|
||||||
|
for (let i=0;i<60;i++)
|
||||||
|
seconds(360*i/60, 100);
|
||||||
|
onSecond();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {init:drawAll, tick:onSecond};
|
||||||
|
}
|
||||||
|
|
||||||
|
return getFace;
|
||||||
|
|
||||||
|
})();
|
|
@ -0,0 +1,2 @@
|
||||||
|
(function(){return function(){function e(a,d,b,c){a*=k;g.fillPoly([120+Math.sin(a)*d,134-Math.cos(a)*d,120+Math.sin(a+h)*c,134-Math.cos(a+h)*c,120+Math.sin(a)*b,134-Math.cos(a)*b,120+Math.sin(a-h)*c,134-Math.cos(a-h)*c])}function l(){g.setColor(0,0,0);e(360*f.getSeconds()/60,-5,90,3);0===f.getSeconds()&&(e(360*(d.getHours()+d.getMinutes()/60)/12,-16,60,7),e(360*d.getMinutes()/60,-16,86,7),d=new Date);g.setColor(1,1,1);e(360*(d.getHours()+d.getMinutes()/60)/12,-16,60,7);e(360*d.getMinutes()/60,-16,
|
||||||
|
86,7);g.setColor(0,1,1);f=new Date;e(360*f.getSeconds()/60,-5,90,3);g.setColor(0,0,0);g.fillCircle(120,134,2)}var h=Math.PI/2,k=Math.PI/180,d,f;return{init:function(){f=d=new Date;g.setColor(1,1,1);for(var a=0;60>a;a++){var e=360*a/60,b=e*k,c=120+100*Math.sin(b);b=134-100*Math.cos(b);0==e%90?(g.setColor(0,1,1),g.fillRect(c-6,b-6,c+6,b+6)):0==e%30?(g.setColor(0,1,1),g.fillRect(c-4,b-4,c+4,b+4)):(g.setColor(1,1,1),g.fillRect(c-1,b-1,c+1,b+1))}l()},tick:l}}})();
|
Binary file not shown.
After Width: | Height: | Size: 44 KiB |
|
@ -0,0 +1,18 @@
|
||||||
|
{ "id": "multiclock",
|
||||||
|
"name": "Multi Clock",
|
||||||
|
"icon": "multiclock.png",
|
||||||
|
"version":"0.06",
|
||||||
|
"description": "Clock with multiple faces - Big, Analogue, Digital, Text.\n Switch between faces with BT1 & BTN3",
|
||||||
|
"readme": "README.md",
|
||||||
|
"tags": "clock",
|
||||||
|
"type":"clock",
|
||||||
|
"allow_emulator":false,
|
||||||
|
"storage": [
|
||||||
|
{"name":"multiclock.app.js","url":"clock.min.js"},
|
||||||
|
{"name":"big.face.js","url":"big.min.js"},
|
||||||
|
{"name":"ana.face.js","url":"ana.min.js"},
|
||||||
|
{"name":"digi.face.js","url":"digi.min.js"},
|
||||||
|
{"name":"txt.face.js","url":"txt.min.js"},
|
||||||
|
{"name":"multiclock.img","url":"multiclock-icon.js","evaluate":true}
|
||||||
|
]
|
||||||
|
},
|
|
@ -0,0 +1,32 @@
|
||||||
|
(() => {
|
||||||
|
|
||||||
|
function getFace(){
|
||||||
|
|
||||||
|
function drawTime(d) {
|
||||||
|
g.reset();
|
||||||
|
var da = d.toString().split(" ");
|
||||||
|
var time = da[4].substr(0, 5).split(":");
|
||||||
|
var hours = time[0],
|
||||||
|
minutes = time[1];
|
||||||
|
g.clearRect(0,24,239,239);
|
||||||
|
g.setColor(1,1,1);
|
||||||
|
g.setFont("Vector",100);
|
||||||
|
g.drawString(hours,50,24,true);
|
||||||
|
g.drawString(minutes,50,135,true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSecond(){
|
||||||
|
var t = new Date();
|
||||||
|
if (t.getSeconds() === 0) drawTime(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawAll(){
|
||||||
|
drawTime(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
return {init:drawAll, tick:onSecond};
|
||||||
|
}
|
||||||
|
|
||||||
|
return getFace;
|
||||||
|
|
||||||
|
})();
|
|
@ -0,0 +1 @@
|
||||||
|
(function(){return function(){function c(a){g.reset();var b=a.toString().split(" ")[4].substr(0,5).split(":");a=b[0];b=b[1];g.clearRect(0,24,239,239);g.setColor(1,1,1);g.setFont("Vector",100);g.drawString(a,50,24,!0);g.drawString(b,50,135,!0)}return{init:function(){c(new Date)},tick:function(){var a=new Date;0===a.getSeconds()&&c(a)}}}})();
|
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
|
@ -0,0 +1,60 @@
|
||||||
|
var FACES = [];
|
||||||
|
var iface = 0;
|
||||||
|
require("Storage").list(/\.face\.js$/).forEach(face=>FACES.push(eval(require("Storage").read(face))));
|
||||||
|
var face = FACES[iface]();
|
||||||
|
var intervalRefSec;
|
||||||
|
|
||||||
|
function stopdraw() {
|
||||||
|
if(intervalRefSec) {intervalRefSec=clearInterval(intervalRefSec);}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startdraw() {
|
||||||
|
g.clear();
|
||||||
|
g.reset();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
face.init();
|
||||||
|
intervalRefSec = setInterval(face.tick,1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setButtons(){
|
||||||
|
function newFace(inc){
|
||||||
|
var n = FACES.length-1;
|
||||||
|
iface+=inc;
|
||||||
|
iface = iface>n?0:iface<0?n:iface;
|
||||||
|
stopdraw();
|
||||||
|
face = FACES[iface]();
|
||||||
|
startdraw();
|
||||||
|
}
|
||||||
|
setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});
|
||||||
|
setWatch(newFace.bind(null,1), BTN1, {repeat:true,edge:"rising"});
|
||||||
|
setWatch(newFace.bind(null,-1), BTN3, {repeat:true,edge:"rising"});
|
||||||
|
}
|
||||||
|
|
||||||
|
var SCREENACCESS = {
|
||||||
|
withApp:true,
|
||||||
|
request:function(){
|
||||||
|
this.withApp=false;
|
||||||
|
stopdraw();
|
||||||
|
clearWatch();
|
||||||
|
},
|
||||||
|
release:function(){
|
||||||
|
this.withApp=true;
|
||||||
|
startdraw();
|
||||||
|
setButtons();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Bangle.on('lcdPower',function(on) {
|
||||||
|
if (!SCREENACCESS.withApp) return;
|
||||||
|
if (on) {
|
||||||
|
startdraw();
|
||||||
|
} else {
|
||||||
|
stopdraw();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
g.clear();
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
startdraw();
|
||||||
|
setButtons();
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
var FACES=[],iface=0;require("Storage").list(/\.face\.js$/).forEach(function(a){return FACES.push(eval(require("Storage").read(a)))});var face=FACES[iface](),intervalRefSec;function stopdraw(){intervalRefSec&&(intervalRefSec=clearInterval(intervalRefSec))}function startdraw(){g.clear();g.reset();Bangle.drawWidgets();face.init();intervalRefSec=setInterval(face.tick,1E3)}
|
||||||
|
function setButtons(){function a(a){var b=FACES.length-1;iface+=a;iface=iface>b?0:0>iface?b:iface;stopdraw();face=FACES[iface]();startdraw()}setWatch(Bangle.showLauncher,BTN2,{repeat:!1,edge:"falling"});setWatch(a.bind(null,1),BTN1,{repeat:!0,edge:"rising"});setWatch(a.bind(null,-1),BTN3,{repeat:!0,edge:"rising"})}var SCREENACCESS={withApp:!0,request:function(){this.withApp=!1;stopdraw();clearWatch()},release:function(){this.withApp=!0;startdraw();setButtons()}};
|
||||||
|
Bangle.on("lcdPower",function(a){SCREENACCESS.withApp&&(a?startdraw():stopdraw())});g.clear();Bangle.loadWidgets();startdraw();setButtons();
|
|
@ -0,0 +1,31 @@
|
||||||
|
(() => {
|
||||||
|
|
||||||
|
function getFace(){
|
||||||
|
|
||||||
|
var buf = Graphics.createArrayBuffer(240,92,1,{msb:true});
|
||||||
|
function flip() {
|
||||||
|
g.setColor(1,1,1);
|
||||||
|
g.drawImage({width:buf.getWidth(),height:buf.getHeight(),buffer:buf.buffer},0,85);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawTime() {
|
||||||
|
buf.clear();
|
||||||
|
buf.setColor(1);
|
||||||
|
var d = new Date();
|
||||||
|
var da = d.toString().split(" ");
|
||||||
|
var time = da[4];
|
||||||
|
buf.setFont("Vector",42);
|
||||||
|
buf.setFontAlign(0,-1);
|
||||||
|
buf.drawString(time,buf.getWidth()/2,0);
|
||||||
|
buf.setFont("6x8",2);
|
||||||
|
buf.setFontAlign(0,-1);
|
||||||
|
var date = d.toString().substr(0,15);
|
||||||
|
buf.drawString(date, buf.getWidth()/2, 70);
|
||||||
|
flip();
|
||||||
|
}
|
||||||
|
return {init:drawTime, tick:drawTime};
|
||||||
|
}
|
||||||
|
|
||||||
|
return getFace;
|
||||||
|
|
||||||
|
})();
|
|
@ -0,0 +1 @@
|
||||||
|
(function(){return function(){function b(){a.clear();a.setColor(1);var c=new Date,b=c.toString().split(" ")[4];a.setFont("Vector",42);a.setFontAlign(0,-1);a.drawString(b,a.getWidth()/2,0);a.setFont("6x8",2);a.setFontAlign(0,-1);c=c.toString().substr(0,15);a.drawString(c,a.getWidth()/2,70);g.setColor(1,1,1);g.drawImage({width:a.getWidth(),height:a.getHeight(),buffer:a.buffer},0,85)}var a=Graphics.createArrayBuffer(240,92,1,{msb:!0});return{init:b,tick:b}}})();
|
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("mEwwkEogA/AFGIAAQVVDKQWHDB1IC5OECx8z///mYYOBoWDCoIADnBJLFwQWGDAgwIEYU/CQXwh4EC+YwKBIOPFQYXE//4C5BGCIQgXF/5IILo4XGMIQXHLoYXIMIRGMC45IHC4KkGC45IBC4yNEC5KRBC7h2HC5B4GC5EggQXOBwvygEAl6QHC4sikRGEhGAJAgNBC75HIgZHNO48AgIJER54xCiYXKa5AxCGAjvPGA4XIwYXHbQs4C46QGGAbZDB4IXEPBQAEOwwXDJBJGEC4xILIxQwDSJCNDFwwXDMIh0ELoQXIJARhDC4hdCIw4wEDAQXDCwQuIGAgABmYXBmYHDFxIYGAAoWLJIgAGCxgYJCxwZGCqIA/AC4A="))
|
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
|
@ -0,0 +1,42 @@
|
||||||
|
(() => {
|
||||||
|
|
||||||
|
function getFace(){
|
||||||
|
|
||||||
|
function drawTime(d) {
|
||||||
|
function convert(n){
|
||||||
|
var t0 = [" ","one","two","three","four","five","six","seven","eight","nine"];
|
||||||
|
var t1 = ["ten","eleven","twelve","thirteen","fourteen","fifteen","sixteen","seventeen","eighteen","nineteen"];
|
||||||
|
var t20= ["twenty","thirty","forty","fifty"];
|
||||||
|
if(n<10) return {top:" ",bot:t0[n]};
|
||||||
|
else if(n<20) return {top:" ",bot:t1[n-10]};
|
||||||
|
else if(n<60) return {top:t20[Math.floor(n/10)-2],bot:t0[n%10]};
|
||||||
|
return "error";
|
||||||
|
}
|
||||||
|
g.reset();
|
||||||
|
g.clearRect(0,40,239,210);
|
||||||
|
g.setColor(1,1,1);
|
||||||
|
g.setFontAlign(0,0);
|
||||||
|
g.setFont("Vector",32);
|
||||||
|
var txt = convert(d.getHours());
|
||||||
|
g.drawString(txt.top,120,60);
|
||||||
|
g.drawString(txt.bot,120,100);
|
||||||
|
txt = convert(d.getMinutes());
|
||||||
|
g.drawString(txt.top,120,140);
|
||||||
|
g.drawString(txt.bot,120,180);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSecond(){
|
||||||
|
var t = new Date();
|
||||||
|
if (t.getSeconds() === 0) drawTime(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawAll(){
|
||||||
|
drawTime(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
return {init:drawAll, tick:onSecond};
|
||||||
|
}
|
||||||
|
|
||||||
|
return getFace;
|
||||||
|
|
||||||
|
})();
|
|
@ -0,0 +1,2 @@
|
||||||
|
(function(){return function(){function a(b){function a(c){var a=" ;one;two;three;four;five;six;seven;eight;nine".split(";"),b="ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen".split(" "),d=["twenty","thirty","forty","fifty"];return 10>c?{top:" ",bot:a[c]}:20>c?{top:" ",bot:b[c-10]}:60>c?{top:d[Math.floor(c/10)-2],bot:a[c%10]}:"error"}g.reset();g.clearRect(0,40,239,210);g.setColor(1,1,1);g.setFontAlign(0,0);g.setFont("Vector",32);var d=a(b.getHours());g.drawString(d.top,
|
||||||
|
120,60);g.drawString(d.bot,120,100);d=a(b.getMinutes());g.drawString(d.top,120,140);g.drawString(d.bot,120,180)}return{init:function(){a(new Date)},tick:function(){var b=new Date;0===b.getSeconds()&&a(b)}}}})();
|
Binary file not shown.
After Width: | Height: | Size: 51 KiB |
Loading…
Reference in New Issue