Merge branch 'master' into ts-lint

pull/3390/head
Anton 2024-05-17 14:40:01 +02:00 committed by GitHub
commit ceb8039711
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
134 changed files with 2276 additions and 975 deletions

View File

@ -6,6 +6,8 @@ Bangle.js App Loader (and Apps)
* Try the **release version** at [banglejs.com/apps](https://banglejs.com/apps)
* Try the **development version** at [espruino.github.io](https://espruino.github.io/BangleApps/)
The release version is manually refreshed with regular intervals while the development version is continuously updated as new code is committed to this repository.
**All software (including apps) in this repository is MIT Licensed - see [LICENSE](LICENSE)** By
submitting code to this repository you confirm that you are happy with it being MIT licensed,
and that it is not licensed in another way that would make this impossible.

View File

@ -6,7 +6,7 @@
"version":"0.12",
"icon": "app.png",
"type": "app",
"tags": "tool,activity",
"tags": "tool,activity,health",
"supports": ["BANGLEJS", "BANGLEJS2"],
"readme": "README.md",
"storage": [

View File

@ -1,2 +1,3 @@
0.01: New Clock!
0.02: Fix fastloading memory leak and clockinfo overwritten by hands
0.03: Use new clockinfo lib with function to render images wirh borders

View File

@ -102,6 +102,7 @@
// Show launcher when middle button pressed
Bangle.setUI({
mode: "clock",
redraw : draw,
remove: function() {
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
@ -116,9 +117,6 @@
Bangle.loadWidgets();
require("widget_utils").hide();
// used for clockinfo image rendering
let clockInfoG = Graphics.createArrayBuffer(28, 28, 2, {msb:true});
clockInfoG.transparent = 3;
// render clockinfos
let clockInfoDraw = function(itm, info, options) {
// itm: the item containing name/hasRange/etc
@ -139,18 +137,8 @@
fg = g.toColor("#f00");
}
if (info.img) {
//g.drawImage(info.img, left ? 2 : W - 27, top ? 18 : H - 41); // draw the image
// fiddle around colouring the border and inside of the image
clockInfoG.clear(1);
// do a border - images need to be transparent for this
clockInfoG.setColor(2).drawImage(info.img, 1,1).drawImage(info.img, 3,1).
drawImage(info.img, 1,3).drawImage(info.img, 3,3);
clockInfoG.setColor(1).drawImage(info.img, 2,2); // main image
clockInfoG.floodFill(27,27,3); // flood fill edge to transparent
clockInfoG.palette = new Uint16Array([bg,fg,bg/*border*/, g.toColor("#888")]);
g.drawImage(clockInfoG, imgx-1, imgy-1);
}
if (info.img)
require("clock_info").drawBorderedImage(info.img,imgx,imgy);
g.setFont("6x8:2").setFontAlign(left ? -1 : 1, -1);
g.setColor(bg).drawString(info.text, textx-2, texty). // draw the text background

View File

@ -1,7 +1,7 @@
{ "id": "analogquadclk",
"name": "Analog Quad Clock",
"shortName":"Quad Clock",
"version":"0.02",
"version":"0.03",
"description": "An analog clock with clockinfos in each of the 4 corners, allowing 4 different data types to be rendered at once",
"icon": "icon.png",
"screenshots" : [ { "url":"screenshot.png" }, { "url":"screenshot2.png" } ],

View File

@ -1,4 +1,7 @@
0.01: Release
0.02: Rename app
0.03: Add type "clock"
0.04: changed update cylce, when locked
0.04: Changed update cylce, when locked
0.05: Fix support for dark theme + support widgets +
add settings for widgets, order of drawing and hour hand length
0.06: Fix issue showing widgets when app is fast-loaded into from launcher with widgets disabled

View File

@ -1,10 +1,16 @@
# Analog Clock
# Dark Analog Clock
## Features
* second hand
* second hand (only on unlocked screen)
* date
* battery percantage
* no widgets
* battery percentage (showing charge status with color)
* turned off or swipeable widgets (choose in settings)
![logo](andark_screen.png)
## Settings
* whether to load widgets, or not; if widgets are loaded, they are swipeable from the top; if not, NO ACTIONS of widgets are available
* date and battery can be printed both below hands (as if hands were physical) and above (more readable)
* hour hand can be made slighly shorter to improve readability when minute hand is behind a number

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -1,22 +1,43 @@
const defaultSettings = {
loadWidgets : false,
textAboveHands : false,
shortHrHand : false
};
const settings = Object.assign(defaultSettings, require('Storage').readJSON('andark.json',1)||{});
const c={"x":g.getWidth()/2,"y":g.getHeight()/2};
let zahlpos=[];
const zahlpos=(function() {
let z=[];
let sk=1;
for(let i=-10;i<50;i+=5){
let win=i*2*Math.PI/60;
let xsk =c.x+2+Math.cos(win)*(c.x-10),
ysk =c.y+2+Math.sin(win)*(c.x-10);
if(sk==3){xsk-=10;}
if(sk==6){ysk-=10;}
if(sk==9){xsk+=10;}
if(sk==12){ysk+=10;}
if(sk==10){xsk+=3;}
z.push([sk,xsk,ysk]);
sk+=1;
}
return z;
})();
let unlock = false;
function zeiger(len,dia,tim){
const x =c.x+ Math.cos(tim)*len/2,
y =c.y + Math.sin(tim)*len/2,
const x=c.x+ Math.cos(tim)*len/2,
y=c.y + Math.sin(tim)*len/2,
d={"d":3,"x":dia/2*Math.cos(tim+Math.PI/2),"y":dia/2*Math.sin(tim+Math.PI/2)},
pol=[c.x-d.x,c.y-d.y,c.x+d.x,c.y+d.y,x+d.x,y+d.y,x-d.x,y-d.y];
return pol;
}
function draw(){
const d=new Date();
function drawHands(d) {
let m=d.getMinutes(), h=d.getHours(), s=d.getSeconds();
//draw black rectangle in the middle to clear screen from scale and hands
g.setColor(0,0,0);
g.fillRect(10,10,2*c.x-10,2*c.x-10);
g.setColor(1,1,1);
if(h>12){
@ -29,17 +50,10 @@ function draw(){
m=2*Math.PI/60*(m)-Math.PI/2;
s=2*Math.PI/60*s-Math.PI/2;
g.setFontAlign(0,0);
g.setFont("Vector",10);
let dateStr = " "+require("locale").date(d)+" ";
g.drawString(dateStr, c.x, c.y+20, true);
// g.drawString(d.getDate(),1.4*c.x,c.y,true);
g.drawString(Math.round(E.getBattery()/5)*5+"%",c.x,c.y+40,true);
drawlet();
//g.setColor(1,0,0);
const hz = zeiger(100,5,h);
const hz = zeiger(settings.shortHrHand?88:100,5,h);
g.fillPoly(hz,true);
// g.setColor(1,1,1);
//g.setColor(1,1,1);
const minz = zeiger(150,5,m);
g.fillPoly(minz,true);
if (unlock){
@ -47,12 +61,53 @@ function draw(){
g.fillPoly(sekz,true);
}
g.fillCircle(c.x,c.y,4);
}
function drawText(d) {
g.setFont("Vector",10);
g.setBgColor(0,0,0);
g.setColor(1,1,1);
let dateStr = require("locale").date(d);
g.drawString(dateStr, c.x, c.y+20, true);
let batStr = Math.round(E.getBattery()/5)*5+"%";
if (Bangle.isCharging()) {
g.setBgColor(1,0,0);
}
g.drawString(batStr, c.x, c.y+40, true);
}
function drawNumbers() {
//draws the numbers on the screen
g.setFont("Vector",20);
g.setColor(1,1,1);
g.setBgColor(0,0,0);
for(let i = 0;i<12;i++){
g.drawString(zahlpos[i][0],zahlpos[i][1],zahlpos[i][2],true);
}
}
function draw(){
// draw black rectangle in the middle to clear screen from scale and hands
g.setColor(0,0,0);
g.fillRect(10,10,2*c.x-10,2*c.x-10);
// prepare for drawing the text
g.setFontAlign(0,0);
// do drawing
drawNumbers();
const d=new Date();
if (settings.textAboveHands) {
drawHands(d); drawText(d);
} else {
drawText(d); drawHands(d);
}
}
//draws the scale once the app is startet
function drawScale(){
// clear the screen
g.setBgColor(0,0,0);
g.clear();
// draw the ticks of the scale
for(let i=-14;i<47;i++){
const win=i*2*Math.PI/60;
let d=2;
@ -64,40 +119,22 @@ function drawScale(){
}
}
//draws the numbers on the screen
//// main running sequence ////
function drawlet(){
g.setFont("Vector",20);
for(let i = 0;i<12;i++){
g.drawString(zahlpos[i][0],zahlpos[i][1],zahlpos[i][2]);
}
}
//calcultes the Position of the numbers when app starts and saves them in an array
function setlet(){
let sk=1;
for(let i=-10;i<50;i+=5){
let win=i*2*Math.PI/60;
let xsk =c.x+2+Math.cos(win)*(c.x-10),
ysk =c.y+2+Math.sin(win)*(c.x-10);
if(sk==3){xsk-=10;}
if(sk==6){ysk-=10;}
if(sk==9){xsk+=10;}
if(sk==12){ysk+=10;}
if(sk==10){xsk+=3;}
zahlpos.push([sk,xsk,ysk]);
sk+=1;
}
}
setlet();
// Show launcher when middle button pressed, and widgets that we're clock
Bangle.setUI("clock");
// Load widgets if needed, and make them show swipeable
if (settings.loadWidgets) {
Bangle.loadWidgets();
require("widget_utils").swipeOn();
} else if (global.WIDGETS) require("widget_utils").hide();
// Clear the screen once, at startup
g.setBgColor(0,0,0);
g.clear();
drawScale();
draw();
let secondInterval= setInterval(draw, 1000);
// Stop updates when LCD is off, restart when on
let secondInterval = setInterval(draw, 1000);
// Stop updates when LCD is off, restart when on
Bangle.on('lcdPower',on=>{
if (secondInterval) clearInterval(secondInterval);
secondInterval = undefined;
@ -107,18 +144,9 @@ Bangle.on('lcdPower',on=>{
}
});
Bangle.on('lock',on=>{
unlock = !on;
if (secondInterval) clearInterval(secondInterval);
secondInterval = undefined;
if (!on) {
secondInterval = setInterval(draw, 1000);
unlock = true;
secondInterval = setInterval(draw, unlock ? 1000 : 60000);
draw(); // draw immediately
}else{
secondInterval = setInterval(draw, 60000);
unlock = false;
draw();
}
});
// Show launcher when middle button pressed
Bangle.setUI("clock");
});
Bangle.on('charging',on=>{draw();});

View File

@ -1,15 +1,18 @@
{ "id": "andark",
"name": "Analog Dark",
"shortName":"AnDark",
"version":"0.04",
"version":"0.06",
"description": "analog clock face without disturbing widgets",
"icon": "andark_icon.png",
"type": "clock",
"tags": "clock",
"supports" : ["BANGLEJS2"],
"screenshots": [{"url":"andark_screen.png"}],
"readme": "README.md",
"storage": [
{"name":"andark.app.js","url":"app.js"},
{"name":"andark.settings.js","url":"settings.js"},
{"name":"andark.img","url":"app_icon.js","evaluate":true}
]
],
"data": [{"name":"andark.json"}]
}

28
apps/andark/settings.js Normal file
View File

@ -0,0 +1,28 @@
(function(back) {
const defaultSettings = {
loadWidgets : false,
textAboveHands : false,
shortHrHand : false
}
let settings = Object.assign(defaultSettings, require('Storage').readJSON('andark.json',1)||{});
const save = () => require('Storage').write('andark.json', settings);
const appMenu = {
'': {title: 'andark'}, '< Back': back,
/*LANG*/'Load widgets': {
value : !!settings.loadWidgets,
onchange : v => { settings.loadWidgets=v; save();}
},
/*LANG*/'Text above hands': {
value : !!settings.textAboveHands,
onchange : v => { settings.textAboveHands=v; save();}
},
/*LANG*/'Short hour hand': {
value : !!settings.shortHrHand,
onchange : v => { settings.shortHrHand=v; save();}
},
};
E.showMenu(appMenu);
});

View File

@ -1,153 +0,0 @@
let result = true;
function assertTrue(condition, text) {
if (!condition) {
result = false;
print("FAILURE: " + text);
} else print("OK: " + text);
}
function assertFalse(condition, text) {
assertTrue(!condition, text);
}
function assertUndefinedOrEmpty(array, text) {
assertTrue(!array || array.length == 0, text);
}
function assertNotEmpty(array, text) {
assertTrue(array && array.length > 0, text);
}
let internalOn = () => {
return getPinMode((process.env.HWVERSION==2)?D30:D26) == "input";
};
let sec = {
connected: false
};
NRF.getSecurityStatus = () => sec;
// add an empty starting point to make the asserts work
Bangle._PWR={};
let teststeps = [];
teststeps.push(()=>{
print("Not connected, should use internal GPS");
assertTrue(!NRF.getSecurityStatus().connected, "Not connected");
assertUndefinedOrEmpty(Bangle._PWR.GPS, "No GPS");
assertFalse(Bangle.isGPSOn(), "isGPSOn");
assertTrue(Bangle.setGPSPower(1, "test"), "Switch GPS on");
assertNotEmpty(Bangle._PWR.GPS, "GPS");
assertTrue(Bangle.isGPSOn(), "isGPSOn");
assertTrue(internalOn(), "Internal GPS on");
assertFalse(Bangle.setGPSPower(0, "test"), "Switch GPS off");
assertUndefinedOrEmpty(Bangle._PWR.GPS, "No GPS");
assertFalse(Bangle.isGPSOn(), "isGPSOn");
assertFalse(internalOn(), "Internal GPS off");
});
teststeps.push(()=>{
print("Connected, should use GB GPS");
sec.connected = true;
assertTrue(NRF.getSecurityStatus().connected, "Connected");
assertUndefinedOrEmpty(Bangle._PWR.GPS, "No GPS");
assertFalse(Bangle.isGPSOn(), "isGPSOn");
assertFalse(internalOn(), "Internal GPS off");
print("Internal GPS stays on until the first GadgetBridge event arrives");
assertTrue(Bangle.setGPSPower(1, "test"), "Switch GPS on");
assertNotEmpty(Bangle._PWR.GPS, "GPS");
assertTrue(Bangle.isGPSOn(), "isGPSOn");
assertTrue(internalOn(), "Internal GPS on");
print("Send minimal GadgetBridge GPS event to trigger switch");
GB({t:"gps"});
});
teststeps.push(()=>{
print("GPS should be on, internal off");
assertNotEmpty(Bangle._PWR.GPS, "GPS");
assertTrue(Bangle.isGPSOn(), "isGPSOn");
assertFalse(internalOn(), "Internal GPS off");
});
teststeps.push(()=>{
print("Switching GPS off turns both GadgetBridge as well as internal off");
assertFalse(Bangle.setGPSPower(0, "test"), "Switch GPS off");
assertUndefinedOrEmpty(Bangle._PWR.GPS, "No GPS");
assertFalse(Bangle.isGPSOn(), "isGPSOn");
assertFalse(internalOn(), "Internal GPS off");
});
teststeps.push(()=>{
print("Wait for all timeouts to run out");
return 12000;
});
teststeps.push(()=>{
print("Check auto switch when no GPS event arrives");
assertTrue(Bangle.setGPSPower(1, "test"), "Switch GPS on");
assertNotEmpty(Bangle._PWR.GPS, "GPS");
assertTrue(Bangle.isGPSOn(), "isGPSOn");
assertTrue(internalOn(), "Internal GPS on");
print("Send minimal GadgetBridge GPS event to trigger switch");
GB({t:"gps"});
print("Internal should be switched off now");
assertNotEmpty(Bangle._PWR.GPS, "GPS");
assertTrue(Bangle.isGPSOn(), "isGPSOn");
assertFalse(internalOn(), "Internal GPS off");
//wait on next test
return 12000;
});
teststeps.push(()=>{
print("Check state and disable GPS, internal should be on");
assertNotEmpty(Bangle._PWR.GPS, "GPS");
assertTrue(Bangle.isGPSOn(), "isGPSOn");
assertTrue(internalOn(), "Internal GPS on");
assertFalse(Bangle.setGPSPower(0, "test"), "Switch GPS off");
});
teststeps.push(()=>{
print("Result Overall is " + (result ? "OK" : "FAIL"));
});
let wrap = (functions) => {
if (functions.length > 0) {
setTimeout(()=>{
let waitingTime = functions.shift()();
if (waitingTime){
print("WAITING: ", waitingTime);
setTimeout(()=>{wrap(functions);}, waitingTime);
} else
wrap(functions);
},0);
}
};
setTimeout(()=>{
wrap(teststeps);
}, 5000);

99
apps/android/test.json Normal file
View File

@ -0,0 +1,99 @@
{
"app" : "android",
"setup" : [{
"id": "default",
"steps" : [
{"t":"cmd", "js": "Bangle.setGPSPower=(isOn, appID)=>{if (!appID) appID='?';if (!Bangle._PWR) Bangle._PWR={};if (!Bangle._PWR.GPS) Bangle._PWR.GPS=[];if (isOn && !Bangle._PWR.GPS.includes(appID)) Bangle._PWR.GPS.push(appID);if (!isOn && Bangle._PWR.GPS.includes(appID)) Bangle._PWR.GPS.splice(Bangle._PWR.GPS.indexOf(appID),1);return Bangle._PWR.GPS.length>0;};", "text": "Fake the setGPSPower"},
{"t":"wrap", "fn": "Bangle.setGPSPower", "id": "gpspower"},
{"t":"cmd", "js": "Serial1.println = () => { }", "text": "Fake the serial port println"},
{"t":"cmd", "js": "Bluetooth.println = () => { }", "text": "Fake the Bluetooth println"},
{"t":"cmd", "js": "Bangle._PWR={}", "text": "Prepare an empty _PWR for following asserts"},
{"t":"cmd", "js": "require('Storage').writeJSON('android.settings.json', {overwriteGps: true})", "text": "Enable GPS overwrite"},
{"t":"cmd", "js": "eval(require('Storage').read('android.boot.js'))", "text": "Load the boot code"}
]
},{
"id": "connected",
"steps" : [
{"t":"cmd", "js": "NRF.getSecurityStatus = () => { return { connected: true };}", "text": "Control the security status to be connected"}
]
},{
"id": "disconnected",
"steps" : [
{"t":"cmd", "js": "NRF.getSecurityStatus = () => { return { connected: false };}", "text": "Control the security status to be disconnected"}
]
}],
"tests" : [{
"description": "Check setGPSPower is replaced",
"steps" : [
{"t":"cmd", "js": "Serial1.println = () => { }", "text": "Fake the serial port"},
{"t":"cmd", "js": "Bluetooth.println = () => { }", "text": "Fake the Bluetooth println"},
{"t":"cmd", "js": "require('Storage').writeJSON('android.settings.json', {overwriteGps: true})", "text": "Enable GPS overwrite"},
{"t":"cmd", "js": "eval(require('Storage').read('android.boot.js'))", "text": "Load the boot code"},
{"t":"assert", "js": "Bangle.setGPSPower.toString().includes('native')", "is":"false", "text": "setGPSPower has been replaced"}
]
},{
"description": "Test switching hardware GPS on and off",
"steps" : [
{"t":"setup", "id": "default"},
{"t":"setup", "id": "connected"},
{"t":"assertArray", "js": "Bangle._PWR.GPS", "is":"undefinedOrEmpty", "text": "No GPS clients"},
{"t":"assert", "js": "Bangle.isGPSOn()", "is":"falsy", "text": "isGPSOn shows GPS as off"},
{"t":"assert", "js": "Bangle.setGPSPower(1, 'test')", "is":"truthy", "text": "setGPSPower returns truthy when switching on"},
{"t":"assertArray", "js": "Bangle._PWR.GPS", "is":"notEmpty", "text": "GPS clients"},
{"t":"assert", "js": "Bangle.isGPSOn()", "is":"truthy", "text": "isGPSOn shows GPS as on"},
{"t":"assertCall", "id": "gpspower", "count": 1, "argAsserts": [ { "t": "assert", "arg": "0", "is": "equal", "to": 1 } ] , "text": "internal GPS switched on"},
{"t":"assert", "js": "Bangle.setGPSPower(0, 'test')", "is":"falsy", "text": "setGPSPower returns falsy when switching off"},
{"t":"assertArray", "js": "Bangle._PWR.GPS", "is":"undefinedOrEmpty", "text": "No GPS clients"},
{"t":"assert", "js": "Bangle.isGPSOn()", "is":"falsy", "text": "isGPSOn shows GPS as off"},
{"t":"assertCall", "id": "gpspower", "count": 2, "argAsserts": [ { "t": "assert", "arg": "0", "is": "equal", "to": 0 } ] , "text": "internal GPS switched off"}
]
},{
"description": "Test switching when GB GPS is available, internal GPS active until GB GPS event arrives",
"steps" : [
{"t":"setup", "id": "default"},
{"t":"setup", "id": "connected"},
{"t":"assertArray", "js": "Bangle._PWR.GPS", "is":"undefinedOrEmpty", "text": "No GPS clients"},
{"t":"assert", "js": "Bangle.isGPSOn()", "is":"falsy", "text": "isGPSOn shows GPS as off"},
{"t":"assert", "js": "Bangle.setGPSPower(1, 'test')", "is":"truthy", "text": "setGPSPower returns truthy when switching on"},
{"t":"assertArray", "js": "Bangle._PWR.GPS", "is":"notEmpty", "text": "GPS clients"},
{"t":"assert", "js": "Bangle.isGPSOn()", "is":"truthy", "text": "isGPSOn shows GPS as on"},
{"t":"assertCall", "id": "gpspower", "count": 1, "argAsserts": [ { "t": "assert", "arg": "0", "is": "equal", "to": 1 } ], "text": "internal GPS switched on"},
{"t":"gb", "obj":{"t":"gps"}},
{"t":"assertArray", "js": "Bangle._PWR.GPS", "is":"notEmpty", "text": "GPS clients still there"},
{"t":"assert", "js": "Bangle.isGPSOn()", "is":"truthy", "text": "isGPSOn still shows GPS as on"},
{"t":"assertCall", "id": "gpspower", "count": 2, "argAsserts": [ { "t": "assert", "arg": "0", "is": "equal", "to": 0 } ], "text": "internal GPS switched off"}
]
},{
"description": "Test switching when GB GPS is available, internal stays off",
"steps" : [
{"t":"setup", "id": "default"},
{"t":"setup", "id": "connected"},
{"t":"assert", "js": "Bangle.setGPSPower(1, 'test')", "is":"truthy", "text": "setGPSPower returns truthy when switching on"},
{"t":"gb", "obj":{"t":"gps"}},
{"t":"assertCall", "id": "gpspower", "argAsserts": [ { "t": "assert", "arg": "0", "is": "equal", "to": 0 } ], "text": "internal GPS switched off"},
{"t":"assert", "js": "Bangle.setGPSPower(0, 'test')", "is":"falsy", "text": "setGPSPower returns truthy when switching on"},
{"t":"assertCall", "id": "gpspower", "argAsserts": [ { "t": "assert", "arg": "0", "is": "equal", "to": 0 } ], "text": "internal GPS still switched off"}
]
},{
"description": "Test switching when GB GPS is available, but no event arrives",
"steps" : [
{"t":"setup", "id": "default"},
{"t":"setup", "id": "connected"},
{"t":"assert", "js": "Bangle.setGPSPower(1, 'test')", "is":"truthy", "text": "setGPSPower returns truthy when switching on"},
{"t":"resetCall", "id": "gpspower"},
{"t":"gb", "obj":{"t":"gps"}, "text": "trigger switch"},
{"t":"assertCall", "id": "gpspower", "argAsserts": [ { "t": "assert", "arg": "0", "is": "equal", "to": 0 } ], "text": "internal GPS switched off"},
{"t":"resetCall", "id": "gpspower"},
{"t":"advanceTimers", "ms":"12000", "text": "wait for fallback"},
{"t":"assertCall", "id": "gpspower", "argAsserts": [ { "t": "assert", "arg": "0", "is": "equal", "to": 1 } ], "text": "internal GPS switched on caused by missing GB event"}
]
}]
}

15
apps/antonclk/test.json Normal file
View File

@ -0,0 +1,15 @@
{
"app" : "antonclk",
"tests" : [{
"description": "Check memory usage after setUI",
"steps" : [
{"t":"cmd", "js": "Bangle.loadWidgets()"},
{"t":"cmd", "js": "eval(require('Storage').read('antonclk.app.js'))"},
{"t":"cmd", "js": "Bangle.setUI()"},
{"t":"saveMemoryUsage"},
{"t":"cmd", "js": "eval(require('Storage').read('antonclk.app.js'))"},
{"t":"cmd", "js":"Bangle.setUI()"},
{"t":"checkMemoryUsage"}
]
}]
}

View File

@ -1 +1,2 @@
0.01: New App!
0.02: Add black- and whitelist for apps. Configure the timout time.

View File

@ -6,11 +6,11 @@ Sets a timeout to load the clock face. The timeout is stopped and started again
Install with app loader and Auto Reset will run in background. If you don't interact with the watch it will time out to the clock face after 10 minutes.
Through the settings apps can be black-/whitelisted and the timeout length can be configured.
## TODO
- Add settings page
- set how many minutes the timeout should count down.
- whitelist/blacklist for apps.
- per app specific timeout lengths?
## Requests

View File

@ -1,12 +1,29 @@
{
const DEFAULTS = {
mode: 0,
apps: [],
timeout: 10
};
const settings = require("Storage").readJSON("autoreset.json", 1) || DEFAULTS;
// Check if the back button should be enabled for the current app.
// app is the src file of the app.
// Derivative of the backswipe app's logic.
function enabledForApp(app) {
if (Bangle.CLOCK==1) return false;
if (!settings) return true;
let isListed = settings.apps.filter((a) => a.files.includes(app)).length > 0;
return settings.mode===0?!isListed:isListed;
}
let timeoutAutoreset;
let resetTimeoutAutoreset = (force)=>{
const resetTimeoutAutoreset = (force)=>{
if (timeoutAutoreset) clearTimeout(timeoutAutoreset);
setTimeout(()=>{ // Short outer timeout to make sure we have time to leave clock face before checking `Bangle.CLOCK!=1`.
if (Bangle.CLOCK!=1) { // Only add timeout if not already on clock face.
if (enabledForApp(global.__FILE__)) {
timeoutAutoreset = setTimeout(()=>{
if (Bangle.CLOCK!=1) Bangle.showClock();
}, 10*60*1000);
}, settings.timeout*60*1000);
}
},200);
};

View File

@ -1,6 +1,6 @@
{ "id": "autoreset",
"name": "Auto Reset",
"version":"0.01",
"version":"0.02",
"description": "Sets a timeout to load the clock face. The timeout is stopped and started again upon user input.",
"icon": "app.png",
"type": "bootloader",
@ -8,6 +8,10 @@
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"autoreset.boot.js","url":"boot.js"}
{"name":"autoreset.boot.js","url":"boot.js"},
{"name":"autoreset.settings.js","url":"settings.js"}
],
"data":[
{"name":"autoreset.json"}
]
}

115
apps/autoreset/settings.js Normal file
View File

@ -0,0 +1,115 @@
(function(back) {
var FILE = 'autoreset.json';
// Mode can be 'blacklist' or 'whitelist'
// Apps is an array of app info objects, where all the apps that are there are either blocked or allowed, depending on the mode
var DEFAULTS = {
'mode': 0,
'apps': [],
'timeout': 10
};
var settings = {};
var loadSettings = function() {
settings = require('Storage').readJSON(FILE, 1) || DEFAULTS;
};
var saveSettings = function(settings) {
require('Storage').write(FILE, settings);
};
// Get all app info files
var getApps = function() {
var apps = require('Storage').list(/\.info$/).map(appInfoFileName => {
var appInfo = require('Storage').readJSON(appInfoFileName, 1);
return appInfo && {
'name': appInfo.name,
'sortorder': appInfo.sortorder,
'src': appInfo.src,
'files': appInfo.files
};
}).filter(app => app && !!app.src);
apps.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 apps;
};
var showMenu = function() {
var menu = {
'': { 'title': 'Auto Reset' },
/*LANG*/'< Back': () => {
back();
},
/*LANG*/'Mode': {
value: settings.mode,
min: 0,
max: 1,
format: v => ["Blacklist", "Whitelist"][v],
onchange: v => {
settings.mode = v;
saveSettings(settings);
},
},
/*LANG*/'App List': () => {
showAppSubMenu();
},
/*LANG*/'Timeout [min]': {
value: settings.timeout,
min: 0.25, max: 30, step : 0.25,
format: v => v,
onchange: v => {
settings.timeout = v;
saveSettings(settings);
},
},
};
E.showMenu(menu);
};
var showAppSubMenu = function() {
var menu = {
'': { 'title': 'Auto Reset' },
'< Back': () => {
showMenu();
},
'Add App': () => {
showAppList();
}
};
settings.apps.forEach(app => {
menu[app.name] = () => {
settings.apps.splice(settings.apps.indexOf(app), 1);
saveSettings(settings);
showAppSubMenu();
}
});
E.showMenu(menu);
}
var showAppList = function() {
var apps = getApps();
var menu = {
'': { 'title': 'Auto Reset' },
/*LANG*/'< Back': () => {
showMenu();
}
};
apps.forEach(app => {
menu[app.name] = () => {
settings.apps.push(app);
saveSettings(settings);
showAppSubMenu();
}
});
E.showMenu(menu);
}
loadSettings();
showMenu();
})

View File

@ -1,3 +1,6 @@
0.01: New App!
0.02: Don't fire if the app uses swipes already.
0.03: Only count defined handlers in the handler array.
0.04: Fix messages auto opened by `messagegui` could not be blacklisted. Needs
a refresh by deselecting and reselecting the "Messages" app throught Back Swipe
settings.

View File

@ -47,9 +47,9 @@
function enabledForApp(app) {
if (!settings) return true;
if (settings.mode === 0) {
return !(settings.apps.filter((a) => a.src === app).length > 0);
return !(settings.apps.filter((a) => (a.src===app)||(a.files&&a.files.includes(app))).length > 0); // The `a.src===app` and `a.files&&...` checks are for backwards compatibility. Otherwise only `a.files.includes(app)` is needed.
} else if (settings.mode === 1) {
return settings.apps.filter((a) => a.src === app).length > 0;
return settings.apps.filter((a) => (a.src===app)||(a.files&&a.files.includes(app))).length > 0;
} else {
return settings.mode === 2 ? true : false;
}

View File

@ -1,7 +1,7 @@
{ "id": "backswipe",
"name": "Back Swipe",
"shortName":"BackSwipe",
"version":"0.03",
"version":"0.04",
"description": "Service that allows you to use an app's back button using left to right swipe gesture",
"icon": "app.png",
"tags": "back,gesture,swipe",

View File

@ -26,7 +26,8 @@
return appInfo && {
'name': appInfo.name,
'sortorder': appInfo.sortorder,
'src': appInfo.src
'src': appInfo.src,
'files': appInfo.files
};
}).filter(app => app && !!app.src);
apps.sort((a, b) => {

View File

@ -69,3 +69,4 @@
0.58: "Make Connectable" temporarily bypasses the whitelist
0.59: Whitelist: Try to resolve peer addresses using NRF.resolveAddress() - for 2v19 or 2v18 cutting edge builds
0.60: Minor code improvements
0.61: Instead of breaking execution with an Exception when updating boot, just use if..else (fix 'Uncaught undefined')

View File

@ -14,15 +14,15 @@ if (DEBUG) {
}
if (require('Storage').hash) { // new in 2v11 - helps ensure files haven't changed
let CRC = E.CRC32(require('Storage').read('setting.json'))+require('Storage').hash(/\.boot\.js/)+E.CRC32(process.env.GIT_COMMIT);
boot += `if (E.CRC32(require('Storage').read('setting.json'))+require('Storage').hash(/\\.boot\\.js/)+E.CRC32(process.env.GIT_COMMIT)!=${CRC})`;
boot += `if(E.CRC32(require('Storage').read('setting.json'))+require('Storage').hash(/\\.boot\\.js/)+E.CRC32(process.env.GIT_COMMIT)!=${CRC})`;
} else {
let CRC = E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\.boot\.js/))+E.CRC32(process.env.GIT_COMMIT);
boot += `if (E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\\.boot\\.js/))+E.CRC32(process.env.GIT_COMMIT)!=${CRC})`;
boot += `if(E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\\.boot\\.js/))+E.CRC32(process.env.GIT_COMMIT)!=${CRC})`;
}
boot += ` { eval(require('Storage').read('bootupdate.js')); throw "Storage Updated!"}\n`;
boot += `{eval(require('Storage').read('bootupdate.js'));print("Storage Updated!")}else{\n`;
boot += `E.setFlags({pretokenise:1});\n`;
boot += `var bleServices = {}, bleServiceOptions = { uart : true};\n`;
bootPost += `NRF.setServices(bleServices, bleServiceOptions);delete bleServices,bleServiceOptions;\n`; // executed after other boot code
bootPost += `NRF.setServices(bleServices,bleServiceOptions);delete bleServices,bleServiceOptions;\n`; // executed after other boot code
if (s.ble!==false) {
if (s.HID) { // Human interface device
if (s.HID=="joy") boot += `Bangle.HID = E.toUint8Array(atob("BQEJBKEBCQGhAAUJGQEpBRUAJQGVBXUBgQKVA3UBgQMFAQkwCTEVgSV/dQiVAoECwMA="));`;
@ -122,6 +122,7 @@ let bootFiles = require('Storage').list(/\.boot\.js$/).sort((a,b)=>{
return a==b ? 0 : (a>b ? 1 : -1);
});
// precalculate file size
bootPost += "}";
let fileSize = boot.length + bootPost.length;
bootFiles.forEach(bootFile=>{
// match the size of data we're adding below in bootFiles.forEach

View File

@ -1,7 +1,7 @@
{
"id": "boot",
"name": "Bootloader",
"version": "0.60",
"version": "0.61",
"description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings",
"icon": "bootloader.png",
"type": "bootloader",

View File

@ -4,3 +4,4 @@
0.04: Added notification
0.05: Fixed boot
0.06: Allow tap to silence notification/buzzing
0.07: Fix notification-tap silencing and notification length

View File

@ -24,8 +24,8 @@
lim = sum / cnt;
require('Storage').writeJSON('chargent.json', {limit: lim});
}
const onHide = () => { id = clearInterval(id) };
require('notify').show({id: 'chargent', title: 'Fully charged', onHide });
const onHide = () => { if(id) id = clearInterval(id) };
require('notify').show({id: 'chargent', title: 'Charged', onHide });
// TODO ? customizable
Bangle.buzz(500);
setTimeout(() => Bangle.buzz(500), 1000);

View File

@ -1,6 +1,6 @@
{ "id": "chargent",
"name": "Charge Gently",
"version": "0.06",
"version": "0.07",
"description": "When charging, reminds you to disconnect the watch to prolong battery life.",
"icon": "icon.png",
"type": "bootloader",

View File

@ -1,3 +1,4 @@
0.01: New App!
0.02: added settings options to change date format
0.03: Remove un-needed font requirement, now outputs transparent image
0.04: Fix image after 0.03 regression

View File

@ -20,7 +20,7 @@
get : () => {
let d = new Date();
let g = Graphics.createArrayBuffer(24,24,1,{msb:true});
g.transparent = 1;
g.transparent = 0;
g.drawImage(atob("FhgBDADAMAMP/////////////////////8AADwAAPAAA8AADwAAPAAA8AADwAAPAAA8AADwAAPAAA8AADwAAP///////"),1,0);
g.setFont("6x15").setFontAlign(0,0).drawString(d.getDate(),11,17);
return {

View File

@ -1,6 +1,6 @@
{ "id": "clkinfocal",
"name": "Calendar Clockinfo",
"version":"0.03",
"version":"0.04",
"description": "For clocks that display 'clockinfo' (messages that can be cycled through using the clock_info module) this displays the day of the month in the icon, and the weekday. There is also a settings menu to select the format of the text",
"icon": "app.png",
"screenshots": [{"url":"screenshot.png"}],

View File

@ -10,3 +10,4 @@
0.09: Save clkinfo settings on kill and remove
0.10: Fix focus bug when changing focus between two clock infos
0.11: Prepend swipe listener if possible
0.12: Add drawFilledImage to allow drawing icons with a separately coloured middle

View File

@ -371,6 +371,46 @@ exports.addInteractive = function(menu, options) {
return options;
};
/* clockinfos usually return a 24x24 image. This draws that image but
recolors it such that it is transparent, with the middle of the image as background
and the image itself as foreground. options is passed to g.drawImage */
exports.drawFilledImage = function(img,x,y,options) {
if (!img) return;
if (!g.floodFill/*2v18+*/) return g.drawImage(img,x,y,options);
let gfx = exports.imgGfx;
if (!gfx) {
gfx = exports.imgGfx = Graphics.createArrayBuffer(26, 26, 2, {msb:true});
gfx.transparent = 3;
gfx.palette = new Uint16Array([g.theme.bg, g.theme.fg, g.toColor("#888"), g.toColor("#888")]);
}
/* img is (usually) a black and white transparent image. But we really would like the bits in
the middle of it to be white. So what we do is we draw a slightly bigger rectangle in white,
draw the image, and then flood-fill the rectangle back to the background color. floodFill
was only added in 2v18 so we have to check for it and fallback if not. */
gfx.clear(1).setColor(1).drawImage(img, 1,1).floodFill(0,0,3);
var scale = (options && options.scale) || 1;
return g.drawImage(gfx, x-scale,y-scale,options);
};
/* clockinfos usually return a 24x24 image. This creates a 26x26 gfx of the image but
recolors it such that it is transparent, with the middle and border of the image as background
and the image itself as foreground. options is passed to g.drawImage */
exports.drawBorderedImage = function(img,x,y,options) {
if (!img) return;
if (!g.floodFill/*2v18+*/) return g.drawImage(img,x,y,options);
let gfx = exports.imgGfxB;
if (!gfx) {
gfx = exports.imgGfxB = Graphics.createArrayBuffer(28, 28, 2, {msb:true});
gfx.transparent = 3;
gfx.palette = new Uint16Array([g.theme.bg, g.theme.fg, g.theme.bg/*border*/, g.toColor("#888")]);
}
gfx.clear(1).setColor(2).drawImage(img, 1,1).drawImage(img, 3,1).drawImage(img, 1,3).drawImage(img, 3,3); // border
gfx.setColor(1).drawImage(img, 2,2); // main image
gfx.floodFill(27,27,3); // flood fill edge to transparent
var o = ((options && options.scale) || 1)*2;
return g.drawImage(gfx, x-o,y-o,options);
};
// Code for testing (plots all elements from first list)
/*
g.clear();

View File

@ -1,11 +1,11 @@
{ "id": "clock_info",
"name": "Clock Info Module",
"shortName": "Clock Info",
"version":"0.11",
"version":"0.12",
"description": "A library used by clocks to provide extra information on the clock face (Altitude, BPM, etc)",
"icon": "app.png",
"type": "module",
"tags": "clkinfo",
"tags": "clkinfo,clockinfo",
"supports" : ["BANGLEJS2"],
"provides_modules" : ["clock_info"],
"readme": "README.md",

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 597 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 470 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 474 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 597 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 664 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 664 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 597 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 664 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 651 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 470 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 477 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 451 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

@ -7,8 +7,8 @@
cursor: pointer;
}
#preview {
width : 176px;
height: 176px;
width : 178px; /* include the border */
height: 178px;
border: 1px solid black;
}
</style>
@ -19,10 +19,12 @@
<p>If you'd like to contribute images you can <a href="https://github.com/espruino/BangleApps/tree/master/apps/patriotclk/img" target="_blank">add them on GitHub</a>!</p>
<div style="float:right">Preview:<br/><canvas width="176" height="176" id="preview"></canvas></div>
<div class="form-group">
<label class="form-switch">
<input type="checkbox" id="box_zoom">
<i class="form-icon"></i> Zoom
</label>
<select class="form-select" id="box_zoom" style="width:inherit">
<option value="0">No Zoom</option>
<option value="1">10% Zoom</option>
<option value="1.5">15% Zoom</option>
<option value="2">20% Zoom</option>
</select><br/>
<label class="form-switch">
<input type="checkbox" id="box_mirror">
<i class="form-icon"></i> Mirror
@ -55,6 +57,8 @@
{"path":"img/ai_robot.jpeg","dither":true},
{"path":"img/ai_eye.jpeg","dither":true},
{"path":"img/ai_hero.jpeg","dither":true},
{"path":"img/ai_flow.jpeg","dither":true},
{"path":"img/ai_horse.jpeg","dither":true},
{"path":"img/icons8-australia-480.png","dither":false},
{"path":"img/icons8-austria-480.png","dither":false},
{"path":"img/icons8-belgium-480.png","dither":false},
@ -100,7 +104,7 @@
if (!selectedImage) return;
var imgPath = selectedImage.getAttribute("data-file");
var img = IMAGES.find(img => img.path == imgPath);
var zoom = document.getElementById("box_zoom").checked;
var zoom = document.getElementById("box_zoom").value;
var dither = document.getElementById("box_dither").value;
if (dither=="" && img.dither) dither="bayer2";
if (dither=="no" || dither=="") dither=undefined;
@ -115,8 +119,13 @@
let imgW = selectedImage.naturalWidth;
let imgH = selectedImage.naturalHeight;
let border = 0;
if (imgW > 400) border = 20;
if (zoom) border = (border*5) >> 1;
let imgMin = Math.min(imgW,imgH);
switch (zoom) {
case "0": border = 0; break;
case "1": border = Math.round(imgMin*0.1); break;
case "1.5": border = Math.round(imgMin*0.15); break;
case "2": border = Math.round(imgMin*0.2); break;
}
ctx.save(); // Save the current state
if (mirror) {
ctx.translate(canvas.width, 0);

View File

@ -1 +1,2 @@
0.01: New App!
0.02: Corrected formatting of punctuation

Binary file not shown.

View File

@ -1,9 +1,9 @@
{ "id": "fontall",
"name": "Fonts (all languages)",
"version":"0.01",
"version":"0.02",
"description": "Installs a font containing over 50,000 Unifont characters for Chinese, Japanese, Korean, Russian, and more. **Requires 2MB storage**",
"icon": "app.png",
"tags": "font",
"tags": "font,fonts,language",
"type": "module",
"provides_modules" : ["font"],
"supports" : ["BANGLEJS2"],

View File

@ -1 +1,2 @@
0.01: New App!
0.02: Corrected formatting of punctuation

Binary file not shown.

View File

@ -1,9 +1,9 @@
{ "id": "fontext",
"name": "Fonts (150+ languages)",
"version":"0.01",
"version":"0.02",
"description": "Installs a font containing 1000 Unifont characters, which should handle the majority of non-Chinese/Japanese/Korean languages (only 20kb)",
"icon": "app.png",
"tags": "font",
"tags": "font,fonts,language",
"type": "module",
"provides_modules" : ["font"],
"default" : true,

View File

@ -1 +1,2 @@
0.01: First release
0.02: Corrected formatting of punctuation

Binary file not shown.

View File

@ -1,9 +1,9 @@
{ "id": "fontkorean",
"name": "Korean font",
"version":"0.01",
"name": "Fonts (Korean)",
"version":"0.02",
"description": "Installs a font data, Unifont characters for Korean **Requires 420 KB storage**",
"icon": "app.png",
"tags": "font",
"tags": "font,fonts,language",
"type": "module",
"provides_modules" : ["font"],
"supports" : ["BANGLEJS2"],

View File

@ -2,3 +2,4 @@
0.02: Move translations to locale module (removed watch settings, now pick language in Bangle App Loader, More..., Settings)
0.03: Change for fast loading, use widget_utils to hide widgets
0.04: Add animation when display changes
0.05: Add toggle for animation + minor bug fixes

View File

@ -11,6 +11,7 @@ Translations are supported to get the time in the language of your choice! To ch
* nn_NO - Norwegian Nynorsk (thank you zerodogg)
* sv_SE - Swedish
* de_DE - German
* da_DK - Danish
Most translations are taken from the original Fuzzy Text International code.

View File

@ -31,61 +31,68 @@
/*LANG*/"ten to *$2",
/*LANG*/"five to *$2"
]
};
};
let text_scale = 4;
let timeout = 2.5*60;
let drawTimeout;
let animInterval;
let time_string = "";
let time_string_old = "";
let time_string_old_wrapped = "";
let text_scale = 4;
let timeout = 2.5*60;
let drawTimeout;
let animInterval;
let time_string = "";
let time_string_old = "";
let time_string_old_wrapped = "";
let settings = {};
let loadSettings = function() {
settings = require("Storage").readJSON(SETTINGS_FILE,1)|| {'showWidgets': false};
};
let loadSettings = function() {
settings = require("Storage").readJSON(SETTINGS_FILE,1)|| {'showWidgets': false, 'animate': true};
};
let queueDraw = function(seconds) {
let queueDraw = function(seconds) {
let millisecs = seconds * 1000;
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = setTimeout(function() {
drawTimeout = undefined;
draw();
}, millisecs - (Date.now() % millisecs));
};
};
let getTimeString = function(date) {
let getTimeString = function(date) {
let segment = Math.round((date.getMinutes()*60 + date.getSeconds() + 1)/300);
let hour = date.getHours() + Math.floor(segment/12);
f_string = fuzzy_string.minutes[segment % 12];
// add "" to load into RAM due to 2v21 firmware .replace on flashstring issue
let f_string = ""+fuzzy_string.minutes[segment % 12];
if (f_string.includes('$1')) {
f_string = f_string.replace('$1', fuzzy_string.hours[(hour) % 12]);
} else {
f_string = f_string.replace('$2', fuzzy_string.hours[(hour + 1) % 12]);
}
return f_string;
};
};
let draw = function() {
let draw = function() {
time_string = getTimeString(new Date()).replace('*', '');
//print(time_string);
if (time_string != time_string_old) {
g.setFont('Vector', R.h/text_scale).setFontAlign(0, 0);
if (settings.animate) {
animate(3);
} else {
quickDraw();
}
}
queueDraw(timeout);
};
};
let animate = function(step) {
let animate = function(step) {
if (animInterval) clearInterval(animInterval);
let time_string_new_wrapped = g.wrapString(time_string, R.w).join("\n");
slideX = 0;
let slideX = 0;
//don't let pulling the drawer change y
let text_y = R.y + R.h/2;
animInterval = setInterval(function() {
let time_start = getTime()
//blank old time
g.setColor(g.theme.bg);
g.drawString(time_string_old_wrapped, R.x + R.w/2 + slideX, R.y + R.h/2);
g.drawString(time_string_new_wrapped, R.x - R.w/2 + slideX, R.y + R.h/2);
g.drawString(time_string_old_wrapped, R.x + R.w/2 + slideX, text_y);
g.drawString(time_string_new_wrapped, R.x - R.w/2 + slideX, text_y);
g.setColor(g.theme.fg);
slideX += step;
let stop = false;
@ -94,32 +101,41 @@ let animate = function(step) {
stop = true;
}
//draw shifted new time
g.drawString(time_string_old_wrapped, R.x + R.w/2 + slideX, R.y + R.h/2);
g.drawString(time_string_new_wrapped, R.x - R.w/2 + slideX, R.y + R.h/2);
g.drawString(time_string_old_wrapped, R.x + R.w/2 + slideX, text_y);
g.drawString(time_string_new_wrapped, R.x - R.w/2 + slideX, text_y);
if (stop) {
time_string_old = time_string;
clearInterval(animInterval);
animInterval=undefined;
time_string_old_wrapped = time_string_new_wrapped;
}
print(Math.round((getTime() - time_start)*1000))
//print(Math.round((getTime() - time_start)*1000));
}, 30);
};
};
g.clear();
loadSettings();
let quickDraw = function() {
let time_string_new_wrapped = g.wrapString(time_string, R.w).join("\n");
g.setColor(g.theme.bg);
g.drawString(time_string_old_wrapped, R.x + R.w/2, R.y + R.h/2);
g.setColor(g.theme.fg);
g.drawString(time_string_new_wrapped, R.x + R.w/2, R.y + R.h/2);
time_string_old_wrapped = time_string_new_wrapped;
};
// Stop updates when LCD is off, restart when on
Bangle.on('lcdPower',on=>{
g.clear();
loadSettings();
// Stop updates when LCD is off, restart when on
Bangle.on('lcdPower',on=>{
if (on) {
draw(); // draw immediately, queue redraw
} else { // stop draw timer
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
}
});
});
Bangle.setUI({
Bangle.setUI({
mode : 'clock',
remove : function() {
// Called to unload all of the clock app
@ -129,15 +145,15 @@ Bangle.setUI({
animInterval = undefined;
require('widget_utils').show(); // re-show widgets
}
});
});
Bangle.loadWidgets();
if (settings.showWidgets) {
Bangle.loadWidgets();
if (settings.showWidgets) {
Bangle.drawWidgets();
} else {
} else {
require("widget_utils").swipeOn(); // hide widgets, make them visible with a swipe
}
}
R = Bangle.appRect;
draw();
let R = Bangle.appRect;
draw();
}

View File

@ -1,32 +1,39 @@
(function(back) {
const SETTINGS_FILE = "fuzzy.settings.json";
const SETTINGS_FILE = "fuzzyw.settings.json";
// initialize with default settings...
let s = {'showWidgets': false}
let s = {'showWidgets': false, 'animate': true};
// ...and overwrite them with any saved values
// This way saved values are preserved if a new version adds more settings
const storage = require('Storage')
const storage = require('Storage');
let settings = storage.readJSON(SETTINGS_FILE, 1) || s;
const saved = settings || {}
const saved = settings || {};
for (const key in saved) {
s[key] = saved[key]
s[key] = saved[key];
}
function save() {
settings = s
storage.write(SETTINGS_FILE, settings)
settings = s;
storage.write(SETTINGS_FILE, settings);
}
E.showMenu({
'': { 'title': 'Fuzzy Word Clock' },
'< Back': back,
'Show Widgets': {
value: settings.showWidgets,
value: s.showWidgets,
onchange: () => {
settings.showWidgets = !settings.showWidgets;
s.showWidgets = !s.showWidgets;
save();
}
},
'Animate': {
value: s.animate,
onchange: () => {
s.animate = !s.animate;
save();
}
}
});
})

View File

@ -2,7 +2,7 @@
"id":"fuzzyw",
"name":"Fuzzy Text Clock",
"shortName": "Fuzzy Text",
"version": "0.04",
"version": "0.05",
"description": "An imprecise clock for when you're not in a rush",
"readme": "README.md",
"icon":"fuzzyw.png",

View File

@ -1,4 +1,4 @@
# Analogue Clock
# Interlaced Clock
![](screenshot_analog.png)
![](screenshot.png)

View File

@ -1,4 +1,4 @@
0.01: New App!
0.02: Minor code improvements
0.03: Better scrolling behaviour
0.04: Fix incorrect appRect handling (missing back buttons and doubled menu titles)

View File

@ -33,12 +33,16 @@
let lastTouchedDrag = 0;
let lastDragStart = 0;
let R = Bangle.appRect;
let menuScrollMin = 0|options.scrollMin;
let menuScrollMax = options.h*options.c - R.h;
const getMenuScrollMax = () => {
let menuScrollMax = options.h*options.c - Bangle.appRect.h;
if (menuScrollMax<menuScrollMin) menuScrollMax=menuScrollMin;
return menuScrollMax;
};
const touchHandler = (_,e)=>{
let R = Bangle.appRect;
if (e.y<R.y-4) return;
velocity = 0;
accDy = 0;
@ -51,6 +55,7 @@
};
const uiDraw = () => {
let R = Bangle.appRect;
g.reset().clearRect(R).setClipRect(R.x,R.y,R.x2,R.y2);
var a = YtoIdx(R.y);
var b = Math.min(YtoIdx(R.y2),options.c-1);
@ -60,6 +65,7 @@
};
const draw = () => {
let R = Bangle.appRect;
if (velocity > MIN_VELOCITY){
if (!scheduledDraw)
scheduledDraw = setTimeout(draw, 0);
@ -70,7 +76,7 @@
s.scroll -= velocity * direction;
}
}
let menuScrollMax = getMenuScrollMax();
if (s.scroll > menuScrollMax){
s.scroll = menuScrollMax;
velocity = 0;
@ -153,16 +159,17 @@
Bangle.setUI(uiOpts);
function idxToY(i) {
return i*options.h + R.y - rScroll;
return i*options.h + Bangle.appRect.y - rScroll;
}
function YtoIdx(y) {
return Math.floor((y + rScroll - R.y)/options.h);
return Math.floor((y + rScroll - Bangle.appRect.y)/options.h);
}
let s = {
scroll : E.clip(0|options.scroll,menuScrollMin,menuScrollMax),
scroll : E.clip(0|options.scroll,menuScrollMin,getMenuScrollMax()),
draw : () => {
let R = Bangle.appRect;
g.reset().clearRect(R).setClipRect(R.x,R.y,R.x2,R.y2);
let a = YtoIdx(R.y);
let b = Math.min(YtoIdx(R.y2),options.c-1);
@ -171,6 +178,7 @@
g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1);
},
drawItem : i => {
let R = Bangle.appRect;
let y = idxToY(i);
g.reset().setClipRect(R.x,Math.max(y,R.y),R.x2,Math.min(y+options.h,R.y2));
options.draw(i, {x:R.x,y:y,w:R.w,h:options.h});

View File

@ -1,4 +1,5 @@
(function(){E.showScroller=function(c){function k(b){return b*c.h+a.y-l}function h(b){return Math.floor((b+l-a.y)/c.h)}if(!c)return Bangle.setUI();let n,f=0,p=0,q=0,t=0,u=0,a=Bangle.appRect,m=0|c.scrollMin,r=c.h*c.c-a.h;r<m&&(r=m);const v=()=>{g.reset().clearRect(a).setClipRect(a.x,a.y,a.x2,a.y2);for(var b=h(a.y),d=Math.min(h(a.y2),c.c-1);b<=d;b++)c.draw(b,{x:a.x,y:k(b),w:a.w,h:c.h});g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1)},w=()=>{.1<f&&(n||(n=setTimeout(w,0)),f*=
1-(Date.now()-t)/8E3,.1>=f?f=0:e.scroll-=f*q);e.scroll>r&&(e.scroll=r,f=0);e.scroll<m&&(e.scroll=m,f=0);var b=l;l=e.scroll&-2;if(b-=l){g.reset().setClipRect(a.x,a.y,a.x2,a.y2).scroll(0,b);if(0>b){b=Math.max(a.y2-(1-b),a.y);g.setClipRect(a.x,b,a.x2,a.y2);var d=h(b);for(b=k(d);b<a.y2;b+=c.h)c.draw(d,{x:a.x,y:b,w:a.w,h:c.h}),d++}else for(b=Math.min(a.y+b,a.y2),g.setClipRect(a.x,a.y,a.x2,b),d=h(b),k(d),b=k(d);b>a.y-c.h;b-=c.h)c.draw(d,{x:a.x,y:b,w:a.w,h:c.h}),d--;g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-
1);n=void 0}};let x={mode:"custom",back:c.back,drag:b=>{let d=Date.now();q=Math.sign(b.dy);e.scroll-=b.dy;if(0<b.b){t=d;if(!u||0>p*q&&0<b.dy*q)u=t,p=f=0;p+=b.dy}else 150>d-t&&(f=q*p/(d-u)*100),u=0;w()},touch:(b,d)=>{d.y<a.y-4||(p=f=0,b=h(d.y),(0>m||0<=b)&&b<c.c&&c.select(b,{x:d.x,y:d.y+l-a.y-b*c.h}))},redraw:v};c.remove&&(x.remove=()=>{n&&clearTimeout(n);c.remove()});Bangle.setUI(x);let e={scroll:E.clip(0|c.scroll,m,r),draw:()=>{g.reset().clearRect(a).setClipRect(a.x,a.y,a.x2,a.y2);var b=h(a.y);let d=
Math.min(h(a.y2),c.c-1);for(;b<=d;b++)c.draw(b,{x:a.x,y:k(b),w:a.w,h:c.h});g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1)},drawItem:b=>{let d=k(b);g.reset().setClipRect(a.x,Math.max(d,a.y),a.x2,Math.min(d+c.h,a.y2));c.draw(b,{x:a.x,y:d,w:a.w,h:c.h});g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1)},isActive:()=>Bangle.uiRedraw==v},l=e.scroll&-2;e.draw();g.flip();return e}})()
(function(){E.showScroller=function(c){function k(a){return a*c.h+Bangle.appRect.y-l}function h(a){return Math.floor((a+l-Bangle.appRect.y)/c.h)}if(!c)return Bangle.setUI();let n,f=0,p=0,q=0,r=0,t=0,m=0|c.scrollMin;const u=()=>{let a=c.h*c.c-Bangle.appRect.h;a<m&&(a=m);return a},v=()=>{let a=Bangle.appRect;g.reset().clearRect(a).setClipRect(a.x,a.y,a.x2,a.y2);for(var b=h(a.y),d=Math.min(h(a.y2),c.c-1);b<=d;b++)c.draw(b,{x:a.x,y:k(b),w:a.w,h:c.h});g.setClipRect(0,0,g.getWidth()-
1,g.getHeight()-1)},w=()=>{let a=Bangle.appRect;.1<f&&(n||(n=setTimeout(w,0)),f*=1-(Date.now()-r)/8E3,.1>=f?f=0:e.scroll-=f*q);var b=u();e.scroll>b&&(e.scroll=b,f=0);e.scroll<m&&(e.scroll=m,f=0);b=l;l=e.scroll&-2;if(b-=l){g.reset().setClipRect(a.x,a.y,a.x2,a.y2).scroll(0,b);if(0>b){b=Math.max(a.y2-(1-b),a.y);g.setClipRect(a.x,b,a.x2,a.y2);var d=h(b);for(b=k(d);b<a.y2;b+=c.h)c.draw(d,{x:a.x,y:b,w:a.w,h:c.h}),d++}else for(b=Math.min(a.y+b,a.y2),g.setClipRect(a.x,a.y,a.x2,b),d=h(b),k(d),b=k(d);b>a.y-
c.h;b-=c.h)c.draw(d,{x:a.x,y:b,w:a.w,h:c.h}),d--;g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1);n=void 0}};let x={mode:"custom",back:c.back,drag:a=>{let b=Date.now();q=Math.sign(a.dy);e.scroll-=a.dy;if(0<a.b){r=b;if(!t||0>p*q&&0<a.dy*q)t=r,p=f=0;p+=a.dy}else 150>b-r&&(f=q*p/(b-t)*100),t=0;w()},touch:(a,b)=>{a=Bangle.appRect;if(!(b.y<a.y-4)){p=f=0;var d=h(b.y);(0>m||0<=d)&&d<c.c&&c.select(d,{x:b.x,y:b.y+l-a.y-d*c.h})}},redraw:v};c.remove&&(x.remove=()=>{n&&clearTimeout(n);c.remove()});Bangle.setUI(x);
let e={scroll:E.clip(0|c.scroll,m,u()),draw:()=>{let a=Bangle.appRect;g.reset().clearRect(a).setClipRect(a.x,a.y,a.x2,a.y2);var b=h(a.y);let d=Math.min(h(a.y2),c.c-1);for(;b<=d;b++)c.draw(b,{x:a.x,y:k(b),w:a.w,h:c.h});g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1)},drawItem:a=>{let b=Bangle.appRect,d=k(a);g.reset().setClipRect(b.x,Math.max(d,b.y),b.x2,Math.min(d+c.h,b.y2));c.draw(a,{x:b.x,y:d,w:b.w,h:c.h});g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1)},isActive:()=>Bangle.uiRedraw==v},l=e.scroll&
-2;e.draw();g.flip();return e}})()

View File

@ -1,7 +1,7 @@
{ "id": "kineticscroll",
"name": "Kinetic Scroll",
"shortName":"Kinetic Scroll",
"version": "0.03",
"version": "0.04",
"description": "Replacement for the system scroller with kinetic scrolling.",
"icon": "app.png",
"type": "bootloader",

View File

@ -275,12 +275,6 @@ module.exports = {
"no-unused-vars"
]
},
"runplus/app.js": {
"hash": "fabec449552d17ceb5d510aa058e420757f3caba11999efbe6ebf2ac1a81eb32",
"rules": [
"no-unused-vars"
]
},
"powermanager/boot.js": {
"hash": "662d9d29a80a4f2c2855097b4073a099604f4f6d444c13a33304346c788cc5cb",
"rules": [
@ -741,12 +735,6 @@ module.exports = {
"no-undef"
]
},
"runplus/karvonen.js": {
"hash": "3011bbc5afc3e17bb873f4544d680acc8765105dd825417eadb01047ecadbcb7",
"rules": [
"no-undef"
]
},
"rescalc/app.js": {
"hash": "925f00a439817fadf92f4e7a7fcd509eb9d9c7e1e4309e315ea92a6881e18b4b",
"rules": [
@ -1275,12 +1263,6 @@ module.exports = {
"no-undef"
]
},
"android/test.js": {
"hash": "703dad907d9b17d281faca2ecd9099d9f127e7241b48c7617f9147978ddb7c43",
"rules": [
"no-undef"
]
},
"android/boot.js": {
"hash": "a537b3f7772e11f44615d80d6c6bacfa69c1854c6da3c01666863bcae8a18059",
"rules": [
@ -1378,7 +1360,7 @@ module.exports = {
]
},
"kineticscroll/boot.min.js": {
"hash": "a5106fa601cdbd2179e32265a8b77e2df62ba44ca0f4b94ce1e594c95a47e20d",
"hash": "1345b3c556f1a268a81c3a57825d096d9bbce9740217339aa6d79223d9daad4d",
"rules": [
"no-cond-assign"
]

View File

@ -12,3 +12,5 @@
Track handler changes done by background code
0.08: Fix linter warnings
0.09: Fix scrolling to last line for long text
0.10: Track Listeners added with prependListener
Handle changed internal callback variable name for watches introduced in 2v21.104

View File

@ -467,10 +467,20 @@ const backupOn = function(event, handler){
else origOn.call(Bangle, event, handler);
};
const origPrependListener = Bangle.prependListener;
const backupPrependListener = function(event, handler){
if (EVENTS.includes(event)){
if (!backup[event])
backup[event] = [];
backup[event].unshift(handler);
}
else origPrependListener.call(Bangle, event, handler);
};
const origClearWatch = clearWatch;
const backupClearWatch = function(w) {
if (w)
backup.watches.filter((e)=>e.index != w);
backup.watches[w] = null;
else
backup.watches = [];
};
@ -479,9 +489,10 @@ const origSetWatch = setWatch;
const backupSetWatch = function(){
if (!backup.watches)
backup.watches = [];
LOG("backup for watch", arguments);
let i = backup.watches.map((e)=>e.index).sort().pop() + 1;
backup.watches.push({index:i, args:arguments});
LOG("current watches", backup.watches);
let i = backup.watches.length + 1;
LOG("backup for watch", arguments, "at index", i);
backup.watches.push(arguments);
return i;
};
@ -521,6 +532,7 @@ const restoreHandlers = function(){
origClearWatch.call(global);
LOG("Restoring", backup.watches.length, "watches");
for(let i = 0; i < backup.watches.length; i++){
let w = backup.watches[i];
LOG("Restoring watch", w);
@ -541,6 +553,7 @@ const restoreHandlers = function(){
global.setWatch = origSetWatch;
global.clearWatch = origClearWatch;
Bangle.on = origOn;
Bangle.prependListener = origPrependListener;
Bangle.removeListener = origRemove;
Bangle.removeAllListeners = origRemoveAll;
@ -568,11 +581,12 @@ const backupHandlers = function(){
LOG("Transform watch", w);
if (w) {
w = [
w.callback,
w.callback ? w.callback : w.cb, // Handle change in name of callback variable to cb in 2v21.104
w.pin,
w
];
delete w[2].callback;
delete w[2].cb;
delete w[2].pin;
w[2].debounce = Math.round(w[2].debounce / 1048.576);
} else {
@ -588,6 +602,7 @@ const backupHandlers = function(){
global.setWatch = backupSetWatch;
global.clearWatch = backupClearWatch;
Bangle.on = backupOn;
Bangle.prependListener = backupPrependListener;
Bangle.removeListener = backupRemove;
Bangle.removeAllListeners = backupRemoveAll;

View File

@ -1,7 +1,7 @@
{
"id": "messagesoverlay",
"name": "Messages Overlay",
"version": "0.09",
"version": "0.10",
"description": "An overlay based implementation of a messages UI (display notifications from iOS and Gadgetbridge/Android)",
"icon": "app.png",
"type": "bootloader",

View File

@ -0,0 +1,40 @@
{
"app" : "messagesoverlay",
"tests" : [{
"description": "Test handler backgrounding",
"steps" : [
{"t":"upload", "file": "modules/widget_utils.js", "as": "widget_utils"},
{"t":"cmd", "js": "Bangle.loadWidgets()", "text": "Load widgets"},
{"t":"assertArray", "js": "Bangle['#onswipe']", "is":"undefinedOrEmpty", "text": "No swipe handlers"},
{"t":"cmd", "js": "require('widget_utils').swipeOn(0)", "text": "Store widgets in overlay"},
{"t":"assert", "js": "Bangle['#onswipe']", "is":"function", "text": "One swipe handler for widgets"},
{"t":"emit", "event":"swipe", "paramsArray": [ 0, 1 ], "text": "Show widgets"},
{"t":"assert", "js": "Bangle['#onswipe']", "is":"function", "text": "One swipe handler for widgets"},
{"t":"cmd", "js": "require('messagesoverlay').message('text', {src:'Messenger',t:'add',type:'text',id:Date.now().toFixed(0),title:'title',body:'body'})", "text": "Show a message overlay"},
{"t":"assertArray", "js": "Bangle['#onswipe']", "is":"undefinedOrEmpty", "text": "No swipe handlers while message overlay is on screen"},
{"t":"emit", "event":"touch", "paramsArray": [ 1, { "x": 10, "y": 10, "type": 0 } ], "text": "Close message"},
{"t":"assert", "js": "Bangle['#onswipe']", "is":"function", "text": "One swipe handler restored"}
]
},{
"description": "Test swipe handler backgrounding with fastloading (setUI)",
"steps" : [
{"t":"cmd", "js": "Bangle.on('swipe',print)", "text": "Create listener for swipes"},
{"t":"cmd", "js": "Bangle.setUI({mode: 'clock',remove: ()=>{}})", "text": "Init UI for clock"},
{"t":"cmd", "js": "require('messagesoverlay').message('text', {src:'Messenger',t:'add',type:'text',id:Date.now().toFixed(0),title:'title',body:'body'})", "text": "Show a message overlay"},
{"t":"assertArray", "js": "Bangle['#onswipe']", "is":"undefinedOrEmpty", "text": "No swipe handlers while message overlay is on screen"},
{"t":"cmd", "js": "Bangle.setUI()", "text": "Trigger removal of UI"},
{"t":"assertArray", "js": "Bangle['#onswipe']", "is":"undefinedOrEmpty", "text": "Still no swipe handlers"},
{"t":"emit", "event":"touch", "paramsArray": [ 1, { "x": 10, "y": 10, "type": 0 } ], "text": "Close message"},
{"t":"assert", "js": "Bangle['#onswipe']", "is":"function", "text": "One swipe handler restored"}
]
},{
"description": "Test watch backgrounding",
"steps" : [
{"t":"cmd", "js": "setWatch(print,BTN)", "text": "Create watch"},
{"t":"cmd", "js": "require('messagesoverlay').message('text', {src:'Messenger',t:'add',type:'text',id:Date.now().toFixed(0),title:'title',body:'body'})", "text": "Show a message overlay"},
{"t":"assertArray", "js": "global[\"\\xff\"].watches", "is":"undefinedOrEmpty", "text": "No watches while message overlay is on screen"},
{"t":"emit", "event":"touch", "paramsArray": [ 1, { "x": 10, "y": 10, "type": 0 } ], "text": "Close message"},
{"t":"assert", "js": "global[\"\\xff\"].watches.length", "is":"equal", "to": "2", "text": "One watch restored, first entry is always empty"}
]
}]
}

View File

@ -0,0 +1 @@
0.01: New App!

19
apps/msgwakefup/README.md Normal file
View File

@ -0,0 +1,19 @@
# Message Wake On Face Up
Temporarily activate wake on face up function when a new message is auto displayed.
## Usage
This is a bootloader app and only needs to be installed to add the functionality to the watch.
## Notes
Tried with "Message UI" app - with and without "Fastload Utils" installed.
## Requests
Mention @thyttan in an issue on the espruino/BangleApps repository.
## Creator
thyttan and Gordon Williams

BIN
apps/msgwakefup/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

9
apps/msgwakefup/boot.js Normal file
View File

@ -0,0 +1,9 @@
// If doing regular loads, not Bangle.load, this is used:
if (global.__FILE__=="messagegui.new.js") Bangle.setOptions({wakeOnFaceUp:true});
// If Fastload Utils is installed this is used:
Bangle.on("message", (_, msg)=>{if (Bangle.CLOCK && msg.new) {
setTimeout(()=>{
if (global.__FILE__=="messagegui.new.js") Bangle.setOptions({wakeOnFaceUp:true});
},700) // It feels like there's a more elegant solution than checking the filename after 700 milliseconds. But this at least seems to work w/o sometimes activating when it shouldn't.
}});

View File

@ -0,0 +1,13 @@
{ "id": "msgwakefup",
"name": "Message wake on face up",
"version":"0.01",
"description": "Temporarily activate wake on face up function when a new message is auto displayed.",
"icon": "app.png",
"tags": "messages,tweak",
"type": "bootloader",
"supports" : ["BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"msgwakefup.boot.js","url":"boot.js"}
]
}

View File

@ -10,3 +10,4 @@
0.09: Minor code improvements
0.10: Handle missing alarm data, e.g. when our reset is fired from
non-multitimer alarms
0.11: Preserve setUI removal callbacks, e.g. those of showMenu

View File

@ -691,10 +691,12 @@ function setUI() {
// E.showMenu/E.showScroller/E.showAlert call setUI, so we register onDrag() separately
// and tack on uiRemove after the fact to avoid interfering
Bangle.on("drag", onDrag);
const origRemove = Bangle.uiRemove;
Bangle.uiRemove = () => {
Bangle.removeListener("drag", onDrag);
Object.values(timerInt1).forEach(clearTimeout);
Object.values(timerInt2).forEach(clearTimeout);
if (origRemove) origRemove();
};
}

View File

@ -1,7 +1,7 @@
{
"id": "multitimer",
"name": "Multi Timer",
"version": "0.10",
"version": "0.11",
"description": "Set timers and chronographs (stopwatches) and watch them count down in real time. Pause, create, edit, and delete timers and chronos, and add custom labels/messages. Also sets alarms.",
"icon": "app.png",
"screenshots": [

View File

@ -10,3 +10,4 @@
0.11: Fix regression that caused no notifications and corrupted background
0.12: Add Bangle.js 2 support with Bangle.setLCDOverlay
0.13: Add a default title background for the dark theme
0.14: Stop event propagation on notification tap

View File

@ -2,7 +2,7 @@
"id": "notify",
"name": "Notifications (default)",
"shortName": "Notifications",
"version": "0.13",
"version": "0.14",
"description": "Provides the default `notify` module used by applications to display notifications on the screen. This module is installed by default by client applications such as the Gadgetbridge app. Installing `Fullscreen Notifications` replaces this module with a version that displays the notifications using the full screen",
"icon": "notify.png",
"type": "notify",

View File

@ -157,6 +157,7 @@ exports.hide = function(options) {
hideCallback = undefined;
id = null;
Bangle.removeListener("touch", exports.hide);
E.stopEventPropagation && E.stopEventPropagation();
function anim() {
pos += 4;
if (pos > 0) {

View File

@ -5,3 +5,5 @@
0.04: Ensure we only scale down clockinfo text if it really won't fit
0.05: Minor code improvements
0.06: Use the clockbg library to allow custom image backgrounds
0.07: Fix automatic coloring of middle of clockinfo images if clockinfo image goes right to the top or left
0.08: Use new clockinfo lib with function to render images wirh borders

Some files were not shown because too many files have changed in this diff Show More