Merge branch 'espruino:master' into calculator

pull/2242/head
thyttan 2022-11-07 00:08:38 +01:00 committed by GitHub
commit b468ac3498
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
80 changed files with 1437 additions and 3596 deletions

1
apps/entonclk/ChangeLog Normal file
View File

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

9
apps/entonclk/README.md Normal file
View File

@ -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

View File

@ -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=="))

67
apps/entonclk/app.js Normal file
View File

@ -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);
}

BIN
apps/entonclk/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

View File

@ -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}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -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

View File

@ -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.

View File

@ -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;
},

View File

@ -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"}],

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -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)

1
apps/henkinen/ChangeLog Normal file
View File

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

7
apps/henkinen/README.md Normal file
View File

@ -0,0 +1,7 @@
# Henkinen
By Jukio Kallio
A tiny app helping you to breath and relax.
![](screenshot1.png)

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwkEogA0/4AKCpNPCxYAB+gtTGJQuOGBAWPGAwuQGAwXH+cykc/C6UhgMSkMQiQXKBQsgiYFDmMCMBIIEmAWEDAUDC5nzBwogDMYgXHBoohJC4wuJEQwXG+ALDmUQgMjEYcPC5MhAYXxgAACj4ICVYYXGIwXzCwYABHAUwC5HyEwXwC4pEC+MvC4/xEoUQC4sBHIQlCC4vwIxBIEGYQXFmJKCC45ECfQQXIRoiRGC5EiOxB4EBwQXdI653XU67XX+QJCPAwrC+JKCC4v/gZIIHIUwCAQXGkIDCSIg4C/8SC5PwEwX/mUQgMjAwXzJQQXH+ZICAA8wEYYXGBgoAEEQoXHGBIhFC44OBcgQADmIgFC5H/kAYEmMCBooXDp4KFkMBiUhiCjDAAX0C5RjBmUjPo4XMABQXEMAwALCwgwRFwowRCwwwPFw4xOCpIArA"))

127
apps/henkinen/app.js Normal file
View File

@ -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");

BIN
apps/henkinen/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -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}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -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

View File

@ -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);
}

View File

@ -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"
}

View File

@ -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);
});

View File

@ -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

View File

@ -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

View File

@ -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",

View File

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

View File

@ -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.
![](screenshot1.png)

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwkBiIA0/4AKCpMfCxYAB+ItTGJQuOGBAWPGAwuQGAwXvCyJgFC+PwgAAEh4X/C/6//A4gX/C/6//A4QX/C/6/vC6sfCyPxC+ZgSCwgwRFwowRCwwwPFw4xOCpIArA"))

184
apps/palikkainen/app.js Normal file
View File

@ -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");

BIN
apps/palikkainen/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -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}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

1
apps/pisteinen/ChangeLog Normal file
View File

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

7
apps/pisteinen/README.md Normal file
View File

@ -0,0 +1,7 @@
# Pisteinen
By Jukio Kallio
A Minimal digital watch face consisting of dots. Big dots for hours, small dots for minutes.
![](screenshot1.png)

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwkDmYA0/4AKCpM/CxYAB+YtTGJQuOGBAWPGAwuQGAwXvCyJgFC+UhiQDNC43ygEAl4DLC4/xBYMfAZYXfI653XX/6//X/6//O5gBKU5gGBAZAXfI66//C7s/CyPzC+ZgSCwgwRFwowRCwwwPFw4xOCpIArA=="))

121
apps/pisteinen/app.js Normal file
View File

@ -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");

BIN
apps/pisteinen/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -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}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

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

View File

@ -0,0 +1,7 @@
# Poikkipuinen
By Jukio Kallio
A Minimal digital watch face. Follows the theme colors.
![](screenshot1.png)

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwkEogA0/4AKCpNPCxYAB+gtTGJQuOGBAWPGAwuQGAwXamQULkYXGBQUgn4WJ+cCMAwXNiQXV+MBC6swh4XU+cAn4XU+IUBC6kgj4XUIwKnV+EDC6sQl4XU+UBd6q8BC6q8BC6i8CC6i8CC6a8DC6a8DC6a8DC6S8EC6S8EC6S8EC6K8FC6K8FC6C8BIwwXOXgwXQXgwXQkIWHd6IXPp4GBmQWJAAMjAQP0C4wAPC7hgDABwWEGCIuFGCIWGGB4uHGJwVJAFY="))

158
apps/poikkipuinen/app.js Normal file
View File

@ -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");

BIN
apps/poikkipuinen/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -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}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@ -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

View File

@ -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",

View File

@ -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();

View File

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

View File

@ -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.
![](screenshot2.png)
Default Colorful theme
![](screenshot1.png)
Grayscale theme
![](screenshot3.png)
Maze theme

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwkEogA0/4AKCpNPCxYAB+gtTGJQuOGBAWPGAwuQGAwXG+chiMRiU/C6HyiDpDgMvC5ItFCoYADGIoXIFoIqDGgUBC5nxB4IoE+YYBj4XLBwJxGJ4IwEC4wuBiYEBmUhiUjAoMxGAgXGmAuCDYIACCYIwBgYXJBYSQGD4IjBC5HyE4QOBgU/+cgEAQ3BTAQXFBQImBN4p/BHARgCC4swCYIaBT4gGDiBgCC4syQ4JVENIsggTvKBgYHG+BRCC5KdDWIYXOiEPC4oUCC8hHUmTJBO44XMCgSnH+SnLa5IABfILXJCgINBgA9CAAnzEIYXF+QKCJAMCn/zkQXCEgJtBR479CEwIADCQRpEC4wLBJAInBAAQ3BD4KxDC4wTBiatCkMSkYFBmKAEa48QGAR1GP4gXHGAMBDAnzEAKvEC44wCgJzC+QGCBwgXIRwoACJ4oXDp4JEFQQACGgYAC+gXJGIMhiMRiR9GC5YALC4hgFABgWEGCIuFGCIWGGB4uHGJwVJAFY"))

146
apps/rinkulainen/app.js Normal file
View File

@ -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");

BIN
apps/rinkulainen/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -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}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@ -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

View File

@ -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",

View File

@ -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);

View File

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

View File

@ -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=="))

220
apps/slopeclockpp/app.js Normal file
View File

@ -0,0 +1,220 @@
Graphics.prototype.setFontPaytoneOne = function(scale) {
// Actual height 81 (91 - 11)
this.setFontCustom(
E.toString(require('heatshrink').decompress(atob('AH8AgP/BpcD//gBpn4Bpn+Bpn/wANMHBRTB//wBphGLBoJGLv4OBBpU/KhkfBoPABpMPMRkHMRh+CMRRwC/hwmMQQNKMQTTNBpRGCRhSpCBpY4BFJY4BBpcAjgMLAHUwBpl4BhcBd5Z/Bd5abCBpa3BTZd/YpcBcIPgBpMHBoPwIhf//BEL/5wKIgP/OBJECAAJELAAJwIIgQABOBBECOBRECOBJEEOBBEEOBBEEOBBEEOBBEEOA5EFBo5EFFI5EFKY5EGN4woGTIpEpj5EMDYzeGG4xEFgEDWZhhFbo59FfI7QFIgynGIgxwGBg5wEIhBwE+ANIOAZEIOAhEIOAgMJOAREJOAZEJOAZEJOAZEKOAQMKOAJELOAJELAAJELAH0EBhaQBSJa6BZJbkCDhMDBof4XJIADBpvAKRIqKBov+Bo0fBogqHBozpGBoyAGBoxjGBo44FBo44FMIpxHBo5xFBo7HFU4pGHBpBGEBpB/EdohGIgINHIwgNJIwgWEn4EC8ANGQ4SNHv4VEQgRUEEgQxCHwRUEYgRNDEQQNKFQRUDAwQNDQoRUDTQQUDHASpDCgR3EHAJiDCgR3ELYJiEBow/BMQgiBbQ4iFSYg/CLYZwBGAg/COAwNGOAwiDJoRwUKggNBOAwGEBoJwEcIT2GaYw4DAoINEMQQ/CHwRbEMQQHCLQTaHI4QvCNIoHCAArMEJoQAFO4gkDBpJUCAAraHBpRUDAAihEIxANFIw4NFIw7EEIxANFRo4NGcQQNKHAwNGHAwNGHAwNHHAoNHf4YNJVQqLFFQ7DEFRDtEKpHgBpCADwANIDgRSHKwvABpQA/AFp7BZwkfXIyXFVoLVFv//bArxFBoLBDga6GfgK0DHwIiEH4TrEcgw/BJogwBa4g/BJogwBEQgNGOAxNBAAwUEJoQAFOAoNHOAoNHOApbBAAxwEBpBwENIIAGOAgNIOAh3BOBYNIOAi2BOBYNIOAgNJOAbEBOBbEIOAjEIOAoNIOAioIOAiaIOAiMIOH5wLAAw/BOAgAGH4JwEAAw/CBpQ/COAYAHWAJwDAA6wBOAYAHWAJwEAAywBODIA/ABsDUBYNBOwpwGZgIcEcIwNBDggNBcIraFBoQjEbQK+DBoThEBoIqDBoThEdAJNDBoThEBpBNEewJbDBoRwEewINGOAiFBNIYNCOAgNJO5INDOAaaBAwYNDOAgGEBoZwEBpBwEVAgNDOAiMBCgQNDOAiMBCgRnCOAqMEBohwDPwgNEOAZ+EBohwDPwQGBFwJwJAwINEOAxUBLAP/+5wHIwIDC/ZwHHAInC/JwHAAn4OBAAD/g/BOAwNEHYJwGBog/BOAgiBAAf+H4JwELwQNDH4JwEMQQNDH4JwEMQv+H4QNDKgoYBOApUGJoRwDKgxNCOAZUGJoRwEIwoGCOAhGFWARwEIwoUCOAhGEBIJwGRogXCOAriEBoRwGHAZBCOAxxDBoRwGFQZrCOAxADEgRwGCwZOCOA4A/AEMBXggAISQ0AjCZFZYgjBTQt/AwqgBBoraFfozgBbQgNBGIgNGEQIGEewJVECgIGEHwJGEAxr9BKggGBewImBfoRUEAwQ7CBIJUFgINCFoIJBO4oNCwAtBBIJ3JFoIJBFoJNEEQQfBBIJNDRgwJCJoaMGBIQ/DPwgNBFoJiHRgYtBMQ4+DFoJiHHwYfBMQbFDPwoJBXww+CFoZwGHwQtDOAz2CFoZwGUIQJCTwRwGGAIJBTwRwGEQICBKAIRDOAngAQJCBJoJwGAAfhD4ZwEAAxwGBpZiBAA4NDMQIAHPwZiCAAx+DMQQNKKhKMDKhKMDKhINEKgf7BoaaDIwn5BpCpD/A8DVAhGD/g8DBooJC/g8DBoqNC/A8DWwg4DIAINIe4k/BpA0BPAI4CBowmBWAI4CBo4uFKYoAFM4KLEAAxZBWogA/ADSMBRZaaCBpTlCwANMXYIAIaQXgBpioKBoTEKaILgLBoRwKn4NBOBQNDOBINDOBN/BoRwJBoZwJBgRwKBoZwJBoZwIgILCOBINDJAJwHfQX8OQJwHBoaqBOA4NC/DUBOA8HBoQDBOA4NC+AfBOA76C8BXBOA4NDQIQNJLwJwILoINCOBANCC4JwIfQQNBOBAbCMwZwGIoQAGJAZ9CAAxIDU4QAGJAbfCAAxIEBpBIEQ4IAGXIhwCAAq5EOAQAGOH5w/OH5wvBoYAELIInEAA4ZKLIiYDAA5ZBTAYAHLIKYDAA5ZBTAgAGZQKYEAAzKBTAhwjAH4A8U4LRCh7xGS4LRCcYwGBAATDBAwLjEBojDBeILVEAwIADwA7Baoj4BAAfAcYLVECgIADGgIRCfAgAD/EAn5UFBohUIv4OEKg4iBKghNBKghwEGgJNCOBJCBD4RwIIQI/BMQZwHH4JUDOArFDOgJwHBIJiGOAQtBBoJiGSYQNBC4JiGSYTPDH4RiDGAP4Z4jFFGAImBBoY/BYoYmDEoZwIRAhwIwDrDBoJwG4AXDJoJwHRAbMCOAzICZgZwGRAXADYRwGK4X4EQLhGOAYADPwZwFcopwHcopwHBpBwEAAaMEOAoACRgjhFBo7hFAAYNDOAZiFBoZwDKgqoDOAZUFBohwCW4QNHfQYNEWwZwDCIQNHGgINBIwgNEOAIDDBo8DLAoNGAAg4DBpJxDMIgAEXAYNJFQYMJXgTtEAA8HIhIA/ACp9BN5SZD8B7JBoX+YZjSJb4f//ANMYpF/BogqHBovwBowMEKpANF/+ABpiAGBoxjGBoyrGBoxxGBo5xFBo5xFPopGHBo5/FBo5GFYYpGHBpCNEj5UMBpCNEh4ICw//g5UGA4X8AYOAHwQNG/EDBoIGCcQYJBH4IDB4EBKgoGCBoQJBQoJUDBoYDBBIJbBVIgNGHAJiEEQIUBAQQtBMQhbBBoQXBGISMFBQN/C4RiFRgIKBD4IxDYoY+BBoIfBC4IRBOAZ+CBoQJBAYJwGwAtBBIIDBOA3AFoIJBOBHgNgY/DOAiMCHYLFCOAp+CFoZwGPwQRBAwINEGAb6CAAR+DGgYtBAAZ+DGgYmCBo5iCIQQACRgZiGAASMEKgYNJKgYtBAASaEYoZiEBohUIVAhUIBoomB/BUEBopUIBoipIBogmBDYJGEBogmBO4JmCBo8/V4QNJh7nCHAYNFgxYEMIxKGBpYqCU4oAFOoLtEAA8PBhYA/AB9///AQ5jFCABEfQ47MCYAbvBXQgiEUYKxFg4iEgbNGh4UEbgRNFCgoNBH4hpBOBYUBAwhwFHwJ3FOApaBNIpwFCYJpFOAovBNIpwFBgJbFOAgECKgwUDIgQABTYhwDJQIACKghwDKQRGGOAYfBAAZwHBghUEOASXCAAaiF/xSEKgprCIgibGAwO/BopUEKApwJAAyMEGoyoGSwhvHWQqLHOARgKbgpSHfAqYGOBJSEOBAMFOAyXEOBBEGOAyXEOBBEGOAyXEOA5EHOAqXFOA5EHOAqXGOAxEIOAgMIOAZEJOAaXHMQpEJAH4AOn6QJbIaDKQgYcKUATXJVxwNCZQ8fCwIND4C4H4ANDHAzUCBoY4GBAP+MIQEBBo//4IDCOIoXD+ANDewozDBoZGFBIZXBIw4NDAAZGFBo6NFEoYAERogNIKgk/Bo5UEBpBUEj5UMh5UMBpKpDg4KFAwRUDbgP4JARCBKgrEB/AsC/BNCAYINEfYQJBCQJiEBIQpDCQJiEv4JBHAT2DRggTBQIReBWAJiDBQJlDYIIgBYoY+BwBGCLwIVBOAYYBCYJUFOAYYBCYIzBHgIVBOAoTBKgYVBOA6NCwAVBOA6zEOAwlDSIhwF4ANCEAJKBOAvwcgYNCOAv/TQQYBGILhFAAn4DYJwDHwQAGBogUBAAx+ERIQAFPwiJCAAwNDL4YNJPYQAGRgZUJRgZUJBoiKC/wNETQZGEMwiaDIwhmEBohGDMwgNFEwS7EVAiNDLAgNFDARYDBowqBWAJGDBo0DH4JYDaQgAFDZKRGBpRxCBpQqCPooAFKoLDEAA8cBhYA/ACM/8AMKcQYAJaASXKWYTdDgwNI/+AawSyHAAJHCn64FBobeCHgwND/xLCeAoNDHAIFBCIINI8BnCKZA0BQYRGEBohxBv5YDBow0Bn5UFGIRGFSIYNG4AiBKgg/CKhQNFPYJUGBohUIBohUICgIADSYSpECgJiEKgwNCKAXAKg0fCgRCCLYWAYggNBCIJiHGAYDBBoJiFGAINBEwJwBMQowCOgQtFPwh0DH4TFEJgYYBOA4XBJgIYBaYRwEHwJMBBQLTDOAYlBJgIKBPwZwFHwIKB+ANCOA5KBD4INBOAwwBTQhwGGAN/BpBiBEQM/HYINBPwhiBS4X8GAR+EMQI4BBoJvCPwiFC/kPAIINGCof//oEDRgYxCAAwNDKgQAGTQZUCBpZUCAAqoDKgYNKKggADWwapDBpZGHBopGHBopGHBoqNHBoqNHBow4GBow4GBow4GBow4GTIgACfIYNJFQrREFRD7EKo/+Bg7HE/ANJDgQ2IeYZRHAH4AmgaYDn50HRgKLCv/8BpD6CZQINIC4QNBVgy2CBoYgCIojEDBoI4GBoRQBn7yHgLuDBoJGGBoQlBj7zIBAIlBh4uDAAhBBEoJYCKgwzCwBKCHgIAEGYY8EAAgzEHgaMHGYI8DPw5wEwBwTEoJwLUgatEMQ4uDPwzhNC4RPBEAKMGC4QNBEAINHC4INBEAIpGKAQgDBo8AnASDRYoAnA='))),
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);
}

BIN
apps/slopeclockpp/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -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}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -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

View File

@ -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);
}
};
})();

View File

@ -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

View File

@ -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%;

View File

@ -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>

View File

@ -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() {

View File

@ -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);

3539
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -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",

View File

@ -10,6 +10,6 @@
"build": "tsc"
},
"dependencies": {
"node-fetch": "^3.2.9"
"node-fetch": "^3.2.10"
}
}