Merge branch 'master' into ts-lint
|
@ -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.
|
||||
|
|
|
@ -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": [
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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" } ],
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||

|
||||
|
||||
## 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
|
||||
|
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
@ -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,30 +50,64 @@ 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){
|
||||
const sekz = zeiger(150,2,s);
|
||||
g.fillPoly(sekz,true);
|
||||
const sekz = zeiger(150,2,s);
|
||||
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,61 +119,34 @@ 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;
|
||||
if (on) {
|
||||
secondInterval = setInterval(draw, 1000);
|
||||
draw(); // draw immediately
|
||||
secondInterval = setInterval(draw, 1000);
|
||||
draw(); // draw immediately
|
||||
}
|
||||
});
|
||||
Bangle.on('lock',on=>{
|
||||
unlock = !on;
|
||||
if (secondInterval) clearInterval(secondInterval);
|
||||
secondInterval = undefined;
|
||||
if (!on) {
|
||||
secondInterval = setInterval(draw, 1000);
|
||||
unlock = true;
|
||||
draw(); // draw immediately
|
||||
}else{
|
||||
secondInterval = setInterval(draw, 60000);
|
||||
unlock = false;
|
||||
draw();
|
||||
}
|
||||
});
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI("clock");
|
||||
secondInterval = setInterval(draw, unlock ? 1000 : 60000);
|
||||
draw(); // draw immediately
|
||||
});
|
||||
Bangle.on('charging',on=>{draw();});
|
||||
|
|
|
@ -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"}]
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
|
@ -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);
|
||||
|
|
@ -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"}
|
||||
]
|
||||
}]
|
||||
}
|
|
@ -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"}
|
||||
]
|
||||
}]
|
||||
}
|
|
@ -1 +1,2 @@
|
|||
0.01: New App!
|
||||
0.02: Add black- and whitelist for apps. Configure the timout time.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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"}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
})
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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')
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
|
@ -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 {
|
||||
|
|
|
@ -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"}],
|
||||
|
|
|
@ -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
|
|
@ -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();
|
||||
|
|
|
@ -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",
|
||||
|
|
After Width: | Height: | Size: 106 KiB |
After Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 462 B |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 597 B |
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.0 KiB |
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 470 B |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 474 B |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 597 B |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 664 B |
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 664 B |
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 597 B |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 684 B |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 664 B |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 651 B |
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 462 B |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 470 B |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 477 B |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 451 B |
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 58 KiB |
|
@ -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);
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
0.01: New App!
|
||||
0.02: Corrected formatting of punctuation
|
|
@ -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"],
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
0.01: New App!
|
||||
0.02: Corrected formatting of punctuation
|
|
@ -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,
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
0.01: First release
|
||||
0.02: Corrected formatting of punctuation
|
|
@ -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"],
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -31,113 +31,129 @@
|
|||
/*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 millisecs = seconds * 1000;
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
}, millisecs - (Date.now() % millisecs));
|
||||
};
|
||||
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 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];
|
||||
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]);
|
||||
}
|
||||
let getTimeString = function(date) {
|
||||
let segment = Math.round((date.getMinutes()*60 + date.getSeconds() + 1)/300);
|
||||
let hour = date.getHours() + Math.floor(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() {
|
||||
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);
|
||||
animate(3);
|
||||
}
|
||||
queueDraw(timeout);
|
||||
};
|
||||
|
||||
let animate = function(step) {
|
||||
if (animInterval) clearInterval(animInterval);
|
||||
let time_string_new_wrapped = g.wrapString(time_string, R.w).join("\n");
|
||||
slideX = 0;
|
||||
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.setColor(g.theme.fg);
|
||||
slideX += step;
|
||||
let stop = false;
|
||||
if (slideX>=R.w) {
|
||||
slideX=R.w;
|
||||
stop = true;
|
||||
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();
|
||||
}
|
||||
}
|
||||
//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);
|
||||
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))
|
||||
}, 30);
|
||||
};
|
||||
queueDraw(timeout);
|
||||
};
|
||||
|
||||
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({
|
||||
mode : 'clock',
|
||||
remove : function() {
|
||||
// Called to unload all of the clock app
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
let animate = function(step) {
|
||||
if (animInterval) clearInterval(animInterval);
|
||||
animInterval = undefined;
|
||||
require('widget_utils').show(); // re-show widgets
|
||||
let time_string_new_wrapped = g.wrapString(time_string, R.w).join("\n");
|
||||
let slideX = 0;
|
||||
//don't let pulling the drawer change y
|
||||
let text_y = R.y + R.h/2;
|
||||
animInterval = setInterval(function() {
|
||||
//blank old time
|
||||
g.setColor(g.theme.bg);
|
||||
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;
|
||||
if (slideX>=R.w) {
|
||||
slideX=R.w;
|
||||
stop = true;
|
||||
}
|
||||
//draw shifted new time
|
||||
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));
|
||||
}, 30);
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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({
|
||||
mode : 'clock',
|
||||
remove : function() {
|
||||
// Called to unload all of the clock app
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
if (animInterval) clearInterval(animInterval);
|
||||
animInterval = undefined;
|
||||
require('widget_utils').show(); // re-show widgets
|
||||
}
|
||||
});
|
||||
|
||||
Bangle.loadWidgets();
|
||||
if (settings.showWidgets) {
|
||||
Bangle.drawWidgets();
|
||||
} else {
|
||||
require("widget_utils").swipeOn(); // hide widgets, make them visible with a swipe
|
||||
}
|
||||
});
|
||||
|
||||
Bangle.loadWidgets();
|
||||
if (settings.showWidgets) {
|
||||
Bangle.drawWidgets();
|
||||
} else {
|
||||
require("widget_utils").swipeOn(); // hide widgets, make them visible with a swipe
|
||||
}
|
||||
|
||||
R = Bangle.appRect;
|
||||
draw();
|
||||
let R = Bangle.appRect;
|
||||
draw();
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Analogue Clock
|
||||
# Interlaced Clock
|
||||
|
||||

|
||||

|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
if (menuScrollMax<menuScrollMin) menuScrollMax=menuScrollMin;
|
||||
|
||||
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});
|
||||
|
|
|
@ -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}})()
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"}
|
||||
]
|
||||
}]
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
|
@ -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
|
After Width: | Height: | Size: 1.4 KiB |
|
@ -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.
|
||||
}});
|
|
@ -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"}
|
||||
]
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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": [
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|