forked from FOSS/BangleApps
commit
8d51f20a7c
41
apps.json
41
apps.json
|
@ -65,7 +65,7 @@
|
|||
{ "id": "locale",
|
||||
"name": "Languages",
|
||||
"icon": "locale.png",
|
||||
"version":"0.08",
|
||||
"version":"0.09",
|
||||
"description": "Translations for different countries",
|
||||
"tags": "tool,system,locale,translate",
|
||||
"type": "locale",
|
||||
|
@ -213,6 +213,19 @@
|
|||
{"name":"wclock.img","url":"clock-word-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "slidingtext",
|
||||
"name": "Sliding Clock",
|
||||
"icon": "slidingtext.png",
|
||||
"version":"0.01",
|
||||
"description": "Inspired by the Pebble sliding clock, old times are scrolled off the screen and new times on. You are also able to change language on the fly so you can see the time written in other languages using button 1. Currently only English, French and Japanese are supported",
|
||||
"tags": "clock",
|
||||
"type":"clock",
|
||||
"allow_emulator":true,
|
||||
"storage": [
|
||||
{"name":"slidingtext.app.js","url":"slidingtext.js"},
|
||||
{"name":"slidingtext.img","url":"slidingtext-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "imgclock",
|
||||
"name": "Image background clock",
|
||||
"shortName":"Image Clock",
|
||||
|
@ -269,6 +282,30 @@
|
|||
{"name":"clock2x3.img","url":"clock2x3-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "geissclk",
|
||||
"name": "Geiss Clock",
|
||||
"icon": "clock.png",
|
||||
"version":"0.02",
|
||||
"description": "7 segment clock with animated background in the style of Ryan Geiss' music visualisation. NOTE: The first run will take ~1 minute to do some precalculation",
|
||||
"tags": "clock",
|
||||
"type":"clock",
|
||||
"storage": [
|
||||
{"name":"geissclk.app.js","url":"clock.js"},
|
||||
{"name":"geissclk.precompute.js","url":"precompute.js"},
|
||||
{"name":"geissclk.img","url":"clock-icon.js","evaluate":true}
|
||||
],
|
||||
"data": [
|
||||
{"name":"geissclk.0.map"},
|
||||
{"name":"geissclk.1.map"},
|
||||
{"name":"geissclk.2.map"},
|
||||
{"name":"geissclk.3.map"},
|
||||
{"name":"geissclk.4.map"},
|
||||
{"name":"geissclk.5.map"},
|
||||
{"name":"geissclk.0.pal"},
|
||||
{"name":"geissclk.1.pal"},
|
||||
{"name":"geissclk.2.pal"}
|
||||
]
|
||||
},
|
||||
{ "id": "trex",
|
||||
"name": "T-Rex",
|
||||
"icon": "trex.png",
|
||||
|
@ -2785,7 +2822,7 @@
|
|||
"name": "Walkers Clock",
|
||||
"shortName":"Walkers Clock",
|
||||
"icon": "walkersclock48.png",
|
||||
"version":"0.01",
|
||||
"version":"0.02",
|
||||
"description": "A larg font watch, displays steps, can switch GPS on/off, displays grid reference",
|
||||
"type":"clock",
|
||||
"tags": "clock, gps, tools, outdoors",
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
0.01: New App!
|
||||
0.02: BTN2->launcher, use smaller text to allow "20:00" to fit on screen
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEw4X/AoMF/3G/2m/wOCmoaKmtAAgVQBYsFoECwALMAA0JBZQABBYIbBBaVUBYMVBYkBIIILDgEKGYUBqkA6oLB6sAlQLDqsAioLBCAOq4AkCqtAivCBYMC1YwDitQgtCwNUhWsOwlUBYVQlWwJAlVgtSxNQFwnwGANBqWFqQuE1QwBqNewtKFwgLBGANWytq2EFWoU6gWBqoLBFwNVYIZZBqtXyurioLF1W1q/W1tVPIIxE1NV72VqpTDDAleEIgADnWq3qgBTIYADvQuBAAVUBYla1XVBYYbDyt632qd40Fqulquq0ALFOwO16t6BYpxBvWX6tW0aoDXwNU1XXNQMD1T7CAwPC1abBegPqFodQdQILCO4hxBlWxq2FoqaFBYOBcYNxQggLCToOBqAwBTgwLCiqoGgtCJ4L0BBYsVBYRmBBY0CwEUJ4IwFBYkVBYsMBYI0CJAwLDAA4L2A="))
|
|
@ -0,0 +1,151 @@
|
|||
var W = 79, H = 64;
|
||||
/*var compiled = E.compiledC(`
|
||||
// void transl(int, int, int )
|
||||
int transl(unsigned char *map, unsigned char *imgfrom, unsigned char *imgto) {
|
||||
int n = 0;
|
||||
const int W = 79;
|
||||
const int H = 64;
|
||||
for (int y=0;y<H;y++)
|
||||
for (int x=0;x<W;x++) {
|
||||
int d = map[n];
|
||||
int nx = (x<<3) + ((d )&0x0F) - 8;
|
||||
int ny = (y<<3) + ((d>>4)&0x0F) - 8;
|
||||
int ax = nx&7;
|
||||
int ay = ny&7;
|
||||
int a = (nx>>3) + ((ny>>3)*W);
|
||||
int c = 0;
|
||||
if (a>=0 && a<(W*H-(W+1))) {
|
||||
c = imgfrom[a]*(8-ax)*(8-ay) +
|
||||
imgfrom[a+1]*(ax)*(8-ay) +
|
||||
imgfrom[a+W]*(8-ax)*(ay) +
|
||||
imgfrom[a+W+1]*(ax)*(ay);
|
||||
c = (c>>6) - 4;
|
||||
if (c<0) c=0;
|
||||
}
|
||||
imgto[n] = c;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
`);*/
|
||||
var compiled = (function(){
|
||||
var bin=atob("Len3TwAnT/BPCPsAAJMI+wfzAOsDCdMYAZMAJhn4BkAAnQTwDwMD68YDBesUFAg7CDzdEE/q5AoI+wpaQfJvNapFItgB6woOA/AHAxH4CsCe+AGww/EIBQX7DPwD+wvMnvhPsATwBwQF+wv1ZUPE8QgKCvsMXJ74UFBrQwT7A8SkEQQ8JOrkdADgACQBm5xVATZPLsLRATdAL7bRA7C96PCPAAA=");
|
||||
return {
|
||||
transl:E.nativeCall(1, "void(int, int, int )", bin),
|
||||
};
|
||||
})();
|
||||
|
||||
//require("Font5x9Numeric7Seg").add(Graphics);
|
||||
Graphics.prototype.setFont5x9Numeric7Seg = function() {
|
||||
this.setFontCustom(atob("AAAAAAAAAAIAAAQCAQAAAd0BgMBdwAAAAAAAdwAB0RiMRcAAAERiMRdwAcAQCAQdwAcERiMRBwAd0RiMRBwAAEAgEAdwAd0RiMRdwAcERiMRdwAFAAd0QiEQdwAdwRCIRBwAd0BgMBAAABwRCIRdwAd0RiMRAAAd0QiEQAAAAAAAAAA="), 32, atob("BgAAAAAAAAAAAAAAAAYCAAYGBgYGBgYGBgYCAAAAAAAABgYGBgYG"), 9);
|
||||
}
|
||||
|
||||
// Allocate the data
|
||||
var dataa = new Uint8Array(W*H);
|
||||
var datab = new Uint8Array(W*H);
|
||||
var map = new Uint8Array(W*H);
|
||||
var pal = new Uint16Array(256);
|
||||
var PALETTES = 3;
|
||||
var MAPS = 6;
|
||||
|
||||
// If we're missing any maps, compute them!
|
||||
(function() {
|
||||
var files = require("Storage").list(/^geissclk/);
|
||||
var allOk = true;
|
||||
for (var n=0;n<PALETTES;n++)
|
||||
if (!files.includes("geissclk."+n+".pal"))
|
||||
allOk = false;
|
||||
for (var n=0;n<MAPS;n++)
|
||||
if (!files.includes("geissclk."+n+".map"))
|
||||
allOk = false;
|
||||
if (!allOk)
|
||||
eval(require("Storage").read("geissclk.precompute.js"));
|
||||
})();
|
||||
|
||||
function randomPalette() {
|
||||
var n = (0|Math.random()*200000) % PALETTES;
|
||||
var p = new Uint8Array(pal.buffer);
|
||||
p.set(require("Storage").readArrayBuffer("geissclk."+n+".pal"));
|
||||
}
|
||||
|
||||
function randomMap() {
|
||||
var n = (0|Math.random()*200000) % MAPS;
|
||||
map.set(require("Storage").readArrayBuffer("geissclk."+n+".map"));
|
||||
}
|
||||
|
||||
|
||||
randomPalette();
|
||||
randomMap();
|
||||
|
||||
// Get the address
|
||||
var addra = E.getAddressOf(dataa,true);
|
||||
if (!addra) throw new Error("Not a Flat String");
|
||||
var addrb = E.getAddressOf(datab,true);
|
||||
if (!addrb) throw new Error("Not a Flat String");
|
||||
var addrmap = E.getAddressOf(map,true);
|
||||
if (!addrmap) throw new Error("Not a Flat String");
|
||||
var gfx = Graphics.createArrayBuffer(W,H,8);
|
||||
gfx.buffer = dataa.buffer;
|
||||
|
||||
var im = {
|
||||
width:W, height:H, bpp:8,
|
||||
palette: pal,
|
||||
buffer : dataa.buffer
|
||||
};
|
||||
var lastSeconds = -1;
|
||||
|
||||
function iterate() { "ram"
|
||||
var d = new Date();
|
||||
var time = require("locale").time(d,1);
|
||||
var seconds = d.getSeconds().toString().padStart(2,0);
|
||||
t = addra; addra = addrb; addrb = t;
|
||||
t = dataa; dataa = datab; datab = t;
|
||||
if (seconds!=lastSeconds) {
|
||||
lastSeconds = seconds;
|
||||
im.buffer = datab.buffer;
|
||||
gfx.buffer = datab.buffer;
|
||||
} else {
|
||||
im.buffer = dataa.buffer;
|
||||
gfx.buffer = dataa.buffer;
|
||||
}
|
||||
var x,y,n,t = getTime()/10;
|
||||
var amt = 100*Bangle.getAccel().diff;
|
||||
for (var i=0;i<amt;i++) {
|
||||
//x = Math.round((W/2) + 20*Math.sin(t));
|
||||
//y = Math.round((H/2) + 20*Math.cos(t));
|
||||
//t += 0.628;
|
||||
x = 1+(Math.random()*(W-2))|0;
|
||||
y = 1+(Math.random()*(H-2))|0;
|
||||
dataa[x + y*W] = 240;
|
||||
}
|
||||
compiled.transl(addrmap, addra, addrb);
|
||||
|
||||
|
||||
x = 8;
|
||||
gfx.setFont("5x9Numeric7Seg",2);
|
||||
gfx.drawString(time, x, 20);
|
||||
gfx.setFont("5x9Numeric7Seg");
|
||||
gfx.drawString(seconds, x+55, 30);
|
||||
// firmwares pre-2v09 wouldn't accelerate a 3x blit if it went right to the RHS - hence we're 79px not 80
|
||||
g.drawImage(im,1,24,{scale:3});
|
||||
}
|
||||
|
||||
|
||||
Bangle.on('lcdPower',function(on) {
|
||||
if (animInterval) {
|
||||
clearInterval(animInterval);
|
||||
animInterval = undefined;
|
||||
}
|
||||
if (on) {
|
||||
randomMap();
|
||||
randomPalette();
|
||||
iterate();
|
||||
animInterval = setInterval(iterate, 50);
|
||||
}
|
||||
});
|
||||
g.clear();
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
iterate();
|
||||
animInterval = setInterval(iterate, 50);
|
||||
|
||||
setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});
|
Binary file not shown.
After Width: | Height: | Size: 2.0 KiB |
|
@ -0,0 +1,108 @@
|
|||
// PALETTES ---------------------------
|
||||
E.showMessage("Precomputing\npalettes\n\nPlease wait...\n0 / 3");
|
||||
(function() { // fire
|
||||
for (var i=0;i<256;i++) {
|
||||
var r = Math.min(i*6,240);
|
||||
var g = Math.min(i*3,240);
|
||||
var b = E.clip((i-192)*8,0,240);
|
||||
pal[i] = (b>>3) | ((g&0xFC)<<3) | ((r&0xF8)<<8);
|
||||
}pal[255] = 65535;
|
||||
})()
|
||||
require("Storage").write("geissclk.0.pal",pal.buffer);
|
||||
E.showMessage("Precomputing\npalettes\n\nPlease wait...\n1 / 3");
|
||||
(function() { // gunge
|
||||
for (var i=0;i<256;i++) {
|
||||
var r = 0;
|
||||
var g = Math.min(i*3,255);
|
||||
var b = Math.min(i,255);
|
||||
pal[i] = (b>>3) | ((g&0xFC)<<3) | ((r&0xF8)<<8);
|
||||
}pal[255] = 65535;
|
||||
})()
|
||||
require("Storage").write("geissclk.1.pal",pal.buffer);
|
||||
E.showMessage("Precomputing\npalettes\n\nPlease wait...\n2 / 3");
|
||||
(function() { // rainbow
|
||||
for (var i=0;i<256;i++) {
|
||||
var cl = E.HSBtoRGB((48+i)/128,1,Math.min(i/16,0.9),true);
|
||||
var r = cl[0];
|
||||
var g = cl[1];
|
||||
var b = cl[2];
|
||||
pal[i] = (b>>3) | ((g&0xFC)<<3) | ((r&0xF8)<<8);
|
||||
}pal[255] = 65535;pal[255] = 65535;
|
||||
})()
|
||||
require("Storage").write("geissclk.2.pal",pal.buffer);
|
||||
|
||||
|
||||
// MAPS ----------------------------------------------
|
||||
E.showMessage("Precomputing\nmaps\n\nPlease wait...\n0 / 5");
|
||||
// straight out
|
||||
(function() { "ram"; var n = 0; for (var y=0;y<H;y++) {
|
||||
for (var x=0;x<W;x++) {
|
||||
var dx = x-(W/2);
|
||||
var dy = y-(H/2);
|
||||
var d = Math.sqrt(dx*dx + dy*dy);
|
||||
var s = -2 + Math.sin(d*0.5);
|
||||
dx = dx*s/d;
|
||||
dy = dy*s/d;
|
||||
map[n++] = ((dx*2 + 8) & 0x0F) | (((dy*2 + 8) & 0x0F)<<4);
|
||||
}
|
||||
}})();
|
||||
require("Storage").write("geissclk.0.map",map);
|
||||
E.showMessage("Precomputing\nmaps\n\nPlease wait...\n1 / 5");
|
||||
// ripple out
|
||||
(function() { "ram"; var n = 0; for (var y=0;y<H;y++) {
|
||||
for (var x=0;x<W;x++) {
|
||||
var dx = x-(W/2);
|
||||
var dy = y-(H/2);
|
||||
var d = Math.sqrt(dx*dx + dy*dy);
|
||||
var s = -2 + Math.sin(d*0.5)/2;
|
||||
dx = dx*s/d;
|
||||
dy = dy*s/d;
|
||||
map[n++] = ((dx*3 + 8) & 0x0F) | (((dy*3 + 8) & 0x0F)<<4);
|
||||
}
|
||||
}})();
|
||||
require("Storage").write("geissclk.1.map",map);
|
||||
E.showMessage("Precomputing\nmaps\n\nPlease wait...\n2 / 5");
|
||||
// twisty outwards
|
||||
(function() { "ram"; var n = 0; for (var y=0;y<H;y++) {
|
||||
for (var x=0;x<W;x++) {
|
||||
var dx = x-(W/2);
|
||||
var dy = y-(H/2);
|
||||
var d = Math.sqrt(dx*dx + dy*dy);
|
||||
dx = (-dx/d) + (((y&7)>3)?0.5:-0.5);
|
||||
dy = (-dy/d) + (((x&7)>3)?0.5:-0.5);
|
||||
map[n++] = ((dx*3 + 8) & 0x0F) | (((dy*3 + 8) & 0x0F)<<4);
|
||||
}
|
||||
}})()
|
||||
require("Storage").write("geissclk.2.map",map);
|
||||
E.showMessage("Precomputing\nmaps\n\nPlease wait...\n3 / 5");
|
||||
// spiral
|
||||
(function() { "ram"; var n = 0; for (var y=0;y<H;y++) {
|
||||
for (var x=0;x<W;x++) {
|
||||
var dx = x-(W/2);
|
||||
var dy = y-(H/2);
|
||||
var d = Math.sqrt(dx*dx + dy*dy);
|
||||
var cx = (2*dy-dx)/(2*d);
|
||||
var cy = (-2*dx-dy)/(2*d);
|
||||
map[n++] = ((cx*3 + 8) & 0x0F) | (((cy*3 + 8) & 0x0F)<<4);
|
||||
}
|
||||
}})();
|
||||
require("Storage").write("geissclk.3.map",map);
|
||||
E.showMessage("Precomputing\nmaps\n\nPlease wait...\n4 / 5");
|
||||
// blur down
|
||||
(function() { "ram"; var n=0; for (var y=0;y<H;y++) {
|
||||
for (var x=0;x<W;x++) {
|
||||
map[n++] = 136 - 6*16 + (y&1)*8-4;
|
||||
}
|
||||
}})()
|
||||
require("Storage").write("geissclk.4.map",map);
|
||||
E.showMessage("Precomputing\nmaps\n\nPlease wait...\n5 / 5");
|
||||
// twisty
|
||||
(function() { "ram"; var n=0; for (var y=0;y<H;y++) {
|
||||
for (var x=0;x<W;x++) {
|
||||
dx = Math.sin(y*0.2);
|
||||
dy = Math.cos(x*0.2);
|
||||
map[n++] = ((dx*6 + 8) & 0x0F) | (((dy*6 + 8) & 0x0F)<<4);
|
||||
}
|
||||
}})()
|
||||
require("Storage").write("geissclk.5.map",map)
|
||||
E.showMessage("Finished!");
|
|
@ -0,0 +1 @@
|
|||
0.01: Initial Release
|
|
@ -0,0 +1,18 @@
|
|||
# Sliding Text Clock - See the time in different languages
|
||||
|
||||
Inspired by the Pebble sliding clock, old times are scrolled off the screen and new times on. You are also able to change language on the fly so you can see the time written in other languages using button 1. Currently only English, French and Japanese are supported
|
||||
|
||||

|
||||
|
||||
## Usage
|
||||
|
||||
Use Button 1 (the top right button) to change the language
|
||||
|
||||
|
||||
## Requests
|
||||
|
||||
[Reach out to Adrian](https://www.github.com/awkirk71) if you have feature requests or notice bugs.
|
||||
|
||||
## Creator
|
||||
|
||||
Made by [Adrian Kirk](https://www.github.com/awkirk71).
|
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("lEowkE/4A/AH//+czCSKbBgXziYhKEQXxgMQgERgASIkf/CgM/+UBiISBCZAUBn4DBmcybAUTHZIUEmEQkcgl5gLJ4XygQCBj50MAYUQCZ3/HgXwkcyiZqBJxQoC+UD+cPXBIIBBQcwHIMBNwpNFDocQBoMwM4IUEn4kBMIZ3DAAMgQYoZCCYg6CVhKJFEQJyIn4VBkZrFDAgIJBxCqHO5DmJT4v/mQSIKxA+DDIIADCRJEDZgRCKIgjUHHJAPCPhprFExwSDJRgPDN5oUHCJ4A/AH4AIA=="))
|
|
@ -0,0 +1,475 @@
|
|||
/**
|
||||
* Adrian Kirk 2021-02
|
||||
* Sliding text clock inspired by the Pebble
|
||||
* clock with the same name
|
||||
*/
|
||||
|
||||
|
||||
class ShiftText {
|
||||
/**
|
||||
* Class Responsible for shifting text around the screen
|
||||
*
|
||||
* This is a object that initializes itself with a position and
|
||||
* text after which you can tell it where you want to move to
|
||||
* using the moveTo method and it will smoothly move the text across
|
||||
* at the selected frame rate and speed
|
||||
*/
|
||||
constructor(x,y,txt,font_name,
|
||||
font_size,speed_x,speed_y,freq_millis, color){
|
||||
this.x = x;
|
||||
this.tgt_x = x;
|
||||
this.init_x = x;
|
||||
this.y = y;
|
||||
this.tgt_y = y;
|
||||
this.init_y = y;
|
||||
this.txt = txt;
|
||||
this.init_txt = txt;
|
||||
this.font_name = font_name;
|
||||
this.font_size = font_size;
|
||||
this.speed_x = Math.abs(speed_x);
|
||||
this.speed_y = Math.abs(speed_y);
|
||||
this.freq_millis = freq_millis;
|
||||
this.colour = color;
|
||||
this.finished_callback=null;
|
||||
this.timeoutId = null;
|
||||
}
|
||||
reset(){
|
||||
this.hide();
|
||||
this.x = this.init_x;
|
||||
this.y = this.init_y;
|
||||
this.txt = this.init_txt;
|
||||
this.show();
|
||||
if(this.timeoutId != null){
|
||||
clearTimeout(this.timeoutId);
|
||||
}
|
||||
}
|
||||
show() {
|
||||
g.setFont(this.font_name,this.font_size);
|
||||
g.setColor(this.colour[0],this.colour[1],this.colour[2]);
|
||||
g.drawString(this.txt, this.x, this.y);
|
||||
}
|
||||
hide(){
|
||||
g.setFont(this.font_name,this.font_size);
|
||||
g.setColor(0,0,0);
|
||||
g.drawString(this.txt, this.x, this.y);
|
||||
}
|
||||
setText(txt){
|
||||
this.txt = txt;
|
||||
}
|
||||
setTextPosition(txt,x,y){
|
||||
this.hide();
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.txt = txt;
|
||||
this.show();
|
||||
}
|
||||
setTextXPosition(txt,x){
|
||||
this.hide();
|
||||
this.x = x;
|
||||
this.txt = txt;
|
||||
this.show();
|
||||
}
|
||||
setTextYPosition(txt,y){
|
||||
this.hide();
|
||||
this.y = y;
|
||||
this.txt = txt;
|
||||
this.show();
|
||||
}
|
||||
moveTo(new_x,new_y){
|
||||
this.tgt_x = new_x;
|
||||
this.tgt_y = new_y;
|
||||
this._doMove();
|
||||
}
|
||||
moveToX(new_x){
|
||||
this.tgt_x = new_x;
|
||||
this._doMove();
|
||||
}
|
||||
moveToY(new_y){
|
||||
this.tgt_y = new_y;
|
||||
this._doMove();
|
||||
}
|
||||
onFinished(finished_callback){
|
||||
this.finished_callback = finished_callback;
|
||||
}
|
||||
/**
|
||||
* private internal method for directing the text move.
|
||||
* It will see how far away we are from the target coords
|
||||
* and move towards the target at the defined speed.
|
||||
*/
|
||||
_doMove(){
|
||||
this.hide();
|
||||
// move closer to the target in the x direction
|
||||
diff_x = this.tgt_x - this.x;
|
||||
finished_x = false;
|
||||
if(Math.abs(diff_x) <= this.speed_x){
|
||||
this.x = this.tgt_x;
|
||||
finished_x = true;
|
||||
} else {
|
||||
if(diff_x > 0){
|
||||
this.x += this.speed_x;
|
||||
} else {
|
||||
this.x -= this.speed_x;
|
||||
}
|
||||
}
|
||||
// move closer to the target in the y direction
|
||||
diff_y = this.tgt_y - this.y;
|
||||
finished_y = false;
|
||||
if(Math.abs(diff_y) <= this.speed_y){
|
||||
this.y = this.tgt_y;
|
||||
finished_y = true;
|
||||
} else {
|
||||
if(diff_y > 0){
|
||||
this.y += this.speed_y;
|
||||
} else {
|
||||
this.y -= this.speed_y;
|
||||
}
|
||||
}
|
||||
this.show();
|
||||
this.timeoutId = null;
|
||||
finished = finished_x & finished_y;
|
||||
if(!finished){
|
||||
this.timeoutId = setTimeout(this._doMove.bind(this), this.freq_millis);
|
||||
} else if(this.finished_callback != null){
|
||||
this.finished_callback.call();
|
||||
this.finished_callback = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DateFormatter {
|
||||
/**
|
||||
* A pure virtual class which all the other date formatters will
|
||||
* inherit from.
|
||||
* The name will be used to declare the date format when selected
|
||||
* and the date formatDate methid will return the time formated
|
||||
* to the lines of text on the screen
|
||||
*/
|
||||
name(){"no name";}
|
||||
formatDate(date){
|
||||
return ["","",""];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* English date formatting
|
||||
*/
|
||||
|
||||
// English String Numbers
|
||||
const numberStr = ["ZERO","ONE", "TWO", "THREE", "FOUR", "FIVE",
|
||||
"SIX", "SEVEN","EIGHT", "NINE", "TEN",
|
||||
"ELEVEN", "TWELVE", "THIRTEEN", "FOURTEEN",
|
||||
"FIFTEEN", "SIXTEEN", "SEVENTEEN", "EIGHTEEN",
|
||||
"NINETEEN", "TWENTY"];
|
||||
const tensStr = ["ZERO", "TEN", "TWENTY", "THIRTY", "FOURTY",
|
||||
"FIFTY"];
|
||||
|
||||
function hoursToText(hours){
|
||||
hours = hours % 12;
|
||||
if(hours == 0){
|
||||
hours = 12;
|
||||
}
|
||||
return numberStr[hours];
|
||||
}
|
||||
|
||||
function numberToText(value){
|
||||
word1 = '';
|
||||
word2 = '';
|
||||
if(value > 20){
|
||||
tens = (value / 10 | 0);
|
||||
word1 = tensStr[tens];
|
||||
remainder = value - tens * 10;
|
||||
if(remainder > 0){
|
||||
word2 = numberStr[remainder];
|
||||
}
|
||||
} else if(value > 0) {
|
||||
word1 = numberStr[value];
|
||||
}
|
||||
return [word1,word2];
|
||||
}
|
||||
|
||||
class EnglishDateFormatter extends DateFormatter{
|
||||
name(){return "English";}
|
||||
formatDate(date){
|
||||
hours_txt = hoursToText(date.getHours());
|
||||
mins_txt = numberToText(date.getMinutes());
|
||||
return [hours_txt,mins_txt[0],mins_txt[1]];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* French date formatting
|
||||
*/
|
||||
const frenchNumberStr = [ "ZERO", "UNE", "DEUX", "TROIS", "QUATRE",
|
||||
"CINQ", "SIX", "SEPT", "HUIT", "NEUF", "DIX",
|
||||
"ONZE", "DOUZE", "TREIZE", "QUATORZE","QUINZE",
|
||||
"SEIZE", "DIX SEPT", "DIX HUIT","DIX NEUF", "VINGT",
|
||||
"VINGT ET UN", "VINGT DEUX", "VINGT TROIS",
|
||||
"VINGT QUATRE", "VINGT CINQ", "VINGT SIX",
|
||||
"VINGT SEPT", "VINGT HUIT", "VINGT NEUF"
|
||||
];
|
||||
|
||||
function frenchHoursToText(hours){
|
||||
hours = hours % 12;
|
||||
if(hours == 0){
|
||||
hours = 12;
|
||||
}
|
||||
return frenchNumberStr[hours];
|
||||
}
|
||||
|
||||
function frenchHeures(hours){
|
||||
if(hours % 12 == 1){
|
||||
return 'HEURE';
|
||||
} else {
|
||||
return 'HEURES';
|
||||
}
|
||||
}
|
||||
|
||||
class FrenchDateFormatter extends DateFormatter {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
name(){return "French";}
|
||||
formatDate(date){
|
||||
hours = frenchHoursToText(date.getHours());
|
||||
heures = frenchHeures(date.getHours());
|
||||
mins = date.getMinutes();
|
||||
if(mins == 0){
|
||||
if(hours == 0){
|
||||
return ["MINUIT", "",""];
|
||||
} else if(hours == 12){
|
||||
return ["MIDI", "",""];
|
||||
} else {
|
||||
return [hours, heures,""];
|
||||
}
|
||||
} else if(mins == 30){
|
||||
return [hours, heures,'ET DEMIE'];
|
||||
} else if(mins == 15){
|
||||
return [hours, heures,'ET QUERT'];
|
||||
} else if(mins == 45){
|
||||
next_hour = date.getHours() + 1;
|
||||
hours = frenchHoursToText(next_hour);
|
||||
heures = frenchHeures(next_hour);
|
||||
return [hours, heures,"MOINS",'LET QUERT'];
|
||||
}
|
||||
if(mins > 30){
|
||||
to_mins = 60-mins;
|
||||
mins_txt = frenchNumberStr[to_mins];
|
||||
next_hour = date.getHours() + 1;
|
||||
hours = frenchHoursToText(next_hour);
|
||||
heures = frenchHeures(next_hour);
|
||||
return [ hours, heures , "MOINS", mins_txt ];
|
||||
} else {
|
||||
mins_txt = frenchNumberStr[mins];
|
||||
return [ hours, heures , mins_txt ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Japanese date formatting
|
||||
*/
|
||||
const japaneseHourStr = [ "ZERO", "ICHII", "NI", "SAN", "YO",
|
||||
"GO", "ROKU", "SHICHI", "HACHI", "KU", "JUU",
|
||||
'JUU ICHI', 'JUU NI'];
|
||||
const tensPrefixStr = [ "",
|
||||
"JUU",
|
||||
'NIJUU',
|
||||
'SAN JUU',
|
||||
'YON JUU',
|
||||
'GO JUU'];
|
||||
|
||||
const japaneseMinuteStr = [ ["", "PUN"],
|
||||
["IP","PUN" ],
|
||||
["NI", "FUN"],
|
||||
["SAN", "PUN"],
|
||||
["YON","FUN"],
|
||||
["GO", "HUN"],
|
||||
["RO", "PUN"],
|
||||
["NANA", "FUN"],
|
||||
["HAP", "PUN"],
|
||||
["KYU","FUN"],
|
||||
["JUP", "PUN"]
|
||||
];
|
||||
|
||||
function japaneseHoursToText(hours){
|
||||
hours = hours % 12;
|
||||
if(hours == 0){
|
||||
hours = 12;
|
||||
}
|
||||
return japaneseHourStr[hours];
|
||||
}
|
||||
|
||||
function japaneseMinsToText(mins){
|
||||
if(mins == 0){
|
||||
return ["",""];
|
||||
} else if(mins == 30)
|
||||
return ["HAN",""];
|
||||
else {
|
||||
units = mins % 10;
|
||||
mins_txt = japaneseMinuteStr[units];
|
||||
tens = mins /10 | 0;
|
||||
if(tens > 0){
|
||||
tens_txt = tensPrefixStr[tens];
|
||||
return [tens_txt + ' ' + mins_txt[0], mins_txt[1]];
|
||||
} else {
|
||||
return [mins_txt[0], mins_txt[1]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class JapaneseDateFormatter extends DateFormatter {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
name(){return "Japanese (Romanji)";}
|
||||
formatDate(date){
|
||||
hours_txt = japaneseHoursToText(date.getHours());
|
||||
mins_txt = japaneseMinsToText(date.getMinutes());
|
||||
return [hours_txt,"JI", mins_txt[0], mins_txt[1] ];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Watch Display
|
||||
*/
|
||||
|
||||
// a list of display rows
|
||||
let row_displays = [
|
||||
new ShiftText(240,60,'',"Vector",40,10,10,40,[1,1,1]),
|
||||
new ShiftText(240,100,'',"Vector",20,10,10,50,[0.85,0.85,0.85]),
|
||||
new ShiftText(240,120,'',"Vector",20,10,10,60,[0.85,0.85,0.85]),
|
||||
new ShiftText(240,140,'',"Vector",20,10,10,70,[0.85,0.85,0.85])
|
||||
];
|
||||
|
||||
// a list of the formatters to cycle through
|
||||
let date_formatters = [
|
||||
new EnglishDateFormatter(),
|
||||
new FrenchDateFormatter(),
|
||||
new JapaneseDateFormatter()
|
||||
];
|
||||
|
||||
// current index of the date formatter to display
|
||||
let date_formatter_idx = 0;
|
||||
let date_formatter = date_formatters[date_formatter_idx];
|
||||
|
||||
// The small display at the top which announces the date format
|
||||
let format_name_display = new ShiftText(55,0,'',"Vector",10,1,1,50,[1,1,1]);
|
||||
|
||||
function changeFormatter(){
|
||||
date_formatter_idx += 1;
|
||||
if(date_formatter_idx >= date_formatters.length){
|
||||
date_formatter_idx = 0;
|
||||
}
|
||||
console.log("changing to formatter " + date_formatter_idx);
|
||||
date_formatter = date_formatters[date_formatter_idx];
|
||||
reset_clock();
|
||||
draw_clock();
|
||||
// now announce the formatter by name
|
||||
format_name_display.setTextYPosition(date_formatter.name(),-10);
|
||||
format_name_display.moveToY(15);
|
||||
// and then move back
|
||||
format_name_display.onFinished(
|
||||
function(){
|
||||
format_name_display.moveToY(-10);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function reset_clock(){
|
||||
//console.log("reset_clock");
|
||||
var i;
|
||||
for (i = 0; i < row_displays.length; i++) {
|
||||
row_displays[i].reset();
|
||||
}
|
||||
}
|
||||
|
||||
function draw_clock(){
|
||||
//console.log("draw_clock");
|
||||
date = new Date();
|
||||
rows = date_formatter.formatDate(date);
|
||||
var i;
|
||||
for (i = 0; i < rows.length; i++) {
|
||||
display = row_displays[i];
|
||||
txt = rows[i];
|
||||
display_row(display,txt);
|
||||
}
|
||||
// If the dateformatter has not returned enough
|
||||
// rows then treat the reamining rows as empty
|
||||
for (j = i; j < row_displays.length; j++) {
|
||||
display = row_displays[j];
|
||||
display_row(display,'');
|
||||
}
|
||||
//console.log(date);
|
||||
}
|
||||
|
||||
function display_row(display,txt){
|
||||
if(display.txt == ''){
|
||||
display.setTextXPosition(txt,240);
|
||||
display.moveToX(20);
|
||||
} else if(txt != display.txt){
|
||||
display.moveToX(-100);
|
||||
display.onFinished(
|
||||
function(){
|
||||
display.setTextXPosition(txt,240);
|
||||
display.moveToX(20);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
display.setTextXPosition(txt,20);
|
||||
}
|
||||
}
|
||||
|
||||
// The interval reference for updating the clock
|
||||
let intervalRef = null;
|
||||
|
||||
function clearTimers(){
|
||||
if(intervalRef) {
|
||||
clearInterval(intervalRef);
|
||||
intervalRef = null;
|
||||
}
|
||||
}
|
||||
|
||||
function startTimers(){
|
||||
let date = new Date();
|
||||
let secs = date.getSeconds();
|
||||
let nextMinuteStart = 60 - secs;
|
||||
//console.log("scheduling clock draw in " + nextMinuteStart + " seconds");
|
||||
setTimeout(scheduleDrawClock,nextMinuteStart * 1000);
|
||||
draw_clock();
|
||||
}
|
||||
|
||||
function scheduleDrawClock(){
|
||||
//console.log("scheduleDrawClock");
|
||||
if(intervalRef) clearTimers();
|
||||
intervalRef = setInterval(draw_clock, 60*1000);
|
||||
draw_clock();
|
||||
}
|
||||
|
||||
Bangle.on('lcdPower', (on) => {
|
||||
if (on) {
|
||||
console.log("lcdPower: on");
|
||||
Bangle.drawWidgets();
|
||||
reset_clock();
|
||||
startTimers();
|
||||
} else {
|
||||
console.log("lcdPower: off");
|
||||
reset_clock();
|
||||
clearTimers();
|
||||
}
|
||||
});
|
||||
Bangle.on('faceUp',function(up){
|
||||
//console.log("faceUp: " + up + " LCD: " + Bangle.isLCDOn());
|
||||
if (up && !Bangle.isLCDOn()) {
|
||||
//console.log("faceUp and LCD off");
|
||||
clearTimers();
|
||||
Bangle.setLCDPower(true);
|
||||
}
|
||||
});
|
||||
|
||||
g.clear();
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
startTimers();
|
||||
// Show launcher when middle button pressed
|
||||
setWatch(Bangle.showLauncher, BTN2,{repeat:false,edge:"falling"});
|
||||
setWatch(changeFormatter, BTN1,{repeat:true,edge:"falling"});
|
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
|
@ -0,0 +1,2 @@
|
|||
0.01: First version of the Walkers Clock
|
||||
0.02: Fixed screen flicker
|
|
@ -57,7 +57,5 @@ on the second line of the watch.
|
|||

|
||||
|
||||
## Future Enhancements
|
||||
* Ability to turn on the Heart Rate monitor
|
||||
* Ability to turn on the heart rate monitor and display the rate on the info line
|
||||
* Maybe a simple stopwatch capability
|
||||
* Fix the screen flicker
|
||||
|
||||
|
|
|
@ -46,6 +46,10 @@ let gpsPowerState = false;
|
|||
let infoMode = INFO_NONE;
|
||||
let functionMode = FN_MODE_OFF;
|
||||
let gpsDisplay = GDISP_OS;
|
||||
let prevInfoStr = "clear";
|
||||
let prevActivityStr = "clear";
|
||||
let prevSteps = "clear";
|
||||
let clearActivityArea = true;
|
||||
|
||||
let last_steps = undefined;
|
||||
let firstPress = 0;
|
||||
|
@ -66,7 +70,8 @@ function drawTime() {
|
|||
var time = da[4].substr(0,5);
|
||||
|
||||
g.reset();
|
||||
g.clearRect(0,24,239,239);
|
||||
g.clearRect(0,Y_TIME, 239, Y_ACTIVITY - 1);
|
||||
|
||||
g.setColor(1,1,1); // white
|
||||
g.setFontAlign(0, -1);
|
||||
|
||||
|
@ -81,15 +86,22 @@ function drawTime() {
|
|||
g.drawString(time, g.getWidth()/2, Y_TIME);
|
||||
}
|
||||
|
||||
function drawSteps() {
|
||||
function drawActivity() {
|
||||
var steps = getSteps();
|
||||
if (!gpsPowerState && steps != prevSteps)
|
||||
clearActivityArea = true;
|
||||
|
||||
prevSteps = steps;
|
||||
|
||||
if (clearActivityArea) {
|
||||
g.clearRect(0, Y_ACTIVITY, 239, Y_MODELINE - 1);
|
||||
clearActivityArea = false;
|
||||
}
|
||||
|
||||
if (!gpsPowerState) {
|
||||
g.setColor(0,255,0); // green
|
||||
g.setFont("Vector", 60);
|
||||
g.drawString(getSteps(), g.getWidth()/2, Y_ACTIVITY);
|
||||
}
|
||||
|
||||
function drawActivity() {
|
||||
if (!gpsPowerState) {
|
||||
drawSteps();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -116,6 +128,7 @@ function drawActivity() {
|
|||
let os = OsGridRef.latLongToOsGrid(last_fix);
|
||||
let ref = to_map_ref(6, os.easting, os.northing);
|
||||
let speed;
|
||||
let activityStr = "";
|
||||
|
||||
if (age < 0) age = 0;
|
||||
g.setFontVector(40);
|
||||
|
@ -123,18 +136,20 @@ function drawActivity() {
|
|||
|
||||
switch(gpsDisplay) {
|
||||
case GDISP_OS:
|
||||
g.drawString(ref, 120, Y_ACTIVITY, true);
|
||||
activityStr = ref;
|
||||
break;
|
||||
case GDISP_SPEED:
|
||||
speed = last_fix.speed;
|
||||
speed = speed.toFixed(1);
|
||||
g.drawString(speed + "kph", 120, Y_ACTIVITY, true);
|
||||
activityStr = speed + "kph"
|
||||
break;
|
||||
case GDISP_ALT:
|
||||
g.drawString(last_fix.alt + "m" , 120, Y_ACTIVITY, true);
|
||||
activityStr = last_fix.alt + "m";
|
||||
break;
|
||||
}
|
||||
|
||||
g.clearRect(0, Y_ACTIVITY, 239, Y_MODELINE - 1);
|
||||
g.drawString(activityStr, 120, Y_ACTIVITY);
|
||||
g.setFont("6x8",2);
|
||||
g.setColor(1,1,1);
|
||||
g.drawString(age, 120, Y_ACTIVITY + 46);
|
||||
|
@ -174,8 +189,6 @@ function drawInfo() {
|
|||
let str = "";
|
||||
let col = 0x07E0; // green
|
||||
|
||||
//console.log("drawInfo(), infoMode=" + infoMode + " funcMode=" + functionMode);
|
||||
|
||||
switch(functionMode) {
|
||||
case FN_MODE_OFF:
|
||||
break;
|
||||
|
@ -217,7 +230,13 @@ function drawInfo() {
|
|||
drawModeLine(str,col);
|
||||
}
|
||||
|
||||
function drawModeLine(str, col) {
|
||||
function drawModeLine(str,col) {
|
||||
// check if we need to draw, avoid flicker
|
||||
if (str == prevInfoStr)
|
||||
return;
|
||||
|
||||
prevInfoStr = str;
|
||||
drawModeLine(str,col);
|
||||
g.setFont("6x8", 3);
|
||||
g.setColor(col);
|
||||
g.fillRect(0, Y_MODELINE - 3, 239, Y_MODELINE + 25);
|
||||
|
@ -249,7 +268,7 @@ function changeInfoMode() {
|
|||
}
|
||||
functionMode = FN_MODE_OFF;
|
||||
infoMode = INFO_NONE;
|
||||
//drawInfo();
|
||||
clearActivityArea = true;
|
||||
return;
|
||||
|
||||
case FN_MODE_GDISP:
|
||||
|
@ -281,7 +300,8 @@ function changeInfoMode() {
|
|||
default:
|
||||
infoMode = INFO_NONE;
|
||||
}
|
||||
//drawInfo();
|
||||
|
||||
clearActivityArea = true;
|
||||
}
|
||||
|
||||
function changeFunctionMode() {
|
||||
|
@ -331,11 +351,16 @@ function resetLastFix() {
|
|||
function processFix(fix) {
|
||||
last_fix.time = fix.time;
|
||||
|
||||
if (gpsState == GPS_TIME)
|
||||
if (gpsState == GPS_TIME) {
|
||||
gpsState = GPS_SATS;
|
||||
clearActivityArea = true;
|
||||
}
|
||||
|
||||
if (fix.fix) {
|
||||
if (!last_fix.fix) Bangle.buzz(); // buzz on first position
|
||||
if (!last_fix.fix) {
|
||||
Bangle.buzz(); // buzz on first position
|
||||
clearActivityArea = true;
|
||||
}
|
||||
gpsState = GPS_RUNNING;
|
||||
last_fix = fix;
|
||||
}
|
||||
|
@ -508,7 +533,13 @@ drawAll();
|
|||
Bangle.on('lcdPower',function(on) {
|
||||
functionMode = FN_MODE_OFF;
|
||||
infoMode = INFO_NONE;
|
||||
if (on) drawAll();
|
||||
if (on) {
|
||||
prevInfoStr = "on"; // forces are redraw
|
||||
drawAll();
|
||||
} else {
|
||||
prevInfoStr = "off"; // forces are redraw
|
||||
drawInfo();
|
||||
}
|
||||
});
|
||||
|
||||
var click = setInterval(onTick, 5000);
|
||||
|
|
Loading…
Reference in New Issue