Merge branch 'master' of github.com:espruino/BangleApps

master
Gordon Williams 2022-11-16 15:23:09 +00:00
commit b51b9e8fab
24 changed files with 294 additions and 117 deletions

View File

@ -21,3 +21,6 @@ back-functionality through setUI, adding the red back button as well. Hardware
button to exit is no longer an option.
0.19: Bangle 2: Utilize new Bangle.load(), Bangle.showClock() functions to
facilitate 'fast switching' of apps where available.
0.20: Bangle 2: Revert use of Bangle.load() to classic load() calls since
widgets would still be loaded when they weren't supposed to.

View File

@ -1,47 +1,47 @@
{ // must be inside our own scope here so that when we are unloaded everything disappears
/* Desktop launcher
*
*/
/* Desktop launcher
*
*/
let settings = Object.assign({
showClocks: true,
showLaunchers: true,
direct: false,
swipeExit: false,
timeOut: "Off"
}, require('Storage').readJSON("dtlaunch.json", true) || {});
let settings = Object.assign({
showClocks: true,
showLaunchers: true,
direct: false,
swipeExit: false,
timeOut: "Off"
}, require('Storage').readJSON("dtlaunch.json", true) || {});
let s = require("Storage");
let s = require("Storage");
var apps = s.list(/\.info$/).map(app=>{
let a=s.readJSON(app,1);
return a && {
name:a.name, type:a.type, icon:a.icon, sortorder:a.sortorder, src:a.src
};}).filter(
app=>app && (app.type=="app" || (app.type=="clock" && settings.showClocks) || (app.type=="launch" && settings.showLaunchers) || !app.type));
let a=s.readJSON(app,1);
return a && {
name:a.name, type:a.type, icon:a.icon, sortorder:a.sortorder, src:a.src
};}).filter(
app=>app && (app.type=="app" || (app.type=="clock" && settings.showClocks) || (app.type=="launch" && settings.showLaunchers) || !app.type));
apps.sort((a,b)=>{
let 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;
});
apps.forEach(app=>{
apps.sort((a,b)=>{
let 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;
});
apps.forEach(app=>{
if (app.icon)
app.icon = s.read(app.icon); // should just be a link to a memory area
});
let Napps = apps.length;
let Npages = Math.ceil(Napps/4);
let maxPage = Npages-1;
let selected = -1;
let oldselected = -1;
let page = 0;
const XOFF = 24;
const YOFF = 30;
let Napps = apps.length;
let Npages = Math.ceil(Napps/4);
let maxPage = Npages-1;
let selected = -1;
let oldselected = -1;
let page = 0;
const XOFF = 24;
const YOFF = 30;
let drawIcon= function(p,n,selected) {
let drawIcon= function(p,n,selected) {
let x = (n%2)*72+XOFF;
let y = n>1?72+YOFF:YOFF;
(selected?g.setColor(g.theme.fgH):g.setColor(g.theme.bg)).fillRect(x+11,y+3,x+60,y+52);
@ -65,91 +65,91 @@ let drawIcon= function(p,n,selected) {
}
}
g.drawString(line.trim(),x+36,y+54+lineY*8);
};
};
let drawPage = function(p){
let drawPage = function(p){
g.reset();
g.clearRect(0,24,175,175);
let O = 88+YOFF/2-12*(Npages/2);
for (let j=0;j<Npages;j++){
let y = O+j*12;
g.setColor(g.theme.fg);
if (j==page) g.fillCircle(XOFF/2,y,4);
else g.drawCircle(XOFF/2,y,4);
let y = O+j*12;
g.setColor(g.theme.fg);
if (j==page) g.fillCircle(XOFF/2,y,4);
else g.drawCircle(XOFF/2,y,4);
}
for (let i=0;i<4;i++) {
if (!apps[p*4+i]) return i;
drawIcon(p,i,selected==i && !settings.direct);
if (!apps[p*4+i]) return i;
drawIcon(p,i,selected==i && !settings.direct);
}
g.flip();
};
};
Bangle.loadWidgets();
drawPage(0);
Bangle.loadWidgets();
drawPage(0);
let swipeListenerDt = function(dirLeftRight, dirUpDown){
let swipeListenerDt = function(dirLeftRight, dirUpDown){
updateTimeoutToClock();
selected = 0;
oldselected=-1;
if(settings.swipeExit && dirLeftRight==1) Bangle.showClock();
if (dirUpDown==-1||dirLeftRight==-1){
++page; if (page>maxPage) page=0;
drawPage(page);
++page; if (page>maxPage) page=0;
drawPage(page);
} else if (dirUpDown==1||(dirLeftRight==1 && !settings.swipeExit)){
--page; if (page<0) page=maxPage;
drawPage(page);
--page; if (page<0) page=maxPage;
drawPage(page);
}
};
};
let isTouched = function(p,n){
let isTouched = function(p,n){
if (n<0 || n>3) return false;
let x1 = (n%2)*72+XOFF; let y1 = n>1?72+YOFF:YOFF;
let x2 = x1+71; let y2 = y1+81;
return (p.x>x1 && p.y>y1 && p.x<x2 && p.y<y2);
};
};
let touchListenerDt = function(_,p){
let touchListenerDt = function(_,p){
updateTimeoutToClock();
let i;
for (i=0;i<4;i++){
if((page*4+i)<Napps){
if (isTouched(p,i)) {
drawIcon(page,i,true && !settings.direct);
if (selected>=0 || settings.direct) {
if (selected!=i && !settings.direct){
drawIcon(page,selected,false);
} else {
Bangle.load(apps[page*4+i].src);
}
}
selected=i;
break;
if((page*4+i)<Napps){
if (isTouched(p,i)) {
drawIcon(page,i,true && !settings.direct);
if (selected>=0 || settings.direct) {
if (selected!=i && !settings.direct){
drawIcon(page,selected,false);
} else {
load(apps[page*4+i].src);
}
}
selected=i;
break;
}
}
}
if ((i==4 || (page*4+i)>Napps) && selected>=0) {
drawIcon(page,selected,false);
selected=-1;
drawIcon(page,selected,false);
selected=-1;
}
};
};
Bangle.setUI({
mode : 'custom',
back : Bangle.showClock,
swipe : swipeListenerDt,
touch : touchListenerDt,
remove : ()=>{if (timeoutToClock) clearTimeout(timeoutToClock);}
});
Bangle.setUI({
mode : 'custom',
back : Bangle.showClock,
swipe : swipeListenerDt,
touch : touchListenerDt,
remove : ()=>{if (timeoutToClock) clearTimeout(timeoutToClock);}
});
// taken from Icon Launcher with minor alterations
let timeoutToClock;
const updateTimeoutToClock = function(){
if (settings.timeOut!="Off"){
let time=parseInt(settings.timeOut); //the "s" will be trimmed by the parseInt
if (timeoutToClock) clearTimeout(timeoutToClock);
timeoutToClock = setTimeout(Bangle.showClock,time*1000);
}
};
updateTimeoutToClock();
// taken from Icon Launcher with minor alterations
let timeoutToClock;
const updateTimeoutToClock = function(){
if (settings.timeOut!="Off"){
let time=parseInt(settings.timeOut); //the "s" will be trimmed by the parseInt
if (timeoutToClock) clearTimeout(timeoutToClock);
timeoutToClock = setTimeout(Bangle.showClock,time*1000);
}
};
updateTimeoutToClock();
} // end of app scope

View File

@ -1,7 +1,7 @@
{
"id": "dtlaunch",
"name": "Desktop Launcher",
"version": "0.19",
"version": "0.20",
"description": "Desktop style App Launcher with six (four for Bangle 2) apps per page - fast access if you have lots of apps installed.",
"screenshots": [{"url":"shot1.png"},{"url":"shot2.png"},{"url":"shot3.png"}],
"icon": "icon.png",

View File

@ -7,3 +7,5 @@
0.21: Add Settings
0.22: Use default Bangle formatter for booleans
0.23: Added note to configure position in "my location" if not done yet. Small fixes.
0.24: Added fast load
0.25: Minor code optimization

View File

@ -1,3 +1,5 @@
{ // must be inside our own scope here so that when we are unloaded everything disappears
// ------- Settings file
const SETTINGSFILE = "hworldclock.json";
var secondsMode;
@ -153,15 +155,15 @@ function updatePos() {
function drawSeconds() {
// get date
var d = new Date();
var da = d.toString().split(" ");
let d = new Date();
let da = d.toString().split(" ");
// default draw styles
g.reset().setBgColor(g.theme.bg).setFontAlign(0, 0);
// draw time
var time = da[4].split(":");
var seconds = time[2];
let time = da[4].split(":");
let seconds = time[2];
g.setFont("5x9Numeric7Seg",primaryTimeFontSize - 3);
if (g.theme.dark) {
@ -184,15 +186,15 @@ function drawSeconds() {
function draw() {
// get date
var d = new Date();
var da = d.toString().split(" ");
let d = new Date();
let da = d.toString().split(" ");
// default draw styles
g.reset().setBgColor(g.theme.bg).setFontAlign(0, 0);
// draw time
var time = da[4].split(":");
var hours = time[0],
let time = da[4].split(":");
let hours = time[0],
minutes = time[1];
@ -223,7 +225,7 @@ function draw() {
// am / PM ?
if (_12hour){
//do 12 hour stuff
//var ampm = require("locale").medidian(new Date()); Not working
//let ampm = require("locale").medidian(new Date()); Not working
g.setFont("Vector", 17);
g.drawString(ampm, xyCenterSeconds, yAmPm, true);
}
@ -232,14 +234,14 @@ function draw() {
// draw Day, name of month, Date
//DATE
var localDate = require("locale").date(new Date(), 1);
let localDate = require("locale").date(new Date(), 1);
localDate = localDate.substring(0, localDate.length - 5);
g.setFont("Vector", 17);
g.drawString(require("locale").dow(new Date(), 1).toUpperCase() + ", " + localDate, xyCenter, yposDate, true);
g.setFont(font, primaryDateFontSize);
// set gmt to UTC+0
var gmt = new Date(d.getTime() + d.getTimezoneOffset() * 60 * 1000);
let gmt = new Date(d.getTime() + d.getTimezoneOffset() * 60 * 1000);
// Loop through offset(s) and render
offsets.forEach((offset, index) => {
@ -249,7 +251,7 @@ function draw() {
if (offsets.length === 1) {
var date = [require("locale").dow(new Date(), 1), require("locale").date(new Date(), 1)];
let date = [require("locale").dow(new Date(), 1), require("locale").date(new Date(), 1)];
// For a single secondary timezone, draw it bigger and drop time zone to second line
const xOffset = 30;
g.setFont(font, secondaryTimeFontSize).drawString(`${hours}:${minutes}`, xyCenter, yposTime2, true);
@ -295,8 +297,18 @@ g.clear();
// Init the settings of the app
loadMySettings();
// Show launcher when button pressed
Bangle.setUI("clock");
// Show launcher when middle button pressed
Bangle.setUI({
mode : "clock",
remove : function() {
// Called to unload all of the clock app
if (PosInterval) clearInterval(PosInterval);
PosInterval = undefined;
if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
drawTimeoutSeconds = undefined;
if (drawTimeout) clearTimeout(drawTimeout);
drawTimeout = undefined;
}});
Bangle.loadWidgets();
Bangle.drawWidgets();
@ -307,7 +319,7 @@ draw();
if (!Bangle.isLocked()) { // Initial state
if (showSunInfo) {
if (PosInterval != 0) clearInterval(PosInterval);
if (PosInterval != 0 && typeof PosInterval != 'undefined') clearInterval(PosInterval);
PosInterval = setInterval(updatePos, 60*10E3); // refesh every 10 mins
updatePos();
}
@ -333,7 +345,7 @@ if (!Bangle.isLocked()) { // Initial state
drawTimeout = undefined;
if (showSunInfo) {
if (PosInterval != 0) clearInterval(PosInterval);
if (PosInterval != 0 && typeof PosInterval != 'undefined') clearInterval(PosInterval);
PosInterval = setInterval(updatePos, 60*60E3); // refesh every 60 mins
updatePos();
}
@ -378,4 +390,5 @@ Bangle.on('lock',on=>{
}
draw(); // draw immediately, queue redraw
}
});
});
}

View File

@ -2,7 +2,7 @@
"id": "hworldclock",
"name": "Hanks World Clock",
"shortName": "Hanks World Clock",
"version": "0.23",
"version": "0.25",
"description": "Current time zone plus up to three others",
"allow_emulator":true,
"icon": "app.png",

View File

@ -15,3 +15,6 @@
0.11: Cleanup timeout when changing to clock
Reset timeout on swipe and drag
0.12: Use Bangle.load and Bangle.showClock
0.13: Fix automatic switch to clock
0.14: Revert use of Bangle.load to classic load calls since widgets would
still be loaded when they weren't supposed to.

View File

@ -94,7 +94,7 @@
const appId = id * appsN + iconN;
if( settings.direct && launchCache.apps[appId])
{
Bangle.load(launchCache.apps[appId].src);
load(launchCache.apps[appId].src);
return;
}
if (appId == selectedItem && launchCache.apps[appId]) {
@ -102,7 +102,7 @@
if (!app.src || s.read(app.src) === undefined) {
E.showMessage( /*LANG*/ "App Source\nNot found");
} else {
Bangle.load(app.src);
load(app.src);
}
}
selectedItem = appId;
@ -198,7 +198,7 @@
if (settings.timeOut!="Off"){
let time=parseInt(settings.timeOut); //the "s" will be trimmed by the parseInt
if (timeout) clearTimeout(timeout);
timeout = setTimeout(returnToClock,time*1000);
timeout = setTimeout(Bangle.showClock,time*1000);
}
};

View File

@ -2,7 +2,7 @@
"id": "iconlaunch",
"name": "Icon Launcher",
"shortName" : "Icon launcher",
"version": "0.12",
"version": "0.14",
"icon": "app.png",
"description": "A launcher inspired by smartphones, with an icon-only scrollable menu.",
"tags": "tool,system,launcher",

View File

@ -1 +1,2 @@
0.01: New app
0.02: Clear between redraws

View File

@ -1,7 +1,7 @@
{
"id": "widanclk",
"name": "Analog clock widget",
"version": "0.01",
"version": "0.02",
"description": "A simple analog clock widget that appears when not showing a fullscreen clock",
"icon": "widget.png",
"type": "widget",

View File

@ -12,7 +12,8 @@ WIDGETS["wdanclk"]={area:"tl",width:Bangle.CLOCK?0:24,draw:function() {
let x=this.x+12, y=this.y+12,
ah = (d.getHours()+d.getMinutes()/60)*Math.PI/6,
am = d.getMinutes()*Math.PI/30;
g.drawCircle(x, y, 11).
g.clearRect(this.x, this.y, this.x+this.width-1, this.y+23).
drawCircle(x, y, 11).
drawLine(x,y, x+Math.sin(ah)*7, y-Math.cos(ah)*7).
drawLine(x,y, x+Math.sin(am)*9, y-Math.cos(am)*9);
// queue draw in one minute

View File

@ -12,4 +12,5 @@
0.13: Added "connection restored" notification. Fixed restoring of the watchface.
0.14: Added configuration option
0.15: Added option to hide widget when connected
0.16: Simplify code, add option to disable displaying a message
0.16: Simplify code, add option to disable displaying a message
0.17: Minor display fix

View File

@ -1,7 +1,7 @@
{
"id": "widbt_notify",
"name": "Bluetooth Widget with Notification",
"version": "0.16",
"version": "0.17",
"description": "Show the current Bluetooth connection status with some optional features: show message, buzz on connect/loss, hide always/if connected.",
"icon": "widget.png",
"type": "widget",

View File

@ -40,7 +40,7 @@
redrawCurrentApp: function() {
if (typeof(draw) == 'function') {
g.clear();
g.reset().clear();
draw();
Bangle.loadWidgets();
Bangle.drawWidgets();

View File

@ -3,3 +3,4 @@
0.03: Ensure redrawing works with variable size widget system
0.04: tag HRM power requests to allow this ot work alongside other widgets/apps (fix #799)
0.05: Use new 'lock' event, not LCD (so it works on Bangle.js 2)
0.06: Allow configuration of minimum confidence for HRM values

View File

@ -1,13 +1,15 @@
{
"id": "widhrm",
"name": "Simple Heart Rate widget",
"version": "0.05",
"version": "0.06",
"description": "When the screen is on, the widget turns on the heart rate monitor and displays the current heart rate (or last known in grey). For this to work well you'll need at least a 15 second LCD Timeout.",
"icon": "widget.png",
"type": "widget",
"tags": "health,widget",
"supports": ["BANGLEJS","BANGLEJS2"],
"storage": [
{"name":"widhrm.wid.js","url":"widget.js"}
]
{"name":"widhrm.wid.js","url":"widget.js"},
{"name":"widhrm.settings.js","url":"settings.js"}
],
"data": [{"name":"widhrm.json"}]
}

36
apps/widhrm/settings.js Normal file
View File

@ -0,0 +1,36 @@
/**
* @param {function} back Use back() to return to settings menu
*/
(function(back) {
const SETTINGS_FILE = "widhrm.json";
const storage = require("Storage");
let s = {
confidence: 0
};
const saved = storage.readJSON(SETTINGS_FILE, 1) || {};
for(const key in saved) {
s[key] = saved[key];
}
function save(key, value) {
s[key] = value;
storage.write(SETTINGS_FILE, s);
}
const menu = {
"": {"title": "Simple Heart Rate widget"},
"< Back": back,
/*LANG*/'min. confidence': {
value: s.confidence,
min: 0,
max : 100,
step: 5,
format: x => {
return x + "%";
},
onchange: x => save('confidence', x),
},
};
E.showMenu(menu);
});

View File

@ -1,5 +1,18 @@
(() => {
if (!Bangle.isLocked) return; // old firmware
const SETTINGS_FILE = 'widhrm.json';
let settings;
function loadSettings() {
settings = require('Storage').readJSON(SETTINGS_FILE, 1) || {};
const DEFAULTS = {
'confidence': 0
};
Object.keys(DEFAULTS).forEach(k=>{
if (settings[k]===undefined) settings[k]=DEFAULTS[k];
});
}
var currentBPM;
var lastBPM;
var isHRMOn = false;
@ -39,7 +52,7 @@
bpm = lastBPM;
isCurrent = false;
}
if (bpm===undefined)
if (bpm===undefined || (settings && bpm<settings["confidence"]))
bpm = "--";
g.setColor(isCurrent ? g.theme.fg : "#808080");
g.drawString(bpm, this.x+width/2, this.y+19);

View File

@ -0,0 +1,4 @@
0.01: First version
0.02: Load settings only once
Better icons
Read sleep status on every draw

View File

@ -0,0 +1,16 @@
{
"id": "widsleepstatus",
"name": "Sleep Status Widget",
"version": "0.02",
"description": "Shows current status of sleep from sleeplog app.",
"icon": "widget.png",
"type": "widget",
"tags": "widget,sleep",
"supports": ["BANGLEJS","BANGLEJS2"],
"dependencies" : { "sleeplog":"app" },
"storage": [
{"name":"widsleepstatus.wid.js","url":"widget.js"},
{"name":"widsleepstatus.settings.js","url":"settings.js"}
],
"data": [{"name":"widsleepstatus.json"}]
}

View File

@ -0,0 +1,32 @@
/**
* @param {function} back Use back() to return to settings menu
*/
(function(back) {
const SETTINGS_FILE = "widsleepstatus.json";
const storage = require("Storage");
let s = {
hidewhenawake: true
};
const saved = storage.readJSON(SETTINGS_FILE, 1) || {};
for(const key in saved) {
s[key] = saved[key];
}
function save(key) {
return function(value) {
s[key] = value;
storage.write(SETTINGS_FILE, s);
};
}
const menu = {
"": {"title": "Sleep Status Widget"},
"< Back": back,
"Hide when awake": {
value: s.hidewhenawake,
onchange: save("hidewhenawake"),
},
};
E.showMenu(menu);
});

View File

@ -0,0 +1,49 @@
(function() {
if (!sleeplog) return;
const SETTINGS_FILE = 'widsleepstatus.json';
let settings;
function loadSettings() {
settings = require('Storage').readJSON(SETTINGS_FILE, 1) || {};
const DEFAULTS = {
'hidewhenawake': true
};
Object.keys(DEFAULTS).forEach(k => {
if (settings[k] === undefined) settings[k] = DEFAULTS[k];
});
}
loadSettings();
WIDGETS.sleepstatus = {
area: "tr",
width: 0,
draw: function(w) {
let status = sleeplog.status || 0;
if (w.width != (status >= 2 ? 24 : 0)){
w.width = status >= 2 ? 24 : 0;
return Bangle.drawWidgets();
}
g.reset();
switch (status) {
case 0:
case 1:
break;
case 2: // awake
if (settings && !settings["hidewhenawake"]) g.drawImage(atob("GBiBAAAAAAAAAAAMAAA+AAAjAAEjMAGyYAGeYAzAwB5/gB4/AB4jAB4jAB4jAB4jAB//+Bv/+Bg2GB+2+B+2eB42eAAAAAAAAAAAAA=="), w.x, w.y);
break;
case 3: // light sleep
g.drawImage(atob("GBiBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAGAAAGAAAGAAAGcf/Ge//GWwBGewBmcwBn///mAABmAABmAABgAAAAAAAAAAAA=="), w.x, w.y);
break;
case 4: // deep sleep
g.drawImage(atob("GBiBAAAAAAAAAAAB4APw4APxwADh8AHAAAOAAGPwAGAAAGAAAGAAAGcf/Ge//GWwBGewBmcwBn///mAABmAABmAABgAAAAAAAAAAAA=="), w.x, w.y);
break;
}
}
};
setInterval(()=>{
WIDGETS.sleepstatus.draw(WIDGETS.sleepstatus);
}, 60000);
Bangle.drawWidgets();
})()

Binary file not shown.

After

Width:  |  Height:  |  Size: 698 B