Merge branch 'espruino:master' into master

master
jukioo 2022-10-22 15:12:25 +03:00 committed by GitHub
commit 2314e95d28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 319 additions and 162 deletions

View File

@ -10,7 +10,7 @@ var img = atob("sIwDkm2S66DYwA2AAAAAHAHGSRxJEkAAgmGGBxDIADIdAFJIbAHF9HP00kBUC6Dt
var imgHeight = g.imageMetrics(img).height;
var imgScroll = Math.floor(Math.random()*imgHeight);
g.reset().setFont("6x15").setFontAlign(0,0);
g.clear(1).setFont("6x15").setFontAlign(0,0);
g.drawString(ENV.VERSION + " " + NRF.getAddress(), g.getWidth()/2, 171);
g.drawImage(img,0,24);

View File

@ -1 +1,2 @@
0.01: New app!
0.01: New app!
0.02: Design improvements and fixes.

View File

@ -57,6 +57,17 @@ function drawCircle(isLocked){
g.fillCircle(cx, cy, 6);
}
function toAngle(a){
if (a < 0){
return 360 + a;
}
if(a > 360) {
return 360 - a;
}
return a
}
function drawTime(){
var drawHourHand = g.drawRotRect.bind(g,8,12,R-38);
@ -74,10 +85,10 @@ function drawTime(){
// Draw minute and hour bg
g.setColor(g.theme.bg);
drawHourHand(Math.max(0, h - 2));
drawHourHand(Math.min(360, h + 2));
drawMinuteHand(Math.max(0, m - 2));
drawMinuteHand(Math.min(360, m + 2));
drawHourHand(toAngle(h-3));
drawHourHand(toAngle(h+3));
drawMinuteHand(toAngle(m-2));
drawMinuteHand(toAngle(m+3));
// Draw minute and hour fg
g.setColor(g.theme.fg);
@ -95,13 +106,15 @@ function drawDate(){
var text = ("0"+date.getDate()).substr(-2) + "/" + ("0"+date.getMonth()).substr(-2);
var w = g.stringWidth(text);
g.setColor(g.theme.bg);
g.fillRect(cx-w/2-4, 20, cx+w/2+2, 40+12);
g.fillRect(cx-w/2-4, 20, cx+w/2+4, 40+12);
g.setColor(g.theme.fg);
g.drawLine(cx+w/2+1, 20, cx+w/2+1, 40+12);
g.drawLine(cx+w/2+2, 20, cx+w/2+2, 40+12);
g.drawLine(cx+w/2+3, 20, cx+w/2+3, 40+12);
g.drawLine(cx+w/2+4, 20, cx+w/2+4, 40+12);
// Draw right line as designed by stable diffusion
g.drawLine(cx+w/2+5, 20, cx+w/2+5, 40+12);
g.drawLine(cx+w/2+6, 20, cx+w/2+6, 40+12);
g.drawLine(cx+w/2+7, 20, cx+w/2+7, 40+12);
// And finally the text
g.drawString(text, cx, 40);
}
@ -115,13 +128,21 @@ function drawDigits(){
var text = ("0"+date.getHours()).substr(-2) + ":" + ("0"+date.getMinutes()).substr(-2); //Bangle.getHealthStatus("day").steps;
var w = g.stringWidth(text);
g.setColor(g.theme.bg);
g.fillRect(cx-w/2-4, 120, cx+w/2+2, 140+20);
g.fillRect(cx-w/2-4, 120, cx+w/2+4, 140+20);
// Draw right line as designed by stable diffusion
g.setColor(g.theme.fg);
g.drawLine(cx+w/2+5, 120, cx+w/2+5, 140+20);
g.drawLine(cx+w/2+6, 120, cx+w/2+6, 140+20);
g.drawLine(cx+w/2+7, 120, cx+w/2+7, 140+20);
// And the 7set text
g.setColor("#BBB");
g.drawString("88:88", cx, 140);
g.drawString("88:88", cx+1, 140);
g.drawString("88:88", cx, 141);
g.setColor(g.theme.fg);
g.drawLine(cx+w/2+1, 120, cx+w/2+1, 140+20);
g.drawLine(cx+w/2+2, 120, cx+w/2+2, 140+20);
g.drawLine(cx+w/2+3, 120, cx+w/2+3, 140+20);
g.drawLine(cx+w/2+4, 120, cx+w/2+4, 140+20);
g.drawString(text, cx, 140);
g.drawString(text, cx+1, 140);
g.drawString(text, cx, 141);

View File

@ -1 +1 @@
require("heatshrink").decompress(atob("mEwxH+lYACq2s64Ai1gqDFwgND6OswIABqwAUDAWsKIlWF4oKDFQI2BAA1XD4QAHCY4cBGQJhHBAZFEDAgAOlhCHAgInCwKNF2QtCFIMlkosPABQgCGAYJCwKMDHAMAFjQACDwIkCMAg1BRocACALsVAA0lGAYqCRwYvBLwaiB1gABAYYACxGIAQIBBeAeCCwogBgMBSAlQRwheDDoYcEGAmsw+HBguBq97C4ZgBF4QrDGYOzBAJeCBgIdDwRPCKYIIDG4umH4xgCkqQEwOzRwj2CDIwAP2QHGMAMrvbADMQOIZoQvBGgIAJroLKAA8rkgiBSAa+ETgJnCAChYBBAyDClgvFXwRfBLxJ0BVoIUBwClCeAQbBDA4kBQYVWIIa+CF4dXCQOBvYiCDQQWCAYOsb4QxDF5TwDF4oVCwWmwWCa4XXCoQvBLQhnCqxFBd42CGAQvGq0BLwIqDAAg5BH4QFBMISXDSYYAGYARCCdwg5CCo68ECoOCAgYCEC5IvJXwIWJACLjDAAYvD1YvGao4ASUQQeEQoQpBPgQvDVYRLHLh+HlcAgAuCFQQvGXwUkF5ClCCQQ0FA4YOBgErroJB6xdCL44vCL4J9IBgIgBQQQICRAZaBVIwkBEwQvIdwJRCJwQAEDYKACld0pIVBFgQVIPQgvD6wvDJYLXDSZAxCyWSLQbIKwQvGR4lQR5CVHkkkIgIAOqC/TDhEsCSEsF4pMCF4J4KADAnBF41Qkp7QACbmBF41WgNW1oHBAD5fE1gvEgBfB1mIIgoQCDooHHComCRwRfECwQ3BF4YAHCAIXBAQPXDIQdDI4QZILwINBF5LVICAIvBAIQHBAQIdBqwdBABGBEoQvFwMlBQI0BAApaBEgQBCIAJzCGIRNCAA0rEgOAF4wKCR5AgCF4VQP4QXBAYKPKEgRNCAASZCSBRfDSIZGCFwTvCRxEAlgvHYAMADoTvGFAToDCoQJCE4TuHEYSOC2QgDBYUrwTvIAYiICDIQDDHYKHBC4SDBgDuDQAiQDEgQAVF4YFCKQQnB2SMBSQTADSBAASrrKDRwYrCAgI7CSAhpBxCtCew4AJwOICgaOG65sBcIgODAAIZBcBILCAYWCwOHAoISCKAeAXwYvBSAgPCCAIACBAIARC4YeCEgIpDZgoVCCIQAJEAQjECJRdBRwqQDGAd7I4QvKKYoNHdQQuELwQwGB4QUCVAQAPCY7ZBEoQuEHoIKCBYLoDXyYxDFgIiEFwphFAAuyDAIAQDY6MFA="))
require("heatshrink").decompress(atob("mEwgP/ACfAEZU/ECZELIKhSR/+PAoWAv4FDhk/x/ggP+j0fx/AgP8n8PCIX8CwIFC/F/w4FBgP4gEHC4QFE//w//DC4QFB8YFC+P/8IdCAoYdBAoPxDoQAd+CiKh4dQwDhfAA4A="))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -3,7 +3,7 @@
"name": "AI Clock",
"shortName":"AI Clock",
"icon": "aiclock.png",
"version":"0.01",
"version":"0.02",
"readme": "README.md",
"supports": ["BANGLEJS2"],
"description": "A watch face that was designed by an AI (stable diffusion) and implemented by a human.",

View File

@ -52,3 +52,6 @@
0.46: Fix no clock found error on Bangle.js 2
0.47: Add polyfill for setUI with an object as an argument (fix regression for 2v12 devices after Layout module changed)
0.48: Workaround for BTHRM issues on Bangle.js 1 (write .boot files in chunks)
0.49: Store first found clock as a setting to speed up further boots
0.50: Allow setting of screen rotation
Remove support for 2v11 and earlier firmware

View File

@ -1,8 +1,8 @@
// This runs after a 'fresh' boot
var clockApp=(require("Storage").readJSON("setting.json",1)||{}).clock;
if (clockApp) clockApp = require("Storage").read(clockApp);
if (!clockApp) {
clockApp = require("Storage").list(/\.info$/)
var s = require("Storage").readJSON("setting.json",1)||{};
var _clkApp = require("Storage").read(s.clock);
if (!_clkApp) {
_clkApp = require("Storage").list(/\.info$/)
.map(file => {
const app = require("Storage").readJSON(file,1);
if (app && app.type == "clock") {
@ -11,9 +11,13 @@ if (!clockApp) {
})
.filter(x=>x)
.sort((a, b) => a.sortorder - b.sortorder)[0];
if (clockApp)
clockApp = require("Storage").read(clockApp.src);
if (_clkApp){
s.clock = _clkApp.src;
_clkApp = require("Storage").read(_clkApp.src);
require("Storage").writeJSON("setting.json", s);
}
}
if (!clockApp) clockApp=`E.showMessage("No Clock Found");setWatch(()=>{Bangle.showLauncher();}, global.BTN2||BTN, {repeat:false,edge:"falling"});`;
eval(clockApp);
delete clockApp;
delete s;
if (!_clkApp) _clkApp=`E.showMessage("No Clock Found");setWatch(()=>{Bangle.showLauncher();}, global.BTN2||BTN, {repeat:false,edge:"falling"});`;
eval(_clkApp);
delete _clkApp;

View File

@ -92,6 +92,7 @@ if (s.options) boot+=`Bangle.setOptions(${E.toJS(s.options)});\n`;
if (s.brightness && s.brightness!=1) boot+=`Bangle.setLCDBrightness(${s.brightness});\n`;
if (s.passkey!==undefined && s.passkey.length==6) boot+=`NRF.setSecurity({passkey:${E.toJS(s.passkey.toString())}, mitm:1, display:1});\n`;
if (s.whitelist) boot+=`NRF.on('connect', function(addr) { if (!(require('Storage').readJSON('setting.json',1)||{}).whitelist.includes(addr)) NRF.disconnect(); });\n`;
if (s.rotate) boot+=`g.setRotation(${s.rotate&3},${s.rotate>>2});\n` // screen rotation
// Pre-2v10 firmwares without a theme/setUI
delete g.theme; // deleting stops us getting confused by our own decl. builtins can't be deleted
if (!g.theme) {
@ -113,54 +114,6 @@ Bangle.setUI=function(mode, cb) {
Bangle._setUI(mode, cb);
};\n`;
}
delete E.showScroller; // deleting stops us getting confused by our own decl. builtins can't be deleted
if (!E.showScroller) { // added in 2v11 - this is a limited functionality polyfill
boot += `E.showScroller = (function(a){function n(){g.reset();b>=l+c&&(c=1+b-l);b<c&&(c=b);g.setColor(g.theme.fg);for(var d=0;d<l;d++){var m=d+c;if(0>m||m>=a.c)break;var f=24+d*a.h;a.draw(m,{x:0,y:f,w:h,h:a.h});d+c==b&&g.setColor(g.theme.fg).drawRect(0,f,h-1,f+a.h-1).drawRect(1,f+1,h-2,f+a.h-2)}g.setColor(c?g.theme.fg:g.theme.bg);g.fillPoly([e,6,e-14,20,e+14,20]);g.setColor(a.c>l+c?g.theme.fg:g.theme.bg);g.fillPoly([e,k-7,e-14,k-21,e+14,k-21])}if(!a)return Bangle.setUI();var b=0,c=0,h=g.getWidth(),
k=g.getHeight(),e=h/2,l=Math.floor((k-48)/a.h);g.reset().clearRect(0,24,h-1,k-1);n();Bangle.setUI("updown",d=>{d?(b+=d,0>b&&(b=a.c-1),b>=a.c&&(b=0),n()):a.select(b)})});\n`;
}
delete g.imageMetrics; // deleting stops us getting confused by our own decl. builtins can't be deleted
if (!g.imageMetrics) { // added in 2v11 - this is a limited functionality polyfill
boot += `Graphics.prototype.imageMetrics=function(src) {
if (src[0]) return {width:src[0],height:src[1]};
else if ('object'==typeof src) return {
width:("width" in src) ? src.width : src.getWidth(),
height:("height" in src) ? src.height : src.getHeight()};
var im = E.toString(src);
return {width:im.charCodeAt(0), height:im.charCodeAt(1)};
};\n`;
}
delete g.stringMetrics; // deleting stops us getting confused by our own decl. builtins can't be deleted
if (!g.stringMetrics) { // added in 2v11 - this is a limited functionality polyfill
boot += `Graphics.prototype.stringMetrics=function(txt) {
txt = txt.toString().split("\\n");
return {width:Math.max.apply(null,txt.map(x=>g.stringWidth(x))), height:this.getFontHeight()*txt.length};
};\n`;
}
delete g.wrapString; // deleting stops us getting confused by our own decl. builtins can't be deleted
if (!g.wrapString) { // added in 2v11 - this is a limited functionality polyfill
boot += `Graphics.prototype.wrapString=function(str, maxWidth) {
var lines = [];
for (var unwrappedLine of str.split("\\n")) {
var words = unwrappedLine.split(" ");
var line = words.shift();
for (var word of words) {
if (g.stringWidth(line + " " + word) > maxWidth) {
lines.push(line);
line = word;
} else {
line += " " + word;
}
}
lines.push(line);
}
return lines;
};\n`;
}
delete Bangle.appRect; // deleting stops us getting confused by our own decl. builtins can't be deleted
if (!Bangle.appRect) { // added in 2v11 - polyfill for older firmwares
boot += `Bangle.appRect = ((y,w,h)=>({x:0,y:0,w:w,h:h,x2:w-1,y2:h-1}))(g.getWidth(),g.getHeight());
(lw=>{ Bangle.loadWidgets = () => { lw(); Bangle.appRect = ((y,w,h)=>({x:0,y:y,w:w,h:h-y,x2:w-1,y2:h-(1+h)}))(global.WIDGETS?24:0,g.getWidth(),g.getHeight()); }; })(Bangle.loadWidgets);\n`;
}
// Append *.boot.js files
// These could change bleServices/bleServiceOptions if needed

View File

@ -1,7 +1,7 @@
{
"id": "boot",
"name": "Bootloader",
"version": "0.48",
"version": "0.50",
"description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings",
"icon": "bootloader.png",
"type": "bootloader",

View File

@ -3,3 +3,4 @@
0.03: Use default Bangle formatter for booleans
0.04: Support new fast app switching
0.05: Allow to directly eval apps instead of loading
0.06: Cache apps for faster start

View File

@ -5,34 +5,24 @@
Bangle.loadWidgets();
Bangle.drawWidgets();
}
var apps = s
.list(/\.info$/)
.map((app) => {
var a = s.readJSON(app, 1);
return (
a && {
name: a.name,
type: a.type,
icon: a.icon,
sortorder: a.sortorder,
src: a.src,
}
);
})
.filter(
(app) =>
app &&
(app.type == "app" ||
(app.type == "clock" && settings.showClocks) ||
!app.type)
);
apps.sort((a, b) => {
var n = (0 | a.sortorder) - (0 | b.sortorder);
if (n) return n;
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
return 0;
});
let launchCache = s.readJSON("launch.cache.json", true)||{};
let launchHash = require("Storage").hash(/\.info/);
if (launchCache.hash!=launchHash) {
launchCache = {
hash : launchHash,
apps : s.list(/\.info$/)
.map(app=>{var a=s.readJSON(app,1);return a&&{name:a.name,type:a.type,icon:a.icon,sortorder:a.sortorder,src:a.src};})
.filter(app=>app && (app.type=="app" || (app.type=="clock" && settings.showClocks) || !app.type))
.sort((a,b)=>{
var n=(0|a.sortorder)-(0|b.sortorder);
if (n) return n; // do sortorder first
if (a.name<b.name) return -1;
if (a.name>b.name) return 1;
return 0;
}) };
s.writeJSON("launch.cache.json", launchCache);
}
let apps = launchCache.apps;
apps.forEach((app) => {
if (app.icon) app.icon = s.read(app.icon);
});

View File

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

View File

@ -15,3 +15,5 @@
0.13: Add fullscreen mode
0.14: Use default Bangle formatter for booleans
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

View File

@ -1,5 +1,6 @@
{ // must be inside our own scope here so that when we are unloaded everything disappears
let s = require("Storage");
// handle customised launcher
let scaleval = 1;
let vectorval = 20;
let font = g.getFonts().includes("12x20") ? "12x20" : "6x8:2";
@ -7,56 +8,51 @@ let settings = Object.assign({
showClocks: true,
fullscreen: false
}, s.readJSON("launch.json", true) || {});
if ("vectorsize" in settings) {
vectorval = parseInt(settings.vectorsize);
}
if ("vectorsize" in settings)
vectorval = parseInt(settings.vectorsize);
if ("font" in settings){
if(settings.font == "Vector"){
scaleval = vectorval/20;
font = "Vector"+(vectorval).toString();
}
else{
font = settings.font;
scaleval = (font.split("x")[1])/20;
}
if(settings.font == "Vector"){
scaleval = vectorval/20;
font = "Vector"+(vectorval).toString();
} else{
font = settings.font;
scaleval = (font.split("x")[1])/20;
}
}
let apps = s.list(/\.info$/).map(app=>{var a=s.readJSON(app,1);return a&&{name:a.name,type:a.type,icon:a.icon,sortorder:a.sortorder,src:a.src};}).filter(app=>app && (app.type=="app" || (app.type=="clock" && settings.showClocks) || !app.type));
apps.sort((a,b)=>{
var n=(0|a.sortorder)-(0|b.sortorder);
if (n) return n; // do sortorder first
if (a.name<b.name) return -1;
if (a.name>b.name) return 1;
return 0;
});
apps.forEach(app=>{
if (app.icon)
app.icon = s.read(app.icon); // should just be a link to a memory area
});
// FIXME: check not needed after 2v11
if (g.wrapString) {
g.setFont(font);
apps.forEach(app=>app.name = g.wrapString(app.name, g.getWidth()-64).join("\n"));
// cache app list so launcher loads more quickly
let launchCache = s.readJSON("launch.cache.json", true)||{};
let launchHash = require("Storage").hash(/\.info/);
if (launchCache.hash!=launchHash) {
launchCache = {
hash : launchHash,
apps : s.list(/\.info$/)
.map(app=>{var a=s.readJSON(app,1);return a&&{name:a.name,type:a.type,icon:a.icon,sortorder:a.sortorder,src:a.src};})
.filter(app=>app && (app.type=="app" || (app.type=="clock" && settings.showClocks) || !app.type))
.sort((a,b)=>{
var n=(0|a.sortorder)-(0|b.sortorder);
if (n) return n; // do sortorder first
if (a.name<b.name) return -1;
if (a.name>b.name) return 1;
return 0;
}) };
s.writeJSON("launch.cache.json", launchCache);
}
function drawApp(i, r) {
var app = apps[i];
if (!app) return;
g.clearRect((r.x),(r.y),(r.x+r.w-1), (r.y+r.h-1));
g.setFont(font).setFontAlign(-1,0).drawString(app.name,64*scaleval,r.y+(32*scaleval));
if (app.icon) try {g.drawImage(app.icon,8*scaleval, r.y+(8*scaleval), {scale: scaleval});} catch(e){}
}
g.clear();
if (!settings.fullscreen) {
let apps = launchCache.apps;
// Now apps list is loaded - render
if (!settings.fullscreen)
Bangle.loadWidgets();
Bangle.drawWidgets();
}
E.showScroller({
h : 64*scaleval, c : apps.length,
draw : drawApp,
draw : (i, r) => {
var app = apps[i];
if (!app) return;
g.clearRect((r.x),(r.y),(r.x+r.w-1), (r.y+r.h-1));
g.setFont(font).setFontAlign(-1,0).drawString(app.name,64*scaleval,r.y+(32*scaleval));
if (app.icon) {
if (!app.img) app.img = s.read(app.icon); // load icon if it wasn't loaded
try {g.drawImage(app.img,8*scaleval, r.y+(8*scaleval), {scale: scaleval});} catch(e){}
}
},
select : i => {
var app = apps[i];
if (!app) return;
@ -64,11 +60,11 @@ E.showScroller({
E.showMessage(/*LANG*/"App Source\nNot found");
setTimeout(drawMenu, 2000);
} else {
E.showMessage(/*LANG*/"Loading...");
load(app.src);
}
}
});
g.flip(); // force a render before widgets have finished drawing
function returnToClock() {
// unload everything manually
@ -96,4 +92,6 @@ function lockHandler(locked) {
lockTimeout = setTimeout(returnToClock, 10000);
}
Bangle.on("lock", lockHandler);
if (!settings.fullscreen) // finally draw widgets
Bangle.drawWidgets();
}

View File

@ -2,7 +2,7 @@
"id": "launch",
"name": "Launcher",
"shortName": "Launcher",
"version": "0.15",
"version": "0.17",
"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",
@ -13,6 +13,6 @@
{"name":"launch.app.js","url":"app.js"},
{"name":"launch.settings.js","url":"settings.js"}
],
"data": [{"name":"launch.json"}],
"data": [{"name":"launch.json"},{"name":"launch.cache.json"}],
"sortorder": -10
}

View File

@ -70,4 +70,6 @@
0.51: Emit "message events"
Setting to hide widget
Add custom event handlers to prevent default app form loading
Move WIDGETS.messages.buzz() to require("messages").buzz()
Move WIDGETS.messages.buzz() to require("messages").buzz()
0.52: Fix require("messages").buzz() regression
Fix background color in messages list after one unread message is shown

View File

@ -68,8 +68,7 @@ function saveMessages() {
function showMapMessage(msg) {
active = "map";
var m;
var distance, street, target, eta;
var m, distance, street, target, eta;
m=msg.title.match(/(.*) - (.*)/);
if (m) {
distance = m[1];
@ -379,7 +378,7 @@ function checkMessages(options) {
draw : function(idx, r) {"ram"
var msg = MESSAGES[idx];
if (msg && msg.new) g.setBgColor(g.theme.bgH).setColor(g.theme.fgH);
else g.setColor(g.theme.fg);
else g.setBgColor(g.theme.bg).setColor(g.theme.fg);
g.clearRect(r.x,r.y,r.x+r.w, r.y+r.h);
if (!msg) return;
var x = r.x+2, title = msg.title, body = msg.body;

View File

@ -159,7 +159,7 @@ exports.buzz = function(msgSrc) {
exports.buzzTimeout = setTimeout(()=>require("buzz").pattern(pattern), repeat*1000);
var vibrateTimeout = (require('Storage').readJSON("messages.settings.json", true) || {}).vibrateTimeout;
if (vibrateTimeout===undefined) vibrateTimeout=60;
if (vibrateTimeout && !exports.stopTimeout) exports.stopTimeout = setTimeout(exports.stopTimeout, vibrateTimeout*1000);
if (vibrateTimeout && !exports.stopTimeout) exports.stopTimeout = setTimeout(exports.stopBuzz, vibrateTimeout*1000);
}
return require("buzz").pattern(pattern);
};

View File

@ -1,7 +1,7 @@
{
"id": "messages",
"name": "Messages",
"version": "0.51",
"version": "0.52",
"description": "App to display notifications from iOS and Gadgetbridge/Android",
"icon": "app.png",
"type": "app",

View File

@ -54,3 +54,7 @@
Improve "Turn Off" user experience
0.48: Allow reading custom themes from files
0.49: Now reloads settings properly after 'Calibrate Battery'
0.50: Add Bangle.js 2 touchscreen calibration - for 2v16 or 2v15 cutting edge builds
0.51: Add setting for configuring a launcher
0.52: Add option for left-handed users

View File

@ -29,10 +29,12 @@ This is Bangle.js's settings menu
* **LCD Brightness** set how bright the LCD is. Due to hardware limitations in the LCD backlight, you may notice flicker if the LCD is not at 100% brightness.
* **LCD Timeout** how long should the LCD stay on for if no activity is detected. 0=stay on forever
* **Rotation** allows you to rotate (or mirror) what's displayed on the screen, eg. for left-handed wearers (needs 2v16 or 2v15 cutting edge firmware to work reliably)
* **Wake on X** should the given activity wake up the Bangle.js LCD?
* On Bangle.js 2 when locked the touchscreen is turned off to save power. Because of this,
`Wake on Touch` actually uses the accelerometer, and you need to actually tap the display to wake Bangle.js.
* **Twist X** these options adjust the sensitivity of `Wake on Twist` to ensure Bangle.js wakes up with just the right amount of wrist movement.
* **Calibrate** on Bangle.js 2, pop up a screen allowing you to calibrate the touchscreen (calibration only works on 2v16 or 2v15 cutting edge builds)
## Locale

View File

@ -1,7 +1,7 @@
{
"id": "setting",
"name": "Settings",
"version": "0.49",
"version": "0.52",
"description": "A menu for setting up Bangle.js",
"icon": "settings.png",
"tags": "tool,system",

View File

@ -87,6 +87,7 @@ function showSystemMenu() {
/*LANG*/'LCD': ()=>showLCDMenu(),
/*LANG*/'Locale': ()=>showLocaleMenu(),
/*LANG*/'Select Clock': ()=>showClockMenu(),
/*LANG*/'Select Launcher': ()=>showLauncherMenu(),
/*LANG*/'Date & Time': ()=>showSetTimeMenu()
};
@ -383,6 +384,8 @@ function showLCDMenu() {
// converts Espruino internal unit to g
function internalToG(u) { return u / 8192; }
var rotNames = [/*LANG*/"No",/*LANG*/"Rotate CW",/*LANG*/"Left Handed",/*LANG*/"Rotate CCW",/*LANG*/"Mirror"];
const lcdMenu = {
'': { 'title': 'LCD' },
'< Back': ()=>showSystemMenu(),
@ -408,6 +411,18 @@ function showLCDMenu() {
Bangle.setLCDTimeout(settings.timeout);
}
},
/*LANG*/'Rotate': {
value: 0|settings.rotate,
min: 0,
max: rotNames.length-1,
format: v=> rotNames[v],
onchange: v => {
settings.rotate = 0 | v;
updateSettings();
g.setRotation(settings.rotate&3,settings.rotate>>2).clear();
Bangle.drawWidgets();
}
},
/*LANG*/'Wake on BTN1': {
value: settings.options.wakeOnBTN1,
format: boolFormat,
@ -491,6 +506,10 @@ function showLCDMenu() {
}
}
});
if (BANGLEJS2)
Object.assign(lcdMenu, {
/*LANG*/'Calibrate': () => showTouchscreenCalibration()
});
return E.showMenu(lcdMenu)
}
@ -667,6 +686,35 @@ function showClockMenu() {
}
return E.showMenu(clockMenu);
}
function showLauncherMenu() {
var launcherApps = require("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);
const launcherMenu = {
'': {
'title': /*LANG*/'Select Launcher',
},
'< Back': ()=>showSystemMenu(),
};
launcherApps.forEach((app, index) => {
var label = app.name;
if ((!settings.launcher && index === 0) || (settings.launcher === app.src)) {
label = "* " + label;
}
launcherMenu[label] = () => {
if (settings.launcher !== app.src) {
settings.launcher = app.src;
updateSettings();
showMainMenu();
}
};
});
if (launcherApps.length === 0) {
launcherMenu[/*LANG*/"No Launchers Found"] = () => { };
}
return E.showMenu(launcherMenu);
}
function showSetTimeMenu() {
d = new Date();
@ -774,4 +822,85 @@ function showAppSettings(app) {
}
}
function showTouchscreenCalibration() {
Bangle.setUI();
// disable touchscreen calibration (passed coords right through)
Bangle.setOptions({touchX1: 0, touchY1: 0, touchX2: g.getWidth(), touchY2: g.getHeight() });
var P = 32;
var corners = [
[P,P],
[g.getWidth()-P,P],
[g.getWidth()-P,g.getHeight()-P],
[P,g.getHeight()-P],
];
var currentCorner = 0;
var currentTry = 0;
var pt = {
x1 : 0, y1 : 0, x2 : 0, y2 : 0
};
function showTapSpot() {
var spot = corners[currentCorner];
g.clear(1);
g.drawLine(spot[0]-32,spot[1],spot[0]+32,spot[1]);
g.drawLine(spot[0],spot[1]-32,spot[0],spot[1]+32);
g.drawCircle(spot[0],spot[1], 16);
var tapsLeft = (1-currentTry)*4+(4-currentCorner);
g.setFont("6x8:2").setFontAlign(0,0).drawString(tapsLeft+" taps\nto go", g.getWidth()/2, g.getHeight()/2);
}
function calcCalibration() {
g.clear(1);
// we should now have 4 of each tap in 'pt'
pt.x1 /= 4;
pt.y1 /= 4;
pt.x2 /= 4;
pt.y2 /= 4;
// work out final values
var calib = {
x1 : Math.round(pt.x1 - (pt.x2-pt.x1)*P/(g.getWidth()-P*2)),
y1 : Math.round(pt.y1 - (pt.y2-pt.y1)*P/(g.getHeight()-P*2)),
x2 : Math.round(pt.x2 + (pt.x2-pt.x1)*P/(g.getWidth()-P*2)),
y2 : Math.round(pt.y2 + (pt.y2-pt.y1)*P/(g.getHeight()-P*2))
};
Bangle.setOptions({
touchX1: calib.x1, touchY1: calib.y1, touchX2: calib.x2, touchY2: calib.y2
});
var s = require("Storage").readJSON("setting.json",1)||{};
s.touch = calib;
require("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);
}
function touchHandler(_,e) {
var spot = corners[currentCorner];
// store averages
if (spot[0]*2 < g.getWidth())
pt.x1 += e.x;
else
pt.x2 += e.x;
if (spot[1]*2 < g.getHeight())
pt.y1 += e.y;
else
pt.y2 += e.y;
// go to next corner
currentCorner++;
if (currentCorner>=corners.length) {
currentCorner = 0;
currentTry++;
if (currentTry==2) {
Bangle.removeListener('touch', touchHandler);
return calcCalibration();
}
}
showTapSpot();
}
Bangle.on('touch', touchHandler);
showTapSpot();
}
showMainMenu();

2
core

@ -1 +1 @@
Subproject commit 76419750083a88ee7a569db3975ae1bdd6dc155a
Subproject commit 80de03d8e665c210dc3443d6869176c848ab103f

View File

@ -151,6 +151,10 @@
<input type="checkbox" id="settings-ble-compat">
<i class="form-icon"></i> Bluetooth Compatibility mode (limit to 20 byte writes)
</label>
<label class="form-switch">
<input type="checkbox" id="settings-usage-stats">
<i class="form-icon"></i> Send app analytics to banglejs.com (apps installed, favourites, firmware version)
</label>
<div class="form-group">
<select class="form-select form-inline" id="settings-lang" style="width: 10em">
<option value="">None (English)</option>

View File

@ -48,8 +48,6 @@ function onFoundDeviceInfo(deviceId, deviceVersion) {
} else if (versionLess(deviceVersion, RECOMMENDED_VERSION)) {
showToast(`You're using an old Bangle.js firmware (${deviceVersion}) and ${RECOMMENDED_VERSION} is available (<a href="http://www.espruino.com/ChangeLog" target="_blank">see changes</a>). You can update ${fwExtraText}<a href="${fwURL}" target="_blank">with the instructions here</a>` ,"warning", 20000);
}
// check against features shown?
filterAppsForDevice(deviceId);
/* if we'd saved a device ID but this device is different, ensure
@ -59,6 +57,43 @@ function onFoundDeviceInfo(deviceId, deviceVersion) {
setSavedDeviceId(undefined);
}
// Called when we refresh the list of installed apps
function onRefreshMyApps() {
/* if we're allowed to, send usage stats. We'll only
actually send if the data has changed */
sendUsageStats();
}
var submittedUsageInfo = "";
/* Send usage stats to servers if it has changed */
function sendUsageStats() {
if (!SETTINGS.sendUsageStats) return; // not allowed!
if (device.uid === undefined) return; // no data yet!
/* Work out what we'll send:
* A suitably garbled UID so we can avoid too many duplicates
* firmware version
* apps installed
* apps favourited
*/
var usageInfo = `uid=${encodeURIComponent(device.uid)}&fw=${encodeURIComponent(device.version)}&apps=${encodeURIComponent(device.appsInstalled.map(a=>a.id).join(","))}&favs=${encodeURIComponent(SETTINGS.favourites.join(","))}`;
// Do a quick check for unchanged data to reduce server load
if (usageInfo != submittedUsageInfo) {
console.log("sendUsageStats: Submitting usage stats...");
var xmlhttp = new XMLHttpRequest(); // new HttpRequest instance
xmlhttp.open("POST", "https://banglejs.com/submit_app_stats.php", true /*async*/);
xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlhttp.onload = (e) => {
if (xmlhttp.readyState === 4)
console.log(`sendUsageStats (${xmlhttp.status}): ${xmlhttp.responseText}`);
};
xmlhttp.onerror = (e) => {
console.error("sendUsageStats ERROR: "+xmlhttp.statusText);
};
xmlhttp.send(usageInfo);
submittedUsageInfo = usageInfo;
}
}
var originalAppJSON = undefined;
function filterAppsForDevice(deviceId) {
if (originalAppJSON===undefined && appJSON.length)
@ -205,6 +240,15 @@ window.addEventListener('load', (event) => {
saveSettings();
});
// Sending usage stats
var selectUsageStats = document.getElementById("settings-usage-stats");
selectUsageStats.checked = !!SETTINGS.sendUsageStats;
selectUsageStats.addEventListener("change",event=>{
console.log("Send Usage Stats "+(event.target.checked?"on":"off"));
SETTINGS.sendUsageStats = event.target.checked;
saveSettings();
});
// Load language list
httpGet("lang/index.json").then(languagesJSON=>{
var languages;