forked from FOSS/BangleApps
Merge branch 'espruino:master' into sleeplogalarm
commit
ada3ea74c8
|
@ -11,3 +11,4 @@
|
||||||
0.11: Bangle.js2: New pixels, btn1 to exit
|
0.11: Bangle.js2: New pixels, btn1 to exit
|
||||||
0.12: Actual pixels as of 29th Nov 2021
|
0.12: Actual pixels as of 29th Nov 2021
|
||||||
0.13: Bangle.js 2: Use setUI to add software back button
|
0.13: Bangle.js 2: Use setUI to add software back button
|
||||||
|
0.14: Add automatic translation of more strings
|
||||||
|
|
|
@ -11,8 +11,8 @@ g.drawString("BANGLEJS.COM",120,y-4);
|
||||||
} else {
|
} else {
|
||||||
y=-(4+h); // small screen, start right at top
|
y=-(4+h); // small screen, start right at top
|
||||||
}
|
}
|
||||||
g.drawString("Powered by Espruino",0,y+=4+h);
|
g.drawString(/*LANG*/"Powered by Espruino",0,y+=4+h);
|
||||||
g.drawString("Version "+ENV.VERSION,0,y+=h);
|
g.drawString(/*LANG*/"Version "+ENV.VERSION,0,y+=h);
|
||||||
g.drawString("Commit "+ENV.GIT_COMMIT,0,y+=h);
|
g.drawString("Commit "+ENV.GIT_COMMIT,0,y+=h);
|
||||||
function getVersion(name,file) {
|
function getVersion(name,file) {
|
||||||
var j = s.readJSON(file,1);
|
var j = s.readJSON(file,1);
|
||||||
|
@ -24,9 +24,9 @@ getVersion("Launcher","launch.info");
|
||||||
getVersion("Settings","setting.info");
|
getVersion("Settings","setting.info");
|
||||||
|
|
||||||
y+=h;
|
y+=h;
|
||||||
g.drawString(MEM.total+" JS Variables available",0,y+=h);
|
g.drawString(MEM.total+/*LANG*/" JS Variables available",0,y+=h);
|
||||||
g.drawString("Storage: "+(require("Storage").getFree()>>10)+"k free",0,y+=h);
|
g.drawString("Storage: "+(require("Storage").getFree()>>10)+/*LANG*/"k free",0,y+=h);
|
||||||
if (ENV.STORAGE) g.drawString(" "+(ENV.STORAGE>>10)+"k total",0,y+=h);
|
if (ENV.STORAGE) g.drawString(" "+(ENV.STORAGE>>10)+/*LANG*/"k total",0,y+=h);
|
||||||
if (ENV.SPIFLASH) g.drawString("SPI Flash: "+(ENV.SPIFLASH>>10)+"k",0,y+=h);
|
if (ENV.SPIFLASH) g.drawString("SPI Flash: "+(ENV.SPIFLASH>>10)+"k",0,y+=h);
|
||||||
g.setFontAlign(0,-1);
|
g.setFontAlign(0,-1);
|
||||||
g.flip();
|
g.flip();
|
||||||
|
|
|
@ -35,17 +35,17 @@ function drawInfo() {
|
||||||
g.setFont("4x6").setFontAlign(0,0).drawString("BANGLEJS.COM",W-30,56);
|
g.setFont("4x6").setFontAlign(0,0).drawString("BANGLEJS.COM",W-30,56);
|
||||||
var h=8, y = 24-h;
|
var h=8, y = 24-h;
|
||||||
g.setFont("6x8").setFontAlign(-1,-1);
|
g.setFont("6x8").setFontAlign(-1,-1);
|
||||||
g.drawString("Powered by Espruino",0,y+=4+h);
|
g.drawString(/*LANG*/"Powered by Espruino",0,y+=4+h);
|
||||||
g.drawString("Version "+ENV.VERSION,0,y+=h);
|
g.drawString(/*LANG*/"Version "+ENV.VERSION,0,y+=h);
|
||||||
g.drawString("Commit "+ENV.GIT_COMMIT,0,y+=h);
|
g.drawString("Commit "+ENV.GIT_COMMIT,0,y+=h);
|
||||||
|
|
||||||
getVersion("Bootloader","boot.info");
|
getVersion("Bootloader","boot.info");
|
||||||
getVersion("Launcher","launch.info");
|
getVersion("Launcher","launch.info");
|
||||||
getVersion("Settings","setting.info");
|
getVersion("Settings","setting.info");
|
||||||
|
|
||||||
g.drawString(MEM.total+" JS Vars",0,y+=h);
|
g.drawString(MEM.total+/*LANG*/" JS Vars",0,y+=h);
|
||||||
g.drawString("Storage: "+(require("Storage").getFree()>>10)+"k free",0,y+=h);
|
g.drawString("Storage: "+(require("Storage").getFree()>>10)+/*LANG*/"k free",0,y+=h);
|
||||||
if (ENV.STORAGE) g.drawString(" "+(ENV.STORAGE>>10)+"k total",0,y+=h);
|
if (ENV.STORAGE) g.drawString(" "+(ENV.STORAGE>>10)+/*LANG*/"k total",0,y+=h);
|
||||||
if (ENV.SPIFLASH) g.drawString("SPI Flash: "+(ENV.SPIFLASH>>10)+"k",0,y+=h);
|
if (ENV.SPIFLASH) g.drawString("SPI Flash: "+(ENV.SPIFLASH>>10)+"k",0,y+=h);
|
||||||
imageTop = y+h;
|
imageTop = y+h;
|
||||||
imgScroll = imgHeight-imageTop;
|
imgScroll = imgHeight-imageTop;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "about",
|
"id": "about",
|
||||||
"name": "About",
|
"name": "About",
|
||||||
"version": "0.13",
|
"version": "0.14",
|
||||||
"description": "Bangle.js About page - showing software version, stats, and a collaborative mural from the Bangle.js KickStarter backers",
|
"description": "Bangle.js About page - showing software version, stats, and a collaborative mural from the Bangle.js KickStarter backers",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,system",
|
"tags": "tool,system",
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
0.01: New app!
|
0.01: New app!
|
||||||
0.02: Design improvements and fixes.
|
0.02: Design improvements and fixes.
|
||||||
0.03: Indicate battery level through line occurrence.
|
0.03: Indicate battery level through line occurrence.
|
||||||
|
0.04: Use widget_utils module.
|
||||||
|
|
|
@ -215,8 +215,7 @@ Bangle.loadWidgets();
|
||||||
* so we will blank out the draw() functions of each widget and change the
|
* so we will blank out the draw() functions of each widget and change the
|
||||||
* area to the top bar doesn't get cleared.
|
* area to the top bar doesn't get cleared.
|
||||||
*/
|
*/
|
||||||
for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";}
|
require('widget_utils').hide();
|
||||||
|
|
||||||
// Clear the screen once, at startup and draw clock
|
// Clear the screen once, at startup and draw clock
|
||||||
g.setTheme({bg:"#fff",fg:"#000",dark:false}).clear();
|
g.setTheme({bg:"#fff",fg:"#000",dark:false}).clear();
|
||||||
draw();
|
draw();
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"name": "AI Clock",
|
"name": "AI Clock",
|
||||||
"shortName":"AI Clock",
|
"shortName":"AI Clock",
|
||||||
"icon": "aiclock.png",
|
"icon": "aiclock.png",
|
||||||
"version":"0.03",
|
"version":"0.04",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"supports": ["BANGLEJS2"],
|
"supports": ["BANGLEJS2"],
|
||||||
"description": "A watch face that was designed by an AI (stable diffusion) and implemented by a human.",
|
"description": "A watch face that was designed by an AI (stable diffusion) and implemented by a human.",
|
||||||
|
|
|
@ -34,4 +34,6 @@
|
||||||
0.32: Fix wrong hidden filter
|
0.32: Fix wrong hidden filter
|
||||||
Add option for auto-delete a timer after it expires
|
Add option for auto-delete a timer after it expires
|
||||||
0.33: Allow hiding timers&alarms
|
0.33: Allow hiding timers&alarms
|
||||||
|
0.34: Add "Confirm" option to alarm/timer edit menus
|
||||||
|
0.35: Add automatic translation of more strings
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,12 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) {
|
||||||
value: alarm.hidden || false,
|
value: alarm.hidden || false,
|
||||||
onchange: v => alarm.hidden = v
|
onchange: v => alarm.hidden = v
|
||||||
},
|
},
|
||||||
/*LANG*/"Cancel": () => showMainMenu()
|
/*LANG*/"Cancel": () => showMainMenu(),
|
||||||
|
/*LANG*/"Confirm": () => {
|
||||||
|
prepareAlarmForSave(alarm, alarmIndex, time);
|
||||||
|
saveAndReload();
|
||||||
|
showMainMenu();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!isNew) {
|
if (!isNew) {
|
||||||
|
@ -178,7 +183,7 @@ function decodeDOW(alarm) {
|
||||||
.map((day, index) => alarm.dow & (1 << (index + firstDayOfWeek)) ? day : "_")
|
.map((day, index) => alarm.dow & (1 << (index + firstDayOfWeek)) ? day : "_")
|
||||||
.join("")
|
.join("")
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
: "Once"
|
: /*LANG*/"Once"
|
||||||
}
|
}
|
||||||
|
|
||||||
function showEditRepeatMenu(repeat, dow, dowChangeCallback) {
|
function showEditRepeatMenu(repeat, dow, dowChangeCallback) {
|
||||||
|
@ -293,7 +298,12 @@ function showEditTimerMenu(selectedTimer, timerIndex) {
|
||||||
onchange: v => timer.hidden = v
|
onchange: v => timer.hidden = v
|
||||||
},
|
},
|
||||||
/*LANG*/"Vibrate": require("buzz_menu").pattern(timer.vibrate, v => timer.vibrate = v),
|
/*LANG*/"Vibrate": require("buzz_menu").pattern(timer.vibrate, v => timer.vibrate = v),
|
||||||
/*LANG*/"Cancel": () => showMainMenu()
|
/*LANG*/"Cancel": () => showMainMenu(),
|
||||||
|
/*LANG*/"Confirm": () => {
|
||||||
|
prepareTimerForSave(timer, timerIndex, time);
|
||||||
|
saveAndReload();
|
||||||
|
showMainMenu();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!isNew) {
|
if (!isNew) {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "alarm",
|
"id": "alarm",
|
||||||
"name": "Alarms & Timers",
|
"name": "Alarms & Timers",
|
||||||
"shortName": "Alarms",
|
"shortName": "Alarms",
|
||||||
"version": "0.33",
|
"version": "0.35",
|
||||||
"description": "Set alarms and timers on your Bangle",
|
"description": "Set alarms and timers on your Bangle",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,alarm,widget",
|
"tags": "tool,alarm,widget",
|
||||||
|
|
|
@ -13,3 +13,4 @@
|
||||||
0.13: Add font setting
|
0.13: Add font setting
|
||||||
0.14: Use ClockFace_menu.addItems
|
0.14: Use ClockFace_menu.addItems
|
||||||
0.15: Add Power saving option
|
0.15: Add Power saving option
|
||||||
|
0.16: Support Fast Loading
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
/* jshint esversion: 6 */
|
/* jshint esversion: 6 */
|
||||||
|
{
|
||||||
/**
|
/**
|
||||||
* A simple digital clock showing seconds as a bar
|
* A simple digital clock showing seconds as a bar
|
||||||
**/
|
**/
|
||||||
|
@ -14,7 +15,7 @@ let locale = require("locale");
|
||||||
}
|
}
|
||||||
|
|
||||||
let barW = 0, prevX = 0;
|
let barW = 0, prevX = 0;
|
||||||
function renderBar(l) {
|
const renderBar = function (l) {
|
||||||
"ram";
|
"ram";
|
||||||
if (l) prevX = 0; // called from Layout: drawing area was cleared
|
if (l) prevX = 0; // called from Layout: drawing area was cleared
|
||||||
else l = clock.layout.bar;
|
else l = clock.layout.bar;
|
||||||
|
@ -27,7 +28,7 @@ function renderBar(l) {
|
||||||
prevX = x2;
|
prevX = x2;
|
||||||
}
|
}
|
||||||
|
|
||||||
function timeText(date) {
|
const timeText = function(date) {
|
||||||
if (!clock.is12Hour) {
|
if (!clock.is12Hour) {
|
||||||
return locale.time(date, true);
|
return locale.time(date, true);
|
||||||
}
|
}
|
||||||
|
@ -40,16 +41,14 @@ function timeText(date) {
|
||||||
}
|
}
|
||||||
return locale.time(date12, true);
|
return locale.time(date12, true);
|
||||||
}
|
}
|
||||||
function ampmText(date) {
|
const ampmText = date => (clock.is12Hour && locale.hasMeridian) ? locale.meridian(date) : "";
|
||||||
return (clock.is12Hour && locale.hasMeridian) ? locale.meridian(date) : "";
|
const dateText = date => {
|
||||||
}
|
|
||||||
function dateText(date) {
|
|
||||||
const dayName = locale.dow(date, true),
|
const dayName = locale.dow(date, true),
|
||||||
month = locale.month(date, true),
|
month = locale.month(date, true),
|
||||||
day = date.getDate();
|
day = date.getDate();
|
||||||
const dayMonth = locale.dayFirst ? `${day} ${month}` : `${month} ${day}`;
|
const dayMonth = locale.dayFirst ? `${day} ${month}` : `${month} ${day}`;
|
||||||
return `${dayName} ${dayMonth}`;
|
return `${dayName} ${dayMonth}`;
|
||||||
}
|
};
|
||||||
|
|
||||||
const ClockFace = require("ClockFace"),
|
const ClockFace = require("ClockFace"),
|
||||||
clock = new ClockFace({
|
clock = new ClockFace({
|
||||||
|
@ -110,15 +109,20 @@ const ClockFace = require("ClockFace"),
|
||||||
prevX = 0; // force redraw of bar
|
prevX = 0; // force redraw of bar
|
||||||
this.layout.forgetLazyState();
|
this.layout.forgetLazyState();
|
||||||
},
|
},
|
||||||
|
remove: function() {
|
||||||
|
if (this.onLock) Bangle.removeListener("lock", this.onLock);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// power saving: only update once a minute while locked, hide bar
|
// power saving: only update once a minute while locked, hide bar
|
||||||
if (clock.powerSave) {
|
if (clock.powerSave) {
|
||||||
Bangle.on("lock", lock => {
|
clock.onLock = lock => {
|
||||||
clock.precision = lock ? 60 : 1;
|
clock.precision = lock ? 60 : 1;
|
||||||
clock.tick();
|
clock.tick();
|
||||||
renderBar(); // hide/redraw bar right away
|
renderBar(); // hide/redraw bar right away
|
||||||
});
|
}
|
||||||
|
Bangle.on("lock", clock.onLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
clock.start();
|
clock.start();
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "barclock",
|
"id": "barclock",
|
||||||
"name": "Bar Clock",
|
"name": "Bar Clock",
|
||||||
"version": "0.15",
|
"version": "0.16",
|
||||||
"description": "A simple digital clock showing seconds as a bar",
|
"description": "A simple digital clock showing seconds as a bar",
|
||||||
"icon": "clock-bar.png",
|
"icon": "clock-bar.png",
|
||||||
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot_pm.png"}],
|
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot_pm.png"}],
|
||||||
|
|
|
@ -2,3 +2,4 @@
|
||||||
0.02: More compact rendering & app icon
|
0.02: More compact rendering & app icon
|
||||||
0.03: Tell clock widgets to hide.
|
0.03: Tell clock widgets to hide.
|
||||||
0.04: Improve current time readability in light theme.
|
0.04: Improve current time readability in light theme.
|
||||||
|
0.05: Show calendar colors & improved all day events.
|
||||||
|
|
|
@ -20,41 +20,55 @@ function zp(str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawEventHeader(event, y) {
|
function drawEventHeader(event, y) {
|
||||||
g.setFont("Vector", 24);
|
var x = 0;
|
||||||
|
|
||||||
var time = isActive(event) ? new Date() : new Date(event.timestamp * 1000);
|
var time = isActive(event) ? new Date() : new Date(event.timestamp * 1000);
|
||||||
|
|
||||||
|
//Don't need to know what time the event is at if its all day
|
||||||
|
if (isActive(event) || !event.allDay) {
|
||||||
|
g.setFont("Vector", 24);
|
||||||
var timeStr = zp(time.getHours()) + ":" + zp(time.getMinutes());
|
var timeStr = zp(time.getHours()) + ":" + zp(time.getMinutes());
|
||||||
g.drawString(timeStr, 5, y);
|
g.drawString(timeStr, 0, y);
|
||||||
y += 24;
|
y += 3;
|
||||||
|
x = 13*timeStr.length+5;
|
||||||
|
}
|
||||||
|
|
||||||
g.setFont("12x20", 1);
|
g.setFont("12x20", 1);
|
||||||
|
|
||||||
if (isActive(event)) {
|
if (isActive(event)) {
|
||||||
g.drawString(zp(time.getDate())+". " + require("locale").month(time,1),15*timeStr.length,y-21);
|
g.drawString(zp(time.getDate())+". " + require("locale").month(time,1),x,y);
|
||||||
} else {
|
} else {
|
||||||
var offset = 0-time.getTimezoneOffset()/1440;
|
var offset = 0-time.getTimezoneOffset()/1440;
|
||||||
var days = Math.floor((time.getTime()/1000)/86400+offset)-Math.floor(getTime()/86400+offset);
|
var days = Math.floor((time.getTime()/1000)/86400+offset)-Math.floor(getTime()/86400+offset);
|
||||||
if(days > 0) {
|
if(days > 0 || event.allDay) {
|
||||||
var daysStr = days===1?/*LANG*/"tomorrow":/*LANG*/"in "+days+/*LANG*/" days";
|
var daysStr = days===1?/*LANG*/"tomorrow":/*LANG*/"in "+days+/*LANG*/" days";
|
||||||
g.drawString(daysStr,15*timeStr.length,y-21);
|
g.drawString(daysStr,x,y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
y += 21;
|
||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawEventBody(event, y) {
|
function drawEventBody(event, y) {
|
||||||
g.setFont("12x20", 1);
|
g.setFont("12x20", 1);
|
||||||
var lines = g.wrapString(event.title, g.getWidth()-10);
|
var lines = g.wrapString(event.title, g.getWidth()-15);
|
||||||
|
var yStart = y;
|
||||||
if (lines.length > 2) {
|
if (lines.length > 2) {
|
||||||
lines = lines.slice(0,2);
|
lines = lines.slice(0,2);
|
||||||
lines[1] = lines[1].slice(0,-3)+"...";
|
lines[1] = lines[1].slice(0,-3)+"...";
|
||||||
}
|
}
|
||||||
g.drawString(lines.join('\n'), 5, y);
|
g.drawString(lines.join('\n'),10,y);
|
||||||
y+=20 * lines.length;
|
y+=20 * lines.length;
|
||||||
if(event.location) {
|
if(event.location) {
|
||||||
g.drawImage(atob("DBSBAA8D/H/nDuB+B+B+B3Dn/j/B+A8A8AYAYAYAAAAAAA=="),5,y);
|
g.drawImage(atob("DBSBAA8D/H/nDuB+B+B+B3Dn/j/B+A8A8AYAYAYAAAAAAA=="),10,y);
|
||||||
g.drawString(event.location, 20, y);
|
g.drawString(event.location,25,y);
|
||||||
y+=20;
|
y+=20;
|
||||||
}
|
}
|
||||||
|
if (event.color) {
|
||||||
|
var oldColor = g.getColor();
|
||||||
|
g.setColor("#"+(0x1000000+Number(event.color)).toString(16).padStart(6,"0"));
|
||||||
|
g.fillRect(0,yStart,5,y-3);
|
||||||
|
g.setColor(oldColor);
|
||||||
|
}
|
||||||
y+=5;
|
y+=5;
|
||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
@ -68,8 +82,8 @@ function drawEvent(event, y) {
|
||||||
var curEventHeight = 0;
|
var curEventHeight = 0;
|
||||||
|
|
||||||
function drawCurrentEvents(y) {
|
function drawCurrentEvents(y) {
|
||||||
g.setColor(g.theme.dark ? "#0ff" : "#0000ff");
|
g.setColor(g.theme.dark ? "#0ff" : "#00f");
|
||||||
g.clearRect(5, y, g.getWidth() - 5, y + curEventHeight);
|
g.clearRect(0,y,g.getWidth()-5,y+curEventHeight);
|
||||||
curEventHeight = y;
|
curEventHeight = y;
|
||||||
|
|
||||||
if(current.length === 0) {
|
if(current.length === 0) {
|
||||||
|
@ -94,7 +108,7 @@ function drawFutureEvents(y) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function fullRedraw() {
|
function fullRedraw() {
|
||||||
g.clearRect(5,24,g.getWidth()-5,g.getHeight());
|
g.clearRect(0,24,g.getWidth()-5,g.getHeight());
|
||||||
updateCalendar();
|
updateCalendar();
|
||||||
var y = 30;
|
var y = 30;
|
||||||
y = drawCurrentEvents(y);
|
y = drawCurrentEvents(y);
|
||||||
|
@ -117,3 +131,4 @@ var minuteInterval = setInterval(redraw, 60 * 1000);
|
||||||
Bangle.setUI("clock");
|
Bangle.setUI("clock");
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "calclock",
|
"id": "calclock",
|
||||||
"name": "Calendar Clock",
|
"name": "Calendar Clock",
|
||||||
"shortName": "CalClock",
|
"shortName": "CalClock",
|
||||||
"version": "0.04",
|
"version": "0.05",
|
||||||
"description": "Show the current and upcoming events synchronized from Gadgetbridge",
|
"description": "Show the current and upcoming events synchronized from Gadgetbridge",
|
||||||
"icon": "calclock.png",
|
"icon": "calclock.png",
|
||||||
"type": "clock",
|
"type": "clock",
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
diff --git a/apps/calclock/calclock.js b/apps/calclock/calclock.js
|
||||||
|
index cb8c6100e..2092c1a4e 100644
|
||||||
|
--- a/apps/calclock/calclock.js
|
||||||
|
+++ b/apps/calclock/calclock.js
|
||||||
|
@@ -3,9 +3,24 @@ var current = [];
|
||||||
|
var next = [];
|
||||||
|
|
||||||
|
function updateCalendar() {
|
||||||
|
- calendar = require("Storage").readJSON("android.calendar.json",true)||[];
|
||||||
|
- calendar = calendar.filter(e => isActive(e) || getTime() <= e.timestamp);
|
||||||
|
- calendar.sort((a,b) => a.timestamp - b.timestamp);
|
||||||
|
+ calendar = [
|
||||||
|
+ {
|
||||||
|
+ t: "calendar",
|
||||||
|
+ id: 2, type: 0, timestamp: getTime(), durationInSeconds: 200,
|
||||||
|
+ title: "Capture Screenshot",
|
||||||
|
+ description: "Capture Screenshot",
|
||||||
|
+ location: "",
|
||||||
|
+ calName: "",
|
||||||
|
+ color: -7151168, allDay: true },
|
||||||
|
+ {
|
||||||
|
+ t: "calendar",
|
||||||
|
+ id: 7186, type: 0, timestamp: getTime() + 2000, durationInSeconds: 100,
|
||||||
|
+ title: "Upload to BangleApps",
|
||||||
|
+ description: "",
|
||||||
|
+ location: "",
|
||||||
|
+ calName: "",
|
||||||
|
+ color: -509406, allDay: false }
|
||||||
|
+ ];
|
||||||
|
|
||||||
|
current = calendar.filter(isActive);
|
||||||
|
next = calendar.filter(e=>!isActive(e));
|
Binary file not shown.
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.1 KiB |
|
@ -31,3 +31,4 @@
|
||||||
0.16: Fix const error
|
0.16: Fix const error
|
||||||
Use widget_utils if available
|
Use widget_utils if available
|
||||||
0.17: Load circles from clkinfo
|
0.17: Load circles from clkinfo
|
||||||
|
0.18: Improved clkinfo handling and using it for the weather circle
|
||||||
|
|
|
@ -106,6 +106,10 @@ let circleItemNum = [
|
||||||
2, // circle3
|
2, // circle3
|
||||||
3, // circle4
|
3, // circle4
|
||||||
];
|
];
|
||||||
|
let weatherCircleNum = 0;
|
||||||
|
let weatherCircleDataNum = 0;
|
||||||
|
let weatherCircleCondNum = 0;
|
||||||
|
let weatherCircleTempNum = 0;
|
||||||
|
|
||||||
function hideWidgets() {
|
function hideWidgets() {
|
||||||
/*
|
/*
|
||||||
|
@ -323,6 +327,55 @@ function getImage(graphic, color) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawWeather(w) {
|
function drawWeather(w) {
|
||||||
|
if (!w) w = getCircleXPosition("weather");
|
||||||
|
let weatherInfo = menu[weatherCircleNum];
|
||||||
|
let weatherCond = weatherCircleCondNum >= 0? weatherInfo.items[weatherCircleCondNum]: undefined;
|
||||||
|
let weatherData = weatherCircleDataNum >= 0? weatherInfo.items[weatherCircleDataNum]: undefined;
|
||||||
|
let weatherTemp = weatherCircleTempNum >= 0? weatherInfo.items[weatherCircleTempNum]: undefined;
|
||||||
|
let color = getCircleColor("weather");
|
||||||
|
let percent = 0;
|
||||||
|
let data = settings.weatherCircleData;
|
||||||
|
let tempString = "?", icon = undefined;
|
||||||
|
let scale = 16/24; //our icons are 16x16 while clkinfo's are 24x24
|
||||||
|
|
||||||
|
if(weatherCond) {
|
||||||
|
weatherCond.show()
|
||||||
|
weatherCond.hide()
|
||||||
|
let data = weatherCond.get()
|
||||||
|
if(settings.legacyWeatherIcons) { //may disappear in future
|
||||||
|
icon = getWeatherIconByCode(data.v);
|
||||||
|
scale = 1;
|
||||||
|
} else
|
||||||
|
icon = data.img;
|
||||||
|
}
|
||||||
|
if(weatherTemp) {
|
||||||
|
weatherTemp.show()
|
||||||
|
weatherTemp.hide()
|
||||||
|
tempString = weatherTemp.get().text;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawCircleBackground(w);
|
||||||
|
|
||||||
|
if(weatherData) {
|
||||||
|
weatherData.show();
|
||||||
|
weatherData.hide();
|
||||||
|
let data = weatherData.get();
|
||||||
|
if(weatherData.hasRange) percent = (data.v-data.min) / (data.max-data.min);
|
||||||
|
drawGauge(w, h3, percent, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
drawInnerCircleAndTriangle(w);
|
||||||
|
|
||||||
|
writeCircleText(w, tempString);
|
||||||
|
|
||||||
|
if(icon) {
|
||||||
|
g.setColor(getCircleIconColor("weather", color, percent))
|
||||||
|
.drawImage(icon, w - iconOffset, h3 + radiusOuter - iconOffset, {scale: scale});
|
||||||
|
} else {
|
||||||
|
g.drawString("?", w, h3 + radiusOuter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function drawWeatherOld(w) {
|
||||||
if (!w) w = getCircleXPosition("weather");
|
if (!w) w = getCircleXPosition("weather");
|
||||||
let weather = getWeather();
|
let weather = getWeather();
|
||||||
let tempString = weather ? locale.temp(weather.temp - 273.15) : undefined;
|
let tempString = weather ? locale.temp(weather.temp - 273.15) : undefined;
|
||||||
|
@ -659,12 +712,16 @@ function reloadMenu() {
|
||||||
let parts = settings['circle'+i].split("/");
|
let parts = settings['circle'+i].split("/");
|
||||||
let infoName = parts[0], itemName = parts[1];
|
let infoName = parts[0], itemName = parts[1];
|
||||||
let infoNum = menu.findIndex(e=>e.name==infoName);
|
let infoNum = menu.findIndex(e=>e.name==infoName);
|
||||||
let itemNum = 0;
|
let itemNum = 0; //get first if dynamic
|
||||||
//suppose unnamed are varying (like timers or events), pick the first
|
if(!menu[infoNum].dynamic)
|
||||||
if(itemName)
|
|
||||||
itemNum = menu[infoNum].items.findIndex(it=>it.name==itemName);
|
itemNum = menu[infoNum].items.findIndex(it=>it.name==itemName);
|
||||||
circleInfoNum[i-1] = infoNum;
|
circleInfoNum[i-1] = infoNum;
|
||||||
circleItemNum[i-1] = itemNum;
|
circleItemNum[i-1] = itemNum;
|
||||||
|
} else if(settings['circle'+i] == "weather") {
|
||||||
|
weatherCircleNum = menu.findIndex(e=>e.name.toLowerCase() == "weather");
|
||||||
|
weatherCircleDataNum = menu[weatherCircleNum].items.findIndex(it=>it.name==settings.weatherCircleData);
|
||||||
|
weatherCircleCondNum = menu[weatherCircleNum].items.findIndex(it=>it.name=="condition");
|
||||||
|
weatherCircleTempNum = menu[weatherCircleNum].items.findIndex(it=>it.name=="temperature");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//reload periodically for changes?
|
//reload periodically for changes?
|
||||||
|
@ -685,22 +742,23 @@ function drawClkInfo(index, w) {
|
||||||
if (!w) w = getCircleXPosition(type);
|
if (!w) w = getCircleXPosition(type);
|
||||||
drawCircleBackground(w);
|
drawCircleBackground(w);
|
||||||
const color = getCircleColor(type);
|
const color = getCircleColor(type);
|
||||||
if(!info || !info.items.length) {
|
var item = info.items[circleItemNum[index-1]];
|
||||||
|
if(!info || !item) {
|
||||||
drawEmpty(info? info.img : null, w, color);
|
drawEmpty(info? info.img : null, w, color);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var item = info.items[circleItemNum[index-1]];
|
|
||||||
//TODO do hide()+get() here
|
|
||||||
item.show();
|
item.show();
|
||||||
item.hide();
|
item.hide();
|
||||||
item=item.get();
|
var data=item.get();
|
||||||
var img = item.img;
|
var img = data.img;
|
||||||
|
var percent = 1; //fill up if no range
|
||||||
|
var txt = data.text;
|
||||||
if(!img) img = info.img;
|
if(!img) img = info.img;
|
||||||
let percent = (item.v-item.min) / item.max;
|
if(item.hasRange) percent = (data.v-data.min) / (data.max-data.min);
|
||||||
if(isNaN(percent)) percent = 1; //fill it up
|
if(data.short) txt = data.short;
|
||||||
drawGauge(w, h3, percent, color);
|
drawGauge(w, h3, percent, color);
|
||||||
drawInnerCircleAndTriangle(w);
|
drawInnerCircleAndTriangle(w);
|
||||||
writeCircleText(w, item.text);
|
writeCircleText(w, txt);
|
||||||
g.setColor(getCircleIconColor(type, color, percent))
|
g.setColor(getCircleIconColor(type, color, percent))
|
||||||
.drawImage(img, w - iconOffset, h3 + radiusOuter - iconOffset, {scale: 16/24});
|
.drawImage(img, w - iconOffset, h3 + radiusOuter - iconOffset, {scale: 16/24});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{ "id": "circlesclock",
|
{ "id": "circlesclock",
|
||||||
"name": "Circles clock",
|
"name": "Circles clock",
|
||||||
"shortName":"Circles clock",
|
"shortName":"Circles clock",
|
||||||
"version":"0.17",
|
"version":"0.18",
|
||||||
"description": "A clock with three or four circles for different data at the bottom in a probably familiar style",
|
"description": "A clock with three or four circles for different data at the bottom in a probably familiar style",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"screenshots": [{"url":"screenshot-dark.png"}, {"url":"screenshot-light.png"}, {"url":"screenshot-dark-4.png"}, {"url":"screenshot-light-4.png"}],
|
"screenshots": [{"url":"screenshot-dark.png"}, {"url":"screenshot-light.png"}, {"url":"screenshot-dark-4.png"}, {"url":"screenshot-light-4.png"}],
|
||||||
|
|
|
@ -17,10 +17,7 @@
|
||||||
var valuesCircleTypes = ["empty","weather", "sunprogress"];
|
var valuesCircleTypes = ["empty","weather", "sunprogress"];
|
||||||
var namesCircleTypes = ["empty","weather", "sun"];
|
var namesCircleTypes = ["empty","weather", "sun"];
|
||||||
clock_info.load().forEach(e=>{
|
clock_info.load().forEach(e=>{
|
||||||
//TODO filter for hasRange and other
|
if(e.dynamic) {
|
||||||
if(!e.items.length || !e.items[0].name) {
|
|
||||||
//suppose unnamed are varying (like timers or events), pick the first
|
|
||||||
item = e.items[0];
|
|
||||||
valuesCircleTypes = valuesCircleTypes.concat([e.name+"/"]);
|
valuesCircleTypes = valuesCircleTypes.concat([e.name+"/"]);
|
||||||
namesCircleTypes = namesCircleTypes.concat([e.name]);
|
namesCircleTypes = namesCircleTypes.concat([e.name]);
|
||||||
} else {
|
} else {
|
||||||
|
@ -85,6 +82,12 @@
|
||||||
},
|
},
|
||||||
onchange: x => save('updateInterval', x),
|
onchange: x => save('updateInterval', x),
|
||||||
},
|
},
|
||||||
|
//TODO deprecated local icons, may disappear in future
|
||||||
|
/*LANG*/'legacy weather icons': {
|
||||||
|
value: !!settings.legacyWeatherIcons,
|
||||||
|
format: () => (settings.legacyWeatherIcons ? 'Yes' : 'No'),
|
||||||
|
onchange: x => save('legacyWeatherIcons', x),
|
||||||
|
},
|
||||||
/*LANG*/'show big weather': {
|
/*LANG*/'show big weather': {
|
||||||
value: !!settings.showBigWeather,
|
value: !!settings.showBigWeather,
|
||||||
format: () => (settings.showBigWeather ? 'Yes' : 'No'),
|
format: () => (settings.showBigWeather ? 'Yes' : 'No'),
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
0.01: New App!
|
||||||
|
0.02: Allow redirection of loads to the launcher
|
||||||
|
0.03: Allow hiding the fastloading info screen
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Fastload Utils
|
||||||
|
|
||||||
|
*EXPERIMENTAL* Use this with caution. When you find something misbehaving please check if the problem actually persists when removing this app.
|
||||||
|
|
||||||
|
This allows fast loading of all apps with two conditions:
|
||||||
|
* Loaded app contains `Bangle.loadWidgets`. This is needed to prevent problems with apps not expecting widgets to be already loaded.
|
||||||
|
* Current app can be removed completely from RAM.
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
|
||||||
|
* Allows to redirect all loads usually loading the clock to the launcher instead
|
||||||
|
* The "Fastloading..." screen can be switched off
|
||||||
|
|
||||||
|
## Technical infos
|
||||||
|
|
||||||
|
This is still experimental but it uses the same mechanism as `.bootcde` does.
|
||||||
|
It checks the app to be loaded for widget use and stores the result of that and a hash of the js in a cache.
|
||||||
|
|
||||||
|
# Creator
|
||||||
|
|
||||||
|
[halemmerich](https://github.com/halemmerich)
|
|
@ -0,0 +1,66 @@
|
||||||
|
{
|
||||||
|
const SETTINGS = require("Storage").readJSON("fastload.json") || {};
|
||||||
|
|
||||||
|
let loadingScreen = function(){
|
||||||
|
g.reset();
|
||||||
|
|
||||||
|
let x = g.getWidth()/2;
|
||||||
|
let y = g.getHeight()/2;
|
||||||
|
g.setColor(g.theme.bg);
|
||||||
|
g.fillRect(x-49, y-19, x+49, y+19);
|
||||||
|
g.setColor(g.theme.fg);
|
||||||
|
g.drawRect(x-50, y-20, x+50, y+20);
|
||||||
|
g.setFont("6x8");
|
||||||
|
g.setFontAlign(0,0);
|
||||||
|
g.drawString("Fastloading...", x, y);
|
||||||
|
g.flip(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
let cache = require("Storage").readJSON("fastload.cache") || {};
|
||||||
|
|
||||||
|
let checkApp = function(n){
|
||||||
|
// no widgets, no problem
|
||||||
|
if (!global.WIDGETS) return true;
|
||||||
|
let app = require("Storage").read(n);
|
||||||
|
if (cache[n] && E.CRC32(app) == cache[n].crc)
|
||||||
|
return cache[n].fast
|
||||||
|
cache[n] = {};
|
||||||
|
cache[n].fast = app.includes("Bangle.loadWidgets");
|
||||||
|
cache[n].crc = E.CRC32(app);
|
||||||
|
require("Storage").writeJSON("fastload.cache", cache);
|
||||||
|
return cache[n].fast;
|
||||||
|
}
|
||||||
|
|
||||||
|
global._load = load;
|
||||||
|
|
||||||
|
let slowload = function(n){
|
||||||
|
global._load(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
let fastload = function(n){
|
||||||
|
if (!n || checkApp(n)){
|
||||||
|
// Bangle.load can call load, to prevent recursion this must be the system load
|
||||||
|
global.load = slowload;
|
||||||
|
Bangle.load(n);
|
||||||
|
// if fastloading worked, we need to set load back to this method
|
||||||
|
global.load = fastload;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
slowload(n);
|
||||||
|
};
|
||||||
|
global.load = fastload;
|
||||||
|
|
||||||
|
Bangle.load = (o => (name) => {
|
||||||
|
if (Bangle.uiRemove && !SETTINGS.hideLoading) loadingScreen();
|
||||||
|
if (SETTINGS.autoloadLauncher && !name){
|
||||||
|
let orig = Bangle.load;
|
||||||
|
Bangle.load = (n)=>{
|
||||||
|
Bangle.load = orig;
|
||||||
|
fastload(n);
|
||||||
|
}
|
||||||
|
Bangle.showLauncher();
|
||||||
|
Bangle.load = orig;
|
||||||
|
} else
|
||||||
|
o(name);
|
||||||
|
})(Bangle.load);
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,16 @@
|
||||||
|
{ "id": "fastload",
|
||||||
|
"name": "Fastload Utils",
|
||||||
|
"shortName" : "Fastload Utils",
|
||||||
|
"version": "0.03",
|
||||||
|
"icon": "icon.png",
|
||||||
|
"description": "Enable experimental fastloading for more apps",
|
||||||
|
"type":"bootloader",
|
||||||
|
"tags": "system",
|
||||||
|
"supports": ["BANGLEJS2"],
|
||||||
|
"readme": "README.md",
|
||||||
|
"storage": [
|
||||||
|
{"name":"fastload.5.boot.js","url":"boot.js"},
|
||||||
|
{"name":"fastload.settings.js","url":"settings.js"}
|
||||||
|
],
|
||||||
|
"data": [{"name":"fastload.json"}]
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
(function(back) {
|
||||||
|
var FILE="fastload.json";
|
||||||
|
var settings;
|
||||||
|
|
||||||
|
function writeSettings(key, value) {
|
||||||
|
var s = require('Storage').readJSON(FILE, true) || {};
|
||||||
|
s[key] = value;
|
||||||
|
require('Storage').writeJSON(FILE, s);
|
||||||
|
readSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
function readSettings(){
|
||||||
|
settings = require('Storage').readJSON(FILE, true) || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
readSettings();
|
||||||
|
|
||||||
|
function buildMainMenu(){
|
||||||
|
var mainmenu = {
|
||||||
|
'': { 'title': 'Fastload', back: back },
|
||||||
|
'Force load to launcher': {
|
||||||
|
value: !!settings.autoloadLauncher,
|
||||||
|
onchange: v => {
|
||||||
|
writeSettings("autoloadLauncher",v);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'Hide "Fastloading..."': {
|
||||||
|
value: !!settings.hideLoading,
|
||||||
|
onchange: v => {
|
||||||
|
writeSettings("hideLoading",v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return mainmenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
E.showMenu(buildMainMenu());
|
||||||
|
})
|
|
@ -8,6 +8,8 @@ Upon opening the gallery app, you will be presented with a list of images that y
|
||||||
|
|
||||||
## Adding images
|
## Adding images
|
||||||
|
|
||||||
|
Once this app is installed you can manage images by pressing the Disk icon next to it or by following the manual steps below:
|
||||||
|
|
||||||
1. The gallery app does not perform any scaling, and does not support panning. Therefore, you should use your favorite image editor to produce an image of the appropriate size for your watch. (240x240 for Bangle 1 or 176x176 for Bangle 2.) How you achieve this is up to you. If on a Bangle 2, I recommend adjusting the colors here to comply with the color restrictions.
|
1. The gallery app does not perform any scaling, and does not support panning. Therefore, you should use your favorite image editor to produce an image of the appropriate size for your watch. (240x240 for Bangle 1 or 176x176 for Bangle 2.) How you achieve this is up to you. If on a Bangle 2, I recommend adjusting the colors here to comply with the color restrictions.
|
||||||
|
|
||||||
2. Upload your image to the [Espruino image converter](https://www.espruino.com/Image+Converter). I recommend enabling compression and choosing one of the following color settings:
|
2. Upload your image to the [Espruino image converter](https://www.espruino.com/Image+Converter). I recommend enabling compression and choosing one of the following color settings:
|
||||||
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script src="../../webtools/heatshrink.js"></script>
|
||||||
|
<script src="../../webtools/imageconverter.js"></script>
|
||||||
|
<script src="../../core/lib/interface.js"></script>
|
||||||
|
|
||||||
|
<h4>Existing Images</h4>
|
||||||
|
<ul id="imagelist">
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h4>Convert & Upload Images</h4>
|
||||||
|
|
||||||
|
<input type="file" id="fileLoader"/><br/>
|
||||||
|
<input type="checkbox" id="compression" onchange="imageLoaded()"> Use Compression?</input><br/>
|
||||||
|
<input type="checkbox" id="alphaToColor" onchange="imageLoaded()"> Transparency to Color</input><br/>
|
||||||
|
<input type="checkbox" id="transparent" onchange="imageLoaded()" checked> Transparency?</input><br/>
|
||||||
|
<input type="checkbox" id="inverted" onchange="imageLoaded()"> Inverted?</input><br/>
|
||||||
|
<input type="checkbox" id="autoCrop" onchange="imageLoaded()"> Crop?</input><br/>
|
||||||
|
Diffusion: <select id="diffusion" onchange="imageLoaded()"></select><br/>
|
||||||
|
|
||||||
|
Brightness: <span id="brightnessv"></span>
|
||||||
|
<input type="range" id="brightness" min="-127" max="127" value="0" onchange="imageLoaded()"></input><br/>
|
||||||
|
Contrast: <span id="contrastv"></span>
|
||||||
|
<input type="range" id="contrast" min="-255" max="255" value="0" onchange="imageLoaded()"></input><br/>
|
||||||
|
Colours: <select id="colorStyle" onchange="imageLoaded()"></select><br/>
|
||||||
|
|
||||||
|
<canvas id="canvas" style="display:none;"></canvas>
|
||||||
|
|
||||||
|
<button class="btn btn-default" id="btnUpload" disabled="disabled">Upload</button>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// load available colour formats and diffusion...
|
||||||
|
imageconverter.setFormatOptions(document.getElementById("colorStyle"));
|
||||||
|
imageconverter.setDiffusionOptions(document.getElementById("diffusion"));
|
||||||
|
|
||||||
|
let img;
|
||||||
|
let screenSize;
|
||||||
|
let imgstr;
|
||||||
|
const uploadBtn = document.getElementById("btnUpload");
|
||||||
|
uploadBtn.addEventListener("click", function() {
|
||||||
|
const filename = document.getElementById("fileLoader").value.split(/(\\|\/)/g).pop();
|
||||||
|
const filenameWithoutExt = filename.replace(/\.[^/.]+$/, "");
|
||||||
|
Util.showModal("Uploading...");
|
||||||
|
Util.writeStorage("gal-" + filenameWithoutExt.substring(0, 12) + ".img", imgstr, () => {
|
||||||
|
Util.hideModal();
|
||||||
|
updateFileList();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function imageLoaded() {
|
||||||
|
if (img === undefined) return;
|
||||||
|
if (screenSize !== img.width + "x" + img.height) {
|
||||||
|
alert("Image must be " + screenSize);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let options = {};
|
||||||
|
let diffusionSelect = document.getElementById("diffusion");
|
||||||
|
options.diffusion = diffusionSelect.options[diffusionSelect.selectedIndex].value;
|
||||||
|
options.compression = document.getElementById("compression").checked;
|
||||||
|
options.alphaToColor = document.getElementById("alphaToColor").checked;
|
||||||
|
options.transparent = document.getElementById("transparent").checked;
|
||||||
|
options.inverted = document.getElementById("inverted").checked;
|
||||||
|
options.autoCrop = document.getElementById("autoCrop").checked;
|
||||||
|
options.brightness = 0|document.getElementById("brightness").value;
|
||||||
|
document.getElementById("brightnessv").innerText = options.brightness;
|
||||||
|
options.contrast = 0|document.getElementById("contrast").value;
|
||||||
|
document.getElementById("contrastv").innerText = options.contrast;
|
||||||
|
let colorSelect = document.getElementById("colorStyle");
|
||||||
|
options.mode = colorSelect.options[colorSelect.selectedIndex].value;
|
||||||
|
|
||||||
|
options.output = "string";
|
||||||
|
|
||||||
|
let canvas = document.getElementById("canvas")
|
||||||
|
canvas.width = img.width*2;
|
||||||
|
canvas.height = img.height;
|
||||||
|
canvas.style = "display:block;border:1px solid black;margin:8px;"
|
||||||
|
let ctx = canvas.getContext("2d");
|
||||||
|
ctx.drawImage(img,0,0);
|
||||||
|
|
||||||
|
let imageData1 = ctx.getImageData(0, 0, img.width, img.height);
|
||||||
|
ctx.fillStyle = 'white';
|
||||||
|
ctx.fillRect(options.width, 0, img.width, img.height);
|
||||||
|
let rgba = imageData1.data;
|
||||||
|
options.rgbaOut = rgba;
|
||||||
|
options.width = img.width;
|
||||||
|
options.height = img.height;
|
||||||
|
imgstr = imageconverter.RGBAtoString(rgba, options);
|
||||||
|
let outputImageData = new ImageData(options.rgbaOut, options.width, options.height);
|
||||||
|
ctx.putImageData(outputImageData,img.width,0);
|
||||||
|
|
||||||
|
// checkerboard for transparency on original image
|
||||||
|
let imageData2 = ctx.getImageData(0, 0, img.width, img.height);
|
||||||
|
imageconverter.RGBAtoCheckerboard(imageData2.data, {width:img.width,height:img.height});
|
||||||
|
ctx.putImageData(imageData2,0,0);
|
||||||
|
|
||||||
|
uploadBtn.disabled=false;
|
||||||
|
}
|
||||||
|
function handleFileSelect(event) {
|
||||||
|
if (event.target.files.length != 1) return;
|
||||||
|
let reader = new FileReader();
|
||||||
|
reader.onload = function(event) {
|
||||||
|
img = new Image();
|
||||||
|
img.onload = imageLoaded;
|
||||||
|
img.src = event.target.result;
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(event.target.files[0]);
|
||||||
|
};
|
||||||
|
document.getElementById('fileLoader').addEventListener('change', handleFileSelect, false);
|
||||||
|
|
||||||
|
function updateFileList() {
|
||||||
|
Puck.write(`\x10(function() {
|
||||||
|
Bluetooth.print(JSON.stringify(require("Storage").list(/^gal-.*\.img/).sort()));
|
||||||
|
})()\n`, contents => {
|
||||||
|
let fileNames = JSON.parse(contents);
|
||||||
|
|
||||||
|
const imagelist = document.getElementById("imagelist");
|
||||||
|
imagelist.innerHTML=""; // remove all children
|
||||||
|
// add a list of existing files
|
||||||
|
if (fileNames.length === 0) {
|
||||||
|
const span = document.createElement("span");
|
||||||
|
span.textContent = "No existing images";
|
||||||
|
imagelist.appendChild(span);
|
||||||
|
}
|
||||||
|
fileNames.forEach(fileName => {
|
||||||
|
const li = document.createElement("li");
|
||||||
|
const span = document.createElement("span");
|
||||||
|
span.classList.add("label");
|
||||||
|
span.textContent = fileName.substr(4, fileName.length - 8);
|
||||||
|
li.appendChild(span);
|
||||||
|
|
||||||
|
const buttonDelete = document.createElement("button");
|
||||||
|
buttonDelete.classList.add('btn');
|
||||||
|
buttonDelete.classList.add('btn-link');
|
||||||
|
buttonDelete.textContent = "Delete";
|
||||||
|
buttonDelete.onclick = () => {
|
||||||
|
Util.showModal(`Erasing ${fileName}...`);
|
||||||
|
Util.eraseStorage(fileName, () => {
|
||||||
|
Util.hideModal();
|
||||||
|
updateFileList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
li.appendChild(buttonDelete);
|
||||||
|
imagelist.appendChild(li);
|
||||||
|
});
|
||||||
|
Util.hideModal(); // Loading modal
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Util.showModal("Loading...");
|
||||||
|
|
||||||
|
// Called when app starts
|
||||||
|
function onInit() {
|
||||||
|
// Read BangleJS screen size
|
||||||
|
Puck.write(`\x10(function() {
|
||||||
|
Bluetooth.print(g.getWidth() + "x" + g.getHeight());
|
||||||
|
})()\n`, contents => {
|
||||||
|
screenSize = contents;
|
||||||
|
updateFileList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -12,6 +12,7 @@
|
||||||
"BANGLEJS"
|
"BANGLEJS"
|
||||||
],
|
],
|
||||||
"allow_emulator": true,
|
"allow_emulator": true,
|
||||||
|
"interface": "interface.html",
|
||||||
"storage": [
|
"storage": [
|
||||||
{
|
{
|
||||||
"name": "gallery.app.js",
|
"name": "gallery.app.js",
|
||||||
|
|
|
@ -14,3 +14,5 @@
|
||||||
0.13: Add support for internationalization
|
0.13: Add support for internationalization
|
||||||
0.14: Move settings
|
0.14: Move settings
|
||||||
0.15: Fix charts (fix #1366)
|
0.15: Fix charts (fix #1366)
|
||||||
|
0.16: Code tidyup, add back button in top left of health app graphs
|
||||||
|
0.17: Add automatic translation of bar chart labels
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
function menuMain() {
|
function menuMain() {
|
||||||
swipe_enabled = false;
|
|
||||||
clearButton();
|
|
||||||
E.showMenu({
|
E.showMenu({
|
||||||
"": { title: /*LANG*/"Health Tracking" },
|
"": { title: /*LANG*/"Health Tracking" },
|
||||||
/*LANG*/"< Back": () => load(),
|
/*LANG*/"< Back": () => load(),
|
||||||
|
@ -12,8 +10,6 @@ function menuMain() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function menuStepCount() {
|
function menuStepCount() {
|
||||||
swipe_enabled = false;
|
|
||||||
clearButton();
|
|
||||||
E.showMenu({
|
E.showMenu({
|
||||||
"": { title:/*LANG*/"Steps" },
|
"": { title:/*LANG*/"Steps" },
|
||||||
/*LANG*/"< Back": () => menuMain(),
|
/*LANG*/"< Back": () => menuMain(),
|
||||||
|
@ -23,8 +19,6 @@ function menuStepCount() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function menuMovement() {
|
function menuMovement() {
|
||||||
swipe_enabled = false;
|
|
||||||
clearButton();
|
|
||||||
E.showMenu({
|
E.showMenu({
|
||||||
"": { title:/*LANG*/"Movement" },
|
"": { title:/*LANG*/"Movement" },
|
||||||
/*LANG*/"< Back": () => menuMain(),
|
/*LANG*/"< Back": () => menuMain(),
|
||||||
|
@ -34,8 +28,6 @@ function menuMovement() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function menuHRM() {
|
function menuHRM() {
|
||||||
swipe_enabled = false;
|
|
||||||
clearButton();
|
|
||||||
E.showMenu({
|
E.showMenu({
|
||||||
"": { title:/*LANG*/"Heart Rate" },
|
"": { title:/*LANG*/"Heart Rate" },
|
||||||
/*LANG*/"< Back": () => menuMain(),
|
/*LANG*/"< Back": () => menuMain(),
|
||||||
|
@ -48,22 +40,16 @@ function stepsPerHour() {
|
||||||
E.showMessage(/*LANG*/"Loading...");
|
E.showMessage(/*LANG*/"Loading...");
|
||||||
var data = new Uint16Array(24);
|
var data = new Uint16Array(24);
|
||||||
require("health").readDay(new Date(), h=>data[h.hr]+=h.steps);
|
require("health").readDay(new Date(), h=>data[h.hr]+=h.steps);
|
||||||
g.clear(1);
|
|
||||||
Bangle.drawWidgets();
|
|
||||||
g.reset();
|
|
||||||
setButton(menuStepCount);
|
setButton(menuStepCount);
|
||||||
barChart("HOUR", data);
|
barChart(/*LANG*/"HOUR", data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function stepsPerDay() {
|
function stepsPerDay() {
|
||||||
E.showMessage(/*LANG*/"Loading...");
|
E.showMessage(/*LANG*/"Loading...");
|
||||||
var data = new Uint16Array(31);
|
var data = new Uint16Array(31);
|
||||||
require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.steps);
|
require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.steps);
|
||||||
g.clear(1);
|
|
||||||
Bangle.drawWidgets();
|
|
||||||
g.reset();
|
|
||||||
setButton(menuStepCount);
|
setButton(menuStepCount);
|
||||||
barChart("DAY", data);
|
barChart(/*LANG*/"DAY", data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hrmPerHour() {
|
function hrmPerHour() {
|
||||||
|
@ -75,11 +61,8 @@ function hrmPerHour() {
|
||||||
if (h.bpm) cnt[h.hr]++;
|
if (h.bpm) cnt[h.hr]++;
|
||||||
});
|
});
|
||||||
data.forEach((d,i)=>data[i] = d/cnt[i]);
|
data.forEach((d,i)=>data[i] = d/cnt[i]);
|
||||||
g.clear(1);
|
|
||||||
Bangle.drawWidgets();
|
|
||||||
g.reset();
|
|
||||||
setButton(menuHRM);
|
setButton(menuHRM);
|
||||||
barChart("HOUR", data);
|
barChart(/*LANG*/"HOUR", data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hrmPerDay() {
|
function hrmPerDay() {
|
||||||
|
@ -91,37 +74,27 @@ function hrmPerDay() {
|
||||||
if (h.bpm) cnt[h.day]++;
|
if (h.bpm) cnt[h.day]++;
|
||||||
});
|
});
|
||||||
data.forEach((d,i)=>data[i] = d/cnt[i]);
|
data.forEach((d,i)=>data[i] = d/cnt[i]);
|
||||||
g.clear(1);
|
|
||||||
Bangle.drawWidgets();
|
|
||||||
g.reset();
|
|
||||||
setButton(menuHRM);
|
setButton(menuHRM);
|
||||||
barChart("DAY", data);
|
barChart(/*LANG*/"DAY", data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function movementPerHour() {
|
function movementPerHour() {
|
||||||
E.showMessage(/*LANG*/"Loading...");
|
E.showMessage(/*LANG*/"Loading...");
|
||||||
var data = new Uint16Array(24);
|
var data = new Uint16Array(24);
|
||||||
require("health").readDay(new Date(), h=>data[h.hr]+=h.movement);
|
require("health").readDay(new Date(), h=>data[h.hr]+=h.movement);
|
||||||
g.clear(1);
|
|
||||||
Bangle.drawWidgets();
|
|
||||||
g.reset();
|
|
||||||
setButton(menuMovement);
|
setButton(menuMovement);
|
||||||
barChart("HOUR", data);
|
barChart(/*LANG*/"HOUR", data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function movementPerDay() {
|
function movementPerDay() {
|
||||||
E.showMessage(/*LANG*/"Loading...");
|
E.showMessage(/*LANG*/"Loading...");
|
||||||
var data = new Uint16Array(31);
|
var data = new Uint16Array(31);
|
||||||
require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.movement);
|
require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.movement);
|
||||||
g.clear(1);
|
|
||||||
Bangle.drawWidgets();
|
|
||||||
g.reset();
|
|
||||||
setButton(menuMovement);
|
setButton(menuMovement);
|
||||||
barChart("DAY", data);
|
barChart(/*LANG*/"DAY", data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bar Chart Code
|
// Bar Chart Code
|
||||||
|
|
||||||
const w = g.getWidth();
|
const w = g.getWidth();
|
||||||
const h = g.getHeight();
|
const h = g.getHeight();
|
||||||
|
|
||||||
|
@ -130,13 +103,10 @@ var chart_index;
|
||||||
var chart_max_datum;
|
var chart_max_datum;
|
||||||
var chart_label;
|
var chart_label;
|
||||||
var chart_data;
|
var chart_data;
|
||||||
var swipe_enabled = false;
|
|
||||||
var btn;
|
|
||||||
|
|
||||||
// find the max value in the array, using a loop due to array size
|
// find the max value in the array, using a loop due to array size
|
||||||
function max(arr) {
|
function max(arr) {
|
||||||
var m = -Infinity;
|
var m = -Infinity;
|
||||||
|
|
||||||
for(var i=0; i< arr.length; i++)
|
for(var i=0; i< arr.length; i++)
|
||||||
if(arr[i] > m) m = arr[i];
|
if(arr[i] > m) m = arr[i];
|
||||||
return m;
|
return m;
|
||||||
|
@ -145,10 +115,8 @@ function max(arr) {
|
||||||
// find the end of the data, the array might be for 31 days but only have 2 days of data in it
|
// find the end of the data, the array might be for 31 days but only have 2 days of data in it
|
||||||
function get_data_length(arr) {
|
function get_data_length(arr) {
|
||||||
var nlen = arr.length;
|
var nlen = arr.length;
|
||||||
|
|
||||||
for(var i = arr.length - 1; i > 0 && arr[i] == 0; i--)
|
for(var i = arr.length - 1; i > 0 && arr[i] == 0; i--)
|
||||||
nlen--;
|
nlen--;
|
||||||
|
|
||||||
return nlen;
|
return nlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,15 +135,11 @@ function drawBarChart() {
|
||||||
const bar_width = (w - 2) / 9; // we want 9 bars, bar 5 in the centre
|
const bar_width = (w - 2) / 9; // we want 9 bars, bar 5 in the centre
|
||||||
var bar_top;
|
var bar_top;
|
||||||
var bar;
|
var bar;
|
||||||
|
g.reset().clearRect(0,24,w,h);
|
||||||
g.setColor(g.theme.bg);
|
|
||||||
g.fillRect(0,24,w,h);
|
|
||||||
|
|
||||||
for (bar = 1; bar < 10; bar++) {
|
for (bar = 1; bar < 10; bar++) {
|
||||||
if (bar == 5) {
|
if (bar == 5) {
|
||||||
g.setFont('6x8', 2);
|
g.setFont('6x8', 2).setFontAlign(0,-1).setColor(g.theme.fg);
|
||||||
g.setFontAlign(0,-1);
|
|
||||||
g.setColor(g.theme.fg);
|
|
||||||
g.drawString(chart_label + " " + (chart_index + bar -1) + " " + chart_data[chart_index + bar - 1], g.getWidth()/2, 150);
|
g.drawString(chart_label + " " + (chart_index + bar -1) + " " + chart_data[chart_index + bar - 1], g.getWidth()/2, 150);
|
||||||
g.setColor("#00f");
|
g.setColor("#00f");
|
||||||
} else {
|
} else {
|
||||||
|
@ -189,45 +153,26 @@ function drawBarChart() {
|
||||||
bar_top = bar_bot;
|
bar_top = bar_bot;
|
||||||
|
|
||||||
g.fillRect( 1 + (bar - 1)* bar_width, bar_bot, 1 + bar*bar_width, bar_top);
|
g.fillRect( 1 + (bar - 1)* bar_width, bar_bot, 1 + bar*bar_width, bar_top);
|
||||||
g.setColor(g.theme.fg);
|
g.setColor(g.theme.fg).drawRect( 1 + (bar - 1)* bar_width, bar_bot, 1 + bar*bar_width, bar_top);
|
||||||
g.drawRect( 1 + (bar - 1)* bar_width, bar_bot, 1 + bar*bar_width, bar_top);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function next_bar() {
|
|
||||||
chart_index = Math.min(data_len - 5, chart_index + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function prev_bar() {
|
|
||||||
// HOUR data starts at index 0, DAY data starts at index 1
|
|
||||||
chart_index = Math.max((chart_label == "DAY") ? -3 : -4, chart_index - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Bangle.on('swipe', dir => {
|
|
||||||
if (!swipe_enabled) return;
|
|
||||||
if (dir == 1) prev_bar(); else next_bar();
|
|
||||||
drawBarChart();
|
|
||||||
});
|
|
||||||
|
|
||||||
// use setWatch() as Bangle.setUI("updown",..) interacts with swipes
|
|
||||||
function setButton(fn) {
|
function setButton(fn) {
|
||||||
// cancel callback, otherwise a slight up down movement will show the E.showMenu()
|
Bangle.setUI({mode:"custom",
|
||||||
Bangle.setUI("updown", undefined);
|
back:fn,
|
||||||
|
swipe:(lr,ud) => {
|
||||||
if (process.env.HWVERSION == 1)
|
if (lr == 1) {
|
||||||
btn = setWatch(fn, BTN2);
|
// HOUR data starts at index 0, DAY data starts at index 1
|
||||||
else
|
chart_index = Math.max((chart_label == /*LANG*/"DAY") ? -3 : -4, chart_index - 1);
|
||||||
btn = setWatch(fn, BTN1);
|
} else if (lr<0) {
|
||||||
}
|
chart_index = Math.min(data_len - 5, chart_index + 1);
|
||||||
|
} else {
|
||||||
function clearButton() {
|
return fn();
|
||||||
if (btn !== undefined) {
|
|
||||||
clearWatch(btn);
|
|
||||||
btn = undefined;
|
|
||||||
}
|
}
|
||||||
|
drawBarChart();
|
||||||
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
menuMain();
|
menuMain();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "health",
|
"id": "health",
|
||||||
"name": "Health Tracking",
|
"name": "Health Tracking",
|
||||||
"version": "0.15",
|
"version": "0.17",
|
||||||
"description": "Logs health data and provides an app to view it",
|
"description": "Logs health data and provides an app to view it",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,system,health",
|
"tags": "tool,system,health",
|
||||||
|
|
|
@ -3,3 +3,4 @@
|
||||||
0.03: Settings page now uses built-in min/max/wrap (fix #1607)
|
0.03: Settings page now uses built-in min/max/wrap (fix #1607)
|
||||||
0.04: Add masking widget input to other apps (using espruino/Espruino#2151), add a oversize option to increase the touch area.
|
0.04: Add masking widget input to other apps (using espruino/Espruino#2151), add a oversize option to increase the touch area.
|
||||||
0.05: Prevent drawing into app area.
|
0.05: Prevent drawing into app area.
|
||||||
|
0.06: Fix issue where .draw was being called by reference (not allowing widgets to be hidden)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "lightswitch",
|
"id": "lightswitch",
|
||||||
"name": "Light Switch Widget",
|
"name": "Light Switch Widget",
|
||||||
"shortName": "Light Switch",
|
"shortName": "Light Switch",
|
||||||
"version": "0.05",
|
"version": "0.06",
|
||||||
"description": "A fast way to switch LCD backlight on/off, change the brightness and show the lock status. All in one widget.",
|
"description": "A fast way to switch LCD backlight on/off, change the brightness and show the lock status. All in one widget.",
|
||||||
"icon": "images/app.png",
|
"icon": "images/app.png",
|
||||||
"screenshots": [
|
"screenshots": [
|
||||||
|
|
|
@ -224,28 +224,20 @@
|
||||||
|
|
||||||
// main widget function //
|
// main widget function //
|
||||||
// display and setup/reset function
|
// display and setup/reset function
|
||||||
draw: function(locked) {
|
draw: function() {
|
||||||
// setup shortcut to this widget
|
// setup shortcut to this widget
|
||||||
var w = WIDGETS.lightswitch;
|
var w = WIDGETS.lightswitch;
|
||||||
|
|
||||||
// set lcd brightness on unlocking
|
|
||||||
// all other cases are catched by the boot file
|
|
||||||
if (locked === false) Bangle.setLCDBrightness(w.isOn ? w.value : 0);
|
|
||||||
|
|
||||||
// read lock status
|
// read lock status
|
||||||
locked = Bangle.isLocked();
|
var locked = Bangle.isLocked();
|
||||||
|
|
||||||
// remove listeners to prevent uncertainties
|
// remove listeners to prevent uncertainties
|
||||||
Bangle.removeListener("lock", w.draw);
|
|
||||||
Bangle.removeListener("touch", w.touchListener);
|
Bangle.removeListener("touch", w.touchListener);
|
||||||
Bangle.removeListener("tap", require("lightswitch.js").tapListener);
|
Bangle.removeListener("tap", require("lightswitch.js").tapListener);
|
||||||
|
|
||||||
// draw widget icon
|
// draw widget icon
|
||||||
w.drawIcon(locked);
|
w.drawIcon(locked);
|
||||||
|
|
||||||
// add lock listener
|
|
||||||
Bangle.on("lock", w.draw);
|
|
||||||
|
|
||||||
// add touch listener to control the light depending on settings at first position
|
// add touch listener to control the light depending on settings at first position
|
||||||
if (w.touchOn === "always" || !global.__FILE__ ||
|
if (w.touchOn === "always" || !global.__FILE__ ||
|
||||||
w.touchOn.includes(__FILE__) ||
|
w.touchOn.includes(__FILE__) ||
|
||||||
|
@ -260,6 +252,14 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Bangle.on("lock", locked => {
|
||||||
|
var w = WIDGETS.lightswitch;
|
||||||
|
// set lcd brightness on unlocking
|
||||||
|
// all other cases are catched by the boot file
|
||||||
|
if (locked === false) Bangle.setLCDBrightness(w.isOn ? w.value : 0);
|
||||||
|
w.draw()
|
||||||
|
});
|
||||||
|
|
||||||
// clear variable
|
// clear variable
|
||||||
settings = undefined;
|
delete settings;
|
||||||
})()
|
})()
|
||||||
|
|
|
@ -77,3 +77,5 @@
|
||||||
0.54: Move icons out to messageicons module
|
0.54: Move icons out to messageicons module
|
||||||
0.55: Rename to messagegui, move global message handling library to message module
|
0.55: Rename to messagegui, move global message handling library to message module
|
||||||
Move widget to widmessage
|
Move widget to widmessage
|
||||||
|
0.56: Fix handling of music messages
|
||||||
|
0.57: Fix "unread Timeout" = off (previously defaulted to 60s)
|
||||||
|
|
|
@ -112,9 +112,11 @@ function showMapMessage(msg) {
|
||||||
Bangle.setUI({mode:"updown", back: back}, back); // any input takes us back
|
Bangle.setUI({mode:"updown", back: back}, back); // any input takes us back
|
||||||
}
|
}
|
||||||
|
|
||||||
var updateLabelsInterval;
|
let updateLabelsInterval,
|
||||||
|
music = {artist: "", album: "", title: ""}; // defaults, so e.g. msg.title.length doesn't error
|
||||||
function showMusicMessage(msg) {
|
function showMusicMessage(msg) {
|
||||||
active = "music";
|
active = "music";
|
||||||
|
msg = Object.assign(music, msg); // combine+remember "musicinfo" and "musicstate" messages
|
||||||
openMusic = msg.state=="play";
|
openMusic = msg.state=="play";
|
||||||
var trackScrollOffset = 0;
|
var trackScrollOffset = 0;
|
||||||
var artistScrollOffset = 0;
|
var artistScrollOffset = 0;
|
||||||
|
@ -444,10 +446,9 @@ require("messages").toggleWidget(false);
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
var unreadTimeoutMillis = (settings.unreadTimeout || 60) * 1000;
|
if (!isFinite(settings.unreadTimeout)) settings.unreadTimeout=60;
|
||||||
if (unreadTimeoutMillis) {
|
if (settings.unreadTimeout)
|
||||||
unreadTimeout = setTimeout(load, unreadTimeoutMillis);
|
unreadTimeout = setTimeout(load, settings.unreadTimeout*1000);
|
||||||
}
|
|
||||||
// only openMusic on launch if music is new
|
// only openMusic on launch if music is new
|
||||||
var newMusic = MESSAGES.some(m => m.id === "music" && m.new);
|
var newMusic = MESSAGES.some(m => m.id === "music" && m.new);
|
||||||
checkMessages({ clockIfNoMsg: 0, clockIfAllRead: 0, showMsgIfUnread: 1, openMusic: newMusic && settings.openMusic });
|
checkMessages({ clockIfNoMsg: 0, clockIfAllRead: 0, showMsgIfUnread: 1, openMusic: newMusic && settings.openMusic });
|
||||||
|
|
|
@ -15,12 +15,12 @@ exports.listener = function(type, msg) {
|
||||||
const appSettings = require("Storage").readJSON("messages.settings.json", 1) || {};
|
const appSettings = require("Storage").readJSON("messages.settings.json", 1) || {};
|
||||||
let loadMessages = (Bangle.CLOCK || event.important);
|
let loadMessages = (Bangle.CLOCK || event.important);
|
||||||
if (type==="music") {
|
if (type==="music") {
|
||||||
if (Bangle.CLOCK && msg.new && appSettings.openMusic) loadMessages = true;
|
if (Bangle.CLOCK && msg.state && msg.title && appSettings.openMusic) loadMessages = true;
|
||||||
else return;
|
else return;
|
||||||
}
|
}
|
||||||
require("messages").save(msg);
|
require("messages").save(msg);
|
||||||
msg.handled = true;
|
msg.handled = true;
|
||||||
if (msg.t!=="add" || !msg.new) {
|
if ((msg.t!=="add" || !msg.new) && (type!=="music")) { // music always has t:"modify"
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "messagegui",
|
"id": "messagegui",
|
||||||
"name": "Message UI",
|
"name": "Message UI",
|
||||||
"version": "0.55",
|
"version": "0.57",
|
||||||
"description": "Default app to display notifications from iOS and Gadgetbridge/Android",
|
"description": "Default app to display notifications from iOS and Gadgetbridge/Android",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "app",
|
"type": "app",
|
||||||
|
|
|
@ -5,44 +5,73 @@ exports.getImage = function(msg) {
|
||||||
*/
|
*/
|
||||||
if (msg.img) return atob(msg.img);
|
if (msg.img) return atob(msg.img);
|
||||||
const s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase();
|
const s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase();
|
||||||
if (s=="airbnb") return atob("GBgBAAAAAAAAAAAAADwAAH4AAGYAAMMAAIEAAYGAAYGAAzzAA2bABmZgBmZgDGYwDDwwCDwQCBgQDDwwB+fgA8PAAAAAAAAAAAAA");
|
if (s=="airbnb") return atob("GBgBAAAAADwAAH4AAMMAAIMAAYGAAQGAAwDAAwDABjxgBn5gDMMwDMMwGMMYGMMYMGYMMGYMIDwEIBgEIDwEMH4MHee4D4HwAAAA"); // icons/airbnb.png
|
||||||
if (s=="alarm" || s =="alarmclockreceiver") return atob("GBjBAP////8AAAAAAAACAEAHAOAefng5/5wTgcgHAOAOGHAMGDAYGBgYGBgYGBgYGBgYDhgYBxgMATAOAHAHAOADgcAB/4AAfgAAAAAAAAA=");
|
if (s=="alarm" || s =="alarmclockreceiver") return atob("GBgBAAAAAAAAAgBABwDgHn54Of+cE8PIBwDgDhhwDBgwHBg4GBgYGBgYGBgYGA4YHAc4DAEwDgBwBwDgA8PAAf+AAH4AAAAAAAAA"); // icons/alarm.png
|
||||||
|
if (s=="amazon shopping") return atob("GBgBAAAAAP8AAf+AA//AA+fAA8PAAIPAAD/AAP/AA//AA+PAB8PAB8fAB8fgB//gA//gA/3AAPCecAAeOAAeDwH0B//kAf+AAAAA"); // icons/amazon.png
|
||||||
if (s=="bibel") return atob("GBgBAAAAA//wD//4D//4H//4H/f4H/f4H+P4H4D4H4D4H/f4H/f4H/f4H/f4H/f4H//4H//4H//4GAAAEAAAEAAACAAAB//4AAAA");
|
if (s=="bibel") return atob("GBgBAAAAA//wD//4D//4H//4H/f4H/f4H+P4H4D4H4D4H/f4H/f4H/f4H/f4H/f4H//4H//4H//4GAAAEAAAEAAACAAAB//4AAAA");
|
||||||
|
if (s=="bitwarden" || s=="1password" || s=="lastpass" || s=="dashlane") return atob("GBgBAAAAABgAAP8AA//AD4/wHg/4GA/4GA/4GA/4GA/4GA/4GA/4H/AYH/AYH/A4D/AwD/BwB/BgB/DgA/HAAfeAAP8AADwAAAAA"); // icons/security.png
|
||||||
if (s=="bring") return atob("GBgBAAAAAAAAAAAAAAAAAHwAAFoAAf+AA/+AA/+AA/+AA/eAA+eAA0+AAx+AA7+AA/+AA//AA/+AAf8AAAIAAAAAAAAAAAAAAAAA");
|
if (s=="bring") return atob("GBgBAAAAAAAAAAAAAAAAAHwAAFoAAf+AA/+AA/+AA/+AA/eAA+eAA0+AAx+AA7+AA/+AA//AA/+AAf8AAAIAAAAAAAAAAAAAAAAA");
|
||||||
if (s=="calendar" || s=="etar") return atob("GBiBAAAAAAAAAAAAAA//8B//+BgAGBgAGBgAGB//+B//+B//+B9m2B//+B//+Btm2B//+B//+Btm+B//+B//+A//8AAAAAAAAAAAAA==");
|
if (s=="calendar" || s=="etar") return atob("GBiBAAAAAAAAAAAAAA//8B//+BgAGBgAGBgAGB//+B//+B//+B9m2B//+B//+Btm2B//+B//+Btm+B//+B//+A//8AAAAAAAAAAAAA==");
|
||||||
if (s=="corona-warn") return atob("GBgBAAAAABwAAP+AAf/gA//wB/PwD/PgDzvAHzuAP8EAP8AAPAAAPMAAP8AAH8AAHzsADzuAB/PAB/PgA//wAP/gAH+AAAwAAAAA");
|
if (s=="chat") return atob("GBgBAAAAAf/8A//+A//+A//+OAB+e/8+e/++e/++e/++e/++e/++e/++ef+8fAAAf//Af//Af//Af//Af/+AcAAAYAAAQAAAAAAA"); // icons/google chat.png
|
||||||
if (s=="discord") return atob("GBgBAAAAAAAAAAAAAIEABwDgDP8wH//4H//4P//8P//8P//8Pjx8fhh+fzz+f//+f//+e//ePH48HwD4AgBAAAAAAAAAAAAAAAAA");
|
if (s=="chrome") return atob("GBgBAAAAAAAAAP8AA//AB+fgDwDwHgB4HAA4Pj/8OmYcO8McMYEMMYEMOMMcOGccOD4cHAw4Hgx4DxjwB//gA//AAP8AAAAAAAAA"); // icons/chrome.png
|
||||||
if (s=="facebook" || s=="messenger") return atob("GBiBAAAAAAAAAAAYAAD/AAP/wAf/4A/48A/g8B/g+B/j+B/n+D/n/D8A/B8A+B+B+B/n+A/n8A/n8Afn4APnwADnAAAAAAAAAAAAAA==");
|
if (s=="corona-warn") return atob("GBgBAAAAAAAAABgAABgABhhgDn5wD//wA8PAA+fAB2bgBgBgPpl8Ppl8BgBgB2bgA+fAA8PAD//wDn5wBhhgABgAABgAAAAAAAAA"); // icons/coronavirus.png
|
||||||
|
if (s=="bmo" || s=="desjardins" || s=="rbc mobile" || s=="nbc" || s=="rabobank" || s=="scotiabank" || s=="td (canada)") return atob("GBgBAAAAADgAAP4AAe8AB4PAHgDwP//4P//4AAAAAAAADjjgDjjgDjjgDjjgDjjgDjjgDjjgAAAAAAAAP//4P//4AAAAAAAAAAAA"); // icons/bank.png
|
||||||
|
if (s=="discord") return atob("GBgBAAAAAAAAAAAAAAAAA4HAD//wH//4H//4P//8P//8P//8fn5+fDw+fDw+fn5+f//+f//+ff++PgB8DgBwAAAAAAAAAAAAAAAA"); // icons/discord.png
|
||||||
|
if (s=="drive") return atob("GBgBAAAAAAAAAH8AAH8AAT+AA7/AA9/AB8/gB+/gD+fwD+fwH8P4P8P8P4H8fwAAf3/+Pn/8Pv/8HP/4Df/wC//wAAAAAAAAAAAA"); // icons/google drive.png
|
||||||
|
if (s=="element") return atob("GBgBAAAAAHwAAH4AAH8AAAeAAePAB+HAD+DgHgDgPADuOADucAAOcAAOdwAcdwA8BwB4BwfwA4fgA8eAAeAAAP4AAH4AAD4AAAAA"); // icons/matrix element.png
|
||||||
|
if (s=="facebook") return atob("GBgBAAAAAAAAAH4AAf+AB//gD//wD/DwH+D4H+P4P+f8P+f8P+f8PwD8PwD8PwD8H+f4H+f4D+fwD+fwB+fgAeeAAOcAAAAAAAAA"); // icons/facebook.png
|
||||||
|
if (s=="messenger") return atob("GBgBAAAAAAAAAP8AA//AB//gD//wH//4H//4P//8P9+8P458PwB8PgD8PnH8Pfv8H//4H//4D//wB//gB//AB/8AAwAAAAAAAAAA"); // icons/facebook messenger.png
|
||||||
|
if (s=="firefox" || s=="firefox beta" || s=="firefox nightly") return atob("GBgBAAAAAAAAAAMAAAcAAAeABA/ADY/gH4P4H4H4H8H8P/H8P+D8PwD8PwD8PwD8H4H4H8P4H//4D//wB//gA//AAP8AAAAAAAAA"); // icons/firefox.png
|
||||||
|
if (s=="f-droid" || s=="neo store" || s=="aurora droid") return atob("GBgBAAAAQAACYAAGP//8H//4H//4HH44HH44H//4AAAAH//4H8P4H734H374HsN4Hvl4Hv14Hvl4HsN4H374H734H8P4D//wAAAA"); // icons/security.png
|
||||||
|
if (s=="github") return atob("GBgBAAAAAAAAAH4AAf+AB//gD//wDv9wHgB4HgB4PAA8PAA8PAA8PAA8PAA8PgB8HwD4G8P4DcPwDgPwB4PgAcOAAAAAAAAAAAAA"); // icons/github.png
|
||||||
|
if (s=="gitlab") return atob("GBgBAAAABAAgDAAwDAAwHgB4HgB4PgB8PwD8P//8f//+f//+f//+f//+f//+f//+P//8H//4D//wA//AAf+AAP8AADwAABgAAAAA"); // icons/gitlab.png
|
||||||
if (s=="gmx") return atob("GBgBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEJmfmd8Zuc85v847/88Z9s8fttmHIHiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
|
if (s=="gmx") return atob("GBgBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEJmfmd8Zuc85v847/88Z9s8fttmHIHiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
|
||||||
if (s=="google") return atob("GBiBAAAAAAD/AAP/wAf/4A/D4B8AwDwAADwAAHgAAHgAAHAAAHAH/nAH/nAH/ngH/ngAHjwAPDwAfB8A+A/D8Af/4AP/wAD/AAAAAA==");
|
if (s=="google") return atob("GBgBAAAAAP8AA//AB//gD//gH+fAP4CAPwAAPgAAfAAAfA/+fA/+fA/+fA/+fAA+PgA+PwB8P4D8H+f4D//4B//wA//AAP8AAAAA"); // icons/google.png
|
||||||
if (s=="google home") return atob("GBiCAAAAAAAAAAAAAAAAAAAAAoAAAAAACqAAAAAAKqwAAAAAqroAAAACquqAAAAKq+qgAAAqr/qoAACqv/6qAAKq//+qgA6r///qsAqr///6sAqv///6sAqv///6sAqv///6sA6v///6sA6v///qsA6qqqqqsA6qqqqqsA6qqqqqsAP7///vwAAAAAAAAAAAAAAAAA=="); // 2 bit unpaletted
|
if (s=="google home") return atob("GBgBAAAAABgAADwAAH4AAf4AA/zAB/vgD/fwH+f4P4H8fwD+fgB+fAA+eAA+cAA+bAA+HAA+PAA+ff++ff++ff++ff++Pf+8AAAA"); // icons/google home.png
|
||||||
|
if (s=="google play store") return atob("GBgBAAAAAAAAAH4AAP8AAMMAAMMAP//8P//8MAAMMAAMMGAMMHgMMH4MMH8MMH4MMHgMMGAMMAAMMAAMP//8H//4AAAAAAAAAAAA"); // icons/google play store.png
|
||||||
if (s=="home assistant") return atob("FhaBAAAAAADAAAeAAD8AAf4AD/3AfP8D7fwft/D/P8ec572zbzbNsOEhw+AfD8D8P4fw/z/D/P8P8/w/z/AAAAA=");
|
if (s=="home assistant") return atob("FhaBAAAAAADAAAeAAD8AAf4AD/3AfP8D7fwft/D/P8ec572zbzbNsOEhw+AfD8D8P4fw/z/D/P8P8/w/z/AAAAA=");
|
||||||
if (s=="instagram") return atob("GBiBAAAAAAAAAAAAAAAAAAP/wAYAYAwAMAgAkAh+EAjDEAiBEAiBEAiBEAiBEAjDEAh+EAgAEAwAMAYAYAP/wAAAAAAAAAAAAAAAAA==");
|
if (s=="instagram") return atob("GBgBAAAAD//wH//4OAAccAAOYABmYDxmYP8GYeeGYYGGY4HGYwDGYwDGY4HGYYGGYeeGYP8GYDwGYAAGcAAOOAAcH//4D//wAAAA"); // icons/instagram.png
|
||||||
if (s=="kalender") return atob("GBgBBgBgBQCgff++RQCiRgBiQAACf//+QAACQAACR//iRJkiRIEiR//iRNsiRIEiRJkiR//iRIEiRIEiR//iQAACQAACf//+AAAA");
|
if (s=="kalender") return atob("GBgBBgBgBQCgff++RQCiRgBiQAACf//+QAACQAACR//iRJkiRIEiR//iRNsiRIEiRJkiR//iRIEiRIEiR//iQAACQAACf//+AAAA");
|
||||||
if (s=="lieferando") return atob("GBgBABgAAH5wAP9wAf/4A//4B//4D//4H//4P/88fV8+fV4//V4//Vw/HVw4HVw4HBg4HBg4HBg4HDg4Hjw4Hj84Hj44Hj44Hj44");
|
if (s=="keep notes") return atob("GBgBAAAAAAAAH//4P//8P8P8Pzz8P378Pv98Pv98Pv98Pv98P378Pzz8P738P4H8P738P738P4GMP8OYP/+wP//gH//AAAAAAAAA"); // icons/google keep.png
|
||||||
if (s=="mattermost") return atob("GBgBAAAAAPAAA+EAB4MADgcYHAcYOA8MOB8OeD8GcD8GcH8GcD8HcD8HeBwHeAAOfAAOfgAePwA8P8D8H//4D//wB//gAf/AAH4A");
|
if (s=="lieferando") return atob("GBgBAAAAADwAAH4AAP/gAf/wA//wB//wD//wH//4H/98Pt58ft5+Ptx8DtxwDtxwDhxwDhhwDhhwDzhwD75wD75wD75wB77gAAAA"); // icons/lieferando.png
|
||||||
|
if (s=="linkedin") return atob("GBgBAAAAf//+f//+f//+ef/+cf/+cf/+f//+f//+ccw+ccAeccAecccOcceOcceOcceOcceOcceOcceOec+ef//+f//+f//+AAAA"); // icons/linkedin.png
|
||||||
|
if (s=="maps" || s=="organic maps" || s=="osmand") return atob("GBgBAAAAAAAAAAAAAeAYD/z4H//4GMeYGMMYGMMYGMMYGMMYGMMYGMMYGMMYGMMYGMMYGMMYGeMYH//4Hz/wGAeAAAAAAAAAAAAA"); // icons/map.png
|
||||||
|
if (s=="mastodon" || s=="fedilab" || s=="tooot" || s=="tusky") return atob("GBgBAAAAB//gD//4H//4P//8PBg8PAA8fOMeeOeeeOeeOOeeOOecOP+cOP+cP//8P//4P//4P//gHwAAH4AAD+cAB/8AAf4AAAAA"); // icons/mastodon.png
|
||||||
|
if (s=="mattermost") return atob("GBgBAAAAAPAAA+EAB4GADgOQHAeYOA+cOB+MeB+OcD+GcD+GcD+GeD8OeB4OeAAOfAAePgA8P4B8H/f4D//wB//gA//AAP8AAAAA"); // icons/mattermost.png
|
||||||
if (s=="n26") return atob("GBgBAAAAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAOIAAOIAAPIAANoAANoAAM4AAMYAAMYAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAAAA");
|
if (s=="n26") return atob("GBgBAAAAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAOIAAOIAAPIAANoAANoAAM4AAMYAAMYAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAAAA");
|
||||||
|
if (s=="netflix") return atob("GBgBAAAAA8PAA+PAAePAAePAAfPAAvPAA/PAAvvAAn/AA/nAA3/AA/7AA5/AA/5AA99AA8/AA89AA8+AA8eAA8eAA8fAA8PAAAAA"); // icons/netflix.png
|
||||||
|
if (s=="news" || s=="cbc news" || s=="rc info" || s=="reuters" || s=="ap news" || s=="la presse" || s=="nbc news") return atob("GBgBAAAAAAAAAAAALaW0P//8P//8P//8P//8MAAMMAAMMAAMP//8P//8MBwcMBwcMB/8MB/8MBwcMBwcP//8P//8AAAAAAAAAAAA"); // icons/news.png
|
||||||
if (s=="nextbike") return atob("GBgBAAAAAAAAAAAAAAAAAAAAAACAfgDAPwDAP4HAH4N4H8f8D82GMd8CMDsDMGMDMGGGGMHOD4D8AAAAAAAAAAAAAAAAAAAAAAAA");
|
if (s=="nextbike") return atob("GBgBAAAAAAAAAAAAAAAAAAAAAACAfgDAPwDAP4HAH4N4H8f8D82GMd8CMDsDMGMDMGGGGMHOD4D8AAAAAAAAAAAAAAAAAAAAAAAA");
|
||||||
if (s=="nina") return atob("GBgBAAAABAAQCAAICAAIEAAEEgAkJAgSJBwSKRxKSj4pUn8lVP+VVP+VUgAlSgApKQBKJAASJAASEgAkEAAECAAICAAIBAAQAAAA");
|
if (s=="nina") return atob("GBgBAAAABAAQCAAICAAIEAAEEgAkJAgSJBwSKRxKSj4pUn8lVP+VVP+VUgAlSgApKQBKJAASJAASEgAkEAAECAAICAAIBAAQAAAA");
|
||||||
if (s=="outlook mail") return atob("HBwBAAAAAAAAAAAIAAAfwAAP/gAB/+AAP/5/A//v/D/+/8P/7/g+Pv8Dye/gPd74w5znHDnOB8Oc4Pw8nv/Dwe/8Pj7/w//v/D/+/8P/7/gf/gAA/+AAAfwAAACAAAAAAAAAAAA=");
|
if (s=="outlook mail") return atob("GBgBAAAAAAAAAP/8AP/8AP/8AJjMf/jMf//8f//8cHjMd3jMZz/+Zz/+d3jecHj+f//mf/eGf/PGAwDmAwA+A//+Af/+AAAAAAAA"); // icons/outlook.png
|
||||||
if (s=="paypal") return atob("GBgBAAAAAAAAAAAAAf+AAf/AAf/gA//gA//gA//wA//wA//wA//wB//wB//wB//gB/+AB/gAB/gAB/gAAPgAAPgAAAAAAAAAAAAA");
|
if (s=="paypal") return atob("GBgBAAAAA/+AA//gA//wB//wB//wB//wB//wB//wB//gD//gD//ID/+ID/wwD4BwD5/gD74AH7gAHzAAHzAAHzAAAHAAAHAAAAAA"); // icons/paypal.png
|
||||||
if (s=="phone") return atob("FxeBABgAAPgAAfAAB/AAD+AAH+AAP8AAP4AAfgAA/AAA+AAA+AAA+AAB+AAB+AAB+OAB//AB//gB//gA//AA/8AAf4AAPAA=");
|
if (s=="phone") return atob("GBgBAAAAAAAAH4AAP8AAP8AAP8AAH8AAH8AAH8AAH4AADwAADwAABwAAA4AAA8HwAeP8AP/8AH/8AD/8AA/8AAP8AAB4AAAAAAAA"); // icons/phone.png
|
||||||
if (s=="post & dhl") return atob("GBgBAPgAE/5wMwZ8NgN8NgP4NgP4HgP4HgPwDwfgD//AB/+AAf8AAAAABs7AHcdgG4MwAAAAGESAFESAEkSAEnyAEkSAFESAGETw");
|
if (s=="plex") return atob("GBgBAAAAB/gAB/gAA/wAAf4AAf4AAP8AAH+AAH+AAD/AAB/gAB/gAB/gAB/gAD/AAH+AAH+AAP8AAf4AAf4AA/wAB/gAB/gAAAAA"); // icons/plex.png
|
||||||
if (s=="signal") return atob("GBgBAAAAAGwAAQGAAhggCP8QE//AB//oJ//kL//wD//0D//wT//wD//wL//0J//kB//oA//ICf8ABfxgBYBAADoABMAABAAAAAAA");
|
if (s=="pocket") return atob("GBgBAAAAAAAAP//8f//+f//+f//+f//+f//+fP8+eH4efDw+fhh+fwD+f4H+P8P8P+f8H//4H//4D//wB//gAf+AAH4AAAAAAAAA"); // icons/pocket.png
|
||||||
if (s=="skype") return atob("GhoBB8AAB//AA//+Af//wH//+D///w/8D+P8Afz/DD8/j4/H4fP5/A/+f4B/n/gP5//B+fj8fj4/H8+DB/PwA/x/A/8P///B///gP//4B//8AD/+AAA+AA==");
|
if (s=="post & dhl") return atob("GBgBAAAAAAAAAAAAAAAAP/+Af/+AYAGAYAGAYAHwYAH4YAGMYAGGYAH+YAH+bwH+f//+ef+eGYGYH4H4DwDwAAAAAAAAAAAAAAAA"); // icons/delivery.png
|
||||||
if (s=="slack") return atob("GBiBAAAAAAAAAABAAAHvAAHvAADvAAAPAB/PMB/veD/veB/mcAAAABzH8B3v+B3v+B3n8AHgAAHuAAHvAAHvAADGAAAAAAAAAAAAAA==");
|
if (s=="proton mail") return atob("GBgBAAAAAAAAAAAAQAACYAAGcAAOeAAePABeXgDebwHed4Pee/fefe/efh/ef//ef//ef//ef//ef//ef//eP//cAAAAAAAAAAAA"); // icons/protonmail.png
|
||||||
if (s=="snapchat") return atob("GBgBAAAAAAAAAH4AAf+AAf+AA//AA//AA//AA//AA//AH//4D//wB//gA//AB//gD//wH//4f//+P//8D//wAf+AAH4AAAAAAAAA");
|
if (s=="reddit" || s=="sync pro" || s=="sync dev" || s=="boost" || s=="infinity" || s=="slide") return atob("GBgBAAAAAAAAAAYwAAX4AAh4AAgwAAgAAAgAAH4AAf+AN//sf//+fn5+PDw8HDw4Hn54H//4H//4DzzwB4HgAf+AAH4AAAAAAAAA"); // icons/reddit.png
|
||||||
if (s=="steam") return atob("GBgBAAAAAAAAAAAAAAAAAAAAAAfgAAwwAAvQABvQABvQADvQgDww4H/g+f8A/zwAf9gAH9AAB8AAACAAAcAAAAAAAAAAAAAAAAAA");
|
if (s=="signal") return atob("GBgBAAAAAL0AAYGABH4gCf+QE//IB//gL//0b//2H//4X//6X//6X//6X//6H//4b//2L//0D//gL//ID/+QYH4gVYGAcL0AAAAA"); // icons/signal.png
|
||||||
if (s=="teams") return atob("GBgBAAAAAAAAAAQAAB4AAD8IAA8cP/M+f/scf/gIeDgAfvvefvvffvvffvvffvvff/vff/veP/PeAA/cAH/AAD+AAD8AAAQAAAAA");
|
if (s=="skype") return atob("GBgBAAAAB8AAH/8AP//AP//gf8fwfwD4fgB4fjx8fj/8Pg/8PwH8P4B8P/h8Pnx+Pjx+Hhh+HwD+D8P+B//8A//8AP/4AAPgAAAA"); // icons/skype.png
|
||||||
if (s=="telegram" || s=="telegram foss") return atob("GBiBAAAAAAAAAAAAAAAAAwAAHwAA/wAD/wAf3gD/Pgf+fh/4/v/z/P/H/D8P/Acf/AM//AF/+AF/+AH/+ADz+ADh+ADAcAAAMAAAAA==");
|
if (s=="slack") return atob("GBgBAAAAAOcAAeeAAeeAAeeAAGeAAAeAP+ecf+eef+e+f+e+AAAAAAAAfef+fef+eef+Oef8AeAAAeYAAeeAAeeAAeeAAOcAAAAA"); // icons/slack.png
|
||||||
if (s=="threema") return atob("GBjB/4Yx//8AAAAAAAAAAAAAfgAB/4AD/8AH/+AH/+AP//AP2/APw/APw/AHw+AH/+AH/8AH/4AH/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=");
|
if (s=="snapchat") return atob("GBgBAAAAAAAAAAAAAH4AAf+AAYGAAwDAAwDAAwDADwDwDwDwDgBwBwDgBwDgDgBwHAA4OAAcHAA4D4HwB//gAH4AAAAAAAAAAAAA"); // icons/snapchat.png
|
||||||
if (s=="to do" || s=="opentasks") return atob("GBgBAAAAAAAAAAAwAAB4AAD8AAH+AAP/DAf/Hg//Px/+f7/8///4///wf//gP//AH/+AD/8AB/4AA/wAAfgAAPAAAGAAAAAAAAAA");
|
if (s=="starbucks") return atob("GBgBAAAAAAAAAAAAD//4D//8DADMDADMDADMDAD8DAD4DADADADADADADADADgHAB/+AA/8AAAAAAAAAP//wP//wAAAAAAAAAAAA"); // icons/cafe.png
|
||||||
if (s=="twitch") return atob("GBgBH//+P//+P//+eAAGeAAGeAAGeDGGeDOGeDOGeDOGeDOGeDOGeDOGeAAOeAAOeAAcf4/4f5/wf7/gf//Af/+AA/AAA+AAAcAA");
|
if (s=="steam") return atob("GBgBAAAAAAAAAf+AA//AD//wD//wH/g4P/OcP/RcP+RcP+ReH8OeB4A+AAH+AMP8IC/8OS/8HN/4Dj/wD//wA//AAf+AAAAAAAAA"); // icons/steam.png
|
||||||
if (s=="twitter") return atob("GhYBAABgAAB+JgA/8cAf/ngH/5+B/8P8f+D///h///4f//+D///g///wD//8B//+AP//gD//wAP/8AB/+AB/+AH//AAf/AAAYAAA");
|
if (s=="teams") return atob("GBgBAAAAAAgAAD4AADcYAGM8AGNmP/dmP/48MDAYMD/+PP/+PPBmPPBmPPBmPPBmP/BmP/BmH+B+AYD4AMDAAOOAAH8AABwAAAAA"); // icons/teams.png
|
||||||
|
if (s=="telegram" || s=="telegram foss") return atob("GBgBAAAAAAAAAAAAAAAeAAB+AAP+AA/+AD/+Af9+B/z+H/n8f+P8f8f8Dw/8AB/8AB/8AB/4AAf4AAP4AAD4AABwAAAAAAAAAAAA"); // icons/telegram.png
|
||||||
|
if (s=="threema") return atob("GBgBAAAAAP8AA//AB//gD//wH8P4H9v4H734P5n8P4H8P4H8H4H4H4H4D//wD//gD//AH/8AHDwAAAAAAAAABhhgDzzwBhhgAAAA"); // icons/threema.png
|
||||||
|
if (s=="tiktok") return atob("GBgBAAAAAAAAAAcAAAcAAAeAAAfAAAfwAAf4AAf4AMd4A8cAB8cAD8cADwcAHgcAHgcAHg8ADw8AD/4AB/4AA/wAAfAAAAAAAAAA"); // icons/tiktok.png
|
||||||
|
if (s=="to do" || s=="opentasks" || s=="tasks") return atob("GBgBAAAAAHwAAf+AA//ID4GcHwA8HAB4PADwOAHgcAPGcAeOcY8Oc94OcfwOcPgOOHAcOCAcHAA4DgB4D4HwB//gAf+AAH4AAAAA"); // icons/task.png
|
||||||
|
if (s=="transit") return atob("GBgBAAAAD//wP//8P//8f//+f/j+ffA+eOA+eOMef+cefef+eOe+fecef+e+eOf+eOcefAcefA++fx/+f//+P//8P//8D//wAAAA"); // icons/transit.png
|
||||||
|
if (s=="twitch") return atob("GBgBAAAAA//8B//8DgAMHgAMPhjMPhjMPhjMPhjMPhjMPgAMPgAMPgAYPgAwP+fgP+/AP/+AP/8AP/4AAeAAAcAAAYAAAQAAAAAA"); // icons/twitch.png
|
||||||
|
if (s=="twitter") return atob("GBgBAAAAAAAAAAAAAAPAIAf8MA/4PA/8Pg/4H//4H//4P//4P//wH//wD//wD//gD//AA//AAf+AB/8AP/wAD/AAAAAAAAAAAAAA"); // icons/twitter.png
|
||||||
|
if (s=="uber" || s=="lyft") return atob("GBgBAAAAAAAAAAAAAH4AAH4AB//gB//gDgBwDAAwDAAwH//4H//4GAAYG4HYG4HYG4HYGAAYH//4H//4HAA4HAA4AAAAAAAAAAAA"); // icons/taxi.png
|
||||||
|
if (s=="vlc") return atob("GBgBAAAAABgAABgAADwAADwAAAAAAAAAAAAAAAAAAIEAAP8AAP8AAf+AAP8AAAAADAAwDAAwHAA4HwD4H//4P//8P//8P//8AAAA"); // icons/vlc.png
|
||||||
if (s=="warnapp") return atob("GBgBAAAAAAAAAAAAAH4AAP8AA//AA//AD//gP//gf//4f//+/+P+/8H//8n//4n/fxh/fzg+Pj88Dn44AA4AAAwAAAwAAAgAAAAA");
|
if (s=="warnapp") return atob("GBgBAAAAAAAAAAAAAH4AAP8AA//AA//AD//gP//gf//4f//+/+P+/8H//8n//4n/fxh/fzg+Pj88Dn44AA4AAAwAAAwAAAgAAAAA");
|
||||||
if (s=="whatsapp") return atob("GBiBAAB+AAP/wAf/4A//8B//+D///H9//n5//nw//vw///x///5///4///8e//+EP3/APn/wPn/+/j///H//+H//8H//4H//wMB+AA==");
|
if (s=="whatsapp") return atob("GBgBAAAAAP8AA//AB4HwDgB4HAA4OAAcMYAMc8AOc8AGY8AGYcAGYeAGYPOGcH/OcD/OMA+MOAAcMAA4MgBwf8Pgf//AcP8AAAAA"); // icons/whatsapp.png
|
||||||
if (s=="wordfeud") return atob("GBgCWqqqqqqlf//////9v//////+v/////++v/////++v8///Lu+v8///L++v8///P/+v8v//P/+v9v//P/+v+fx/P/+v+Pk+P/+v/PN+f/+v/POuv/+v/Ofdv/+v/NvM//+v/I/Y//+v/k/k//+v/i/w//+v/7/6//+v//////+v//////+f//////9Wqqqqqql");
|
if (s=="wordfeud") return atob("GBgCWqqqqqqlf//////9v//////+v/////++v/////++v8///Lu+v8///L++v8///P/+v8v//P/+v9v//P/+v+fx/P/+v+Pk+P/+v/PN+f/+v/POuv/+v/Ofdv/+v/NvM//+v/I/Y//+v/k/k//+v/i/w//+v/7/6//+v//////+v//////+f//////9Wqqqqqql");
|
||||||
if (s=="youtube" || s=="newpipe") return atob("GBgBAAAAAAAAAAAAAAAAAf8AH//4P//4P//8P//8P5/8P4/8f4P8f4P8P4/8P5/8P//8P//8P//4H//4Af8AAAAAAAAAAAAAAAAA");
|
if (s=="youtube" || s=="newpipe") return atob("GBgBAAAAAAAAAAAAAAAAAAAAH//4P//8P//8f//+f8/+f8P+f8D+f8D+f8P+f8/+f//+P//8P//8H//4AAAAAAAAAAAAAAAAAAAA"); // icons/youtube.png
|
||||||
|
if (s=="zoom" || s=="meet") return atob("GBgBAAAAAAAAAAAAP/+Af//Af//AcADicADmcADucAD+cAD+cAD+cAD+cAD+cAD+cADucADmcADif//Af//AP/+AAAAAAAAAAAAA"); // icons/videoconf.png
|
||||||
if (msg.id=="music") return atob("FhaBAH//+/////////////h/+AH/4Af/gB/+H3/7/f/v9/+/3/7+f/vB/w8H+Dwf4PD/x/////////////3//+A=");
|
if (msg.id=="music") return atob("FhaBAH//+/////////////h/+AH/4Af/gB/+H3/7/f/v9/+/3/7+f/vB/w8H+Dwf4PD/x/////////////3//+A=");
|
||||||
// if (s=="sms message" || s=="mail" || s=="gmail") // .. default icon (below)
|
// if (s=="sms message" || s=="mail" || s=="gmail") // .. default icon (below)
|
||||||
return atob("FhKBAH//+P//yf/+c//z5/+fz/z/n+f/Pz/+ef/8D///////////////////////f//4///A");
|
return atob("FhKBAH//+P//yf/+c//z5/+fz/z/n+f/Pz/+ef/8D///////////////////////f//4///A");
|
||||||
|
@ -57,7 +86,7 @@ exports.getColor = function(msg,options) {
|
||||||
return {
|
return {
|
||||||
// generic colors, using B2-safe colors
|
// generic colors, using B2-safe colors
|
||||||
// DO NOT USE BLACK OR WHITE HERE, just leave the declaration out and then the theme's fg color will be used
|
// DO NOT USE BLACK OR WHITE HERE, just leave the declaration out and then the theme's fg color will be used
|
||||||
"airbnb": "#f00",
|
"airbnb": "#ff385c", // https://news.airbnb.com/media-assets/category/brand/
|
||||||
"mail": "#ff0",
|
"mail": "#ff0",
|
||||||
"music": "#f0f",
|
"music": "#f0f",
|
||||||
"phone": "#0f0",
|
"phone": "#0f0",
|
||||||
|
@ -66,39 +95,44 @@ exports.getColor = function(msg,options) {
|
||||||
// all dithered on B2, but we only use the color for the icons. (Could maybe pick the closest 3-bit color for B2?)
|
// all dithered on B2, but we only use the color for the icons. (Could maybe pick the closest 3-bit color for B2?)
|
||||||
"bibel": "#54342c",
|
"bibel": "#54342c",
|
||||||
"bring": "#455a64",
|
"bring": "#455a64",
|
||||||
"discord": "#738adb",
|
"discord": "#5865f2", // https://discord.com/branding
|
||||||
"etar": "#36a18b",
|
"etar": "#36a18b",
|
||||||
"facebook": "#4267b2",
|
"facebook": "#1877f2", // https://www.facebook.com/brand/resources/facebookapp/logo
|
||||||
"gmail": "#ea4335",
|
"gmail": "#ea4335",
|
||||||
"gmx": "#1c449b",
|
"gmx": "#1c449b",
|
||||||
"google": "#4285F4",
|
"google": "#4285F4",
|
||||||
"google home": "#fbbc05",
|
"google home": "#fbbc05",
|
||||||
// "home assistant": "#41bdf5", // ha-blue is #41bdf5, but that's the background
|
// "home assistant": "#41bdf5", // ha-blue is #41bdf5, but that's the background
|
||||||
"instagram": "#dd2a7b",
|
"instagram": "#ff0069", // https://about.instagram.com/brand/gradient
|
||||||
"lieferando": "#ee5c00",
|
"lieferando": "#ff8000",
|
||||||
|
"linkedin": "#0a66c2", // https://brand.linkedin.com/
|
||||||
"messenger": "#0078ff",
|
"messenger": "#0078ff",
|
||||||
|
"mastodon": "#563acc", // https://www.joinmastodon.org/branding
|
||||||
"mattermost": "#00f",
|
"mattermost": "#00f",
|
||||||
"n26": "#36a18b",
|
"n26": "#36a18b",
|
||||||
"nextbike": "#00f",
|
"nextbike": "#00f",
|
||||||
"newpipe": "#f00",
|
"newpipe": "#f00",
|
||||||
"nina": "#e57004",
|
"nina": "#e57004",
|
||||||
"opentasks": "#409f8f",
|
"opentasks": "#409f8f",
|
||||||
"outlook mail": "#0072c6",
|
"outlook mail": "#0078d4", // https://developer.microsoft.com/en-us/fluentui#/styles/web/colors/products
|
||||||
"paypal": "#003087",
|
"paypal": "#003087",
|
||||||
|
"pocket": "#ef4154f", // https://blog.getpocket.com/press/
|
||||||
"post & dhl": "#f2c101",
|
"post & dhl": "#f2c101",
|
||||||
"signal": "#00f",
|
"reddit": "#ff4500", // https://www.redditinc.com/brand
|
||||||
"skype": "#00aff0",
|
"signal": "#3a76f0", // https://github.com/signalapp/Signal-Desktop/blob/main/images/signal-logo.svg
|
||||||
|
"skype": "#0078d4", // https://developer.microsoft.com/en-us/fluentui#/styles/web/colors/products
|
||||||
"slack": "#e51670",
|
"slack": "#e51670",
|
||||||
"snapchat": "#ff0",
|
"snapchat": "#ff0",
|
||||||
"steam": "#171a21",
|
"steam": "#171a21",
|
||||||
"teams": "#464eb8",
|
"teams": "#6264a7", // https://developer.microsoft.com/en-us/fluentui#/styles/web/colors/products
|
||||||
"telegram": "#0088cc",
|
"telegram": "#0088cc",
|
||||||
"telegram foss": "#0088cc",
|
"telegram foss": "#0088cc",
|
||||||
"to do": "#3999e5",
|
"to do": "#3999e5",
|
||||||
"twitch": "#6441A4",
|
"twitch": "#9146ff", // https://brand.twitch.tv/
|
||||||
"twitter": "#1da1f2",
|
"twitter": "#1d9bf0", // https://about.twitter.com/en/who-we-are/brand-toolkit
|
||||||
|
"vlc": "#ff8800",
|
||||||
"whatsapp": "#4fce5d",
|
"whatsapp": "#4fce5d",
|
||||||
"wordfeud": "#e7d3c7",
|
"wordfeud": "#e7d3c7",
|
||||||
"youtube": "#f00",
|
"youtube": "#f00", // https://www.youtube.com/howyoutubeworks/resources/brand-resources/#logos-icons-and-colors
|
||||||
}[s]||options.default;
|
}[s]||options.default;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
0.55: Moved messages library into standalone library
|
0.55: Moved messages library into standalone library
|
||||||
|
0.56: Fix handling of music messages
|
||||||
|
|
|
@ -9,13 +9,14 @@ Assuming you are using GadgetBridge and "overlay notifications":
|
||||||
|
|
||||||
1. Gadgetbridge sends an event to your watch for an incoming message
|
1. Gadgetbridge sends an event to your watch for an incoming message
|
||||||
2. The `android` app parses the message, and calls `require("messages").pushMessage({/** the message */})`
|
2. The `android` app parses the message, and calls `require("messages").pushMessage({/** the message */})`
|
||||||
3. `require("messages")` (provided by `messagelib`) calls `Bangle.emit("message", "text", {/** the message */})`
|
3. `require("messages")` calls `Bangle.emit("message", "text", {/** the message */})`
|
||||||
4. Overlay Notifications shows the message in an overlay, and marks it as `handled`
|
4. Overlay Notifications shows the message in an overlay, and marks it as `handled`
|
||||||
5. The default GUI app (`messages`) sees the event is marked as `handled`, so does nothing.
|
5. The default UI app (Message UI, `messagegui`) sees the event is marked as `handled`, so does nothing.
|
||||||
6. The default widget (`widmessages`) does nothing with `handled`, and shows a notification icon.
|
6. The default widget (`widmessages`) does nothing with `handled`, and shows a notification icon.
|
||||||
7. You tap the notification, in order to open the full GUI Overlay Notifications
|
7. You tap the notification, in order to open the full GUI: Overlay Notifications
|
||||||
calls `require("messages").openGUI({/** the message */})`
|
calls `require("messages").openGUI({/** the message */})`
|
||||||
8. The default GUI app (`messages`) sees the "messageGUI" event, and launches itself
|
8. `openGUI` calls `require("messagegui").open(/** copy of the message */)`.
|
||||||
|
9. The `messagegui` library loads the Message UI app.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,7 +29,7 @@ it like this:
|
||||||
myMessageListener = Bangle.on("message", (type, message)=>{
|
myMessageListener = Bangle.on("message", (type, message)=>{
|
||||||
if (message.handled) return; // another app already handled this message
|
if (message.handled) return; // another app already handled this message
|
||||||
// <type> is one of "text", "call", "alarm", "map", or "music"
|
// <type> is one of "text", "call", "alarm", "map", or "music"
|
||||||
// see `messagelib/lib.js` for possible <message> formats
|
// see `messages/lib.js` for possible <message> formats
|
||||||
// message.t could be "add", "modify" or "remove"
|
// message.t could be "add", "modify" or "remove"
|
||||||
E.showMessage(`${message.title}\n${message.body}`, `${message.t} ${type} message`);
|
E.showMessage(`${message.title}\n${message.body}`, `${message.t} ${type} message`);
|
||||||
// You can prevent the default `message` app from loading by setting `message.handled = true`:
|
// You can prevent the default `message` app from loading by setting `message.handled = true`:
|
||||||
|
@ -52,7 +53,7 @@ Bangle.on("messageGUI", message=>{
|
||||||
|
|
||||||
## Requests
|
## Requests
|
||||||
|
|
||||||
Please file any issues on https://github.com/espruino/BangleApps/issues/new?title=messagelib%library
|
Please file any issues on https://github.com/espruino/BangleApps/issues/new?title=[messages]%20library
|
||||||
|
|
||||||
## Creator
|
## Creator
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,6 @@ exports.music = {};
|
||||||
function emit(msg) {
|
function emit(msg) {
|
||||||
let type = "text";
|
let type = "text";
|
||||||
if (["call", "music", "map"].includes(msg.id)) type = msg.id;
|
if (["call", "music", "map"].includes(msg.id)) type = msg.id;
|
||||||
if (type==="music" && msg.t!=="remove" && (!("state" in msg) || (!("track" in msg)))) return; // wait for complete music info
|
|
||||||
if (msg.src && msg.src.toLowerCase().startsWith("alarm")) type = "alarm";
|
if (msg.src && msg.src.toLowerCase().startsWith("alarm")) type = "alarm";
|
||||||
Bangle.emit("message", type, msg);
|
Bangle.emit("message", type, msg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "messages",
|
"id": "messages",
|
||||||
"name": "Messages",
|
"name": "Messages",
|
||||||
"version": "0.55",
|
"version": "0.56",
|
||||||
"description": "Library to handle, load and store message events received from Android/iOS",
|
"description": "Library to handle, load and store message events received from Android/iOS",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
0.01: New App!
|
0.01: New App!
|
||||||
0.02: Remove one line of code that didn't do anything other than in some instances hinder the function of the app.
|
0.02: Remove one line of code that didn't do anything other than in some instances hinder the function of the app.
|
||||||
0.03: Use the new messages library
|
0.03: Use the new messages library
|
||||||
|
0.04: Fix dependency on messages library
|
||||||
|
Fix loading message UI
|
|
@ -1 +1 @@
|
||||||
require('messages').openGUI({"t":"add","artist":" ","album":" ","track":" ","dur":0,"c":-1,"n":-1,"id":"music","title":"Music","state":"play","new":true});
|
setTimeout(()=>require('messages').openGUI({"t":"add","artist":" ","album":" ","track":" ","dur":0,"c":-1,"n":-1,"id":"music","title":"Music","state":"play","new":true}));
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "messagesmusic",
|
"id": "messagesmusic",
|
||||||
"name":"Messages Music",
|
"name":"Messages Music",
|
||||||
"version":"0.03",
|
"version":"0.04",
|
||||||
"description": "Uses Messages library to push a music message which in turn displays Messages app music controls",
|
"description": "Uses Messages library to push a music message which in turn displays Messages app music controls",
|
||||||
"icon":"app.png",
|
"icon":"app.png",
|
||||||
"type": "app",
|
"type": "app",
|
||||||
|
@ -13,6 +13,5 @@
|
||||||
{"name":"messagesmusic.app.js","url":"app.js"},
|
{"name":"messagesmusic.app.js","url":"app.js"},
|
||||||
{"name":"messagesmusic.img","url":"app-icon.js","evaluate":true}
|
{"name":"messagesmusic.img","url":"app-icon.js","evaluate":true}
|
||||||
],
|
],
|
||||||
"dependencies" : { "messagelib":"module" }
|
"dependencies":{"messages":"module"}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,11 @@
|
||||||
var notesElement = document.getElementById("notes");
|
var notesElement = document.getElementById("notes");
|
||||||
var notes = {};
|
var notes = {};
|
||||||
|
|
||||||
|
function disableFormInput() {
|
||||||
|
document.querySelectorAll(".form-input").forEach(el => el.disabled = true);
|
||||||
|
document.querySelectorAll(".btn").forEach(el => el.disabled = true);
|
||||||
|
}
|
||||||
|
|
||||||
function getData() {
|
function getData() {
|
||||||
// show loading window
|
// show loading window
|
||||||
Util.showModal("Loading...");
|
Util.showModal("Loading...");
|
||||||
|
@ -53,8 +58,10 @@ function getData() {
|
||||||
buttonSave.classList.add('btn-default');
|
buttonSave.classList.add('btn-default');
|
||||||
buttonSave.onclick = function() {
|
buttonSave.onclick = function() {
|
||||||
notes[i].note = textarea.value;
|
notes[i].note = textarea.value;
|
||||||
Util.writeStorage("noteify.json", JSON.stringify(notes));
|
disableFormInput();
|
||||||
location.reload();
|
Util.writeStorage("noteify.json", JSON.stringify(notes), () => {
|
||||||
|
location.reload(); // reload so we see current data
|
||||||
|
});
|
||||||
}
|
}
|
||||||
divColumn2.appendChild(buttonSave);
|
divColumn2.appendChild(buttonSave);
|
||||||
|
|
||||||
|
@ -64,8 +71,10 @@ function getData() {
|
||||||
buttonDelete.onclick = function() {
|
buttonDelete.onclick = function() {
|
||||||
notes[i].note = textarea.value;
|
notes[i].note = textarea.value;
|
||||||
notes.splice(i, 1);
|
notes.splice(i, 1);
|
||||||
Util.writeStorage("noteify.json", JSON.stringify(notes));
|
disableFormInput();
|
||||||
|
Util.writeStorage("noteify.json", JSON.stringify(notes), () => {
|
||||||
location.reload(); // reload so we see current data
|
location.reload(); // reload so we see current data
|
||||||
|
});
|
||||||
}
|
}
|
||||||
divColumn2.appendChild(buttonDelete);
|
divColumn2.appendChild(buttonDelete);
|
||||||
divColumn.appendChild(divColumn2);
|
divColumn.appendChild(divColumn2);
|
||||||
|
@ -77,10 +86,12 @@ function getData() {
|
||||||
document.getElementById("btnAdd").addEventListener("click", function() {
|
document.getElementById("btnAdd").addEventListener("click", function() {
|
||||||
const note = document.getElementById("note-new").value;
|
const note = document.getElementById("note-new").value;
|
||||||
notes.push({"note": note});
|
notes.push({"note": note});
|
||||||
Util.writeStorage("noteify.json", JSON.stringify(notes));
|
disableFormInput();
|
||||||
|
Util.writeStorage("noteify.json", JSON.stringify(notes), () => {
|
||||||
location.reload(); // reload so we see current data
|
location.reload(); // reload so we see current data
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when app starts
|
// Called when app starts
|
||||||
|
|
|
@ -48,16 +48,18 @@
|
||||||
<div style="display:inline-block;text-align:center;vertical-align: top;" id="3bitdiv"> <input type="checkbox" id="3bit"></input><br/><span>3 bit</span></div>
|
<div style="display:inline-block;text-align:center;vertical-align: top;" id="3bitdiv"> <input type="checkbox" id="3bit"></input><br/><span>3 bit</span></div>
|
||||||
<div class="form-group" style="display:inline-block;">
|
<div class="form-group" style="display:inline-block;">
|
||||||
<select class="form-select" id="mapSize">
|
<select class="form-select" id="mapSize">
|
||||||
<option value="4">Small</option>
|
<option value="4">Small (4x4)</option>
|
||||||
<option value="5" selected>Medium</option>
|
<option value="5" selected>Medium (5x5)</option>
|
||||||
<option value="6">Large</option>
|
<option value="7">Large (7x7)</option>
|
||||||
<option value="7">XL</option>
|
<option value="10">XL (10x10)</option>
|
||||||
|
<option value="15">XXL (15x15)</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<button id="getmap" class="btn btn-primary">Get Map</button><button class="btn" onclick="showLoadedMaps()">Map List</button><br/>
|
<button id="getmap" class="btn btn-primary">Get Map</button><button class="btn" onclick="showLoadedMaps()">Map List</button><br/>
|
||||||
<canvas id="maptiles" style="display:none"></canvas>
|
<canvas id="maptiles" style="display:none"></canvas>
|
||||||
<div id="uploadbuttons" style="display:none"><button id="upload" class="btn btn-primary">Upload</button>
|
<div id="uploadbuttons" style="display:none"><button id="upload" class="btn btn-primary">Upload</button>
|
||||||
<button id="cancel" class="btn">Cancel</button></div>
|
<button id="cancel" class="btn">Cancel</button>
|
||||||
|
<span id="mapstats"></span></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -174,12 +176,14 @@ TODO:
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
setTimeout(function() {
|
||||||
let map = L.map(`tile-map-${mapNumber}`);
|
let map = L.map(`tile-map-${mapNumber}`);
|
||||||
L.tileLayer(PREVIEWTILELAYER, {
|
L.tileLayer(PREVIEWTILELAYER, {
|
||||||
maxZoom: 18
|
maxZoom: 18
|
||||||
}).addTo(map);
|
}).addTo(map);
|
||||||
let marker = new L.marker(latlon).addTo(map);
|
let marker = new L.marker(latlon).addTo(map);
|
||||||
map.fitBounds(latlon.toBounds(2000/*meters*/), {animation: false});
|
map.fitBounds(latlon.toBounds(2000/*meters*/), {animation: false});
|
||||||
|
}, 100);
|
||||||
}
|
}
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
|
@ -312,11 +316,13 @@ TODO:
|
||||||
|
|
||||||
var zoom = map.getZoom();
|
var zoom = map.getZoom();
|
||||||
var centerlatlon = map.getBounds().getCenter();
|
var centerlatlon = map.getBounds().getCenter();
|
||||||
var center = map.project(centerlatlon, zoom).divideBy(OSMTILESIZE);
|
var center = map.project(centerlatlon, zoom).divideBy(OSMTILESIZE); // the center of our map
|
||||||
// Reason for 16px adjustment below not 100% known, but it seems to
|
// ox/oy = offset in pixels
|
||||||
// align everything perfectly: https://github.com/espruino/BangleApps/issues/984
|
var ox = Math.round((center.x - Math.floor(center.x)) * OSMTILESIZE);
|
||||||
var ox = Math.round((center.x - Math.floor(center.x)) * OSMTILESIZE) + 16;
|
var oy = Math.round((center.y - Math.floor(center.y)) * OSMTILESIZE);
|
||||||
var oy = Math.round((center.y - Math.floor(center.y)) * OSMTILESIZE) + 16;
|
// adjust offset because we want to center our map
|
||||||
|
ox -= MAPTILES * TILESIZE / 2;
|
||||||
|
oy -= MAPTILES * TILESIZE / 2;
|
||||||
center = center.floor(); // make sure we're in the middle of a tile
|
center = center.floor(); // make sure we're in the middle of a tile
|
||||||
// JS version of Bangle.js's projection
|
// JS version of Bangle.js's projection
|
||||||
function bproject(lat, lon) {
|
function bproject(lat, lon) {
|
||||||
|
@ -353,10 +359,12 @@ TODO:
|
||||||
var ctx = canvas.getContext('2d');
|
var ctx = canvas.getContext('2d');
|
||||||
canvas.width = MAPSIZE;
|
canvas.width = MAPSIZE;
|
||||||
canvas.height = MAPSIZE;
|
canvas.height = MAPSIZE;
|
||||||
for (var i = 0; i < OSMTILECOUNT; i++) {
|
var tileMin = Math.round(-OSMTILECOUNT/2);
|
||||||
for (var j = 0; j < OSMTILECOUNT; j++) {
|
var tileMax = Math.round(OSMTILECOUNT/2);
|
||||||
|
for (var i = tileMin; i <= tileMax; i++) {
|
||||||
|
for (var j = tileMin; j <= tileMax; j++) {
|
||||||
(function(i,j){
|
(function(i,j){
|
||||||
var coords = new L.Point(center.x+i-1, center.y+j-1);
|
var coords = new L.Point(center.x+i, center.y+j);
|
||||||
coords.z = zoom;
|
coords.z = zoom;
|
||||||
var img = new Image();
|
var img = new Image();
|
||||||
img.crossOrigin = "Anonymous";
|
img.crossOrigin = "Anonymous";
|
||||||
|
@ -368,6 +376,8 @@ TODO:
|
||||||
ctx.fillRect(testPt.x-1, testPt.y-5, 3,10);
|
ctx.fillRect(testPt.x-1, testPt.y-5, 3,10);
|
||||||
ctx.fillRect(testPt.x-5, testPt.y-1, 10,3);
|
ctx.fillRect(testPt.x-5, testPt.y-1, 10,3);
|
||||||
}*/
|
}*/
|
||||||
|
/*ctx.fillStyle="black";
|
||||||
|
ctx.fillRect(i*OSMTILESIZE - ox, j*OSMTILESIZE - oy, 6,6);*/
|
||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
|
@ -395,6 +405,8 @@ TODO:
|
||||||
h : Math.round(canvas.height / TILESIZE), // height in tiles
|
h : Math.round(canvas.height / TILESIZE), // height in tiles
|
||||||
fn : mapImageFile
|
fn : mapImageFile
|
||||||
})});
|
})});
|
||||||
|
var mapSizeInK = Math.round(mapFiles.reduce((r,m)=>m.content.length+r,0)/1000);
|
||||||
|
document.getElementById("mapstats").innerText = "Size : "+ (mapSizeInK+"kb");
|
||||||
console.log(mapFiles);
|
console.log(mapFiles);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
0.01: New App!
|
0.01: New App!
|
||||||
|
0.02: Fix fast loading on swipe to clock
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
{
|
||||||
require("Font8x12").add(Graphics);
|
require("Font8x12").add(Graphics);
|
||||||
|
|
||||||
// load pinned apps from config
|
// load pinned apps from config
|
||||||
var settings = require("Storage").readJSON("qcenter.json", 1) || {};
|
let settings = require("Storage").readJSON("qcenter.json", 1) || {};
|
||||||
var pinnedApps = settings.pinnedApps || [];
|
let pinnedApps = settings.pinnedApps || [];
|
||||||
var exitGesture = settings.exitGesture || "swipeup";
|
let exitGesture = settings.exitGesture || "swipeup";
|
||||||
|
|
||||||
// if empty load a default set of apps as an example
|
// if empty load a default set of apps as an example
|
||||||
if (pinnedApps.length == 0) {
|
if (pinnedApps.length == 0) {
|
||||||
|
@ -14,12 +15,12 @@ if (pinnedApps.length == 0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// button drawing from Layout.js, edited to have completely custom button size with icon
|
// button drawing from Layout.js, edited to have completely custom button size with icon
|
||||||
function drawButton(l) {
|
let drawButton = function(l) {
|
||||||
var x = l.x + (0 | l.pad),
|
let x = l.x + (0 | l.pad),
|
||||||
y = l.y + (0 | l.pad),
|
y = l.y + (0 | l.pad),
|
||||||
w = l.w - (l.pad << 1),
|
w = l.w - (l.pad << 1),
|
||||||
h = l.h - (l.pad << 1);
|
h = l.h - (l.pad << 1);
|
||||||
var poly = [
|
let poly = [
|
||||||
x,
|
x,
|
||||||
y + 4,
|
y + 4,
|
||||||
x + 4,
|
x + 4,
|
||||||
|
@ -54,14 +55,14 @@ function drawButton(l) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// function to split array into group of 3, for button placement
|
// function to split array into group of 3, for button placement
|
||||||
function groupBy3(data) {
|
let groupBy3 = function(data) {
|
||||||
var result = [];
|
let result = [];
|
||||||
for (var i = 0; i < data.length; i += 3) result.push(data.slice(i, i + 3));
|
for (let i = 0; i < data.length; i += 3) result.push(data.slice(i, i + 3));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate object with buttons for apps by group of 3
|
// generate object with buttons for apps by group of 3
|
||||||
var appButtons = groupBy3(pinnedApps).map((appGroup, i) => {
|
let appButtons = groupBy3(pinnedApps).map((appGroup, i) => {
|
||||||
return appGroup.map((app, j) => {
|
return appGroup.map((app, j) => {
|
||||||
return {
|
return {
|
||||||
type: "custom",
|
type: "custom",
|
||||||
|
@ -71,13 +72,13 @@ var appButtons = groupBy3(pinnedApps).map((appGroup, i) => {
|
||||||
pad: 5,
|
pad: 5,
|
||||||
src: require("Storage").read(app.icon),
|
src: require("Storage").read(app.icon),
|
||||||
scale: 0.75,
|
scale: 0.75,
|
||||||
cb: (l) => Bangle.load(app.src),
|
cb: (l) => load(app.src),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// create basic layout content with status info and sensor status on top
|
// create basic layout content with status info and sensor status on top
|
||||||
var layoutContent = [
|
let layoutContent = [
|
||||||
{
|
{
|
||||||
type: "h",
|
type: "h",
|
||||||
pad: 5,
|
pad: 5,
|
||||||
|
@ -102,19 +103,27 @@ appButtons.forEach((appGroup) => {
|
||||||
|
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
|
|
||||||
var Layout = require("Layout");
|
let Layout = require("Layout");
|
||||||
var layout = new Layout({
|
let layout = new Layout({
|
||||||
type: "v",
|
type: "v",
|
||||||
c: layoutContent,
|
c: layoutContent
|
||||||
|
}, {
|
||||||
|
remove: ()=>{
|
||||||
|
Bangle.removeListener("swipe", onSwipe);
|
||||||
|
delete Graphics.prototype.setFont8x12;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
g.clear();
|
g.clear();
|
||||||
layout.render();
|
layout.render();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
// swipe event listener for exit gesture
|
// swipe event listener for exit gesture
|
||||||
Bangle.on("swipe", function (lr, ud) {
|
let onSwipe = function (lr, ud) {
|
||||||
if(exitGesture == "swipeup" && ud == -1) Bangle.showClock();
|
if(exitGesture == "swipeup" && ud == -1) Bangle.showClock();
|
||||||
if(exitGesture == "swipedown" && ud == 1) Bangle.showClock();
|
if(exitGesture == "swipedown" && ud == 1) Bangle.showClock();
|
||||||
if(exitGesture == "swipeleft" && lr == -1) Bangle.showClock();
|
if(exitGesture == "swipeleft" && lr == -1) Bangle.showClock();
|
||||||
if(exitGesture == "swiperight" && lr == 1) Bangle.showClock();
|
if(exitGesture == "swiperight" && lr == 1) Bangle.showClock();
|
||||||
});
|
}
|
||||||
|
|
||||||
|
Bangle.on("swipe", onSwipe);
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "qcenter",
|
"id": "qcenter",
|
||||||
"name": "Quick Center",
|
"name": "Quick Center",
|
||||||
"shortName": "QCenter",
|
"shortName": "QCenter",
|
||||||
"version": "0.01",
|
"version": "0.02",
|
||||||
"description": "An app for quickly launching your favourite apps, inspired by the control centres of other watches.",
|
"description": "An app for quickly launching your favourite apps, inspired by the control centres of other watches.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "",
|
"tags": "",
|
||||||
|
@ -13,5 +13,6 @@
|
||||||
{ "name": "qcenter.app.js", "url": "app.js" },
|
{ "name": "qcenter.app.js", "url": "app.js" },
|
||||||
{ "name": "qcenter.settings.js", "url": "settings.js" },
|
{ "name": "qcenter.settings.js", "url": "settings.js" },
|
||||||
{ "name": "qcenter.img", "url": "app-icon.js", "evaluate": true }
|
{ "name": "qcenter.img", "url": "app-icon.js", "evaluate": true }
|
||||||
]
|
],
|
||||||
|
"data": [{"name":"qcenter.json"}]
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,10 +41,7 @@
|
||||||
|
|
||||||
function showMainMenu() {
|
function showMainMenu() {
|
||||||
var mainmenu = {
|
var mainmenu = {
|
||||||
"": { title: "Quick Center" },
|
"": { title: "Quick Center", back: back},
|
||||||
"< Back": () => {
|
|
||||||
load();
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set exit gesture
|
// Set exit gesture
|
||||||
|
@ -56,10 +53,7 @@
|
||||||
pinnedApps.forEach((app, i) => {
|
pinnedApps.forEach((app, i) => {
|
||||||
mainmenu[app.name] = function () {
|
mainmenu[app.name] = function () {
|
||||||
E.showMenu({
|
E.showMenu({
|
||||||
"": { title: app.name },
|
"": { title: app.name, back: showMainMenu },
|
||||||
"< Back": () => {
|
|
||||||
showMainMenu();
|
|
||||||
},
|
|
||||||
"Unpin": () => {
|
"Unpin": () => {
|
||||||
pinnedApps.splice(i, 1);
|
pinnedApps.splice(i, 1);
|
||||||
save("pinnedApps", pinnedApps);
|
save("pinnedApps", pinnedApps);
|
||||||
|
@ -97,8 +91,7 @@
|
||||||
|
|
||||||
// menu for adding apps to the quick launch menu, listing all apps
|
// menu for adding apps to the quick launch menu, listing all apps
|
||||||
var pinAppMenu = {
|
var pinAppMenu = {
|
||||||
"": { title: "Add App" },
|
"": { title: "Add App", back: showMainMenu }
|
||||||
"< Back": showMainMenu,
|
|
||||||
};
|
};
|
||||||
apps.forEach((a) => {
|
apps.forEach((a) => {
|
||||||
pinAppMenu[a.name] = function () {
|
pinAppMenu[a.name] = function () {
|
||||||
|
@ -113,8 +106,7 @@
|
||||||
|
|
||||||
// menu for setting exit gesture
|
// menu for setting exit gesture
|
||||||
var exitGestureMenu = {
|
var exitGestureMenu = {
|
||||||
"": { title: "Exit Gesture" },
|
"": { title: "Exit Gesture", back: showMainMenu }
|
||||||
"< Back": showMainMenu,
|
|
||||||
};
|
};
|
||||||
exitGestureMenu["Swipe Up"] = function () {
|
exitGestureMenu["Swipe Up"] = function () {
|
||||||
exitGesture = "swipeup";
|
exitGesture = "swipeup";
|
||||||
|
|
|
@ -15,3 +15,4 @@
|
||||||
Fix wrong fallback for buzz pattern
|
Fix wrong fallback for buzz pattern
|
||||||
0.13: Ask to delete a timer after stopping it
|
0.13: Ask to delete a timer after stopping it
|
||||||
0.14: Added clkinfo for alarms and timers
|
0.14: Added clkinfo for alarms and timers
|
||||||
|
0.15: Automatic translation of some string in clkinfo
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
|
|
||||||
function getAlarmText(a){
|
function getAlarmText(a){
|
||||||
if(a.timer) {
|
if(a.timer) {
|
||||||
if(!a.on) return "off";
|
if(!a.on) return /*LANG*/"off";
|
||||||
let time = Math.round(alarm.getTimeToAlarm(a)/(60*1000));
|
let time = Math.round(alarm.getTimeToAlarm(a)/(60*1000));
|
||||||
if(time > 60)
|
if(time > 60)
|
||||||
time = Math.round(time / 60) + "h";
|
time = Math.round(time / 60) + "h";
|
||||||
|
@ -52,7 +52,7 @@
|
||||||
var img = iconAlarmOn;
|
var img = iconAlarmOn;
|
||||||
//get only alarms not created by other apps
|
//get only alarms not created by other apps
|
||||||
var alarmItems = {
|
var alarmItems = {
|
||||||
name: "Alarms",
|
name: /*LANG*/"Alarms",
|
||||||
img: img,
|
img: img,
|
||||||
dynamic: true,
|
dynamic: true,
|
||||||
items: alarm.getAlarms().filter(a=>!a.appid)
|
items: alarm.getAlarms().filter(a=>!a.appid)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "sched",
|
"id": "sched",
|
||||||
"name": "Scheduler",
|
"name": "Scheduler",
|
||||||
"version": "0.14",
|
"version": "0.15",
|
||||||
"description": "Scheduling library for alarms and timers",
|
"description": "Scheduling library for alarms and timers",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "scheduler",
|
"type": "scheduler",
|
||||||
|
|
|
@ -59,3 +59,4 @@
|
||||||
0.52: Add option for left-handed users
|
0.52: Add option for left-handed users
|
||||||
0.53: Ensure that when clock is set, clockHasWidgets is set correctly too
|
0.53: Ensure that when clock is set, clockHasWidgets is set correctly too
|
||||||
0.54: If setting.json is corrupt, ensure it gets re-written
|
0.54: If setting.json is corrupt, ensure it gets re-written
|
||||||
|
0.55: More strings tagged for automatic translation.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "setting",
|
"id": "setting",
|
||||||
"name": "Settings",
|
"name": "Settings",
|
||||||
"version": "0.54",
|
"version": "0.55",
|
||||||
"description": "A menu for setting up Bangle.js",
|
"description": "A menu for setting up Bangle.js",
|
||||||
"icon": "settings.png",
|
"icon": "settings.png",
|
||||||
"tags": "tool,system",
|
"tags": "tool,system",
|
||||||
|
|
|
@ -146,7 +146,7 @@ function showAlertsMenu() {
|
||||||
},
|
},
|
||||||
/*LANG*/"Quiet Mode": {
|
/*LANG*/"Quiet Mode": {
|
||||||
value: settings.quiet|0,
|
value: settings.quiet|0,
|
||||||
format: v => ["Off", "Alarms", "Silent"][v%3],
|
format: v => [/*LANG*/"Off", /*LANG*/"Alarms", /*LANG*/"Silent"][v%3],
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.quiet = v%3;
|
settings.quiet = v%3;
|
||||||
updateSettings();
|
updateSettings();
|
||||||
|
@ -162,9 +162,9 @@ function showAlertsMenu() {
|
||||||
|
|
||||||
function showBLEMenu() {
|
function showBLEMenu() {
|
||||||
var hidV = [false, "kbmedia", "kb", "com", "joy"];
|
var hidV = [false, "kbmedia", "kb", "com", "joy"];
|
||||||
var hidN = ["Off", "Kbrd & Media", "Kbrd", "Kbrd & Mouse" ,"Joystick"];
|
var hidN = [/*LANG*/"Off", /*LANG*/"Kbrd & Media", /*LANG*/"Kbrd", /*LANG*/"Kbrd & Mouse", /*LANG*/"Joystick"];
|
||||||
E.showMenu({
|
E.showMenu({
|
||||||
'': { 'title': 'Bluetooth' },
|
'': { 'title': /*LANG*/'Bluetooth' },
|
||||||
'< Back': ()=>showMainMenu(),
|
'< Back': ()=>showMainMenu(),
|
||||||
/*LANG*/'Make Connectable': ()=>makeConnectable(),
|
/*LANG*/'Make Connectable': ()=>makeConnectable(),
|
||||||
/*LANG*/'BLE': {
|
/*LANG*/'BLE': {
|
||||||
|
@ -193,11 +193,11 @@ function showBLEMenu() {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/*LANG*/'Passkey BETA': {
|
/*LANG*/'Passkey BETA': {
|
||||||
value: settings.passkey?settings.passkey:"none",
|
value: settings.passkey?settings.passkey:/*LANG*/"none",
|
||||||
onchange: () => setTimeout(showPasskeyMenu) // graphical_menu redraws after the call
|
onchange: () => setTimeout(showPasskeyMenu) // graphical_menu redraws after the call
|
||||||
},
|
},
|
||||||
/*LANG*/'Whitelist': {
|
/*LANG*/'Whitelist': {
|
||||||
value: settings.whitelist?(settings.whitelist.length+" devs"):"off",
|
value: settings.whitelist?(settings.whitelist.length+/*LANG*/" devs"):/*LANG*/"off",
|
||||||
onchange: () => setTimeout(showWhitelistMenu) // graphical_menu redraws after the call
|
onchange: () => setTimeout(showWhitelistMenu) // graphical_menu redraws after the call
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -606,7 +606,7 @@ function showUtilMenu() {
|
||||||
menu[/*LANG*/'Reset Settings'] = () => {
|
menu[/*LANG*/'Reset Settings'] = () => {
|
||||||
E.showPrompt(/*LANG*/'Reset to Defaults?',{title:/*LANG*/"Settings"}).then((v) => {
|
E.showPrompt(/*LANG*/'Reset to Defaults?',{title:/*LANG*/"Settings"}).then((v) => {
|
||||||
if (v) {
|
if (v) {
|
||||||
E.showMessage('Resetting');
|
E.showMessage(/*LANG*/'Resetting');
|
||||||
resetSettings();
|
resetSettings();
|
||||||
setTimeout(showMainMenu, 50);
|
setTimeout(showMainMenu, 50);
|
||||||
} else showUtilMenu();
|
} else showUtilMenu();
|
||||||
|
@ -824,6 +824,7 @@ function showAppSettings(app) {
|
||||||
|
|
||||||
function showTouchscreenCalibration() {
|
function showTouchscreenCalibration() {
|
||||||
Bangle.setUI();
|
Bangle.setUI();
|
||||||
|
require('widget_utils').hide();
|
||||||
// disable touchscreen calibration (passed coords right through)
|
// disable touchscreen calibration (passed coords right through)
|
||||||
Bangle.setOptions({touchX1: 0, touchY1: 0, touchX2: g.getWidth(), touchY2: g.getHeight() });
|
Bangle.setOptions({touchX1: 0, touchY1: 0, touchX2: g.getWidth(), touchY2: g.getHeight() });
|
||||||
|
|
||||||
|
@ -847,7 +848,7 @@ function showTouchscreenCalibration() {
|
||||||
g.drawLine(spot[0],spot[1]-32,spot[0],spot[1]+32);
|
g.drawLine(spot[0],spot[1]-32,spot[0],spot[1]+32);
|
||||||
g.drawCircle(spot[0],spot[1], 16);
|
g.drawCircle(spot[0],spot[1], 16);
|
||||||
var tapsLeft = (1-currentTry)*4+(4-currentCorner);
|
var tapsLeft = (1-currentTry)*4+(4-currentCorner);
|
||||||
g.setFont("6x8:2").setFontAlign(0,0).drawString(tapsLeft+" taps\nto go", g.getWidth()/2, g.getHeight()/2);
|
g.setFont("6x8:2").setFontAlign(0,0).drawString(tapsLeft+/*LANG*/" taps\nto go", g.getWidth()/2, g.getHeight()/2);
|
||||||
}
|
}
|
||||||
|
|
||||||
function calcCalibration() {
|
function calcCalibration() {
|
||||||
|
@ -870,7 +871,7 @@ function showTouchscreenCalibration() {
|
||||||
var s = storage.readJSON("setting.json",1)||{};
|
var s = storage.readJSON("setting.json",1)||{};
|
||||||
s.touch = calib;
|
s.touch = calib;
|
||||||
storage.writeJSON("setting.json",s);
|
storage.writeJSON("setting.json",s);
|
||||||
g.setFont("6x8:2").setFontAlign(0,0).drawString("Calibrated!", g.getWidth()/2, g.getHeight()/2);
|
g.setFont("6x8:2").setFontAlign(0,0).drawString(/*LANG*/"Calibrated!", g.getWidth()/2, g.getHeight()/2);
|
||||||
// now load the main menu again
|
// now load the main menu again
|
||||||
setTimeout(showLCDMenu, 500);
|
setTimeout(showLCDMenu, 500);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,3 +7,4 @@
|
||||||
0.10: Complete rework off this app!
|
0.10: Complete rework off this app!
|
||||||
0.10beta: Add interface.html to view saved log data, add "View log" function for debugging log, send data for gadgetbridge, change caching for global getStats
|
0.10beta: Add interface.html to view saved log data, add "View log" function for debugging log, send data for gadgetbridge, change caching for global getStats
|
||||||
0.11: Prevent module not found error
|
0.11: Prevent module not found error
|
||||||
|
0.12: Improve README, option to add functions triggered by status changes or time periods, remove old log (<0.10) conversion
|
|
@ -6,6 +6,14 @@ This app logs and displays the following states:
|
||||||
|
|
||||||
It is using the built in movement calculation to decide your sleeping state. While charging it is assumed that you are not wearing the watch and if the status changes to _deep sleep_ the internal heartrate sensor is used to detect if you are wearing the watch.
|
It is using the built in movement calculation to decide your sleeping state. While charging it is assumed that you are not wearing the watch and if the status changes to _deep sleep_ the internal heartrate sensor is used to detect if you are wearing the watch.
|
||||||
|
|
||||||
|
#### Explanations
|
||||||
|
* __Detection of Sleep__
|
||||||
|
The movement value of bangle's build in health event that is triggered every 10 minutes is checked against the thresholds for light and deep sleep. If the measured movement is lower or equal to the __Deep Sleep__-threshold a deep sleep phase is detected for the last 10 minutes. If the threshold is exceeded but not the __Light Sleep__-threshold than the last timeperiod is detected as light sleep phase. On exceeding even this threshold it is assumed that you were awake.
|
||||||
|
* __True Sleep__
|
||||||
|
The true sleep value is a simple addition of all registered sleeping periods.
|
||||||
|
* __Consecutive Sleep__
|
||||||
|
In addition the consecutive sleep value tries to predict the complete time you were asleep, even the very light sleeping periods when an awake period is detected based on the registered movements. All periods after a sleeping period will be summarized until the first following non sleeping period that is longer then the maximal awake duration (__Max Awake__). If this sum is lower than the minimal consecutive sleep duration (__Min Consecutive__) it is not considered, otherwise it will be added to the consecutive sleep value.
|
||||||
|
|
||||||
Logfiles are not removed on un-/reinstall to prevent data loss.
|
Logfiles are not removed on un-/reinstall to prevent data loss.
|
||||||
|
|
||||||
| Filename (* _example_) | Content | Removeable in |
|
| Filename (* _example_) | Content | Removeable in |
|
||||||
|
@ -16,10 +24,10 @@ Logfiles are not removed on un-/reinstall to prevent data loss.
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
### App Usage
|
### Main App Usage
|
||||||
---
|
---
|
||||||
|
|
||||||
#### On the main app screen:
|
#### Controls:
|
||||||
- __swipe left & right__
|
- __swipe left & right__
|
||||||
to change the displayed day
|
to change the displayed day
|
||||||
- __touch the "title"__ (e.g. `Night to Fri 20/05/2022`)
|
- __touch the "title"__ (e.g. `Night to Fri 20/05/2022`)
|
||||||
|
@ -32,7 +40,21 @@ Logfiles are not removed on un-/reinstall to prevent data loss.
|
||||||
- __use back button widget__ (upper left corner)
|
- __use back button widget__ (upper left corner)
|
||||||
exit the app
|
exit the app
|
||||||
|
|
||||||
#### Inside the settings:
|
#### View:
|
||||||
|
| Status | Color | Height |
|
||||||
|
|-------------|:------:|----------:|
|
||||||
|
| unknown | black | 0% |
|
||||||
|
| not worn | red | 40% |
|
||||||
|
| awake | green | 60% |
|
||||||
|
| light sleep | cyan | 80% |
|
||||||
|
| deep sleep | blue | 100% |
|
||||||
|
| consecutive | violet | as status |
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
### Settings Usage
|
||||||
|
---
|
||||||
|
|
||||||
- __Thresholds__ submenu
|
- __Thresholds__ submenu
|
||||||
Changes take effect from now on, not retrospective!
|
Changes take effect from now on, not retrospective!
|
||||||
- __Max Awake__ | maximal awake duration
|
- __Max Awake__ | maximal awake duration
|
||||||
|
@ -87,7 +109,7 @@ Available through the App Loader when your watch is connected.
|
||||||
Deletes the logfile from the watch. __Please backup your data first!__
|
Deletes the logfile from the watch. __Please backup your data first!__
|
||||||
|
|
||||||
---
|
---
|
||||||
### Timestamps and files
|
### Timestamps and Files
|
||||||
---
|
---
|
||||||
|
|
||||||
1. externally visible/usable timestamps (in `global.sleeplog`) are formatted as Bangle timestamps:
|
1. externally visible/usable timestamps (in `global.sleeplog`) are formatted as Bangle timestamps:
|
||||||
|
@ -110,8 +132,10 @@ Available through the App Loader when your watch is connected.
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
### Access statistics (developer information)
|
### Developer Information
|
||||||
---
|
---
|
||||||
|
|
||||||
|
#### Access statistics
|
||||||
- Last Asleep Time [Date]:
|
- Last Asleep Time [Date]:
|
||||||
`Date(sleeplog.awakeSince)`
|
`Date(sleeplog.awakeSince)`
|
||||||
- Last Awake Duration [ms]:
|
- Last Awake Duration [ms]:
|
||||||
|
@ -149,6 +173,31 @@ Available through the App Loader when your watch is connected.
|
||||||
require("sleeplog").getStats(0, 0, require("sleeplog").readLog());
|
require("sleeplog").getStats(0, 0, require("sleeplog").readLog());
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Add functions triggered by status changes or inside a specified time period
|
||||||
|
With the following code it is possible to add functions that will be called every 10 minutes after new movement data when meeting the specified parameters on each :
|
||||||
|
```
|
||||||
|
// first ensure that the sleeplog trigger object is available (sleeplog is enabled)
|
||||||
|
if (typeof (global.sleeplog || {}).trigger === "object") {
|
||||||
|
// then add your parameters with the function to call as object into the trigger object
|
||||||
|
sleeplog.trigger["my app name"] = {
|
||||||
|
onChange: false, // false as default, if true call fn only on a status change
|
||||||
|
from: 0, // 0 as default, in ms, first time fn will be called
|
||||||
|
to: 24*60*60*1000, // 24h as default, in ms, last time fn will be called
|
||||||
|
// reference time to from & to is rounded to full minutes
|
||||||
|
fn: function(data) { print(data); } // function to be executed
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
The passed data object has the following properties:
|
||||||
|
- timestamp: of the status change as date object,
|
||||||
|
(should be around 10min. before "now", the actual call of the function)
|
||||||
|
- status: value of the new status (0-4),
|
||||||
|
(0 = unknown, 1 = not worn, 2 = awake, 3 = light sleep, 4 = deep sleep)
|
||||||
|
- consecutive: value of the new status (0-2),
|
||||||
|
(0 = unknown, 1 = no consecutive sleep, 2 = consecutive sleep)
|
||||||
|
- prevStatus: if changed the value of the previous status (0-4) else undefined,
|
||||||
|
- prevConsecutive: if changed the value of the previous status (0-2) else undefined
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
### Worth Mentioning
|
### Worth Mentioning
|
||||||
|
@ -156,14 +205,14 @@ Available through the App Loader when your watch is connected.
|
||||||
#### To do list
|
#### To do list
|
||||||
- Check translations.
|
- Check translations.
|
||||||
- Add more functionallities to interface.html.
|
- Add more functionallities to interface.html.
|
||||||
- Enable recieving data on the Gadgetbridge side + testing.
|
- Enable receiving data on the Gadgetbridge side + testing.
|
||||||
__Help appreciated!__
|
__Help appreciated!__
|
||||||
|
|
||||||
#### Requests, Bugs and Feedback
|
#### Requests, Bugs and Feedback
|
||||||
Please leave requests and bug reports by raising an issue at [github.com/storm64/BangleApps](https://github.com/storm64/BangleApps) (or send me a [mail](mailto:banglejs@storm64.de)).
|
Please leave requests and bug reports by raising an issue at [github.com/storm64/BangleApps](https://github.com/storm64/BangleApps) (or send me a [mail](mailto:banglejs@storm64.de)).
|
||||||
|
|
||||||
#### Creator
|
#### Creator
|
||||||
Storm64 ([Mail](mailto:banglejs@storm64.de), [github](https://github.com/storm64))
|
Storm64 ([mail](mailto:banglejs@storm64.de), [github](https://github.com/storm64))
|
||||||
|
|
||||||
#### Contributors
|
#### Contributors
|
||||||
myxor ([github](https://github.com/myxor))
|
myxor ([github](https://github.com/myxor))
|
||||||
|
|
|
@ -253,11 +253,38 @@ if (sleeplog.conf.enabled) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if the status has changed
|
||||||
|
var changed = data.status !== this.status || data.consecutive !== this.consecutive;
|
||||||
|
|
||||||
|
// read and check trigger entries
|
||||||
|
var triggers = Object.keys(this.trigger) || [];
|
||||||
|
if (triggers.length) {
|
||||||
|
// calculate time from timestamp in ms on full minutes
|
||||||
|
var time = new Date();
|
||||||
|
time = (time.getHours() * 60 + time.getMinutes()) * 60 * 1000;
|
||||||
|
// go through all triggers
|
||||||
|
triggers.forEach(key => {
|
||||||
|
// read entry to key
|
||||||
|
var entry = this.trigger[key];
|
||||||
|
// check if the event matches the entries requirements
|
||||||
|
if (typeof entry.fn === "function" && (changed || !entry.onChange) &&
|
||||||
|
(entry.from || 0) <= time && (entry.to || 24 * 60 * 60 * 1000) >= time)
|
||||||
|
// and call afterwards with status data
|
||||||
|
setTimeout(entry.fn, 100, {
|
||||||
|
timestamp: new Date(data.timestamp),
|
||||||
|
status: data.status,
|
||||||
|
consecutive: data.consecutive,
|
||||||
|
prevStatus: data.status === this.status ? undefined : this.status,
|
||||||
|
prevConsecutive: data.consecutive === this.consecutive ? undefined : this.consecutive
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// cache change into a known consecutive state
|
// cache change into a known consecutive state
|
||||||
var changeIntoConsec = data.consecutive;
|
var changeIntoConsec = data.consecutive;
|
||||||
|
|
||||||
// check if the status has changed
|
// actions on a status change
|
||||||
if (data.status !== this.status || data.consecutive !== this.consecutive) {
|
if (changed) {
|
||||||
// append status
|
// append status
|
||||||
this.appendStatus(data.timestamp, data.status, data.consecutive);
|
this.appendStatus(data.timestamp, data.status, data.consecutive);
|
||||||
|
|
||||||
|
@ -319,7 +346,10 @@ if (sleeplog.conf.enabled) {
|
||||||
}
|
}
|
||||||
// return stats cache
|
// return stats cache
|
||||||
return this.statsCache;
|
return this.statsCache;
|
||||||
}
|
},
|
||||||
|
|
||||||
|
// define trigger object
|
||||||
|
trigger: {}
|
||||||
}, sleeplog);
|
}, sleeplog);
|
||||||
|
|
||||||
// initial starting
|
// initial starting
|
||||||
|
|
|
@ -149,14 +149,6 @@ exports = {
|
||||||
|
|
||||||
// define move log function, move StorageFile content into files seperated by fortnights
|
// define move log function, move StorageFile content into files seperated by fortnights
|
||||||
moveLog: function(force) {
|
moveLog: function(force) {
|
||||||
/** convert old logfile (< v0.10) if present **/
|
|
||||||
if (require("Storage").list("sleeplog.log", {
|
|
||||||
sf: false
|
|
||||||
}).length) {
|
|
||||||
convertOldLog();
|
|
||||||
}
|
|
||||||
/** may be removed in later versions **/
|
|
||||||
|
|
||||||
// first day of this fortnight period
|
// first day of this fortnight period
|
||||||
var thisFirstDay = this.fnToMs(this.msToFn(Date.now()));
|
var thisFirstDay = this.fnToMs(this.msToFn(Date.now()));
|
||||||
|
|
||||||
|
@ -384,82 +376,5 @@ exports = {
|
||||||
"unknown,not worn,awake,light sleep,deep sleep".split(",")[entry[1]].padEnd(12) +
|
"unknown,not worn,awake,light sleep,deep sleep".split(",")[entry[1]].padEnd(12) +
|
||||||
"for" + (duration + "min").padStart(8));
|
"for" + (duration + "min").padStart(8));
|
||||||
});
|
});
|
||||||
},
|
|
||||||
|
|
||||||
/** convert old (< v0.10) to new logfile data **/
|
|
||||||
convertOldLog: function() {
|
|
||||||
// read old logfile
|
|
||||||
var oldLog = require("Storage").read("sleeplog.log") || "";
|
|
||||||
// decode data if needed
|
|
||||||
if (!oldLog.startsWith("[")) oldLog = atob(oldLog);
|
|
||||||
// delete old logfile and return if it is empty or corrupted
|
|
||||||
if (!oldLog.startsWith("[[") || !oldLog.endsWith("]]")) {
|
|
||||||
require("Storage").erase("sleeplog.log");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// transform into StorageFile and clear oldLog to have more free ram accessable
|
|
||||||
require("Storage").open("sleeplog_old.log", "w").write(JSON.parse(oldLog).reverse().join("\n"));
|
|
||||||
oldLog = undefined;
|
|
||||||
|
|
||||||
// calculate fortnight from now
|
|
||||||
var fnOfNow = this.msToFn(Date.now());
|
|
||||||
|
|
||||||
// open StorageFile with old log data
|
|
||||||
var file = require("Storage").open("sleeplog_old.log", "r");
|
|
||||||
// define active fortnight and file cache
|
|
||||||
var activeFn = true;
|
|
||||||
var fileCache = [];
|
|
||||||
// loop through StorageFile entries
|
|
||||||
while (activeFn) {
|
|
||||||
// define fortnight for this entry
|
|
||||||
var thisFn = false;
|
|
||||||
// cache new line
|
|
||||||
var line = file.readLine();
|
|
||||||
// check if line is filled
|
|
||||||
if (line) {
|
|
||||||
// parse line
|
|
||||||
line = line.substr(0, 15).split(",").map(e => parseInt(e));
|
|
||||||
// calculate fortnight for this entry
|
|
||||||
thisFn = this.msToFn(line[0]);
|
|
||||||
// convert timestamp into 10min steps
|
|
||||||
line[0] = line[0] / 6E5 | 0;
|
|
||||||
// set consecutive to unknown
|
|
||||||
line.push(0);
|
|
||||||
}
|
|
||||||
// check if active fortnight and file cache is set, fortnight has changed and
|
|
||||||
// active fortnight is not fortnight from now
|
|
||||||
if (activeFn && fileCache.length && activeFn !== thisFn && activeFn !== fnOfNow) {
|
|
||||||
// write file cache into new file according to fortnight
|
|
||||||
require("Storage").writeJSON("sleeplog_" + activeFn + ".log", fileCache);
|
|
||||||
// clear file cache
|
|
||||||
fileCache = [];
|
|
||||||
}
|
|
||||||
// add line to file cache if it is filled
|
|
||||||
if (line) fileCache.push(line);
|
|
||||||
// set active fortnight
|
|
||||||
activeFn = thisFn;
|
|
||||||
}
|
|
||||||
// check if entries are leftover
|
|
||||||
if (fileCache.length) {
|
|
||||||
// format fileCache entries into a string
|
|
||||||
fileCache = fileCache.map(e => e.join(",")).join("\n");
|
|
||||||
// read complete new log StorageFile as string
|
|
||||||
file = require("Storage").open("sleeplog.log", "r");
|
|
||||||
var newLogString = file.read(file.getLength());
|
|
||||||
// add entries at the beginning of the new log string
|
|
||||||
newLogString = fileCache + "\n" + newLogString;
|
|
||||||
// rewrite new log StorageFile
|
|
||||||
require("Storage").open("sleeplog.log", "w").write(newLogString);
|
|
||||||
}
|
|
||||||
|
|
||||||
// free ram
|
|
||||||
file = undefined;
|
|
||||||
fileCache = undefined;
|
|
||||||
|
|
||||||
// clean up old files
|
|
||||||
require("Storage").erase("sleeplog.log");
|
|
||||||
require("Storage").open("sleeplog_old.log", "w").erase();
|
|
||||||
}
|
|
||||||
/** may be removed in later versions **/
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id":"sleeplog",
|
"id":"sleeplog",
|
||||||
"name":"Sleep Log",
|
"name":"Sleep Log",
|
||||||
"shortName": "SleepLog",
|
"shortName": "SleepLog",
|
||||||
"version": "0.11",
|
"version": "0.12",
|
||||||
"description": "Log and view your sleeping habits. This app is using the built in movement calculation.",
|
"description": "Log and view your sleeping habits. This app is using the built in movement calculation.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "app",
|
"type": "app",
|
||||||
|
|
|
@ -11,3 +11,4 @@
|
||||||
Add setting to defer start of algorithm
|
Add setting to defer start of algorithm
|
||||||
Add setting to disable scheduler alarm
|
Add setting to disable scheduler alarm
|
||||||
0.10: Fix: Do not wake when falling asleep
|
0.10: Fix: Do not wake when falling asleep
|
||||||
|
0.11: Minor tweaks
|
||||||
|
|
|
@ -21,8 +21,8 @@ let logs = [];
|
||||||
//
|
//
|
||||||
// Function needs to be called for every measurement but returns a value at maximum once a second (see winwidth)
|
// Function needs to be called for every measurement but returns a value at maximum once a second (see winwidth)
|
||||||
// start of sleep marker is delayed by sleepthresh due to continous data reading
|
// start of sleep marker is delayed by sleepthresh due to continous data reading
|
||||||
const winwidth=13;
|
const winwidth=13; // Actually 12.5 Hz, rounded
|
||||||
const nomothresh=0.03; // 0.006 was working on Bangle1, but Bangle2 has higher noise.
|
const nomothresh=0.023; // Original implementation: 6, resolution 11 bit, scale +-4G = 6/(2^(11-1))*4 = 0.023438 in G
|
||||||
const sleepthresh=600;
|
const sleepthresh=600;
|
||||||
var ess_values = [];
|
var ess_values = [];
|
||||||
var slsnds = 0;
|
var slsnds = 0;
|
||||||
|
@ -69,6 +69,9 @@ active.forEach(alarm => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const LABEL_ETA = /*LANG*/"ETA";
|
||||||
|
const LABEL_WAKEUP_TIME = /*LANG*/"Alarm at";
|
||||||
|
|
||||||
var layout = new Layout({
|
var layout = new Layout({
|
||||||
type:"v", c: [
|
type:"v", c: [
|
||||||
{type:"txt", font:"10%", label:"Sleep Phase Alarm", bgCol:g.theme.bgH, fillx: true, height:Bangle.appRect.h/6},
|
{type:"txt", font:"10%", label:"Sleep Phase Alarm", bgCol:g.theme.bgH, fillx: true, height:Bangle.appRect.h/6},
|
||||||
|
@ -84,7 +87,7 @@ function drawApp() {
|
||||||
var alarmMinute = nextAlarmDate.getMinutes();
|
var alarmMinute = nextAlarmDate.getMinutes();
|
||||||
if (alarmHour < 10) alarmHour = "0" + alarmHour;
|
if (alarmHour < 10) alarmHour = "0" + alarmHour;
|
||||||
if (alarmMinute < 10) alarmMinute = "0" + alarmMinute;
|
if (alarmMinute < 10) alarmMinute = "0" + alarmMinute;
|
||||||
layout.alarm_date.label = "Alarm at " + alarmHour + ":" + alarmMinute;
|
layout.alarm_date.label = `${LABEL_WAKEUP_TIME}: ${alarmHour}:${alarmMinute}`;
|
||||||
layout.render();
|
layout.render();
|
||||||
|
|
||||||
function drawTime() {
|
function drawTime() {
|
||||||
|
@ -94,7 +97,7 @@ function drawApp() {
|
||||||
const diff = nextAlarmDate - now;
|
const diff = nextAlarmDate - now;
|
||||||
const diffHour = Math.floor((diff % 86400000) / 3600000).toString();
|
const diffHour = Math.floor((diff % 86400000) / 3600000).toString();
|
||||||
const diffMinutes = Math.floor(((diff % 86400000) % 3600000) / 60000).toString();
|
const diffMinutes = Math.floor(((diff % 86400000) % 3600000) / 60000).toString();
|
||||||
layout.eta.label = "ETA: -"+ diffHour + ":" + diffMinutes.padStart(2, '0');
|
layout.eta.label = `${LABEL_ETA}: ${diffHour}:${diffMinutes.padStart(2, '0')}`;
|
||||||
layout.render();
|
layout.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +142,7 @@ if (nextAlarmDate !== undefined) {
|
||||||
// minimum alert 30 minutes early
|
// minimum alert 30 minutes early
|
||||||
minAlarm.setTime(nextAlarmDate.getTime() - (30*60*1000));
|
minAlarm.setTime(nextAlarmDate.getTime() - (30*60*1000));
|
||||||
run = () => {
|
run = () => {
|
||||||
layout.state.label = "Start";
|
layout.state.label = /*LANG*/"Start";
|
||||||
layout.render();
|
layout.render();
|
||||||
Bangle.setOptions({powerSave: false}); // do not dynamically change accelerometer poll interval
|
Bangle.setOptions({powerSave: false}); // do not dynamically change accelerometer poll interval
|
||||||
Bangle.setPollInterval(80); // 12.5Hz
|
Bangle.setPollInterval(80); // 12.5Hz
|
||||||
|
@ -150,7 +153,7 @@ if (nextAlarmDate !== undefined) {
|
||||||
|
|
||||||
if (swest !== undefined) {
|
if (swest !== undefined) {
|
||||||
if (Bangle.isLCDOn()) {
|
if (Bangle.isLCDOn()) {
|
||||||
layout.state.label = swest ? "Sleep" : "Awake";
|
layout.state.label = swest ? /*LANG*/"Sleep" : /*LANG*/"Awake";
|
||||||
layout.render();
|
layout.render();
|
||||||
}
|
}
|
||||||
// log
|
// log
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "sleepphasealarm",
|
"id": "sleepphasealarm",
|
||||||
"name": "SleepPhaseAlarm",
|
"name": "SleepPhaseAlarm",
|
||||||
"shortName": "SleepPhaseAlarm",
|
"shortName": "SleepPhaseAlarm",
|
||||||
"version": "0.10",
|
"version": "0.11",
|
||||||
"description": "Uses the accelerometer to estimate sleep and wake states with the principle of Estimation of Stationary Sleep-segments (ESS, see https://ubicomp.eti.uni-siegen.de/home/datasets/ichi14/index.html.en). This app will read the next alarm from the alarm application and will wake you up to 30 minutes early at the best guessed time when you are almost already awake.",
|
"description": "Uses the accelerometer to estimate sleep and wake states with the principle of Estimation of Stationary Sleep-segments (ESS, see https://ubicomp.eti.uni-siegen.de/home/datasets/ichi14/index.html.en). This app will read the next alarm from the alarm application and will wake you up to 30 minutes early at the best guessed time when you are almost already awake.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "alarm",
|
"tags": "alarm",
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
0.01: Initial creation of "Swipe back to the Clock" App. Let's you swipe from left to right on any app to return back to the clock face.
|
0.01: Initial creation of "Swipe back to the Clock" App. Let's you swipe from left to right on any app to return back to the clock face.
|
||||||
0.02: Fix deleting from white and black lists.
|
0.02: Fix deleting from white and black lists.
|
||||||
0.03: Adapt to availability of Bangle.showClock and Bangle.load
|
0.03: Adapt to availability of Bangle.showClock and Bangle.load
|
||||||
|
0.04: Fix 'Uncaught ReferenceError: "__FILE__" is not defined' error (fix #2326)
|
||||||
|
|
|
@ -29,12 +29,13 @@
|
||||||
})(Bangle.load);
|
})(Bangle.load);
|
||||||
|
|
||||||
let swipeHandler = (dir) => {
|
let swipeHandler = (dir) => {
|
||||||
log("swipe:" + dir + " on app: " + __FILE__);
|
let currentFile = global.__FILE__||"default";
|
||||||
|
log("swipe:" + dir + " on app: " + currentFile);
|
||||||
|
|
||||||
if (!inhibit && dir === 1 && !Bangle.CLOCK && __FILE__ != ".bootcde") {
|
if (!inhibit && dir === 1 && !Bangle.CLOCK) {
|
||||||
log("on a not clock app " + __FILE__);
|
log("on a not clock app " + currentFile);
|
||||||
if ((settings.mode === 1 && settings.whiteList.includes(__FILE__)) || // "White List"
|
if ((settings.mode === 1 && settings.whiteList.includes(currentFile)) || // "White List"
|
||||||
(settings.mode === 2 && !settings.blackList.includes(__FILE__)) || // "Black List"
|
(settings.mode === 2 && !settings.blackList.includes(currentFile)) || // "Black List"
|
||||||
settings.mode === 3) { // "Always"
|
settings.mode === 3) { // "Always"
|
||||||
log("load clock");
|
log("load clock");
|
||||||
Bangle.showClock();
|
Bangle.showClock();
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "swp2clk",
|
"id": "swp2clk",
|
||||||
"name": "Swipe back to the Clock",
|
"name": "Swipe back to the Clock",
|
||||||
"shortName": "Swipe to Clock",
|
"shortName": "Swipe to Clock",
|
||||||
"version": "0.03",
|
"version": "0.04",
|
||||||
"description": "Let's you swipe from left to right on any app to return back to the clock face. Please configure in the settings app after installing to activate, since its disabled by default.",
|
"description": "Let's you swipe from left to right on any app to return back to the clock face. Please configure in the settings app after installing to activate, since its disabled by default.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "bootloader",
|
"type": "bootloader",
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
0.01: New App!
|
0.01: New App!
|
||||||
|
0.02: Use Bangle.showClock for changing to clock (Backport from launch)
|
||||||
|
|
|
@ -69,16 +69,6 @@ let tagKeys = Object.keys(tags).filter(tag => tag !== "clock" || settings.showCl
|
||||||
if (!settings.fullscreen)
|
if (!settings.fullscreen)
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
|
|
||||||
let returnToClock = function() {
|
|
||||||
// unload everything manually
|
|
||||||
// ... or we could just call `load();` but it will be slower
|
|
||||||
Bangle.setUI(); // remove scroller's handling
|
|
||||||
if (lockTimeout) clearTimeout(lockTimeout);
|
|
||||||
Bangle.removeListener("lock", lockHandler);
|
|
||||||
// now load the default clock - just call .bootcde as this has the code already
|
|
||||||
setTimeout(eval,0,s.read(".bootcde"));
|
|
||||||
};
|
|
||||||
|
|
||||||
let showTagMenu = (tag) => {
|
let showTagMenu = (tag) => {
|
||||||
E.showScroller({
|
E.showScroller({
|
||||||
h : 64*scaleval, c : appsByTag[tag].length,
|
h : 64*scaleval, c : appsByTag[tag].length,
|
||||||
|
@ -121,7 +111,12 @@ let showMainMenu = () => {
|
||||||
let tag = tagKeys[i];
|
let tag = tagKeys[i];
|
||||||
showTagMenu(tag);
|
showTagMenu(tag);
|
||||||
},
|
},
|
||||||
back : returnToClock // button press or tap in top left calls returnToClock now
|
back : Bangle.showClock, // button press or tap in top left shows clock now
|
||||||
|
remove : () => {
|
||||||
|
// cleanup the timeout to not leave anything behind after being removed from ram
|
||||||
|
if (lockTimeout) clearTimeout(lockTimeout);
|
||||||
|
Bangle.removeListener("lock", lockHandler);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
showMainMenu();
|
showMainMenu();
|
||||||
|
@ -134,7 +129,7 @@ let lockHandler = function(locked) {
|
||||||
if (lockTimeout) clearTimeout(lockTimeout);
|
if (lockTimeout) clearTimeout(lockTimeout);
|
||||||
lockTimeout = undefined;
|
lockTimeout = undefined;
|
||||||
if (locked) {
|
if (locked) {
|
||||||
lockTimeout = setTimeout(returnToClock, 10000);
|
lockTimeout = setTimeout(Bangle.showClock, 10000);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Bangle.on("lock", lockHandler);
|
Bangle.on("lock", lockHandler);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "taglaunch",
|
"id": "taglaunch",
|
||||||
"name": "Tag Launcher",
|
"name": "Tag Launcher",
|
||||||
"shortName": "Taglauncher",
|
"shortName": "Taglauncher",
|
||||||
"version": "0.01",
|
"version": "0.02",
|
||||||
"description": "Launcher that puts all applications into submenus based on their tag. With many applications installed this can result in a faster application selection than the linear access of the default launcher.",
|
"description": "Launcher that puts all applications into submenus based on their tag. With many applications installed this can result in a faster application selection than the linear access of the default launcher.",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
"description": "Tetris",
|
"description": "Tetris",
|
||||||
"icon": "tetris.png",
|
"icon": "tetris.png",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"tags": "games",
|
"tags": "game",
|
||||||
"supports" : ["BANGLEJS2"],
|
"supports" : ["BANGLEJS2"],
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"tetris.app.js","url":"tetris.app.js"},
|
{"name":"tetris.app.js","url":"tetris.app.js"},
|
||||||
|
|
|
@ -8,3 +8,4 @@
|
||||||
0.08: Force background of widget field to the torch colour
|
0.08: Force background of widget field to the torch colour
|
||||||
0.09: Change code taking FW tweaks into account
|
0.09: Change code taking FW tweaks into account
|
||||||
0.10: Introduce fast switching.
|
0.10: Introduce fast switching.
|
||||||
|
0.11: Make compatible with Fastload Utils by loading and hiding widgets.
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
{
|
{
|
||||||
const SETTINGS_FILE = "torch.json";
|
const SETTINGS_FILE = "torch.json";
|
||||||
let settings;
|
let settings;
|
||||||
|
let s = require("Storage");
|
||||||
|
let wu = require("widget_utils");
|
||||||
|
|
||||||
let loadSettings = function() {
|
let loadSettings = function() {
|
||||||
settings = require("Storage").readJSON(SETTINGS_FILE,1)|| {'bg': '#FFFFFF', 'color': 'White'};
|
settings = s.readJSON(SETTINGS_FILE,1)|| {'bg': '#FFFFFF', 'color': 'White'};
|
||||||
};
|
};
|
||||||
|
|
||||||
loadSettings();
|
loadSettings();
|
||||||
|
|
||||||
let brightnessBackup = require("Storage").readJSON('setting.json').brightness;
|
let brightnessBackup = s.readJSON('setting.json').brightness;
|
||||||
let optionsBackup = Bangle.getOptions();
|
let optionsBackup = Bangle.getOptions();
|
||||||
Bangle.setLCDBrightness(1);
|
Bangle.setLCDBrightness(1);
|
||||||
Bangle.setLCDPower(1);
|
Bangle.setLCDPower(1);
|
||||||
|
@ -18,6 +20,8 @@
|
||||||
g.setTheme({bg:settings.bg,fg:"#000"});
|
g.setTheme({bg:settings.bg,fg:"#000"});
|
||||||
g.setColor(settings.bg);
|
g.setColor(settings.bg);
|
||||||
g.fillRect(0,0,g.getWidth(),g.getHeight());
|
g.fillRect(0,0,g.getWidth(),g.getHeight());
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
wu.hide();
|
||||||
Bangle.setUI({
|
Bangle.setUI({
|
||||||
mode : 'custom',
|
mode : 'custom',
|
||||||
back : Bangle.showClock, // B2: SW back button to exit
|
back : Bangle.showClock, // B2: SW back button to exit
|
||||||
|
@ -26,6 +30,7 @@
|
||||||
Bangle.setLCDBrightness(brightnessBackup);
|
Bangle.setLCDBrightness(brightnessBackup);
|
||||||
Bangle.setOptions(optionsBackup);
|
Bangle.setOptions(optionsBackup);
|
||||||
g.setTheme(themeBackup);
|
g.setTheme(themeBackup);
|
||||||
|
wu.show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "torch",
|
"id": "torch",
|
||||||
"name": "Torch",
|
"name": "Torch",
|
||||||
"shortName": "Torch",
|
"shortName": "Torch",
|
||||||
"version": "0.10",
|
"version": "0.11",
|
||||||
"description": "Turns screen white to help you see in the dark. Select from the launcher or press BTN1,BTN3,BTN1,BTN3 quickly to start when in any app that shows widgets on Bangle.js 1. You can also set the color through the app's setting menu.",
|
"description": "Turns screen white to help you see in the dark. Select from the launcher or press BTN1,BTN3,BTN1,BTN3 quickly to start when in any app that shows widgets on Bangle.js 1. You can also set the color through the app's setting menu.",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"tags": "tool,torch",
|
"tags": "tool,torch",
|
||||||
|
|
|
@ -15,3 +15,5 @@
|
||||||
0.16: Don't mark app as clock
|
0.16: Don't mark app as clock
|
||||||
0.17: Added clkinfo for clocks.
|
0.17: Added clkinfo for clocks.
|
||||||
0.18: Added hasRange to clkinfo.
|
0.18: Added hasRange to clkinfo.
|
||||||
|
0.19: Added weather condition to clkinfo.
|
||||||
|
0.20: Added weather condition with temperature to clkinfo.
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
temp: "?",
|
temp: "?",
|
||||||
hum: "?",
|
hum: "?",
|
||||||
wind: "?",
|
wind: "?",
|
||||||
|
txt: "?",
|
||||||
};
|
};
|
||||||
|
|
||||||
var weatherJson = require("Storage").readJSON('weather.json');
|
var weatherJson = require("Storage").readJSON('weather.json');
|
||||||
|
@ -14,17 +15,41 @@
|
||||||
weather.wind = Math.round(weather.wind[1]) + "kph";
|
weather.wind = Math.round(weather.wind[1]) + "kph";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function weatherIcon(code) {
|
||||||
|
var ovr = Graphics.createArrayBuffer(24,24,1,{msb:true});
|
||||||
|
require("weather").drawIcon({code:code},12,12,12,ovr);
|
||||||
|
var img = ovr.asImage();
|
||||||
|
img.transparent = 0;
|
||||||
|
//for (var i=0;i<img.buffer.length;i++) img.buffer[i]^=255;
|
||||||
|
return img;
|
||||||
|
//g.setColor("#0ff").drawImage(img, 42, 42);
|
||||||
|
}
|
||||||
|
|
||||||
//FIXME ranges are somehow arbitrary
|
//FIXME ranges are somehow arbitrary
|
||||||
var weatherItems = {
|
var weatherItems = {
|
||||||
name: "Weather",
|
name: "Weather",
|
||||||
img: atob("GBiBAf+///u5//n7//8f/9wHP8gDf/gB//AB/7AH/5AcP/AQH/DwD/uAD84AD/4AA/wAAfAAAfAAAfAAAfgAA/////+bP/+zf/+zfw=="),
|
img: atob("GBiBAf+///u5//n7//8f/9wHP8gDf/gB//AB/7AH/5AcP/AQH/DwD/uAD84AD/4AA/wAAfAAAfAAAfAAAfgAA/////+bP/+zf/+zfw=="),
|
||||||
items: [
|
items: [
|
||||||
|
{
|
||||||
|
name: "conditionWithTemperature",
|
||||||
|
get: () => ({ text: weather.temp, img: weatherIcon(weather.code),
|
||||||
|
v: parseInt(weather.temp), min: -30, max: 55}),
|
||||||
|
show: function() { this.emit("redraw"); },
|
||||||
|
hide: function () {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "condition",
|
||||||
|
get: () => ({ text: weather.txt, img: weatherIcon(weather.code),
|
||||||
|
v: weather.code}),
|
||||||
|
show: function() { this.emit("redraw"); },
|
||||||
|
hide: function () {}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "temperature",
|
name: "temperature",
|
||||||
hasRange : true,
|
hasRange : true,
|
||||||
get: () => ({ text: weather.temp, img: atob("GBiBAAA8AAB+AADnAADDAADDAADDAADDAADDAADbAADbAADbAADbAADbAADbAAHbgAGZgAM8wAN+wAN+wAM8wAGZgAHDgAD/AAA8AA=="),
|
get: () => ({ text: weather.temp, img: atob("GBiBAAA8AAB+AADnAADDAADDAADDAADDAADDAADbAADbAADbAADbAADbAADbAAHbgAGZgAM8wAN+wAN+wAM8wAGZgAHDgAD/AAA8AA=="),
|
||||||
v: parseInt(weather.temp), min: -30, max: 55}),
|
v: parseInt(weather.temp), min: -30, max: 55}),
|
||||||
show: function() { weatherItems.items[0].emit("redraw"); },
|
show: function() { this.emit("redraw"); },
|
||||||
hide: function () {}
|
hide: function () {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -32,7 +57,7 @@
|
||||||
hasRange : true,
|
hasRange : true,
|
||||||
get: () => ({ text: weather.hum, img: atob("GBiBAAAEAAAMAAAOAAAfAAAfAAA/gAA/gAI/gAY/AAcfAA+AQA+A4B/A4D/B8D/h+D/j+H/n/D/n/D/n/B/H/A+H/AAH/AAD+AAA8A=="),
|
get: () => ({ text: weather.hum, img: atob("GBiBAAAEAAAMAAAOAAAfAAAfAAA/gAA/gAI/gAY/AAcfAA+AQA+A4B/A4D/B8D/h+D/j+H/n/D/n/D/n/B/H/A+H/AAH/AAD+AAA8A=="),
|
||||||
v: parseInt(weather.hum), min: 0, max: 100}),
|
v: parseInt(weather.hum), min: 0, max: 100}),
|
||||||
show: function() { weatherItems.items[1].emit("redraw"); },
|
show: function() { this.emit("redraw"); },
|
||||||
hide: function () {}
|
hide: function () {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -40,7 +65,7 @@
|
||||||
hasRange : true,
|
hasRange : true,
|
||||||
get: () => ({ text: weather.wind, img: atob("GBiBAAHgAAPwAAYYAAwYAAwMfAAY/gAZh3/xg//hgwAAAwAABg///g//+AAAAAAAAP//wH//4AAAMAAAMAAYMAAYMAAMcAAP4AADwA=="),
|
get: () => ({ text: weather.wind, img: atob("GBiBAAHgAAPwAAYYAAwYAAwMfAAY/gAZh3/xg//hgwAAAwAABg///g//+AAAAAAAAP//wH//4AAAMAAAMAAYMAAYMAAMcAAP4AADwA=="),
|
||||||
v: parseInt(weather.wind), min: 0, max: 118}),
|
v: parseInt(weather.wind), min: 0, max: 118}),
|
||||||
show: function() { weatherItems.items[2].emit("redraw"); },
|
show: function() { this.emit("redraw"); },
|
||||||
hide: function () {}
|
hide: function () {}
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
@ -62,12 +62,29 @@ scheduleExpiry(storage.readJSON('weather.json')||{});
|
||||||
* @param x Left
|
* @param x Left
|
||||||
* @param y Top
|
* @param y Top
|
||||||
* @param r Icon Size
|
* @param r Icon Size
|
||||||
|
* @param ovr Graphics instance (or undefined for g)
|
||||||
*/
|
*/
|
||||||
exports.drawIcon = function(cond, x, y, r) {
|
exports.drawIcon = function(cond, x, y, r, ovr) {
|
||||||
var palette;
|
var palette;
|
||||||
|
var monochrome=1;
|
||||||
|
if(!ovr) {
|
||||||
|
ovr = g;
|
||||||
|
monochrome=0;
|
||||||
|
}
|
||||||
|
if(monochrome) {
|
||||||
|
palette = {
|
||||||
|
sun: '#FFF',
|
||||||
|
cloud: '#FFF',
|
||||||
|
bgCloud: '#FFF',
|
||||||
|
rain: '#FFF',
|
||||||
|
lightning: '#FFF',
|
||||||
|
snow: '#FFF',
|
||||||
|
mist: '#FFF',
|
||||||
|
background: '#000'
|
||||||
|
};
|
||||||
|
} else
|
||||||
if (B2) {
|
if (B2) {
|
||||||
if (g.theme.dark) {
|
if (ovr.theme.dark) {
|
||||||
palette = {
|
palette = {
|
||||||
sun: '#FF0',
|
sun: '#FF0',
|
||||||
cloud: '#FFF',
|
cloud: '#FFF',
|
||||||
|
@ -89,7 +106,7 @@ exports.drawIcon = function(cond, x, y, r) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (g.theme.dark) {
|
if (ovr.theme.dark) {
|
||||||
palette = {
|
palette = {
|
||||||
sun: '#FE0',
|
sun: '#FE0',
|
||||||
cloud: '#BBB',
|
cloud: '#BBB',
|
||||||
|
@ -113,19 +130,19 @@ exports.drawIcon = function(cond, x, y, r) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawSun(x, y, r) {
|
function drawSun(x, y, r) {
|
||||||
g.setColor(palette.sun);
|
ovr.setColor(palette.sun);
|
||||||
g.fillCircle(x, y, r);
|
ovr.fillCircle(x, y, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawCloud(x, y, r, c) {
|
function drawCloud(x, y, r, c) {
|
||||||
const u = r/12;
|
const u = r/12;
|
||||||
if (c==null) c = palette.cloud;
|
if (c==null) c = palette.cloud;
|
||||||
g.setColor(c);
|
ovr.setColor(c);
|
||||||
g.fillCircle(x-8*u, y+3*u, 4*u);
|
ovr.fillCircle(x-8*u, y+3*u, 4*u);
|
||||||
g.fillCircle(x-4*u, y-2*u, 5*u);
|
ovr.fillCircle(x-4*u, y-2*u, 5*u);
|
||||||
g.fillCircle(x+4*u, y+0*u, 4*u);
|
ovr.fillCircle(x+4*u, y+0*u, 4*u);
|
||||||
g.fillCircle(x+9*u, y+4*u, 3*u);
|
ovr.fillCircle(x+9*u, y+4*u, 3*u);
|
||||||
g.fillPoly([
|
ovr.fillPoly([
|
||||||
x-8*u, y+7*u,
|
x-8*u, y+7*u,
|
||||||
x-8*u, y+3*u,
|
x-8*u, y+3*u,
|
||||||
x-4*u, y-2*u,
|
x-4*u, y-2*u,
|
||||||
|
@ -137,19 +154,23 @@ exports.drawIcon = function(cond, x, y, r) {
|
||||||
|
|
||||||
function drawBrokenClouds(x, y, r) {
|
function drawBrokenClouds(x, y, r) {
|
||||||
drawCloud(x+1/8*r, y-1/8*r, 7/8*r, palette.bgCloud);
|
drawCloud(x+1/8*r, y-1/8*r, 7/8*r, palette.bgCloud);
|
||||||
|
if(monochrome)
|
||||||
|
drawCloud(x-1/8*r, y+2/16*r, r, palette.background);
|
||||||
drawCloud(x-1/8*r, y+1/8*r, 7/8*r);
|
drawCloud(x-1/8*r, y+1/8*r, 7/8*r);
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawFewClouds(x, y, r) {
|
function drawFewClouds(x, y, r) {
|
||||||
drawSun(x+3/8*r, y-1/8*r, 5/8*r);
|
drawSun(x+3/8*r, y-1/8*r, 5/8*r);
|
||||||
|
if(monochrome)
|
||||||
|
drawCloud(x-1/8*r, y+2/16*r, r, palette.background);
|
||||||
drawCloud(x-1/8*r, y+1/8*r, 7/8*r);
|
drawCloud(x-1/8*r, y+1/8*r, 7/8*r);
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawRainLines(x, y, r) {
|
function drawRainLines(x, y, r) {
|
||||||
g.setColor(palette.rain);
|
ovr.setColor(palette.rain);
|
||||||
const y1 = y+1/2*r;
|
const y1 = y+1/2*r;
|
||||||
const y2 = y+1*r;
|
const y2 = y+1*r;
|
||||||
const poly = g.fillPolyAA ? p => g.fillPolyAA(p) : p => g.fillPoly(p);
|
const poly = ovr.fillPolyAA ? p => ovr.fillPolyAA(p) : p => ovr.fillPoly(p);
|
||||||
poly([
|
poly([
|
||||||
x-6/12*r, y1,
|
x-6/12*r, y1,
|
||||||
x-8/12*r, y2,
|
x-8/12*r, y2,
|
||||||
|
@ -182,8 +203,8 @@ exports.drawIcon = function(cond, x, y, r) {
|
||||||
|
|
||||||
function drawThunderstorm(x, y, r) {
|
function drawThunderstorm(x, y, r) {
|
||||||
function drawLightning(x, y, r) {
|
function drawLightning(x, y, r) {
|
||||||
g.setColor(palette.lightning);
|
ovr.setColor(palette.lightning);
|
||||||
g.fillPoly([
|
ovr.fillPoly([
|
||||||
x-2/6*r, y-r,
|
x-2/6*r, y-r,
|
||||||
x-4/6*r, y+1/6*r,
|
x-4/6*r, y+1/6*r,
|
||||||
x-1/6*r, y+1/6*r,
|
x-1/6*r, y+1/6*r,
|
||||||
|
@ -194,8 +215,9 @@ exports.drawIcon = function(cond, x, y, r) {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
drawBrokenClouds(x, y-1/3*r, r);
|
if(monochrome) drawBrokenClouds(x, y-1/3*r, r);
|
||||||
drawLightning(x-1/12*r, y+1/2*r, 1/2*r);
|
drawLightning(x-1/12*r, y+1/2*r, 1/2*r);
|
||||||
|
drawBrokenClouds(x, y-1/3*r, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawSnow(x, y, r) {
|
function drawSnow(x, y, r) {
|
||||||
|
@ -210,7 +232,7 @@ exports.drawIcon = function(cond, x, y, r) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g.setColor(palette.snow);
|
ovr.setColor(palette.snow);
|
||||||
const w = 1/12*r;
|
const w = 1/12*r;
|
||||||
for(let i = 0; i<=6; ++i) {
|
for(let i = 0; i<=6; ++i) {
|
||||||
const points = [
|
const points = [
|
||||||
|
@ -220,7 +242,7 @@ exports.drawIcon = function(cond, x, y, r) {
|
||||||
x+w, y+r,
|
x+w, y+r,
|
||||||
];
|
];
|
||||||
rotatePoints(points, x, y, i/3*Math.PI);
|
rotatePoints(points, x, y, i/3*Math.PI);
|
||||||
g.fillPoly(points);
|
ovr.fillPoly(points);
|
||||||
|
|
||||||
for(let j = -1; j<=1; j += 2) {
|
for(let j = -1; j<=1; j += 2) {
|
||||||
const points = [
|
const points = [
|
||||||
|
@ -231,7 +253,7 @@ exports.drawIcon = function(cond, x, y, r) {
|
||||||
];
|
];
|
||||||
rotatePoints(points, x, y+7/12*r, j/3*Math.PI);
|
rotatePoints(points, x, y+7/12*r, j/3*Math.PI);
|
||||||
rotatePoints(points, x, y, i/3*Math.PI);
|
rotatePoints(points, x, y, i/3*Math.PI);
|
||||||
g.fillPoly(points);
|
ovr.fillPoly(points);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -245,18 +267,18 @@ exports.drawIcon = function(cond, x, y, r) {
|
||||||
[-0.2, 0.3],
|
[-0.2, 0.3],
|
||||||
];
|
];
|
||||||
|
|
||||||
g.setColor(palette.mist);
|
ovr.setColor(palette.mist);
|
||||||
for(let i = 0; i<5; ++i) {
|
for(let i = 0; i<5; ++i) {
|
||||||
g.fillRect(x+layers[i][0]*r, y+(0.4*i-0.9)*r, x+layers[i][1]*r,
|
ovr.fillRect(x+layers[i][0]*r, y+(0.4*i-0.9)*r, x+layers[i][1]*r,
|
||||||
y+(0.4*i-0.7)*r-1);
|
y+(0.4*i-0.7)*r-1);
|
||||||
g.fillCircle(x+layers[i][0]*r, y+(0.4*i-0.8)*r-0.5, 0.1*r-0.5);
|
ovr.fillCircle(x+layers[i][0]*r, y+(0.4*i-0.8)*r-0.5, 0.1*r-0.5);
|
||||||
g.fillCircle(x+layers[i][1]*r, y+(0.4*i-0.8)*r-0.5, 0.1*r-0.5);
|
ovr.fillCircle(x+layers[i][1]*r, y+(0.4*i-0.8)*r-0.5, 0.1*r-0.5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawUnknown(x, y, r) {
|
function drawUnknown(x, y, r) {
|
||||||
drawCloud(x, y, r, palette.bgCloud);
|
drawCloud(x, y, r, palette.bgCloud);
|
||||||
g.setColor(g.theme.fg).setFontAlign(0, 0).setFont('Vector', r*2).drawString("?", x+r/10, y+r/6);
|
ovr.setColor(ovr.theme.fg).setFontAlign(0, 0).setFont('Vector', r*2).drawString("?", x+r/10, y+r/6);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "weather",
|
"id": "weather",
|
||||||
"name": "Weather",
|
"name": "Weather",
|
||||||
"version": "0.18",
|
"version": "0.20",
|
||||||
"description": "Show Gadgetbridge weather report",
|
"description": "Show Gadgetbridge weather report",
|
||||||
"icon": "icon.png",
|
"icon": "icon.png",
|
||||||
"screenshots": [{"url":"screenshot.png"}],
|
"screenshots": [{"url":"screenshot.png"}],
|
||||||
|
|
|
@ -4,3 +4,4 @@
|
||||||
0.04: Set sortorder to -1 so that widget always takes up the furthest left position
|
0.04: Set sortorder to -1 so that widget always takes up the furthest left position
|
||||||
0.05: Set sortorder to -10 so that others can take -1 etc
|
0.05: Set sortorder to -10 so that others can take -1 etc
|
||||||
0.06: Set sortorder to -10 in widget code
|
0.06: Set sortorder to -10 in widget code
|
||||||
|
0.07: Remove check for .isLocked (extremely old firmwares), speed up widget loading
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "widlock",
|
"id": "widlock",
|
||||||
"name": "Lock Widget",
|
"name": "Lock Widget",
|
||||||
"version": "0.06",
|
"version": "0.07",
|
||||||
"description": "On devices with always-on display (Bangle.js 2) this displays lock icon whenever the display is locked",
|
"description": "On devices with always-on display (Bangle.js 2) this displays lock icon whenever the display is locked",
|
||||||
"icon": "widget.png",
|
"icon": "widget.png",
|
||||||
"type": "widget",
|
"type": "widget",
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
(function(){
|
Bangle.on("lock", function() {
|
||||||
if (!Bangle.isLocked) return; // bail out on old firmware
|
|
||||||
Bangle.on("lock", function(on) {
|
|
||||||
WIDGETS["lock"].width = Bangle.isLocked()?16:0;
|
WIDGETS["lock"].width = Bangle.isLocked()?16:0;
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
});
|
});
|
||||||
|
@ -8,4 +6,3 @@
|
||||||
if (Bangle.isLocked())
|
if (Bangle.isLocked())
|
||||||
g.reset().drawImage(atob("DhABH+D/wwMMDDAwwMf/v//4f+H/h/8//P/z///f/g=="), w.x+1, w.y+4);
|
g.reset().drawImage(atob("DhABH+D/wwMMDDAwwMf/v//4f+H/h/8//P/z///f/g=="), w.x+1, w.y+4);
|
||||||
}};
|
}};
|
||||||
})()
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: First commit
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"id": "widlockunlock",
|
||||||
|
"name": "Lock/Unlock Widget",
|
||||||
|
"version": "0.01",
|
||||||
|
"description": "On devices with always-on display (Bangle.js 2) this displays lock icon whenever the display is locked, or an unlock icon otherwise",
|
||||||
|
"icon": "widget.png",
|
||||||
|
"type": "widget",
|
||||||
|
"tags": "widget,lock",
|
||||||
|
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||||
|
"storage": [
|
||||||
|
{"name":"widlockunlock.wid.js","url":"widget.js"}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
Bangle.on("lockunlock", function() {
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
});
|
||||||
|
WIDGETS["lockunlock"]={area:"tl",sortorder:10,width:14,draw:function(w) {
|
||||||
|
g.reset().drawImage(atob(Bangle.isLocked() ? "DBGBAAAA8DnDDCBCBP////////n/n/n//////z/A" : "DBGBAAAA8BnDDCBABP///8A8A8Y8Y8Y8A8A//z/A"), w.x+1, w.y+3);
|
||||||
|
}};
|
Binary file not shown.
After Width: | Height: | Size: 726 B |
2
core
2
core
|
@ -1 +1 @@
|
||||||
Subproject commit f15e99fbe25b2991719011e6da9bc9c7be401a7e
|
Subproject commit 3a953179b7bb9f574d4e77d5f34b6b7deee1e884
|
|
@ -9,7 +9,7 @@ function ClockFace(options) {
|
||||||
if (![
|
if (![
|
||||||
"precision",
|
"precision",
|
||||||
"init", "draw", "update",
|
"init", "draw", "update",
|
||||||
"pause", "resume",
|
"pause", "resume", "remove",
|
||||||
"up", "down", "upDown",
|
"up", "down", "upDown",
|
||||||
"settingsFile",
|
"settingsFile",
|
||||||
].includes(k)) throw `Invalid ClockFace option: ${k}`;
|
].includes(k)) throw `Invalid ClockFace option: ${k}`;
|
||||||
|
@ -27,6 +27,7 @@ function ClockFace(options) {
|
||||||
if (options.init) this.init = options.init;
|
if (options.init) this.init = options.init;
|
||||||
if (options.pause) this._pause = options.pause;
|
if (options.pause) this._pause = options.pause;
|
||||||
if (options.resume) this._resume = options.resume;
|
if (options.resume) this._resume = options.resume;
|
||||||
|
if (options.remove) this._remove = options.remove;
|
||||||
if ((options.up || options.down) && options.upDown) throw "ClockFace up/down and upDown cannot be used together";
|
if ((options.up || options.down) && options.upDown) throw "ClockFace up/down and upDown cannot be used together";
|
||||||
if (options.up || options.down) this._upDown = (dir) => {
|
if (options.up || options.down) this._upDown = (dir) => {
|
||||||
if (dir<0 && options.up) options.up.apply(this);
|
if (dir<0 && options.up) options.up.apply(this);
|
||||||
|
@ -44,8 +45,15 @@ function ClockFace(options) {
|
||||||
["showDate", "loadWidgets"].forEach(k => {
|
["showDate", "loadWidgets"].forEach(k => {
|
||||||
if (this[k]===undefined) this[k] = true;
|
if (this[k]===undefined) this[k] = true;
|
||||||
});
|
});
|
||||||
|
let s = require("Storage").readJSON("setting.json",1)||{};
|
||||||
|
if ((global.__FILE__===undefined || global.__FILE__===s.clock)
|
||||||
|
&& s.clockHasWidgets!==this.loadWidgets) {
|
||||||
|
// save whether we can Fast Load
|
||||||
|
s.clockHasWidgets = this.loadWidgets;
|
||||||
|
require("Storage").writeJSON("setting.json", s);
|
||||||
|
}
|
||||||
// use global 24/12-hour setting if not set by clock-settings
|
// use global 24/12-hour setting if not set by clock-settings
|
||||||
if (!('is12Hour' in this)) this.is12Hour = !!(require("Storage").readJSON("setting.json", true) || {})["12hour"];
|
if (!('is12Hour' in this)) this.is12Hour = !!(s["12hour"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClockFace.prototype.tick = function() {
|
ClockFace.prototype.tick = function() {
|
||||||
|
@ -85,16 +93,27 @@ ClockFace.prototype.start = function() {
|
||||||
Bangle.CLOCK = 1;
|
Bangle.CLOCK = 1;
|
||||||
if (this.loadWidgets) Bangle.loadWidgets();
|
if (this.loadWidgets) Bangle.loadWidgets();
|
||||||
if (this.init) this.init.apply(this);
|
if (this.init) this.init.apply(this);
|
||||||
if (this._upDown) Bangle.setUI("clockupdown", d=>this._upDown.apply(this,[d]));
|
const uiRemove = this._remove ? () => this.remove() : undefined;
|
||||||
else Bangle.setUI("clock");
|
if (this._upDown) {
|
||||||
|
Bangle.setUI({
|
||||||
|
mode: "clockupdown",
|
||||||
|
remove: uiRemove,
|
||||||
|
}, d => this._upDown.apply(this, [d]));
|
||||||
|
} else {
|
||||||
|
Bangle.setUI({
|
||||||
|
mode: "clock",
|
||||||
|
remove: uiRemove,
|
||||||
|
});
|
||||||
|
}
|
||||||
delete this._last;
|
delete this._last;
|
||||||
this.paused = false;
|
this.paused = false;
|
||||||
this.tick();
|
this.tick();
|
||||||
|
|
||||||
Bangle.on("lcdPower", on => {
|
this._onLcd = on => {
|
||||||
if (on) this.resume();
|
if (on) this.resume();
|
||||||
else this.pause();
|
else this.pause();
|
||||||
});
|
};
|
||||||
|
Bangle.on("lcdPower", this._onLcd);
|
||||||
};
|
};
|
||||||
|
|
||||||
ClockFace.prototype.pause = function() {
|
ClockFace.prototype.pause = function() {
|
||||||
|
@ -111,6 +130,11 @@ ClockFace.prototype.resume = function() {
|
||||||
if (this._resume) this._resume.apply(this);
|
if (this._resume) this._resume.apply(this);
|
||||||
this.tick();
|
this.tick();
|
||||||
};
|
};
|
||||||
|
ClockFace.prototype.remove = function() {
|
||||||
|
if (this._timeout) clearTimeout(this._timeout);
|
||||||
|
Bangle.removeListener("lcdPower", this._onLcd);
|
||||||
|
if (this._remove) this._remove.apply(this);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Force a complete redraw
|
* Force a complete redraw
|
||||||
|
|
|
@ -77,6 +77,11 @@ var clock = new ClockFace({
|
||||||
resume: function() { // optional, called when the screen turns on
|
resume: function() { // optional, called when the screen turns on
|
||||||
// for example: turn GPS/compass back on
|
// for example: turn GPS/compass back on
|
||||||
},
|
},
|
||||||
|
remove: function() { // optional, used for Fast Loading
|
||||||
|
// for example: remove listeners
|
||||||
|
// Fast Loading will not be used unless this function is present,
|
||||||
|
// if there is nothing to clean up, you can just leave it empty.
|
||||||
|
},
|
||||||
up: function() { // optional, up handler
|
up: function() { // optional, up handler
|
||||||
},
|
},
|
||||||
down: function() { // optional, down handler
|
down: function() { // optional, down handler
|
||||||
|
|
|
@ -83,7 +83,7 @@ Layout.prototype.setUI = function() {
|
||||||
let uiSet;
|
let uiSet;
|
||||||
if (this.buttons) {
|
if (this.buttons) {
|
||||||
// multiple buttons so we'll jus use back/next/select
|
// multiple buttons so we'll jus use back/next/select
|
||||||
Bangle.setUI({mode:"updown", back:this.options.back}, dir=>{
|
Bangle.setUI({mode:"updown", back:this.options.back, remove:this.options.remove}, dir=>{
|
||||||
var s = this.selectedButton, l=this.buttons.length;
|
var s = this.selectedButton, l=this.buttons.length;
|
||||||
if (dir===undefined && this.buttons[s])
|
if (dir===undefined && this.buttons[s])
|
||||||
return this.buttons[s].cb();
|
return this.buttons[s].cb();
|
||||||
|
@ -100,7 +100,7 @@ Layout.prototype.setUI = function() {
|
||||||
});
|
});
|
||||||
uiSet = true;
|
uiSet = true;
|
||||||
}
|
}
|
||||||
if (this.options.back && !uiSet) Bangle.setUI({mode: "custom", back: this.options.back});
|
if ((this.options.back || this.options.remove) && !uiSet) Bangle.setUI({mode: "custom", back: this.options.back, remove: this.options.remove});
|
||||||
// physical buttons -> actual applications
|
// physical buttons -> actual applications
|
||||||
if (this.b) {
|
if (this.b) {
|
||||||
// Handler for button watch events
|
// Handler for button watch events
|
||||||
|
|
|
@ -59,6 +59,7 @@ layout.render();
|
||||||
- `cb` - a callback function
|
- `cb` - a callback function
|
||||||
- `cbl` - a callback function for long presses
|
- `cbl` - a callback function for long presses
|
||||||
- `back` - a callback function, passed as `back` into Bangle.setUI (which usually adds an icon in the top left)
|
- `back` - a callback function, passed as `back` into Bangle.setUI (which usually adds an icon in the top left)
|
||||||
|
- `remove` - a cleanup function, passed as `remove` into Bangle.setUI (allows to cleanly remove the app from memory)
|
||||||
|
|
||||||
If automatic lazy rendering is enabled, calls to `layout.render()` will attempt to automatically determine what objects have changed or moved, clear their previous locations, and re-render just those objects.
|
If automatic lazy rendering is enabled, calls to `layout.render()` will attempt to automatically determine what objects have changed or moved, clear their previous locations, and re-render just those objects.
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
function p(d,h){function b(e){"ram";e.id&&(a[e.id]=e);e.type||(e.type="");e.c&&e.c.forEach(b)}this._l=this.l=d;this.options=h||{};this.lazy=this.options.lazy||!1;this.physBtns=1;let f;if(2!=process.env.HWVERSION){this.physBtns=3;f=[];function e(l){"ram";"btn"==l.type&&f.push(l);l.c&&l.c.forEach(e)}e(d);f.length&&(this.physBtns=0,this.buttons=f,this.selectedButton=-1)}if(this.options.btns)if(d=this.options.btns,this.physBtns>=d.length){this.b=d;let e=Math.floor(Bangle.appRect.h/
|
function p(d,h){function b(e){"ram";e.id&&(a[e.id]=e);e.type||(e.type="");e.c&&e.c.forEach(b)}this._l=this.l=d;this.options=h||{};this.lazy=this.options.lazy||!1;this.physBtns=1;let f;if(2!=process.env.HWVERSION){this.physBtns=3;f=[];function e(l){"ram";"btn"==l.type&&f.push(l);l.c&&l.c.forEach(e)}e(d);f.length&&(this.physBtns=0,this.buttons=f,this.selectedButton=-1)}if(this.options.btns)if(d=this.options.btns,this.physBtns>=d.length){this.b=d;let e=Math.floor(Bangle.appRect.h/this.physBtns);
|
||||||
this.physBtns);for(2<this.physBtns&&1==d.length&&d.unshift({label:""});this.physBtns>d.length;)d.push({label:""});this._l.width=g.getWidth()-8;this._l={type:"h",filly:1,c:[this._l,{type:"v",pad:1,filly:1,c:d.map(l=>(l.type="txt",l.font="6x8",l.height=e,l.r=1,l))}]}}else this._l.width=g.getWidth()-32,this._l={type:"h",c:[this._l,{type:"v",c:d.map(e=>(e.type="btn",e.filly=1,e.width=32,e.r=1,e))}]},f&&f.push.apply(f,this._l.c[1].c);this.setUI();var a=this;b(this._l);this.updateNeeded=!0}function t(d,
|
for(2<this.physBtns&&1==d.length&&d.unshift({label:""});this.physBtns>d.length;)d.push({label:""});this._l.width=g.getWidth()-8;this._l={type:"h",filly:1,c:[this._l,{type:"v",pad:1,filly:1,c:d.map(l=>(l.type="txt",l.font="6x8",l.height=e,l.r=1,l))}]}}else this._l.width=g.getWidth()-32,this._l={type:"h",c:[this._l,{type:"v",c:d.map(e=>(e.type="btn",e.filly=1,e.width=32,e.r=1,e))}]},f&&f.push.apply(f,this._l.c[1].c);this.setUI();var a=this;b(this._l);this.updateNeeded=!0}function t(d,h,b,f,a){var e=
|
||||||
h,b,f,a){var e=null==d.bgCol?a:g.toColor(d.bgCol);if(e!=a||"txt"==d.type||"btn"==d.type||"img"==d.type||"custom"==d.type){var l=d.c;delete d.c;var k="H"+E.CRC32(E.toJS(d));l&&(d.c=l);delete h[k]||((f[k]=[d.x,d.y,d.x+d.w-1,d.y+d.h-1]).bg=null==a?g.theme.bg:a,b&&(b.push(d),b=null))}if(d.c)for(var c of d.c)t(c,h,b,f,e)}p.prototype.setUI=function(){Bangle.setUI();let d;this.buttons&&(Bangle.setUI({mode:"updown",back:this.options.back},h=>{var b=this.selectedButton,f=this.buttons.length;if(void 0===h&&
|
null==d.bgCol?a:g.toColor(d.bgCol);if(e!=a||"txt"==d.type||"btn"==d.type||"img"==d.type||"custom"==d.type){var l=d.c;delete d.c;var k="H"+E.CRC32(E.toJS(d));l&&(d.c=l);delete h[k]||((f[k]=[d.x,d.y,d.x+d.w-1,d.y+d.h-1]).bg=null==a?g.theme.bg:a,b&&(b.push(d),b=null))}if(d.c)for(var c of d.c)t(c,h,b,f,e)}p.prototype.setUI=function(){Bangle.setUI();let d;this.buttons&&(Bangle.setUI({mode:"updown",back:this.options.back,remove:this.options.remove},h=>{var b=this.selectedButton,f=this.buttons.length;if(void 0===
|
||||||
this.buttons[b])return this.buttons[b].cb();this.buttons[b]&&(delete this.buttons[b].selected,this.render(this.buttons[b]));b=(b+f+h)%f;this.buttons[b]&&(this.buttons[b].selected=1,this.render(this.buttons[b]));this.selectedButton=b}),d=!0);this.options.back&&!d&&Bangle.setUI({mode:"custom",back:this.options.back});if(this.b){function h(b,f){.75<f.time-f.lastTime&&this.b[b].cbl?this.b[b].cbl(f):this.b[b].cb&&this.b[b].cb(f)}Bangle.btnWatches&&Bangle.btnWatches.forEach(clearWatch);Bangle.btnWatches=
|
h&&this.buttons[b])return this.buttons[b].cb();this.buttons[b]&&(delete this.buttons[b].selected,this.render(this.buttons[b]));b=(b+f+h)%f;this.buttons[b]&&(this.buttons[b].selected=1,this.render(this.buttons[b]));this.selectedButton=b}),d=!0);!this.options.back&&!this.options.remove||d||Bangle.setUI({mode:"custom",back:this.options.back,remove:this.options.remove});if(this.b){function h(b,f){.75<f.time-f.lastTime&&this.b[b].cbl?this.b[b].cbl(f):this.b[b].cb&&this.b[b].cb(f)}Bangle.btnWatches&&Bangle.btnWatches.forEach(clearWatch);
|
||||||
[];this.b[0]&&Bangle.btnWatches.push(setWatch(h.bind(this,0),BTN1,{repeat:!0,edge:-1}));this.b[1]&&Bangle.btnWatches.push(setWatch(h.bind(this,1),BTN2,{repeat:!0,edge:-1}));this.b[2]&&Bangle.btnWatches.push(setWatch(h.bind(this,2),BTN3,{repeat:!0,edge:-1}))}if(2==process.env.HWVERSION){function h(b,f){b.cb&&f.x>=b.x&&f.y>=b.y&&f.x<=b.x+b.w&&f.y<=b.y+b.h&&(2==f.type&&b.cbl?b.cbl(f):b.cb&&b.cb(f));b.c&&b.c.forEach(a=>h(a,f))}Bangle.touchHandler=(b,f)=>h(this._l,f);Bangle.on("touch",Bangle.touchHandler)}};
|
Bangle.btnWatches=[];this.b[0]&&Bangle.btnWatches.push(setWatch(h.bind(this,0),BTN1,{repeat:!0,edge:-1}));this.b[1]&&Bangle.btnWatches.push(setWatch(h.bind(this,1),BTN2,{repeat:!0,edge:-1}));this.b[2]&&Bangle.btnWatches.push(setWatch(h.bind(this,2),BTN3,{repeat:!0,edge:-1}))}if(2==process.env.HWVERSION){function h(b,f){b.cb&&f.x>=b.x&&f.y>=b.y&&f.x<=b.x+b.w&&f.y<=b.y+b.h&&(2==f.type&&b.cbl?b.cbl(f):b.cb&&b.cb(f));b.c&&b.c.forEach(a=>h(a,f))}Bangle.touchHandler=(b,f)=>h(this._l,f);Bangle.on("touch",
|
||||||
p.prototype.render=function(d){function h(c){"ram";b.reset();void 0!==c.col&&b.setColor(c.col);void 0!==c.bgCol&&b.setBgColor(c.bgCol).clearRect(c.x,c.y,c.x+c.w-1,c.y+c.h-1);f[c.type](c)}d||(d=this._l);this.updateNeeded&&this.update();var b=g,f={"":function(){},txt:function(c){"ram";if(c.wrap){var m=b.setFont(c.font).setFontAlign(0,-1).wrapString(c.label,c.w),n=c.y+(c.h-b.getFontHeight()*m.length>>1);b.drawString(m.join("\n"),c.x+(c.w>>1),n)}else b.setFont(c.font).setFontAlign(0,0,c.r).drawString(c.label,
|
Bangle.touchHandler)}};p.prototype.render=function(d){function h(c){"ram";b.reset();void 0!==c.col&&b.setColor(c.col);void 0!==c.bgCol&&b.setBgColor(c.bgCol).clearRect(c.x,c.y,c.x+c.w-1,c.y+c.h-1);f[c.type](c)}d||(d=this._l);this.updateNeeded&&this.update();var b=g,f={"":function(){},txt:function(c){"ram";if(c.wrap){var m=b.setFont(c.font).setFontAlign(0,-1).wrapString(c.label,c.w),n=c.y+(c.h-b.getFontHeight()*m.length>>1);b.drawString(m.join("\n"),c.x+(c.w>>1),n)}else b.setFont(c.font).setFontAlign(0,
|
||||||
c.x+(c.w>>1),c.y+(c.h>>1))},btn:function(c){"ram";var m=c.x+(0|c.pad),n=c.y+(0|c.pad),q=c.w-(c.pad<<1),r=c.h-(c.pad<<1);m=[m,n+4,m+4,n,m+q-5,n,m+q-1,n+4,m+q-1,n+r-5,m+q-5,n+r-1,m+4,n+r-1,m,n+r-5,m,n+4];n=c.selected?b.theme.bgH:b.theme.bg2;b.setColor(n).fillPoly(m).setColor(c.selected?b.theme.fgH:b.theme.fg2).drawPoly(m);void 0!==c.col&&b.setColor(c.col);c.src?b.setBgColor(n).drawImage("function"==typeof c.src?c.src():c.src,c.x+c.w/2,c.y+c.h/2,{scale:c.scale||void 0,rotate:.5*Math.PI*(c.r||0)}):b.setFont(c.font||
|
0,c.r).drawString(c.label,c.x+(c.w>>1),c.y+(c.h>>1))},btn:function(c){"ram";var m=c.x+(0|c.pad),n=c.y+(0|c.pad),q=c.w-(c.pad<<1),r=c.h-(c.pad<<1);m=[m,n+4,m+4,n,m+q-5,n,m+q-1,n+4,m+q-1,n+r-5,m+q-5,n+r-1,m+4,n+r-1,m,n+r-5,m,n+4];n=c.selected?b.theme.bgH:b.theme.bg2;b.setColor(n).fillPoly(m).setColor(c.selected?b.theme.fgH:b.theme.fg2).drawPoly(m);void 0!==c.col&&b.setColor(c.col);c.src?b.setBgColor(n).drawImage("function"==typeof c.src?c.src():c.src,c.x+c.w/2,c.y+c.h/2,{scale:c.scale||void 0,rotate:.5*
|
||||||
"6x8:2").setFontAlign(0,0,c.r).drawString(c.label,c.x+c.w/2,c.y+c.h/2)},img:function(c){"ram";b.drawImage("function"==typeof c.src?c.src():c.src,c.x+c.w/2,c.y+c.h/2,{scale:c.scale||void 0,rotate:.5*Math.PI*(c.r||0)})},custom:function(c){"ram";c.render(c)},h:function(c){"ram";c.c.forEach(h)},v:function(c){"ram";c.c.forEach(h)}};if(this.lazy){this.rects||(this.rects={});var a=this.rects.clone(),e=[];t(d,a,e,this.rects,null);for(var l in a)delete this.rects[l];d=Object.keys(a).map(c=>a[c]).reverse();
|
Math.PI*(c.r||0)}):b.setFont(c.font||"6x8:2").setFontAlign(0,0,c.r).drawString(c.label,c.x+c.w/2,c.y+c.h/2)},img:function(c){"ram";b.drawImage("function"==typeof c.src?c.src():c.src,c.x+c.w/2,c.y+c.h/2,{scale:c.scale||void 0,rotate:.5*Math.PI*(c.r||0)})},custom:function(c){"ram";c.render(c)},h:function(c){"ram";c.c.forEach(h)},v:function(c){"ram";c.c.forEach(h)}};if(this.lazy){this.rects||(this.rects={});var a=this.rects.clone(),e=[];t(d,a,e,this.rects,null);for(var l in a)delete this.rects[l];d=
|
||||||
for(var k of d)b.setBgColor(k.bg).clearRect.apply(g,k);e.forEach(h)}else h(d)};p.prototype.forgetLazyState=function(){this.rects={}};p.prototype.layout=function(d){var h={h:function(b){"ram";var f=b.x+(0|b.pad),a=0,e=b.c&&b.c.reduce((k,c)=>k+(0|c.fillx),0);e||(f+=b.w-b._w>>1,e=1);var l=f;b.c.forEach(k=>{k.x=0|l;f+=k._w;a+=0|k.fillx;l=f+Math.floor(a*(b.w-b._w)/e);k.w=0|l-k.x;k.h=0|(k.filly?b.h-(b.pad<<1):k._h);k.y=0|b.y+(0|b.pad)+((1+(0|k.valign))*(b.h-(b.pad<<1)-k.h)>>1);if(k.c)h[k.type](k)})},v:function(b){"ram";
|
Object.keys(a).map(c=>a[c]).reverse();for(var k of d)b.setBgColor(k.bg).clearRect.apply(g,k);e.forEach(h)}else h(d)};p.prototype.forgetLazyState=function(){this.rects={}};p.prototype.layout=function(d){var h={h:function(b){"ram";var f=b.x+(0|b.pad),a=0,e=b.c&&b.c.reduce((k,c)=>k+(0|c.fillx),0);e||(f+=b.w-b._w>>1,e=1);var l=f;b.c.forEach(k=>{k.x=0|l;f+=k._w;a+=0|k.fillx;l=f+Math.floor(a*(b.w-b._w)/e);k.w=0|l-k.x;k.h=0|(k.filly?b.h-(b.pad<<1):k._h);k.y=0|b.y+(0|b.pad)+((1+(0|k.valign))*(b.h-(b.pad<<
|
||||||
var f=b.y+(0|b.pad),a=0,e=b.c&&b.c.reduce((k,c)=>k+(0|c.filly),0);e||(f+=b.h-b._h>>1,e=1);var l=f;b.c.forEach(k=>{k.y=0|l;f+=k._h;a+=0|k.filly;l=f+Math.floor(a*(b.h-b._h)/e);k.h=0|l-k.y;k.w=0|(k.fillx?b.w-(b.pad<<1):k._w);k.x=0|b.x+(0|b.pad)+((1+(0|k.halign))*(b.w-(b.pad<<1)-k.w)>>1);if(k.c)h[k.type](k)})}};h[d.type](d)};p.prototype.debug=function(d,h){d||(d=this._l);h=h||1;g.setColor(h&1,h&2,h&4).drawRect(d.x+h-1,d.y+h-1,d.x+d.w-h,d.y+d.h-h);d.pad&&g.drawRect(d.x+d.pad-1,d.y+d.pad-1,d.x+d.w-d.pad,
|
1)-k.h)>>1);if(k.c)h[k.type](k)})},v:function(b){"ram";var f=b.y+(0|b.pad),a=0,e=b.c&&b.c.reduce((k,c)=>k+(0|c.filly),0);e||(f+=b.h-b._h>>1,e=1);var l=f;b.c.forEach(k=>{k.y=0|l;f+=k._h;a+=0|k.filly;l=f+Math.floor(a*(b.h-b._h)/e);k.h=0|l-k.y;k.w=0|(k.fillx?b.w-(b.pad<<1):k._w);k.x=0|b.x+(0|b.pad)+((1+(0|k.halign))*(b.w-(b.pad<<1)-k.w)>>1);if(k.c)h[k.type](k)})}};h[d.type](d)};p.prototype.debug=function(d,h){d||(d=this._l);h=h||1;g.setColor(h&1,h&2,h&4).drawRect(d.x+h-1,d.y+h-1,d.x+d.w-h,d.y+d.h-h);
|
||||||
d.y+d.h-d.pad);h++;d.c&&d.c.forEach(b=>this.debug(b,h))};p.prototype.update=function(){function d(a){"ram";b[a.type](a);if(a.r&1){var e=a._w;a._w=a._h;a._h=e}a._w=Math.max(a._w+(a.pad<<1),0|a.width);a._h=Math.max(a._h+(a.pad<<1),0|a.height)}delete this.updateNeeded;var h=g,b={txt:function(a){"ram";a.font.endsWith("%")&&(a.font="Vector"+Math.round(h.getHeight()*a.font.slice(0,-1)/100));if(a.wrap)a._h=a._w=0;else{var e=g.setFont(a.font).stringMetrics(a.label);a._w=e.width;a._h=e.height}},btn:function(a){"ram";
|
d.pad&&g.drawRect(d.x+d.pad-1,d.y+d.pad-1,d.x+d.w-d.pad,d.y+d.h-d.pad);h++;d.c&&d.c.forEach(b=>this.debug(b,h))};p.prototype.update=function(){function d(a){"ram";b[a.type](a);if(a.r&1){var e=a._w;a._w=a._h;a._h=e}a._w=Math.max(a._w+(a.pad<<1),0|a.width);a._h=Math.max(a._h+(a.pad<<1),0|a.height)}delete this.updateNeeded;var h=g,b={txt:function(a){"ram";a.font.endsWith("%")&&(a.font="Vector"+Math.round(h.getHeight()*a.font.slice(0,-1)/100));if(a.wrap)a._h=a._w=0;else{var e=g.setFont(a.font).stringMetrics(a.label);
|
||||||
a.font&&a.font.endsWith("%")&&(a.font="Vector"+Math.round(h.getHeight()*a.font.slice(0,-1)/100));var e=a.src?h.imageMetrics("function"==typeof a.src?a.src():a.src):h.setFont(a.font||"6x8:2").stringMetrics(a.label);a._h=16+e.height;a._w=20+e.width},img:function(a){"ram";var e=h.imageMetrics("function"==typeof a.src?a.src():a.src),l=a.scale||1;a._w=e.width*l;a._h=e.height*l},"":function(a){"ram";a._w=0;a._h=0},custom:function(a){"ram";a._w=0;a._h=0},h:function(a){"ram";a.c.forEach(d);a._h=a.c.reduce((e,
|
a._w=e.width;a._h=e.height}},btn:function(a){"ram";a.font&&a.font.endsWith("%")&&(a.font="Vector"+Math.round(h.getHeight()*a.font.slice(0,-1)/100));var e=a.src?h.imageMetrics("function"==typeof a.src?a.src():a.src):h.setFont(a.font||"6x8:2").stringMetrics(a.label);a._h=16+e.height;a._w=20+e.width},img:function(a){"ram";var e=h.imageMetrics("function"==typeof a.src?a.src():a.src),l=a.scale||1;a._w=e.width*l;a._h=e.height*l},"":function(a){"ram";a._w=0;a._h=0},custom:function(a){"ram";a._w=0;a._h=0},
|
||||||
l)=>Math.max(e,l._h),0);a._w=a.c.reduce((e,l)=>e+l._w,0);null==a.fillx&&a.c.some(e=>e.fillx)&&(a.fillx=1);null==a.filly&&a.c.some(e=>e.filly)&&(a.filly=1)},v:function(a){"ram";a.c.forEach(d);a._h=a.c.reduce((e,l)=>e+l._h,0);a._w=a.c.reduce((e,l)=>Math.max(e,l._w),0);null==a.fillx&&a.c.some(e=>e.fillx)&&(a.fillx=1);null==a.filly&&a.c.some(e=>e.filly)&&(a.filly=1)}},f=this._l;d(f);delete b;f.fillx||f.filly?(f.w=Bangle.appRect.w,f.h=Bangle.appRect.h,f.x=Bangle.appRect.x,f.y=Bangle.appRect.y):(f.w=f._w,
|
h:function(a){"ram";a.c.forEach(d);a._h=a.c.reduce((e,l)=>Math.max(e,l._h),0);a._w=a.c.reduce((e,l)=>e+l._w,0);null==a.fillx&&a.c.some(e=>e.fillx)&&(a.fillx=1);null==a.filly&&a.c.some(e=>e.filly)&&(a.filly=1)},v:function(a){"ram";a.c.forEach(d);a._h=a.c.reduce((e,l)=>e+l._h,0);a._w=a.c.reduce((e,l)=>Math.max(e,l._w),0);null==a.fillx&&a.c.some(e=>e.fillx)&&(a.fillx=1);null==a.filly&&a.c.some(e=>e.filly)&&(a.filly=1)}},f=this._l;d(f);delete b;f.fillx||f.filly?(f.w=Bangle.appRect.w,f.h=Bangle.appRect.h,
|
||||||
f.h=f._h,f.x=Bangle.appRect.w-f.w>>1,f.y=Bangle.appRect.y+(Bangle.appRect.h-f.h>>1));this.layout(f)};p.prototype.clear=function(d){d||(d=this._l);g.reset();void 0!==d.bgCol&&g.setBgColor(d.bgCol);g.clearRect(d.x,d.y,d.x+d.w-1,d.y+d.h-1)};exports=p
|
f.x=Bangle.appRect.x,f.y=Bangle.appRect.y):(f.w=f._w,f.h=f._h,f.x=Bangle.appRect.w-f.w>>1,f.y=Bangle.appRect.y+(Bangle.appRect.h-f.h>>1));this.layout(f)};p.prototype.clear=function(d){d||(d=this._l);g.reset();void 0!==d.bgCol&&g.setBgColor(d.bgCol);g.clearRect(d.x,d.y,d.x+d.w-1,d.y+d.h-1)};exports=p
|
Loading…
Reference in New Issue