Merge branch 'espruino:master' into master
commit
2314e95d28
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
0.01: New app!
|
||||
0.01: New app!
|
||||
0.02: Design improvements and fixes.
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 |
|
|
@ -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.",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
2
core
|
|
@ -1 +1 @@
|
|||
Subproject commit 76419750083a88ee7a569db3975ae1bdd6dc155a
|
||||
Subproject commit 80de03d8e665c210dc3443d6869176c848ab103f
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
48
loader.js
48
loader.js
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue