Merge branch 'espruino:master' into calculator
|
@ -0,0 +1 @@
|
|||
0.1: New App!
|
|
@ -0,0 +1,9 @@
|
|||
Enton - Enhanced Anton Clock
|
||||
|
||||
This clock face is based on the 'Anton Clock'.
|
||||
|
||||
Things I changed:
|
||||
|
||||
- The main font for the time is now Audiowide
|
||||
- Removed the written out day name and replaced it with steps and bpm
|
||||
- Changed the date string to a (for me) more readable string
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwkE/4A/AH4A/AH4A/AH4Aw+cikf/mQDCAAIFBAwQDBBYgXCgEDAQIABn4JBkAFBgIKDgQwFmMD+UCmcgl/zEIMzmcQmYKBmYiCAAfxC4QrBl8wBwcgkYsGC4sAiMAF4UxiIGBn8QAgMSC48wgMRiEDBAISCiYcFC48v//yC4PzgJAGiAXIiczPgPzC4JyBmf/AYQXI+KcCj8wmYFCgEjAYQ3G+cjbQIABJIMzAoUin7XIADpSEK4rWGI4MhmRJBn8j+U/d4MimUTkUzIw5dBl4UBMgIXBAgMyLYKOBmQXHiSbCDgMyl8z+UjmJ1BHgJbHCgM/IYQABAgQJBYYYA/AH4AtaQU/mTvBBozWBd44KBkUSkLnBEo8jkcvBI0/CgMiDAIXHHYIXImUzJQJHH+Y+Bn6Z/ABQA=="))
|
|
@ -0,0 +1,67 @@
|
|||
Graphics.prototype.setFontAudiowide = function() {
|
||||
// Actual height 33 (36 - 4)
|
||||
var widths = atob("CiAsESQjJSQkHyQkDA==");
|
||||
var font = atob("AAAAAAAAAAAAAAAAAAAAAPAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAADgAAAAAAHgAAAAAAfgAAAAAA/gAAAAAD/gAAAAAH/gAAAAAf/AAAAAB/8AAAAAD/4AAAAAP/gAAAAAf/AAAAAB/8AAAAAD/4AAAAAP/gAAAAAf+AAAAAB/8AAAAAH/wAAAAAP/gAAAAA/+AAAAAB/8AAAAAD/wAAAAAD/gAAAAAD+AAAAAAD4AAAAAADwAAAAAADAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAAAA//+AAAAB///AAAAH///wAAAP///4AAAf///8AAA////+AAA/4AP+AAB/gAD/AAB/AA9/AAD+AB+/gAD+AD+/gAD+AD+/gAD8AH+fgAD8AP8fgAD8AP4fgAD8Af4fgAD8A/wfgAD8A/gfgAD8B/gfgAD8D/AfgAD8D+AfgAD8H+AfgAD8P8AfgAD8P4AfgAD8f4AfgAD8/wAfgAD8/gAfgAD+/gA/gAD+/AA/gAB/eAB/AAB/sAD/AAB/wAH/AAA////+AAAf///8AAAP///4AAAH///wAAAD///gAAAA//+AAAAAP/4AAAAAAAAAAAAAAAAAAAAAAAAAAAD8AAAAAAD8AAAAAAD8AAAAAAD8AAAAAAD8AAAAAAD8AAAAAAD/////gAD/////gAD/////gAD/////gAD/////gAD/////gAD/////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//gAAAAH//gAAAAP//gAD8Af//gAD8A///gAD8B///gAD8B///gAD8B/AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD+D+AfgAD//+AfgAD//+AfgAB//8AfgAA//4AfgAAf/wAfgAAP/gAfgAAB8AAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD+B+A/gAD/////gAB/////AAB/////AAA////+AAAf///8AAAP///4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//4AAAAD//8AAAAD//+AAAAD//+AAAAD//+AAAAD//+AAAAD//+AAAAAAB+AAAAAAB+AAAAAAB+AAAAAAB+AAAAAAB+AAAAAAB+AAAAAAB+AAAAAAB+AAAAAAB+AAAAAAB+AAAAAAB+AAAAAAB+AAAAAAB+AAAAAAB+AAAAAAB+AAAAAAB+AAAAAAB+AAAAAAB+AAAAD/////gAD/////gAD/////gAD/////gAD/////gAD/////gAD/////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//AAfgAD//wAfgAD//4AfgAD//8AfgAD//8AfgAD//+AfgAD8D+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B/A/gAD8B///gAD8B///gAD8A///AAD8A///AAAAAf/+AAAAAP/4AAAAAD/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB///AAAAH///wAAAf///8AAAf///8AAA////+AAB/////AAB/h+H/AAD/B+B/gAD+B+A/gAD+B+A/gAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B/A/gAD8B///gAD8B///gAD8A///AAAAAf//AAAAAf/+AAAAAH/4AAAAAB/gAAAAAAAAAAAAAAAAAAAAAAAAAAD8AAAAAAD8AAAAAAD8AAAAAAD8AAAAAAD8AAAAgAD8AAABgAD8AAAHgAD8AAAfgAD8AAA/gAD8AAD/gAD8AAP/gAD8AA//gAD8AB//AAD8AH/8AAD8Af/wAAD8A//AAAD8D/+AAAD8P/4AAAD8f/gAAAD9//AAAAD//8AAAAD//wAAAAD//gAAAAD/+AAAAAD/4AAAAAD/wAAAAAD/AAAAAAD8AAAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/gAAAAAH/4AAAAAP/8AAAH+f/+AAAf////AAA/////gAB/////gAB///A/gAD//+AfgAD//+AfgAD+D+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD+D+AfgAD//+AfgAD//+AfgAB///A/gAB/////gAA/////AAAP////AAAD+f/+AAAAAP/8AAAAAH/4AAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/AAAAAAf/wAAAAA//4AAAAB//8AAAAB//8AfgAD//+AfgAD/D+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD8B+AfgAD+B+A/gAD+B+A/gAD/B+B/gAB/////AAB/////AAA////+AAAf///8AAAP///4AAAH///wAAAB///AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAPAAAA/AAfgAAA/AAfgAAA/AAfgAAA/AAfgAAAeAAPAAAAAAAAAAAAAAAAAAAAAAAAAA");
|
||||
var scale = 1; // size multiplier for this font
|
||||
g.setFontCustom(font, 46, widths, 48+(scale<<8)+(1<<16));
|
||||
};
|
||||
|
||||
function getSteps() {
|
||||
var steps = 0;
|
||||
try{
|
||||
if (WIDGETS.wpedom !== undefined) {
|
||||
steps = WIDGETS.wpedom.getSteps();
|
||||
} else if (WIDGETS.activepedom !== undefined) {
|
||||
steps = WIDGETS.activepedom.getSteps();
|
||||
} else {
|
||||
steps = Bangle.getHealthStatus("day").steps;
|
||||
}
|
||||
} catch(ex) {
|
||||
// In case we failed, we can only show 0 steps.
|
||||
return "?";
|
||||
}
|
||||
|
||||
return Math.round(steps);
|
||||
}
|
||||
|
||||
{ // must be inside our own scope here so that when we are unloaded everything disappears
|
||||
// we also define functions using 'let fn = function() {..}' for the same reason. function decls are global
|
||||
let drawTimeout;
|
||||
|
||||
|
||||
// Actually draw the watch face
|
||||
let draw = function() {
|
||||
var x = g.getWidth() / 2;
|
||||
var y = g.getHeight() / 2;
|
||||
g.reset().clearRect(Bangle.appRect); // clear whole background (w/o widgets)
|
||||
var date = new Date();
|
||||
var timeStr = require("locale").time(date, 1); // Hour and minute
|
||||
g.setFontAlign(0, 0).setFont("Audiowide").drawString(timeStr, x, y);
|
||||
var dateStr = require("locale").date(date, 1).toUpperCase();
|
||||
g.setFontAlign(0, 0).setFont("6x8", 2).drawString(dateStr, x, y+28);
|
||||
g.setFontAlign(0, 0).setFont("6x8", 2);
|
||||
g.drawString(getSteps(), 50, y+70);
|
||||
g.drawString(Math.round(Bangle.getHealthStatus("last").bpm), g.getWidth() -37, y + 70);
|
||||
|
||||
// queue next draw
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
}, 60000 - (Date.now() % 60000));
|
||||
};
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI({
|
||||
mode : "clock",
|
||||
remove : function() {
|
||||
// Called to unload all of the clock app
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
delete Graphics.prototype.setFontAnton;
|
||||
}});
|
||||
// Load widgets
|
||||
Bangle.loadWidgets();
|
||||
draw();
|
||||
setTimeout(Bangle.drawWidgets,0);
|
||||
}
|
After Width: | Height: | Size: 905 B |
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "entonclk",
|
||||
"name": "Enton Clock",
|
||||
"version": "0.1",
|
||||
"description": "A simple clock using the Audiowide font. ",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"readme":"README.md",
|
||||
"storage": [
|
||||
{"name":"entonclk.app.js","url":"app.js"},
|
||||
{"name":"entonclk.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 2.0 KiB |
|
@ -6,3 +6,5 @@
|
|||
0.05: Added adjustment for Bangle.js magnetometer heading fix
|
||||
0.06: Fix waypoint menu always selecting last waypoint
|
||||
Fix widget adding listeners more than once
|
||||
0.07: Show checkered flag for target markers
|
||||
Single waypoints are now shown in the compass view
|
||||
|
|
|
@ -10,7 +10,7 @@ Tapping or button to switch to the next information display, swipe right for the
|
|||
|
||||
Choose either a route or a waypoint as basis for the display.
|
||||
|
||||
After this selection and availability of a GPS fix the compass will show a blue dot for your destination and a green one for possibly available waypoints on the way.
|
||||
After this selection and availability of a GPS fix the compass will show a checkered flag for your destination and a green dot for possibly available waypoints on the way.
|
||||
Waypoints are shown with name if available and distance to waypoint.
|
||||
|
||||
As long as no GPS signal is available the compass shows the heading from the build in magnetometer. When a GPS fix becomes available, the compass display shows the GPS course. This can be differentiated by the display of bubble levels on top and sides of the compass.
|
||||
|
|
|
@ -239,10 +239,16 @@ function getCompassSlice(compassDataSource){
|
|||
} else {
|
||||
bpos=Math.round(bpos*increment);
|
||||
}
|
||||
if (p.color){
|
||||
graphics.setColor(p.color);
|
||||
}
|
||||
if (p.icon){
|
||||
graphics.drawImage(p.icon, bpos,y+height-12, {rotate:0,scale:2});
|
||||
} else {
|
||||
graphics.fillCircle(bpos,y+height-12,Math.floor(width*0.03));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (compassDataSource.getMarkers){
|
||||
for (let m of compassDataSource.getMarkers()){
|
||||
g.setColor(m.fillcolor);
|
||||
|
@ -595,8 +601,8 @@ function showBackgroundMenu(){
|
|||
"title" : "Background",
|
||||
back : showMenu,
|
||||
},
|
||||
"Start" : ()=>{ E.showPrompt("Start?").then((v)=>{ if (v) {WIDGETS.gpstrek.start(true); removeMenu();} else {E.showMenu(mainmenu);}});},
|
||||
"Stop" : ()=>{ E.showPrompt("Stop?").then((v)=>{ if (v) {WIDGETS.gpstrek.stop(true); removeMenu();} else {E.showMenu(mainmenu);}});},
|
||||
"Start" : ()=>{ E.showPrompt("Start?").then((v)=>{ if (v) {WIDGETS.gpstrek.start(true); removeMenu();} else {showMenu();}}).catch(()=>{E.showMenu(mainmenu);});},
|
||||
"Stop" : ()=>{ E.showPrompt("Stop?").then((v)=>{ if (v) {WIDGETS.gpstrek.stop(true); removeMenu();} else {showMenu();}}).catch(()=>{E.showMenu(mainmenu);});},
|
||||
};
|
||||
E.showMenu(menu);
|
||||
}
|
||||
|
@ -677,13 +683,15 @@ function setClosestWaypoint(route, startindex, progress){
|
|||
|
||||
let screen = 1;
|
||||
|
||||
const finishIcon = atob("CggB//meZmeZ+Z5n/w==");
|
||||
|
||||
const compassSliceData = {
|
||||
getCourseType: function(){
|
||||
return (state.currentPos && state.currentPos.course) ? "GPS" : "MAG";
|
||||
},
|
||||
getCourse: function (){
|
||||
if(compassSliceData.getCourseType() == "GPS") return state.currentPos.course;
|
||||
return state.compassHeading?360-state.compassHeading:undefined;
|
||||
return state.compassHeading?state.compassHeading:undefined;
|
||||
},
|
||||
getPoints: function (){
|
||||
let points = [];
|
||||
|
@ -691,7 +699,10 @@ const compassSliceData = {
|
|||
points.push({bearing:bearing(state.currentPos, state.route.currentWaypoint), color:"#0f0"});
|
||||
}
|
||||
if (state.currentPos && state.currentPos.lon && state.route){
|
||||
points.push({bearing:bearing(state.currentPos, getLast(state.route)), color:"#00f"});
|
||||
points.push({bearing:bearing(state.currentPos, getLast(state.route)), icon: finishIcon});
|
||||
}
|
||||
if (state.currentPos && state.currentPos.lon && state.waypoint){
|
||||
points.push({bearing:bearing(state.currentPos, state.waypoint), icon: finishIcon});
|
||||
}
|
||||
return points;
|
||||
},
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "gpstrek",
|
||||
"name": "GPS Trekking",
|
||||
"version": "0.06",
|
||||
"version": "0.07",
|
||||
"description": "Helper for tracking the status/progress during hiking. Do NOT depend on this for navigation!",
|
||||
"icon": "icon.png",
|
||||
"screenshots": [{"url":"screen1.png"},{"url":"screen2.png"},{"url":"screen3.png"},{"url":"screen4.png"}],
|
||||
|
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.8 KiB |
|
@ -24,7 +24,7 @@ function onGPS(fix) {
|
|||
}
|
||||
|
||||
function onMag(e) {
|
||||
if (!state.compassHeading) state.compassHeading = 360-e.heading;
|
||||
if (!state.compassHeading) state.compassHeading = e.heading;
|
||||
|
||||
//if (a+180)mod 360 == b then
|
||||
//return (a+b)/2 mod 360 and ((a+b)/2 mod 360) + 180 (they are both the solution, so you may choose one depending if you prefer counterclockwise or clockwise direction)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
|
@ -0,0 +1,7 @@
|
|||
# Henkinen
|
||||
|
||||
By Jukio Kallio
|
||||
|
||||
A tiny app helping you to breath and relax.
|
||||
|
||||

|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwkEogA0/4AKCpNPCxYAB+gtTGJQuOGBAWPGAwuQGAwXH+cykc/C6UhgMSkMQiQXKBQsgiYFDmMCMBIIEmAWEDAUDC5nzBwogDMYgXHBoohJC4wuJEQwXG+ALDmUQgMjEYcPC5MhAYXxgAACj4ICVYYXGIwXzCwYABHAUwC5HyEwXwC4pEC+MvC4/xEoUQC4sBHIQlCC4vwIxBIEGYQXFmJKCC45ECfQQXIRoiRGC5EiOxB4EBwQXdI653XU67XX+QJCPAwrC+JKCC4v/gZIIHIUwCAQXGkIDCSIg4C/8SC5PwEwX/mUQgMjAwXzJQQXH+ZICAA8wEYYXGBgoAEEQoXHGBIhFC44OBcgQADmIgFC5H/kAYEmMCBooXDp4KFkMBiUhiCjDAAX0C5RjBmUjPo4XMABQXEMAwALCwgwRFwowRCwwwPFw4xOCpIArA"))
|
|
@ -0,0 +1,127 @@
|
|||
// Henkinen
|
||||
//
|
||||
// Bangle.js 2 breathing helper
|
||||
// by Jukio Kallio
|
||||
// www.jukiokallio.com
|
||||
|
||||
require("FontHaxorNarrow7x17").add(Graphics);
|
||||
|
||||
// settings
|
||||
const breath = {
|
||||
theme: "default",
|
||||
x:0, y:0, w:0, h:0,
|
||||
size: 60,
|
||||
|
||||
bgcolor: g.theme.bg,
|
||||
incolor: g.theme.fg,
|
||||
keepcolor: g.theme.fg,
|
||||
outcolor: g.theme.fg,
|
||||
|
||||
font: "HaxorNarrow7x17", fontsize: 1,
|
||||
textcolor: g.theme.fg,
|
||||
texty: 18,
|
||||
|
||||
in: 4000,
|
||||
keep: 7000,
|
||||
out: 8000
|
||||
};
|
||||
|
||||
// set some additional settings
|
||||
breath.w = g.getWidth(); // size of the background
|
||||
breath.h = g.getHeight();
|
||||
breath.x = breath.w * 0.5; // position of the circles
|
||||
breath.y = breath.h * 0.45;
|
||||
breath.texty = breath.y + breath.size + breath.texty; // text position
|
||||
|
||||
var wait = 100; // wait time, normally a minute
|
||||
var time = 0; // for time keeping
|
||||
|
||||
|
||||
// timeout used to update every minute
|
||||
var drawTimeout;
|
||||
|
||||
// schedule a draw for the next minute
|
||||
function queueDraw() {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
}, wait - (Date.now() % wait));
|
||||
}
|
||||
|
||||
|
||||
// main function
|
||||
function draw() {
|
||||
// make date object
|
||||
var date = new Date();
|
||||
|
||||
// update current time
|
||||
time += wait - (Date.now() % wait);
|
||||
if (time > breath.in + breath.keep + breath.out) time = 0; // reset time
|
||||
|
||||
// Reset the state of the graphics library
|
||||
g.reset();
|
||||
|
||||
// Clear the area where we want to draw the time
|
||||
g.setColor(breath.bgcolor);
|
||||
g.fillRect(0, 0, breath.w, breath.h);
|
||||
|
||||
// calculate circle size
|
||||
var circle = 0;
|
||||
if (time < breath.in) {
|
||||
// breath in
|
||||
circle = time / breath.in;
|
||||
g.setColor(breath.incolor);
|
||||
|
||||
} else if (time < breath.in + breath.keep) {
|
||||
// keep breath
|
||||
circle = 1;
|
||||
g.setColor(breath.keepcolor);
|
||||
|
||||
} else if (time < breath.in + breath.keep + breath.out) {
|
||||
// breath out
|
||||
circle = ((breath.in + breath.keep + breath.out) - time) / breath.out;
|
||||
g.setColor(breath.outcolor);
|
||||
|
||||
}
|
||||
|
||||
// draw breath circle
|
||||
g.fillCircle(breath.x, breath.y, breath.size * circle);
|
||||
|
||||
// breath area
|
||||
g.setColor(breath.textcolor);
|
||||
g.drawCircle(breath.x, breath.y, breath.size);
|
||||
|
||||
// draw text
|
||||
g.setFontAlign(0,0).setFont(breath.font, breath.fontsize).setColor(breath.textcolor);
|
||||
|
||||
if (time < breath.in) {
|
||||
// breath in
|
||||
g.drawString("Breath in", breath.x, breath.texty);
|
||||
|
||||
} else if (time < breath.in + breath.keep) {
|
||||
// keep breath
|
||||
g.drawString("Keep it in", breath.x, breath.texty);
|
||||
|
||||
} else if (time < breath.in + breath.keep + breath.out) {
|
||||
// breath out
|
||||
g.drawString("Breath out", breath.x, breath.texty);
|
||||
|
||||
}
|
||||
|
||||
// queue draw
|
||||
queueDraw();
|
||||
}
|
||||
|
||||
|
||||
// Clear the screen once, at startup
|
||||
g.clear();
|
||||
// draw immediately at first
|
||||
draw();
|
||||
|
||||
|
||||
// keep LCD on
|
||||
Bangle.setLCDPower(1);
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI("clock");
|
After Width: | Height: | Size: 2.9 KiB |
|
@ -0,0 +1,15 @@
|
|||
{ "id": "henkinen",
|
||||
"name": "Henkinen - Tiny Breathing Helper",
|
||||
"shortName":"Henkinen",
|
||||
"version":"0.01",
|
||||
"description": "A tiny app helping you to breath and relax.",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot1.png"}],
|
||||
"tags": "outdoors",
|
||||
"supports" : ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"henkinen.app.js","url":"app.js"},
|
||||
{"name":"henkinen.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 13 KiB |
|
@ -8,3 +8,7 @@
|
|||
Add swipe-to-exit
|
||||
0.08: Only use fast loading for switching to clock to prevent problems in full screen apps
|
||||
0.09: Remove fast load option since clocks containing Bangle.loadWidgets are now always normally loaded
|
||||
0.10: changed the launch.json file name in iconlaunch.json ( launch.cache.json -> iconlaunch.cache.json)
|
||||
used Object.assing for the settings
|
||||
fix cache not deleted when "showClocks" options is changed
|
||||
added timeOut to return to the clock
|
||||
|
|
|
@ -1,12 +1,21 @@
|
|||
{
|
||||
const s = require("Storage");
|
||||
const settings = s.readJSON("launch.json", true) || { showClocks: true, fullscreen: false,direct:false,swipeExit:false,oneClickExit:false};
|
||||
const settings = Object.assign({
|
||||
showClocks: true,
|
||||
fullscreen: false,
|
||||
direct: false,
|
||||
oneClickExit: false,
|
||||
swipeExit: false,
|
||||
timeOut:"Off"
|
||||
}, s.readJSON("iconlaunch.json", true) || {});
|
||||
|
||||
console.log(settings);
|
||||
if (!settings.fullscreen) {
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
}
|
||||
let launchCache = s.readJSON("launch.cache.json", true)||{};
|
||||
let launchHash = require("Storage").hash(/\.info/);
|
||||
let launchCache = s.readJSON("iconlaunch.cache.json", true)||{};
|
||||
let launchHash = s.hash(/\.info/);
|
||||
if (launchCache.hash!=launchHash) {
|
||||
launchCache = {
|
||||
hash : launchHash,
|
||||
|
@ -20,7 +29,7 @@
|
|||
if (a.name>b.name) return 1;
|
||||
return 0;
|
||||
}) };
|
||||
s.writeJSON("launch.cache.json", launchCache);
|
||||
s.writeJSON("iconlaunch.cache.json", launchCache);
|
||||
}
|
||||
let scroll = 0;
|
||||
let selectedItem = -1;
|
||||
|
@ -198,6 +207,11 @@
|
|||
|
||||
|
||||
if (settings.oneClickExit) mode.btn = returnToClock;
|
||||
if (settings.timeOut!="Off"){
|
||||
let time=parseInt(settings.timeOut); //the "s" will be trimmed by the parseInt
|
||||
setTimeout(returnToClock,time*1000);
|
||||
}
|
||||
|
||||
|
||||
Bangle.setUI(mode);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "iconlaunch",
|
||||
"name": "Icon Launcher",
|
||||
"shortName" : "Icon launcher",
|
||||
"version": "0.09",
|
||||
"version": "0.10",
|
||||
"icon": "app.png",
|
||||
"description": "A launcher inspired by smartphones, with an icon-only scrollable menu.",
|
||||
"tags": "tool,system,launcher",
|
||||
|
@ -12,6 +12,7 @@
|
|||
{ "name": "iconlaunch.app.js", "url": "app.js" },
|
||||
{ "name": "iconlaunch.settings.js", "url": "settings.js" }
|
||||
],
|
||||
"data": [{"name":"iconlaunch.json"},{"name":"iconlaunch.cache.json"}],
|
||||
"screenshots": [{ "url": "screenshot1.png" }, { "url": "screenshot2.png" }],
|
||||
"readme": "README.md"
|
||||
}
|
||||
|
|
|
@ -1,24 +1,29 @@
|
|||
// make sure to enclose the function in parentheses
|
||||
(function(back) {
|
||||
const s = require("Storage");
|
||||
let settings = Object.assign({
|
||||
showClocks: true,
|
||||
fullscreen: false,
|
||||
direct: false,
|
||||
oneClickExit: false,
|
||||
swipeExit: false
|
||||
}, require("Storage").readJSON("launch.json", true) || {});
|
||||
swipeExit: false,
|
||||
timeOut:"Off"
|
||||
}, s.readJSON("iconlaunch.json", true) || {});
|
||||
|
||||
let fonts = g.getFonts();
|
||||
function save(key, value) {
|
||||
settings[key] = value;
|
||||
require("Storage").write("launch.json",settings);
|
||||
s.write("iconlaunch.json",settings);
|
||||
}
|
||||
const timeOutChoices = [/*LANG*/"Off", "10s", "15s", "20s", "30s"];
|
||||
const appMenu = {
|
||||
"": { "title": /*LANG*/"Launcher" },
|
||||
/*LANG*/"< Back": back,
|
||||
/*LANG*/"Show Clocks": {
|
||||
value: settings.showClocks == true,
|
||||
onchange: (m) => { save("showClocks", m) }
|
||||
onchange: (m) => {
|
||||
save("showClocks", m);
|
||||
s.erase("iconlaunch.cache.json"); //delete the cache app list
|
||||
}
|
||||
},
|
||||
/*LANG*/"Fullscreen": {
|
||||
value: settings.fullscreen == true,
|
||||
|
@ -35,7 +40,15 @@
|
|||
/*LANG*/"Swipe exit": {
|
||||
value: settings.swipeExit == true,
|
||||
onchange: m => { save("swipeExit", m) }
|
||||
},
|
||||
/*LANG*/'Time Out': {
|
||||
value: timeOutChoices.indexOf(settings.timeOut),
|
||||
min: 0, max: timeOutChoices.length-1,
|
||||
format: v => timeOutChoices[v],
|
||||
onchange: m => {
|
||||
save("timeOut", timeOutChoices[m]);
|
||||
}
|
||||
},
|
||||
};
|
||||
E.showMenu(appMenu);
|
||||
});
|
||||
|
|
|
@ -17,3 +17,4 @@
|
|||
0.15: Support for unload and quick return to the clock on 2v16
|
||||
0.16: Use a cache of app.info files to speed up loading the launcher
|
||||
0.17: Don't display 'Loading...' now the watch has its own loading screen
|
||||
0.18: Add 'back' icon in top-left to go back to clock
|
||||
|
|
|
@ -41,6 +41,17 @@ let apps = launchCache.apps;
|
|||
// Now apps list is loaded - render
|
||||
if (!settings.fullscreen)
|
||||
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"));
|
||||
}
|
||||
|
||||
E.showScroller({
|
||||
h : 64*scaleval, c : apps.length,
|
||||
draw : (i, r) => {
|
||||
|
@ -62,19 +73,12 @@ E.showScroller({
|
|||
} else {
|
||||
load(app.src);
|
||||
}
|
||||
}
|
||||
},
|
||||
back : returnToClock
|
||||
});
|
||||
g.flip(); // force a render before widgets have finished drawing
|
||||
|
||||
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"));
|
||||
}
|
||||
|
||||
|
||||
// on bangle.js 2, the screen is used for navigating, so the single button goes back
|
||||
// on bangle.js 1, the buttons are used for navigating
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "launch",
|
||||
"name": "Launcher",
|
||||
"shortName": "Launcher",
|
||||
"version": "0.17",
|
||||
"version": "0.18",
|
||||
"description": "This is needed to display a menu allowing you to choose your own applications. You can replace this with a customised launcher.",
|
||||
"readme": "README.md",
|
||||
"icon": "app.png",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
|
@ -0,0 +1,7 @@
|
|||
# Palikkainen
|
||||
|
||||
By Jukio Kallio
|
||||
|
||||
A minimal watch face consisting of blocks. Minutes fills the blocks, and after 12 hours it starts to empty them.
|
||||
|
||||

|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwkBiIA0/4AKCpMfCxYAB+ItTGJQuOGBAWPGAwuQGAwXvCyJgFC+PwgAAEh4X/C/6//A4gX/C/6//A4QX/C/6/vC6sfCyPxC+ZgSCwgwRFwowRCwwwPFw4xOCpIArA"))
|
|
@ -0,0 +1,184 @@
|
|||
// Palikkainen
|
||||
//
|
||||
// Bangle.js 2 watch face
|
||||
// by Jukio Kallio
|
||||
// www.jukiokallio.com
|
||||
|
||||
require("Font6x8").add(Graphics);
|
||||
|
||||
// settings
|
||||
const watch = {
|
||||
x:0, y:0, w:0, h:0,
|
||||
bgcolor:g.theme.bg,
|
||||
fgcolor:g.theme.fg,
|
||||
font: "6x8", fontsize: 1,
|
||||
finland:true, // change if you want Finnish style date, or US style
|
||||
};
|
||||
|
||||
// set some additional settings
|
||||
watch.w = g.getWidth(); // size of the background
|
||||
watch.h = g.getHeight();
|
||||
watch.x = watch.w * 0.5; // position of the circles
|
||||
watch.y = watch.h * 0.45;
|
||||
|
||||
const dateWeekday = { 0: "SUN", 1: "MON", 2: "TUE", 3: "WED", 4:"THU", 5:"FRI", 6:"SAT" }; // weekdays
|
||||
|
||||
var wait = 60000; // wait time, normally a minute
|
||||
|
||||
|
||||
// timeout used to update every minute
|
||||
var drawTimeout;
|
||||
|
||||
// schedule a draw for the next minute
|
||||
function queueDraw() {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
}, wait - (Date.now() % wait));
|
||||
}
|
||||
|
||||
|
||||
// main function
|
||||
function draw() {
|
||||
// make date object
|
||||
var date = new Date();
|
||||
|
||||
// work out the date string
|
||||
var dateDay = date.getDate();
|
||||
var dateMonth = date.getMonth() + 1;
|
||||
var dateYear = date.getFullYear();
|
||||
var dateStr = dateWeekday[date.getDay()] + " " + dateMonth + "." + dateDay + "." + dateYear;
|
||||
if (watch.finland) dateStr = dateWeekday[date.getDay()] + " " + dateDay + "." + dateMonth + "." + dateYear; // the true way of showing date
|
||||
|
||||
// Reset the state of the graphics library
|
||||
g.reset();
|
||||
|
||||
// Clear the area where we want to draw the time
|
||||
g.setColor(watch.bgcolor);
|
||||
g.fillRect(0, 0, watch.w, watch.h);
|
||||
|
||||
// setup watch face
|
||||
const block = {
|
||||
w: watch.w / 2 - 6,
|
||||
h: 18,
|
||||
pad: 4,
|
||||
};
|
||||
|
||||
// get hours and minutes
|
||||
var hour = date.getHours();
|
||||
var minute = date.getMinutes();
|
||||
|
||||
// calculate size of the block face
|
||||
var facew = block.w * 2 + block.pad;
|
||||
var faceh = (block.h + block.pad) * 6;
|
||||
|
||||
|
||||
// loop through first 12 hours and draw blocks accordingly
|
||||
g.setColor(watch.fgcolor); // set foreground color
|
||||
|
||||
for (var i = 0; i < 12; i++) {
|
||||
// where to draw
|
||||
var x = watch.x - facew / 2; // starting position
|
||||
var y = watch.y + faceh / 2 - block.h - block.pad / 2; // draw blocks from bottom up
|
||||
if (i > 5) {
|
||||
// second column
|
||||
x += block.w + block.pad;
|
||||
y -= (block.h + block.pad) * (i - 6);
|
||||
} else {
|
||||
// first column
|
||||
x += 0;
|
||||
y -= (block.h + block.pad) * i;
|
||||
}
|
||||
|
||||
if (i < hour) {
|
||||
// draw full hour block
|
||||
g.fillRect(x, y, x + block.w, y + block.h);
|
||||
} else if (i == hour) {
|
||||
// draw minutes
|
||||
g.fillRect(x, y, x + block.w * (minute / 60), y + block.h);
|
||||
|
||||
// minute reading help
|
||||
for (var m = 1; m < 12; m++) {
|
||||
// set color
|
||||
if (m * 5 < minute) g.setColor(watch.bgcolor); else g.setColor(watch.fgcolor);
|
||||
|
||||
var mlineh = 1; // minute line height
|
||||
if (m == 3 || m == 6 || m == 9) mlineh = 3; // minute line height at 15, 30 and 45 minutes
|
||||
|
||||
g.drawLine(x + (block.w / 12 * m), y + block.h / 2 - mlineh, x + (block.w / 12 * m), y + block.h / 2 + mlineh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// loop through second 12 hours and draw blocks accordingly
|
||||
if (hour >= 12) {
|
||||
g.setColor(watch.bgcolor); // set foreground color
|
||||
|
||||
for (var i2 = 0; i2 < 12; i2++) {
|
||||
// where to draw
|
||||
var x2 = watch.x - facew / 2; // starting position
|
||||
var y2 = watch.y + faceh / 2 - block.h - block.pad / 2; // draw blocks from bottom up
|
||||
if (i2 > 5) {
|
||||
// second column
|
||||
x2 += block.w + block.pad;
|
||||
y2 -= (block.h + block.pad) * (i2 - 6);
|
||||
} else {
|
||||
// first column
|
||||
x2 += 0;
|
||||
y2 -= (block.h + block.pad) * i2;
|
||||
}
|
||||
|
||||
if (i2 < hour % 12) {
|
||||
// draw full hour block
|
||||
g.fillRect(x2, y2, x2 + block.w, y2 + block.h);
|
||||
} else if (i2 == hour % 12) {
|
||||
// draw minutes
|
||||
g.fillRect(x2, y2, x2 + block.w * (minute / 60), y2 + block.h);
|
||||
|
||||
// minute reading help
|
||||
for (var m2 = 1; m2 < 12; m2++) {
|
||||
// set color
|
||||
if (m2 * 5 < minute) g.setColor(watch.fgcolor); else g.setColor(watch.bgcolor);
|
||||
|
||||
var mlineh2 = 1; // minute line height
|
||||
if (m2 == 3 || m2 == 6 || m2 == 9) mlineh2 = 3; // minute line height at 15, 30 and 45 minutes
|
||||
|
||||
g.drawLine(x2 + (block.w / 12 * m2), y2 + block.h / 2 - mlineh2, x2 + (block.w / 12 * m2), y2 + block.h / 2 + mlineh2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// draw date
|
||||
var datey = 11;
|
||||
g.setFontAlign(0,-1).setFont(watch.font, watch.fontsize).setColor(watch.fgcolor);
|
||||
g.drawString(dateStr, watch.x, watch.y + faceh / 2 + datey);
|
||||
|
||||
|
||||
// queue draw
|
||||
queueDraw();
|
||||
}
|
||||
|
||||
|
||||
// Clear the screen once, at startup
|
||||
g.clear();
|
||||
// draw immediately at first
|
||||
draw();
|
||||
|
||||
|
||||
// Stop updates when LCD is off, restart when on
|
||||
Bangle.on('lcdPower',on=>{
|
||||
if (on) {
|
||||
draw(); // draw immediately, queue redraw
|
||||
} else { // stop draw timer
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI("clock");
|
After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1,16 @@
|
|||
{ "id": "palikkainen",
|
||||
"name": "Palikkainen - A blocky watch face",
|
||||
"shortName":"Palikkainen",
|
||||
"version":"0.01",
|
||||
"description": "A minimal watch face consisting of blocks.",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot1.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports" : ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"palikkainen.app.js","url":"app.js"},
|
||||
{"name":"palikkainen.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 14 KiB |
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
|
@ -0,0 +1,7 @@
|
|||
# Pisteinen
|
||||
|
||||
By Jukio Kallio
|
||||
|
||||
A Minimal digital watch face consisting of dots. Big dots for hours, small dots for minutes.
|
||||
|
||||

|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwkDmYA0/4AKCpM/CxYAB+YtTGJQuOGBAWPGAwuQGAwXvCyJgFC+UhiQDNC43ygEAl4DLC4/xBYMfAZYXfI653XX/6//X/6//O5gBKU5gGBAZAXfI66//C7s/CyPzC+ZgSCwgwRFwowRCwwwPFw4xOCpIArA=="))
|
|
@ -0,0 +1,121 @@
|
|||
// Pisteinen
|
||||
//
|
||||
// Bangle.js 2 watch face
|
||||
// by Jukio Kallio
|
||||
// www.jukiokallio.com
|
||||
|
||||
|
||||
// settings
|
||||
const watch = {
|
||||
x:0, y:0, w:0, h:0,
|
||||
bgcolor:g.theme.bg,
|
||||
fgcolor:g.theme.fg,
|
||||
};
|
||||
|
||||
// set some additional settings
|
||||
watch.w = g.getWidth(); // size of the background
|
||||
watch.h = g.getHeight();
|
||||
watch.x = watch.w * 0.5; // position of the circles
|
||||
watch.y = watch.h * 0.5;
|
||||
|
||||
var wait = 60000; // wait time, normally a minute
|
||||
|
||||
|
||||
// timeout used to update every minute
|
||||
var drawTimeout;
|
||||
|
||||
// schedule a draw for the next minute
|
||||
function queueDraw() {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
}, wait - (Date.now() % wait));
|
||||
}
|
||||
|
||||
|
||||
// main function
|
||||
function draw() {
|
||||
// make date object
|
||||
var date = new Date();
|
||||
|
||||
// Reset the state of the graphics library
|
||||
g.reset();
|
||||
|
||||
// Clear the area where we want to draw the time
|
||||
g.setColor(watch.bgcolor);
|
||||
g.fillRect(0, 0, watch.w, watch.h);
|
||||
|
||||
// setup watch face
|
||||
const hball = {
|
||||
size: 9,
|
||||
pad: 9,
|
||||
};
|
||||
const mball = {
|
||||
size: 3,
|
||||
pad: 4,
|
||||
pad2: 2,
|
||||
};
|
||||
|
||||
// get hours and minutes
|
||||
var hour = date.getHours();
|
||||
var minute = date.getMinutes();
|
||||
|
||||
// calculate size of the hour face
|
||||
var hfacew = (hball.size * 2 + hball.pad) * 6 - hball.pad;
|
||||
var hfaceh = (hball.size * 2 + hball.pad) * 4 - hball.pad;
|
||||
var mfacew = (mball.size * 2 + mball.pad) * 15 - mball.pad + mball.pad2 * 2;
|
||||
var mfaceh = (mball.size * 2 + mball.pad) * 4 - mball.pad;
|
||||
var faceh = hfaceh + mfaceh + hball.pad + mball.pad;
|
||||
|
||||
g.setColor(watch.fgcolor); // set foreground color
|
||||
|
||||
// draw hour balls
|
||||
for (var i = 0; i < 24; i++) {
|
||||
var x = ((hball.size * 2 + hball.pad) * (i % 6)) + (watch.x - hfacew / 2) + hball.size;
|
||||
var y = watch.y - faceh / 2 + hball.size;
|
||||
if (i >= 6) y += hball.size * 2 + hball.pad;
|
||||
if (i >= 12) y += hball.size * 2 + hball.pad;
|
||||
if (i >= 18) y += hball.size * 2 + hball.pad;
|
||||
|
||||
if (i < hour) g.fillCircle(x, y, hball.size); else g.drawCircle(x, y, hball.size);
|
||||
}
|
||||
|
||||
// draw minute balls
|
||||
for (var j = 0; j < 60; j++) {
|
||||
var x2 = ((mball.size * 2 + mball.pad) * (j % 15)) + (watch.x - mfacew / 2) + mball.size;
|
||||
if (j % 15 >= 5) x2 += mball.pad2;
|
||||
if (j % 15 >= 10) x2 += mball.pad2;
|
||||
var y2 = watch.y - faceh / 2 + hfaceh + mball.size + hball.pad + mball.pad;
|
||||
if (j >= 15) y2 += mball.size * 2 + mball.pad;
|
||||
if (j >= 30) y2 += mball.size * 2 + mball.pad;
|
||||
if (j >= 45) y2 += mball.size * 2 + mball.pad;
|
||||
|
||||
if (j < minute) g.fillCircle(x2, y2, mball.size); else g.drawCircle(x2, y2, mball.size);
|
||||
}
|
||||
|
||||
|
||||
// queue draw
|
||||
queueDraw();
|
||||
}
|
||||
|
||||
|
||||
// Clear the screen once, at startup
|
||||
g.clear();
|
||||
// draw immediately at first
|
||||
draw();
|
||||
|
||||
|
||||
// Stop updates when LCD is off, restart when on
|
||||
Bangle.on('lcdPower',on=>{
|
||||
if (on) {
|
||||
draw(); // draw immediately, queue redraw
|
||||
} else { // stop draw timer
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI("clock");
|
After Width: | Height: | Size: 2.3 KiB |
|
@ -0,0 +1,16 @@
|
|||
{ "id": "pisteinen",
|
||||
"name": "Pisteinen - Dotted watch face",
|
||||
"shortName":"Pisteinen",
|
||||
"version":"0.01",
|
||||
"description": "A minimal digital watch face made with dots.",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot1.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports" : ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"pisteinen.app.js","url":"app.js"},
|
||||
{"name":"pisteinen.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 5.9 KiB |
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
|
@ -0,0 +1,7 @@
|
|||
# Poikkipuinen
|
||||
|
||||
By Jukio Kallio
|
||||
|
||||
A Minimal digital watch face. Follows the theme colors.
|
||||
|
||||

|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwkEogA0/4AKCpNPCxYAB+gtTGJQuOGBAWPGAwuQGAwXamQULkYXGBQUgn4WJ+cCMAwXNiQXV+MBC6swh4XU+cAn4XU+IUBC6kgj4XUIwKnV+EDC6sQl4XU+UBd6q8BC6q8BC6i8CC6i8CC6a8DC6a8DC6a8DC6S8EC6S8EC6S8EC6K8FC6K8FC6C8BIwwXOXgwXQXgwXQkIWHd6IXPp4GBmQWJAAMjAQP0C4wAPC7hgDABwWEGCIuFGCIWGGB4uHGJwVJAFY="))
|
|
@ -0,0 +1,158 @@
|
|||
// Poikkipuinen
|
||||
//
|
||||
// Bangle.js 2 watch face
|
||||
// by Jukio Kallio
|
||||
// www.jukiokallio.com
|
||||
|
||||
require("Font5x9Numeric7Seg").add(Graphics);
|
||||
require("FontSinclair").add(Graphics);
|
||||
|
||||
// settings
|
||||
const watch = {
|
||||
x:0, y:0, w:0, h:0,
|
||||
bgcolor:g.theme.bg,
|
||||
fgcolor:g.theme.fg,
|
||||
font: "5x9Numeric7Seg", fontsize: 1,
|
||||
font2: "Sinclair", font2size: 1,
|
||||
finland:true, // change if you want Finnish style date, or US style
|
||||
};
|
||||
|
||||
|
||||
// set some additional settings
|
||||
watch.w = g.getWidth(); // size of the background
|
||||
watch.h = g.getHeight();
|
||||
watch.x = watch.w * 0.5; // position of the circles
|
||||
watch.y = watch.h * 0.41;
|
||||
|
||||
const dateWeekday = { 0: "SUN", 1: "MON", 2: "TUE", 3: "WED", 4:"THU", 5:"FRI", 6:"SAT" }; // weekdays
|
||||
|
||||
var wait = 60000; // wait time, normally a minute
|
||||
|
||||
|
||||
// timeout used to update every minute
|
||||
var drawTimeout;
|
||||
|
||||
// schedule a draw for the next minute
|
||||
function queueDraw() {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
}, wait - (Date.now() % wait));
|
||||
}
|
||||
|
||||
|
||||
// main function
|
||||
function draw() {
|
||||
// make date object
|
||||
var date = new Date();
|
||||
|
||||
// work out the date string
|
||||
var dateDay = date.getDate();
|
||||
var dateMonth = date.getMonth() + 1;
|
||||
var dateYear = date.getFullYear();
|
||||
var dateStr = dateMonth + "." + dateDay + "." + dateYear;
|
||||
if (watch.finland) dateStr = dateDay + "." + dateMonth + "." + dateYear; // the true way of showing date
|
||||
var dateStr2 = dateWeekday[date.getDay()];
|
||||
|
||||
// Reset the state of the graphics library
|
||||
g.reset();
|
||||
|
||||
// Clear the area where we want to draw the time
|
||||
g.setColor(watch.bgcolor);
|
||||
g.fillRect(0, 0, watch.w, watch.h);
|
||||
|
||||
// set foreground color
|
||||
g.setColor(watch.fgcolor);
|
||||
g.setFontAlign(1,-1).setFont(watch.font, watch.fontsize);
|
||||
|
||||
// watch face size
|
||||
var facew, faceh; // halves of the size for easier calculation
|
||||
facew = 50;
|
||||
faceh = 59;
|
||||
|
||||
// save hour and minute y positions
|
||||
var houry, minutey;
|
||||
|
||||
// draw hour meter
|
||||
g.drawLine(watch.x - facew, watch.y - faceh, watch.x - facew, watch.y + faceh);
|
||||
var lines = 13;
|
||||
var lineh = faceh * 2 / (lines - 2);
|
||||
for (var i = 1; i < lines; i++) {
|
||||
var w = 3;
|
||||
var y = faceh - lineh * (i - 1);
|
||||
|
||||
if (i % 3 == 0) {
|
||||
// longer line and numbers every 3
|
||||
w = 5;
|
||||
g.drawString(i, watch.x - facew - 2, y + watch.y);
|
||||
}
|
||||
|
||||
g.drawLine(watch.x - facew, y + watch.y, watch.x - facew + w, y + watch.y);
|
||||
|
||||
// get hour y position
|
||||
var hour = date.getHours() % 12; // modulate away the 24h
|
||||
if (hour == 0) hour = 12; // fix a problem with 0-23 hours
|
||||
//var hourMin = date.getMinutes() / 60; // move hour line by minutes
|
||||
var hourMin = Math.floor(date.getMinutes() / 15) / 4; // move hour line by 15-minutes
|
||||
if (hour == 12) hourMin = 0; // don't do minute moving if 12 (line ends there)
|
||||
if (i == hour) houry = y - (lineh * hourMin);
|
||||
}
|
||||
|
||||
// draw minute meter
|
||||
g.drawLine(watch.x + facew, watch.y - faceh, watch.x + facew, watch.y + faceh);
|
||||
g.setFontAlign(-1,-1);
|
||||
lines = 60;
|
||||
lineh = faceh * 2 / (lines - 1);
|
||||
for (i = 0; i < lines; i++) {
|
||||
var mw = 3;
|
||||
var my = faceh - lineh * i;
|
||||
|
||||
if (i % 15 == 0 && i != 0) {
|
||||
// longer line and numbers every 3
|
||||
mw = 5;
|
||||
g.drawString(i, watch.x + facew + 4, my + watch.y);
|
||||
}
|
||||
|
||||
//if (i % 2 == 0 || i == 15 || i == 45)
|
||||
g.drawLine(watch.x + facew, my + watch.y, watch.x + facew - mw, my + watch.y);
|
||||
|
||||
// get minute y position
|
||||
if (i == date.getMinutes()) minutey = my;
|
||||
}
|
||||
|
||||
// draw the time
|
||||
var timexpad = 8;
|
||||
g.drawLine(watch.x - facew + timexpad, watch.y + houry, watch.x + facew - timexpad, watch.y + minutey);
|
||||
|
||||
// draw date
|
||||
var datey = 14;
|
||||
g.setFontAlign(0,-1);
|
||||
g.drawString(dateStr, watch.x, watch.y + faceh + datey);
|
||||
g.setFontAlign(0,-1).setFont(watch.font2, watch.font2size);
|
||||
g.drawString(dateStr2, watch.x, watch.y + faceh + datey*2);
|
||||
|
||||
// queue draw
|
||||
queueDraw();
|
||||
}
|
||||
|
||||
|
||||
// Clear the screen once, at startup
|
||||
g.clear();
|
||||
// draw immediately at first
|
||||
draw();
|
||||
|
||||
|
||||
// Stop updates when LCD is off, restart when on
|
||||
Bangle.on('lcdPower',on=>{
|
||||
if (on) {
|
||||
draw(); // draw immediately, queue redraw
|
||||
} else { // stop draw timer
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI("clock");
|
After Width: | Height: | Size: 2.6 KiB |
|
@ -0,0 +1,16 @@
|
|||
{ "id": "poikkipuinen",
|
||||
"name": "Poikkipuinen - Minimal watch face",
|
||||
"shortName":"Poikkipuinen",
|
||||
"version":"0.01",
|
||||
"description": "A minimal digital watch face.",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot1.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports" : ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"poikkipuinen.app.js","url":"app.js"},
|
||||
{"name":"poikkipuinen.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 5.9 KiB |
|
@ -9,3 +9,4 @@
|
|||
0.09: fix battery icon size
|
||||
0.10: Tell clock widgets to hide.
|
||||
0.11: fix issue https://github.com/espruino/BangleApps/issues/2128 (#2128) ( settings undefined )
|
||||
0.12: implemented widget_utils
|
|
@ -2,7 +2,7 @@
|
|||
"id": "rebble",
|
||||
"name": "Rebble Clock",
|
||||
"shortName": "Rebble",
|
||||
"version": "0.11",
|
||||
"version": "0.12",
|
||||
"description": "A Pebble style clock, with configurable background, three sidebars including steps, day, date, sunrise, sunset, long live the rebellion",
|
||||
"readme": "README.md",
|
||||
"icon": "rebble.png",
|
||||
|
|
|
@ -309,16 +309,8 @@ else{
|
|||
}
|
||||
|
||||
|
||||
g.clear();
|
||||
Bangle.loadWidgets();
|
||||
/*
|
||||
* we are not drawing the widgets as we are taking over the whole screen
|
||||
* so we will blank out the draw() functions of each widget and change the
|
||||
* area to the top bar doesn't get cleared.
|
||||
*/
|
||||
for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";}
|
||||
|
||||
|
||||
require("widget_utils").hide();
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
|
@ -0,0 +1,14 @@
|
|||
# Rinkulainen
|
||||
|
||||
By Jukio Kallio
|
||||
|
||||
A Minimal & stylish watch face, with rings or disks for hours and minutes. Date underneath. With easy to mod source code for making your own themes. Some example themes included.
|
||||
|
||||

|
||||
Default Colorful theme
|
||||
|
||||

|
||||
Grayscale theme
|
||||
|
||||

|
||||
Maze theme
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwkEogA0/4AKCpNPCxYAB+gtTGJQuOGBAWPGAwuQGAwXG+chiMRiU/C6HyiDpDgMvC5ItFCoYADGIoXIFoIqDGgUBC5nxB4IoE+YYBj4XLBwJxGJ4IwEC4wuBiYEBmUhiUjAoMxGAgXGmAuCDYIACCYIwBgYXJBYSQGD4IjBC5HyE4QOBgU/+cgEAQ3BTAQXFBQImBN4p/BHARgCC4swCYIaBT4gGDiBgCC4syQ4JVENIsggTvKBgYHG+BRCC5KdDWIYXOiEPC4oUCC8hHUmTJBO44XMCgSnH+SnLa5IABfILXJCgINBgA9CAAnzEIYXF+QKCJAMCn/zkQXCEgJtBR479CEwIADCQRpEC4wLBJAInBAAQ3BD4KxDC4wTBiatCkMSkYFBmKAEa48QGAR1GP4gXHGAMBDAnzEAKvEC44wCgJzC+QGCBwgXIRwoACJ4oXDp4JEFQQACGgYAC+gXJGIMhiMRiR9GC5YALC4hgFABgWEGCIuFGCIWGGB4uHGJwVJAFY"))
|
|
@ -0,0 +1,146 @@
|
|||
// Rinkulainen
|
||||
//
|
||||
// Bangle.js 2 watch face
|
||||
// by Jukio Kallio
|
||||
// www.jukiokallio.com
|
||||
|
||||
// settings
|
||||
const watch = {
|
||||
theme: "default",
|
||||
x:0, y:0, w:0, h:0,
|
||||
color:"#000000", // change background color
|
||||
finland:true, // change if you want Finnish style date, or US style
|
||||
|
||||
// default theme "colorful"
|
||||
hour: { size:60, weight:8, color:"#00FFFF", cursor:10 },
|
||||
minute: { size:40, weight:16, color:"#FFFF00", cursor:6 },
|
||||
second: { on: false, cursor:2 }, // if on, uses a lot more battery
|
||||
date: { font:"6x8", size:1, y:15, color:"#FFFF00" }
|
||||
};
|
||||
|
||||
// more themes
|
||||
if (watch.theme == "grayscale") {
|
||||
watch.hour = { size:60, weight:20, color:"#999999", cursor:8 };
|
||||
watch.minute = { size:40, weight:20, color:"#dddddd", cursor:8 };
|
||||
watch.second = { on: false, cursor:2 }; // if on, uses a lot more battery
|
||||
watch.date = { font:"6x8", size:1, y:15, color:"#ffffff" };
|
||||
} else if (watch.theme == "maze") {
|
||||
watch.hour = { size:50, weight:7, color:"#ffffff", cursor:6 };
|
||||
watch.minute = { size:30, weight:7, color:"#ffffff", cursor:6 };
|
||||
watch.second = { on: false, cursor:2 }; // if on, uses a lot more battery
|
||||
watch.date = { font:"6x8", size:1, y:15, color:"#ffffff" };
|
||||
} else if (watch.theme == "disks") {
|
||||
watch.hour = { size:72, weight:30, color:"#00ff66", cursor:4 };
|
||||
watch.minute = { size:36, weight:32, color:"#0066ff", cursor:4 };
|
||||
watch.second = { on: false, cursor:2 }; // if on, uses a lot more battery
|
||||
watch.date = { font:"6x8", size:1, y:10, color:"#ffffff" };
|
||||
}
|
||||
|
||||
// set some additional settings
|
||||
watch.w = g.getWidth(); // size of the background
|
||||
watch.h = g.getHeight();
|
||||
watch.x = watch.w * 0.5; // position of the circles
|
||||
watch.y = watch.h * 0.46;
|
||||
watch.date.y = watch.date.y + watch.y + watch.hour.size; // final position of the date
|
||||
|
||||
const dateWeekday = { 0: "Sunday", 1: "Monday", 2: "Tuesday", 3: "Wednesday", 4:"Thursday", 5:"Friday", 6:"Saturday" }; // weekdays
|
||||
|
||||
var wait = 60000; // wait time, normally a minute
|
||||
if (watch.second.on) wait = 1000; // a second if seconds are used
|
||||
|
||||
|
||||
// timeout used to update every minute
|
||||
var drawTimeout;
|
||||
|
||||
// schedule a draw for the next minute
|
||||
function queueDraw() {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
}, wait - (Date.now() % wait));
|
||||
}
|
||||
|
||||
|
||||
// main function
|
||||
function draw() {
|
||||
// make date object
|
||||
var date = new Date();
|
||||
|
||||
// work out the date string
|
||||
var dateDay = date.getDate();
|
||||
var dateMonth = date.getMonth() + 1;
|
||||
var dateYear = date.getFullYear();
|
||||
var dateStr = dateWeekday[date.getDay()] + " " + dateMonth + "." + dateDay + "." + dateYear;
|
||||
if (watch.finland) dateStr = dateWeekday[date.getDay()] + " " + dateDay + "." + dateMonth + "." + dateYear; // the true way of showing date
|
||||
|
||||
// Reset the state of the graphics library
|
||||
g.reset();
|
||||
|
||||
// Clear the area where we want to draw the time
|
||||
g.setColor(watch.color);
|
||||
g.fillRect(0, 0, watch.w, watch.h);
|
||||
|
||||
// variables for vertex transformation
|
||||
var tver, tobj, tran;
|
||||
|
||||
// draw hour circle
|
||||
g.setColor(watch.hour.color).fillCircle(watch.x, watch.y, watch.hour.size);
|
||||
g.setColor(watch.color).fillCircle(watch.x, watch.y, watch.hour.size - watch.hour.weight);
|
||||
// draw hour line
|
||||
g.setColor(watch.color);
|
||||
var thour = (date.getHours() / 12) * (Math.PI * 2);
|
||||
tver = [-watch.hour.cursor, 0, watch.hour.cursor, 0, watch.hour.cursor, -watch.hour.size*1.05, -watch.hour.cursor, -watch.hour.size*1.05];
|
||||
tobj = { x:watch.x, y:watch.y, scale:1, rotate:thour };
|
||||
tran = g.transformVertices(tver, tobj);
|
||||
g.fillPoly(tran);
|
||||
|
||||
// draw minute circle
|
||||
g.setColor(watch.minute.color).fillCircle(watch.x, watch.y, watch.minute.size);
|
||||
g.setColor(watch.color).fillCircle(watch.x, watch.y, watch.minute.size - watch.minute.weight);
|
||||
// draw minute line
|
||||
g.setColor(watch.color);
|
||||
var tmin = (date.getMinutes() / 60) * (Math.PI * 2);
|
||||
tver = [-watch.minute.cursor, 0, watch.minute.cursor, 0, watch.minute.cursor, -watch.minute.size*1.05, -watch.minute.cursor, -watch.minute.size*1.05];
|
||||
tobj = { x:watch.x, y:watch.y, scale:1, rotate:tmin };
|
||||
tran = g.transformVertices(tver, tobj);
|
||||
g.fillPoly(tran);
|
||||
|
||||
// draw seconds line, if the feature is on
|
||||
if (watch.second.on) {
|
||||
g.setColor(watch.color);
|
||||
var tsec = (date.getSeconds() / 60) * (Math.PI * 2);
|
||||
tver = [-watch.second.cursor, 0, watch.second.cursor, 0, watch.second.cursor, -watch.second.size*1.045, -watch.second.cursor, -watch.second.size*1.045];
|
||||
tobj = { x:watch.x, y:watch.y, scale:1, rotate:tsec };
|
||||
tran = g.transformVertices(tver, tobj);
|
||||
g.fillPoly(tran);
|
||||
}
|
||||
|
||||
// draw date
|
||||
g.setFontAlign(0,0).setFont(watch.date.font, 1).setColor(watch.date.color);
|
||||
g.drawString(dateStr, watch.x, watch.date.y + watch.date.size + 2);
|
||||
|
||||
// queue draw
|
||||
queueDraw();
|
||||
}
|
||||
|
||||
|
||||
// Clear the screen once, at startup
|
||||
g.clear();
|
||||
// draw immediately at first
|
||||
draw();
|
||||
|
||||
|
||||
// Stop updates when LCD is off, restart when on
|
||||
Bangle.on('lcdPower',on=>{
|
||||
if (on) {
|
||||
draw(); // draw immediately, queue redraw
|
||||
} else { // stop draw timer
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI("clock");
|
After Width: | Height: | Size: 3.1 KiB |
|
@ -0,0 +1,16 @@
|
|||
{ "id": "rinkulainen",
|
||||
"name": "Rinkulainen - Minimal & Stylish watch face",
|
||||
"shortName":"Rinkulainen",
|
||||
"version":"0.01",
|
||||
"description": "A minimal watch face, with rings/disks for hours and minutes. Date underneath. With easy to mod source code for making your own themes. Some example themes included.",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot2.png"}, {"url":"screenshot1.png"}, {"url":"screenshot3.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports" : ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"rinkulainen.app.js","url":"app.js"},
|
||||
{"name":"rinkulainen.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 6.3 KiB |
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 5.9 KiB |
|
@ -58,3 +58,4 @@
|
|||
0.51: Add setting for configuring a launcher
|
||||
0.52: Add option for left-handed users
|
||||
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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "setting",
|
||||
"name": "Settings",
|
||||
"version": "0.53",
|
||||
"version": "0.54",
|
||||
"description": "A menu for setting up Bangle.js",
|
||||
"icon": "settings.png",
|
||||
"tags": "tool,system",
|
||||
|
|
|
@ -60,7 +60,9 @@ function resetSettings() {
|
|||
}
|
||||
|
||||
settings = storage.readJSON('setting.json', 1);
|
||||
if (!settings) resetSettings();
|
||||
if (("object" != typeof settings) ||
|
||||
("object" != typeof settings.options))
|
||||
resetSettings();
|
||||
|
||||
const boolFormat = v => v ? /*LANG*/"On" : /*LANG*/"Off";
|
||||
|
||||
|
@ -237,9 +239,9 @@ function showThemeMenu() {
|
|||
}
|
||||
};
|
||||
|
||||
require("Storage").list(/^.*\.theme$/).forEach(
|
||||
storage.list(/^.*\.theme$/).forEach(
|
||||
n => {
|
||||
let newTheme = require("Storage").readJSON(n);
|
||||
let newTheme = storage.readJSON(n);
|
||||
themesMenu[newTheme.name ? newTheme.name : n] = () => {
|
||||
upd({
|
||||
fg:cl(newTheme.fg), bg:cl(newTheme.bg),
|
||||
|
@ -567,11 +569,11 @@ function showUtilMenu() {
|
|||
},
|
||||
/*LANG*/'Compact Storage': () => {
|
||||
E.showMessage(/*LANG*/"Compacting...\nTakes approx\n1 minute",{title:/*LANG*/"Storage"});
|
||||
require("Storage").compact();
|
||||
storage.compact();
|
||||
showUtilMenu();
|
||||
},
|
||||
/*LANG*/'Rewrite Settings': () => {
|
||||
require("Storage").write(".boot0","eval(require('Storage').read('bootupdate.js'));");
|
||||
storage.write(".boot0","eval(require('Storage').read('bootupdate.js'));");
|
||||
load("setting.app.js");
|
||||
},
|
||||
/*LANG*/'Flatten Battery': () => {
|
||||
|
@ -592,9 +594,9 @@ function showUtilMenu() {
|
|||
menu[/*LANG*/'Calibrate Battery'] = () => {
|
||||
E.showPrompt(/*LANG*/"Is the battery fully charged?",{title:/*LANG*/"Calibrate"}).then(ok => {
|
||||
if (ok) {
|
||||
var s=require("Storage").readJSON("setting.json");
|
||||
var s=storage.readJSON("setting.json");
|
||||
s.batFullVoltage = (analogRead(D3)+analogRead(D3)+analogRead(D3)+analogRead(D3))/4;
|
||||
require("Storage").writeJSON("setting.json",s);
|
||||
storage.writeJSON("setting.json",s);
|
||||
E.showAlert(/*LANG*/"Calibrated!").then(() => load("setting.app.js"));
|
||||
} else {
|
||||
E.showAlert(/*LANG*/"Please charge Bangle.js for 3 hours and try again").then(() => load("settings.app.js"));
|
||||
|
@ -659,7 +661,7 @@ function makeConnectable() {
|
|||
});
|
||||
}
|
||||
function showClockMenu() {
|
||||
var clockApps = require("Storage").list(/\.info$/)
|
||||
var clockApps = storage.list(/\.info$/)
|
||||
.map(app => {var a=storage.readJSON(app, 1);return (a&&a.type == "clock")?a:undefined})
|
||||
.filter(app => app) // filter out any undefined apps
|
||||
.sort((a, b) => a.sortorder - b.sortorder);
|
||||
|
@ -676,7 +678,7 @@ function showClockMenu() {
|
|||
}
|
||||
clockMenu[label] = () => {
|
||||
settings.clock = app.src;
|
||||
settings.clockHasWidgets = require("Storage").read(app.src).includes("Bangle.loadWidgets");
|
||||
settings.clockHasWidgets = storage.read(app.src).includes("Bangle.loadWidgets");
|
||||
updateSettings();
|
||||
showMainMenu();
|
||||
};
|
||||
|
@ -687,7 +689,7 @@ function showClockMenu() {
|
|||
return E.showMenu(clockMenu);
|
||||
}
|
||||
function showLauncherMenu() {
|
||||
var launcherApps = require("Storage").list(/\.info$/)
|
||||
var launcherApps = storage.list(/\.info$/)
|
||||
.map(app => {var a=storage.readJSON(app, 1);return (a&&a.type == "launch")?a:undefined})
|
||||
.filter(app => app) // filter out any undefined apps
|
||||
.sort((a, b) => a.sortorder - b.sortorder);
|
||||
|
@ -865,9 +867,9 @@ function showTouchscreenCalibration() {
|
|||
Bangle.setOptions({
|
||||
touchX1: calib.x1, touchY1: calib.y1, touchX2: calib.x2, touchY2: calib.y2
|
||||
});
|
||||
var s = require("Storage").readJSON("setting.json",1)||{};
|
||||
var s = storage.readJSON("setting.json",1)||{};
|
||||
s.touch = calib;
|
||||
require("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);
|
||||
// now load the main menu again
|
||||
setTimeout(showLCDMenu, 500);
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEw4P/AAMA/Ayq8EH8AEBgfgj4zCj/gn/8Aod//wFDvk/gEEAoP4AoMAEIP4j4FFwAFC/gFEv//ApM/74FDg4XBgZLCFIMzAoU4g8BK4dwgMP+Ewg+AgMfK4PhAoXwh+B/0Bj0B/4FBgYnB/8B/kDgf/+ED/kHn//HgIFBW4IFB/AFDgf4h4FB+EBFgLKCAoInBAAOAAoqkBAgPAWAIuBAoXAn+zCAMB4F/8YFBgYFB4YFBRgY7BYwIoCABX4zkY74FB/mMiALC/3mug6CAAgA=="))
|
|
@ -0,0 +1,220 @@
|
|||
Graphics.prototype.setFontPaytoneOne = function(scale) {
|
||||
// Actual height 81 (91 - 11)
|
||||
this.setFontCustom(
|
||||
E.toString(require('heatshrink').decompress(atob('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'))),
|
||||
46,
|
||||
atob("ITZOMzs7SDxHNUdGIQ=="),
|
||||
113+(scale<<8)+(1<<16)
|
||||
);
|
||||
return this;
|
||||
};
|
||||
|
||||
{ // must be inside our own scope here so that when we are unloaded everything disappears
|
||||
// we also define functions using 'let fn = function() {..}' for the same reason. function decls are global
|
||||
let drawTimeout;
|
||||
|
||||
let g2 = Graphics.createArrayBuffer(g.getWidth(),90,1,{msb:true});
|
||||
let g2img = {
|
||||
width:g2.getWidth(), height:g2.getHeight(), bpp:1,
|
||||
buffer:g2.buffer, transparent:0
|
||||
};
|
||||
const slope = 20;
|
||||
const offsy = 20; // offset of numbers from middle
|
||||
const fontBorder = 4; // offset from left/right
|
||||
const slopeBorder = 10, slopeBorderUpper = 4; // fudge-factor to move minutes down from slope
|
||||
let R,x,y; // middle of the clock face
|
||||
let dateStr = "";
|
||||
let bgColors = g.theme.dark ? ["#ff0","#0ff","#f0f"] : ["#f00","#0f0","#00f"];
|
||||
let bgColor = bgColors[(Math.random()*bgColors.length)|0];
|
||||
|
||||
|
||||
// Draw the hour, and the minute into an offscreen buffer
|
||||
let draw = function() {
|
||||
R = Bangle.appRect;
|
||||
x = R.w / 2;
|
||||
y = R.y + R.h / 2 - 12; // 12 = room for date
|
||||
var date = new Date();
|
||||
var hourStr = date.getHours();
|
||||
var minStr = date.getMinutes().toString().padStart(2,0);
|
||||
dateStr = require("locale").dow(date, 1).toUpperCase()+ " "+
|
||||
require("locale").date(date, 0).toUpperCase();
|
||||
|
||||
// Draw hour
|
||||
g.reset().clearRect(R); // clear whole background (w/o widgets)
|
||||
g.setFontAlign(-1, 0).setFont("PaytoneOne");
|
||||
g.drawString(hourStr, fontBorder, y-offsy);
|
||||
// add slope in background color
|
||||
g.setColor(g.theme.bg).fillPoly([0,y+slope-slopeBorderUpper, R.w,y-slope-slopeBorderUpper,
|
||||
R.w,y-slope, 0,y+slope]);
|
||||
|
||||
// Draw minute to offscreen buffer
|
||||
g2.setColor(0).fillRect(0,0,g2.getWidth(),g2.getHeight()).setFontAlign(1, 0).setFont("PaytoneOne");
|
||||
g2.setColor(1).drawString(minStr, g2.getWidth()-fontBorder, g2.getHeight()/2);
|
||||
g2.setColor(0).fillPoly([0,0, g2.getWidth(),0, 0,slope*2]);
|
||||
// start the animation *in*
|
||||
animate(true);
|
||||
|
||||
// queue next draw
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
animate(false, function() {
|
||||
draw();
|
||||
});
|
||||
}, 60000 - (Date.now() % 60000));
|
||||
};
|
||||
|
||||
let isAnimIn = true;
|
||||
let animInterval;
|
||||
// Draw *just* the minute image
|
||||
let drawMinute = function() {
|
||||
var yo = slopeBorder + offsy + y - 2*slope*minuteX/R.w;
|
||||
// draw over the slanty bit
|
||||
g.setColor(bgColor).fillPoly([0,y+slope, R.w,y-slope, R.w,R.h+R.y, 0,R.h+R.y]);
|
||||
// draw the minutes
|
||||
g.setColor(g.theme.bg).drawImage(g2img, x+minuteX-(g2.getWidth()/2), yo-(g2.getHeight()/2));
|
||||
};
|
||||
let animate = function(isIn, callback) {
|
||||
if (animInterval) clearInterval(animInterval);
|
||||
isAnimIn = isIn;
|
||||
minuteX = isAnimIn ? -g2.getWidth() : 0;
|
||||
drawMinute();
|
||||
animInterval = setInterval(function() {
|
||||
minuteX += 8;
|
||||
let stop = false;
|
||||
if (isAnimIn && minuteX>=0) {
|
||||
minuteX=0;
|
||||
stop = true;
|
||||
} else if (!isAnimIn && minuteX>=R.w)
|
||||
stop = true;
|
||||
drawMinute();
|
||||
if (stop) {
|
||||
clearInterval(animInterval);
|
||||
animInterval=undefined;
|
||||
if (isAnimIn) {
|
||||
// draw the date
|
||||
g.setColor(g.theme.bg).setFontAlign(0, 0).setFont("6x15").drawString(dateStr, R.x + R.w/2, R.y+R.h-9);
|
||||
|
||||
// draw steps to bottom left
|
||||
const steps = getSteps();
|
||||
if (steps > 0)
|
||||
g.setFontAlign(-1, 0).drawString(shortValue(steps), 3, R.y+R.h-30);
|
||||
|
||||
// draw weather to top right
|
||||
const weather = getWeather();
|
||||
const tempString = weather ? require("locale").temp(weather.temp - 273.15) : undefined;
|
||||
const code = weather ? weather.code : -1;
|
||||
if (code > -1) {
|
||||
g.setColor(g.theme.fg).setFontAlign(1, 0).drawString(tempString, R.w - 3, y-slope-slopeBorderUpper);
|
||||
const icon = getWeatherIconByCode(code);
|
||||
if (icon) g.drawImage(icon, R.w - 3 - 15, y-slope-slopeBorderUpper - 15 - 15);
|
||||
}
|
||||
}
|
||||
if (callback) callback();
|
||||
}
|
||||
}, 20);
|
||||
};
|
||||
|
||||
let getSteps = function() {
|
||||
if (Bangle.getHealthStatus) {
|
||||
return Bangle.getHealthStatus("day").steps;
|
||||
}
|
||||
if (WIDGETS && WIDGETS.wpedom !== undefined) {
|
||||
return WIDGETS.wpedom.getSteps();
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
let shortValue = function(v) {
|
||||
if (isNaN(v)) return '-';
|
||||
if (v <= 999) return v;
|
||||
if (v >= 1000 && v < 10000) {
|
||||
v = Math.floor(v / 100) * 100;
|
||||
return (v / 1000).toFixed(1).replace(/\.0$/, '') + 'k';
|
||||
}
|
||||
if (v >= 10000) {
|
||||
v = Math.floor(v / 1000) * 1000;
|
||||
return (v / 1000).toFixed(1).replace(/\.0$/, '') + 'k';
|
||||
}
|
||||
};
|
||||
|
||||
let getWeather = function() {
|
||||
let jsonWeather = require("Storage").readJSON('weather.json');
|
||||
return jsonWeather && jsonWeather.weather ? jsonWeather.weather : undefined;
|
||||
};
|
||||
|
||||
/*
|
||||
* Choose weather icon to display based on weather conditition code
|
||||
* https://openweathermap.org/weather-conditions#Weather-Condition-Codes-2
|
||||
*/
|
||||
let getWeatherIconByCode = function(code) {
|
||||
let codeGroup = Math.round(code / 100);
|
||||
|
||||
// weather icons:
|
||||
let weatherCloudy = atob("EBCBAAAAAAAAAAfgD/Af8H/4//7///////9//z/+AAAAAAAA");
|
||||
let weatherSunny = atob("EBCBAAAAAYAQCBAIA8AH4A/wb/YP8A/gB+ARiBAIAYABgAAA");
|
||||
let weatherMoon = atob("EBCBAAAAAYAP8B/4P/w//D/8f/5//j/8P/w//B/4D/ABgAAA");
|
||||
let weatherPartlyCloudy = atob("EBCBAAAAAAAYQAMAD8AIQBhoW+AOYBwwOBBgHGAGP/wf+AAA");
|
||||
let weatherRainy = atob("EBCBAAAAAYAH4AwwOBBgGEAOQAJBgjPOEkgGYAZgA8ABgAAA");
|
||||
let weatherPartlyRainy = atob("EBCBAAAAEEAQAAeADMAYaFvoTmAMMDgQIBxhhiGGG9wDwAGA");
|
||||
let weatherSnowy = atob("EBCBAAAAAAADwAGAEYg73C50BCAEIC50O9wRiAGAA8AAAAAA");
|
||||
let weatherFoggy = atob("EBCBAAAAAAADwAZgDDA4EGAcQAZAAgAAf74AAAAAd/4AAAAA");
|
||||
let weatherStormy = atob("EBCBAAAAAYAH4AwwOBBgGEAOQMJAgjmOGcgAgACAAAAAAAAA");
|
||||
let unknown = undefined;
|
||||
|
||||
switch (codeGroup) {
|
||||
case 2:
|
||||
return weatherStormy;
|
||||
case 3:
|
||||
return weatherCloudy;
|
||||
case 5:
|
||||
switch (code) {
|
||||
case 511:
|
||||
return weatherSnowy;
|
||||
case 520:
|
||||
return weatherPartlyRainy;
|
||||
case 521:
|
||||
return weatherPartlyRainy;
|
||||
case 522:
|
||||
return weatherPartlyRainy;
|
||||
case 531:
|
||||
return weatherPartlyRainy;
|
||||
default:
|
||||
return weatherRainy;
|
||||
}
|
||||
case 6:
|
||||
return weatherSnowy;
|
||||
case 7:
|
||||
return weatherFoggy;
|
||||
case 8:
|
||||
switch (code) {
|
||||
case 800:
|
||||
return weatherSunny;
|
||||
case 801:
|
||||
return weatherPartlyCloudy;
|
||||
case 802:
|
||||
return weatherPartlyCloudy;
|
||||
default:
|
||||
return weatherCloudy;
|
||||
}
|
||||
default:
|
||||
return unknown;
|
||||
}
|
||||
}
|
||||
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI({
|
||||
mode : "clock",
|
||||
remove : function() {
|
||||
// Called to unload all of the clock app
|
||||
if (animInterval) clearInterval(animInterval);
|
||||
animInterval = undefined;
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
delete Graphics.prototype.setFontPaytoneOne;
|
||||
}});
|
||||
// Load widgets
|
||||
Bangle.loadWidgets();
|
||||
draw();
|
||||
setTimeout(Bangle.drawWidgets,0);
|
||||
}
|
After Width: | Height: | Size: 10 KiB |
|
@ -0,0 +1,14 @@
|
|||
{ "id": "slopeclockpp",
|
||||
"name": "Slope Clock ++",
|
||||
"version":"0.01",
|
||||
"description": "A clock where hours and minutes are divided by a sloping line. When the minute changes, the numbers slide off the screen. This is a clone of the original Slope Clock which shows weather and steps.",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"slopeclockpp.app.js","url":"app.js"},
|
||||
{"name":"slopeclockpp.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 15 KiB |
|
@ -1,3 +1,4 @@
|
|||
0.01: New App!
|
||||
0.02: Fix issue with mode being undefined
|
||||
0.03: Update setUI to work with new Bangle.js 2v13 menu style
|
||||
0.04: Update to work with new 'fast switch' clock->launcher functionality
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
// clock -> launcher
|
||||
(function() {
|
||||
var sui = Bangle.setUI;
|
||||
Bangle.setUI = function(mode, cb) {
|
||||
sui(mode,cb);
|
||||
if(!mode) return;
|
||||
if ("object"==typeof mode) mode = mode.mode;
|
||||
if (!mode.startsWith("clock")) return;
|
||||
if (mode.startsWith("clock")) {
|
||||
// clock -> launcher
|
||||
Bangle.swipeHandler = dir => { if (dir<0) Bangle.showLauncher(); };
|
||||
Bangle.on("swipe", Bangle.swipeHandler);
|
||||
};
|
||||
})();
|
||||
// launcher -> clock
|
||||
setTimeout(function() {
|
||||
} else {
|
||||
if (global.__FILE__ && __FILE__.endsWith(".app.js") && (require("Storage").readJSON(__FILE__.slice(0,-6)+"info",1)||{}).type=="launch") {
|
||||
// launcher -> clock
|
||||
Bangle.swipeHandler = dir => { if (dir>0) load(); };
|
||||
Bangle.on("swipe", Bangle.swipeHandler);
|
||||
}
|
||||
}, 10);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "swiperclocklaunch",
|
||||
"name": "Swiper Clock Launch",
|
||||
"version": "0.03",
|
||||
"version": "0.04",
|
||||
"description": "Navigate between clock and launcher with Swipe action",
|
||||
"icon": "swiperclocklaunch.png",
|
||||
"type": "bootloader",
|
||||
|
|
2
core
|
@ -1 +1 @@
|
|||
Subproject commit 80de03d8e665c210dc3443d6869176c848ab103f
|
||||
Subproject commit 87cf5420322cd78a13e3d2b76cd12c9fdbddc7d2
|
15
css/main.css
|
@ -102,12 +102,23 @@ a.btn.btn-link.dropdown-toggle {
|
|||
content: url("data:image/svg+xml,%3Csvg fill='rgb(87, 85, 217)' xmlns='http://www.w3.org/2000/svg' viewBox='4 4 40 40' width='1em' height='1em'%3E%3Cpath d='M 8.5 5 C 6.0324991 5 4 7.0324991 4 9.5 L 4 30.5 C 4 32.967501 6.0324991 35 8.5 35 L 17 35 L 17 40 L 13.5 40 A 1.50015 1.50015 0 1 0 13.5 43 L 18.253906 43 A 1.50015 1.50015 0 0 0 18.740234 43 L 29.253906 43 A 1.50015 1.50015 0 0 0 29.740234 43 L 34.5 43 A 1.50015 1.50015 0 1 0 34.5 40 L 31 40 L 31 35 L 39.5 35 C 41.967501 35 44 32.967501 44 30.5 L 44 9.5 C 44 7.0324991 41.967501 5 39.5 5 L 8.5 5 z M 8.5 8 L 39.5 8 C 40.346499 8 41 8.6535009 41 9.5 L 41 30.5 C 41 31.346499 40.346499 32 39.5 32 L 29.746094 32 A 1.50015 1.50015 0 0 0 29.259766 32 L 18.746094 32 A 1.50015 1.50015 0 0 0 18.259766 32 L 8.5 32 C 7.6535009 32 7 31.346499 7 30.5 L 7 9.5 C 7 8.6535009 7.6535009 8 8.5 8 z M 17.5 12 C 16.136406 12 15 13.136406 15 14.5 L 15 25.5 C 15 26.863594 16.136406 28 17.5 28 L 30.5 28 C 31.863594 28 33 26.863594 33 25.5 L 33 14.5 C 33 13.136406 31.863594 12 30.5 12 L 17.5 12 z M 18 18 L 30 18 L 30 25 L 18 25 L 18 18 z M 20 35 L 28 35 L 28 40 L 20 40 L 20 35 z'/%3E%3C/svg%3E");
|
||||
}
|
||||
.icon.icon-favourite { text-indent: 0px; } /*override spectre*/
|
||||
.icon.icon-favourite-active { text-indent: 0px; } /*override spectre*/
|
||||
.icon.icon-favourite::before {
|
||||
content: "\02661"; /* 0x2661 = empty heart; 0x2606 = empty star */
|
||||
content: url("data:image/svg+xml,%3Csvg fill='rgb(255, 0, 0)' xmlns='http://www.w3.org/2000/svg' viewBox='0 -3 50 47' width='1.5em' height='1.5em'%3E%3Cpath d='M 16.375 9 C 10.117188 9 5 14.054688 5 20.28125 C 5 33.050781 19.488281 39.738281 24.375 43.78125 L 25 44.3125 L 25.625 43.78125 C 30.511719 39.738281 45 33.050781 45 20.28125 C 45 14.054688 39.882813 9 33.625 9 C 30.148438 9 27.085938 10.613281 25 13.0625 C 22.914063 10.613281 19.851563 9 16.375 9 Z M 16.375 11 C 19.640625 11 22.480469 12.652344 24.15625 15.15625 L 25 16.40625 L 25.84375 15.15625 C 27.519531 12.652344 30.359375 11 33.625 11 C 38.808594 11 43 15.144531 43 20.28125 C 43 31.179688 30.738281 37.289063 25 41.78125 C 19.261719 37.289063 7 31.179688 7 20.28125 C 7 15.144531 11.1875 11 16.375 11 Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
.icon.icon-favourite-active::before {
|
||||
content: "\02665"; /* 0x2665 = solid heart; 0x2605 = solid star */
|
||||
content: url("data:image/svg+xml,%3Csvg fill='rgb(255, 0, 0)' xmlns='http://www.w3.org/2000/svg' viewBox='0 -3 50 47' width='1.5em' height='1.5em'%3E%3Cpath d='M 25 44.296875 L 24.363281 43.769531 C 23.363281 42.941406 22.019531 42.027344 20.46875 40.96875 C 14.308594 36.765625 5 30.414063 5 20.285156 C 5 14.0625 10.097656 9 16.363281 9 C 19.714844 9 22.851563 10.457031 25 12.957031 C 27.148438 10.457031 30.289063 9 33.636719 9 C 39.902344 9 45 14.0625 45 20.285156 C 45 30.414063 35.691406 36.765625 29.53125 40.96875 C 27.976563 42.027344 26.636719 42.941406 25.636719 43.769531 Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
.icon.icon-favourite span {
|
||||
font-size: 50%;
|
||||
color : #F66;
|
||||
position:relative;
|
||||
top:-0.7em;
|
||||
}
|
||||
.icon.icon-favourite-active span {
|
||||
color : white;
|
||||
}
|
||||
|
||||
.icon.icon-interface {text-indent: 0px;} /*override spectre*/
|
||||
.icon.icon-interface::before {
|
||||
position: absolute; left: 50%; top: 70%;
|
||||
|
|
|
@ -88,8 +88,10 @@
|
|||
<div class="sort-nav hidden">
|
||||
<span>Sort by:</span>
|
||||
<label class="chip active" sortid="">None</label>
|
||||
<label class="chip" sortid="created">New</label>
|
||||
<label class="chip" sortid="modified">Updated</label>
|
||||
<label class="chip hidden" sortid="created">New</label>
|
||||
<label class="chip hidden" sortid="modified">Updated</label>
|
||||
<label class="chip hidden" sortid="installs">Installed</label>
|
||||
<label class="chip hidden" sortid="favourites">Favourited</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -21,6 +21,9 @@ var RECOMMENDED_VERSION = "2v15";
|
|||
|
||||
// We're only interested in Bangles
|
||||
DEVICEINFO = DEVICEINFO.filter(x=>x.id.startsWith("BANGLEJS"));
|
||||
// Where we get our usage data from
|
||||
Const.APP_USAGE_JSON = "https://banglejs.com/apps/appusage.json";
|
||||
Const.APP_DATES_CSV = "appdates.csv";
|
||||
|
||||
// Set up source code URL
|
||||
(function() {
|
||||
|
|
|
@ -111,12 +111,71 @@ exports.load = function() {
|
|||
return menu;
|
||||
};
|
||||
|
||||
/** Adds an interactive menu that could be used on a clock face by swiping.
|
||||
Simply supply the menu data (from .load) and a function to draw the clock info.
|
||||
|
||||
// Code for testing
|
||||
For example:
|
||||
|
||||
var clockInfoMenu = require("clock_info").addInteractive(require("clock_info").load(), (itm, info) => {
|
||||
var y = 0;
|
||||
g.reset().setFont("6x8:2").setFontAlign(-1,0);
|
||||
g.clearRect(0,y,g.getWidth(),y+23);
|
||||
g.drawImage(info.img, 0,y);
|
||||
g.drawString(info.text, 48,y+12);
|
||||
});
|
||||
|
||||
Then if you need to unload the clock info so it no longer
|
||||
uses memory or responds to swipes, you can call clockInfoMenu.remove()
|
||||
and delete clockInfoMenu
|
||||
*/
|
||||
exports.addInteractive = function(menu, drawFn) {
|
||||
if (!menu.length || !menu[0].items.length) return; // no info
|
||||
var menuA = 0, menuB = 0;
|
||||
function menuShowItem(itm) {
|
||||
itm.on('redraw', ()=>drawFn(itm, itm.get()));
|
||||
itm.show();
|
||||
itm.emit("redraw");
|
||||
}
|
||||
// handling for swipe between menu items
|
||||
function swipeHandler(lr,ud){
|
||||
var oldMenuItem;
|
||||
if (ud) {
|
||||
if (menu[menuA].items.length==1) return; // 1 item - can't move
|
||||
oldMenuItem = menu[menuA].items[menuB];
|
||||
menuB += ud;
|
||||
if (menuB<0) menuB = menu[menuA].items.length-1;
|
||||
if (menuB>=menu[menuA].items.length) menuB = 0;
|
||||
} else if (lr) {
|
||||
if (menu.length==1) return; // 1 item - can't move
|
||||
oldMenuItem = menu[menuA].items[menuB];
|
||||
menuA += ud;
|
||||
if (menuA<0) menuA = menu.length-1;
|
||||
if (menuA>=menu.length) menuA = 0;
|
||||
menuB = 0;
|
||||
}
|
||||
if (oldMenuItem) {
|
||||
oldMenuItem.hide();
|
||||
oldMenuItem.removeAllListeners("draw");
|
||||
menuShowItem(menu[menuA].items[menuB]);
|
||||
}
|
||||
}
|
||||
Bangle.on("swipe",swipeHandler);
|
||||
// draw the first item
|
||||
menuShowItem(menu[menuA].items[menuB]);
|
||||
// return an object with info that can be used to remove the info
|
||||
return {
|
||||
remove : function() {
|
||||
Bangle.removeListener("swipe",swipeHandler);
|
||||
menu[menuA].items[menuB].hide();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Code for testing (plots all elements from first list)
|
||||
/*
|
||||
g.clear();
|
||||
var menu = exports.load(); // or require("clock_info").load()
|
||||
var itemsFirstMenu = menu[0].items;
|
||||
var items = menu[0].items;
|
||||
items.forEach((itm,i) => {
|
||||
var y = i*24;
|
||||
console.log("Starting", itm.name);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"name": "Bangle.ts",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"node-fetch": "^3.2.9"
|
||||
"node-fetch": "^3.2.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "4.5.2"
|
||||
|
@ -74,9 +74,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "3.2.9",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.9.tgz",
|
||||
"integrity": "sha512-/2lI+DBecVvVm9tDhjziTVjo2wmTsSxSk58saUYP0P/fRJ3xxtfMDY24+CKTkfm0Dlhyn3CSXNL0SoRiCZ8Rzg==",
|
||||
"version": "3.2.10",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.10.tgz",
|
||||
"integrity": "sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==",
|
||||
"dependencies": {
|
||||
"data-uri-to-buffer": "^4.0.0",
|
||||
"fetch-blob": "^3.1.4",
|
||||
|
@ -141,9 +141,9 @@
|
|||
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "3.2.9",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.9.tgz",
|
||||
"integrity": "sha512-/2lI+DBecVvVm9tDhjziTVjo2wmTsSxSk58saUYP0P/fRJ3xxtfMDY24+CKTkfm0Dlhyn3CSXNL0SoRiCZ8Rzg==",
|
||||
"version": "3.2.10",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.10.tgz",
|
||||
"integrity": "sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==",
|
||||
"requires": {
|
||||
"data-uri-to-buffer": "^4.0.0",
|
||||
"fetch-blob": "^3.1.4",
|
||||
|
|
|
@ -10,6 +10,6 @@
|
|||
"build": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"node-fetch": "^3.2.9"
|
||||
"node-fetch": "^3.2.10"
|
||||
}
|
||||
}
|
||||
|
|