diff --git a/apps/barclock/ChangeLog b/apps/barclock/ChangeLog index 0b8470b6a..5df032c4d 100644 --- a/apps/barclock/ChangeLog +++ b/apps/barclock/ChangeLog @@ -8,3 +8,4 @@ 0.08: Use theme colors, Layout library 0.09: Fix time/date disappearing after fullscreen notification 0.10: Use ClockFace library +0.11: Use ClockFace.is12Hour diff --git a/apps/barclock/clock-bar.js b/apps/barclock/clock-bar.js index a465bb692..987d41cc6 100644 --- a/apps/barclock/clock-bar.js +++ b/apps/barclock/clock-bar.js @@ -3,7 +3,6 @@ * A simple digital clock showing seconds as a bar **/ // Check settings for what type our clock should be -const is12Hour = (require("Storage").readJSON("setting.json", 1) || {})["12hour"]; let locale = require("locale"); { // add some more info to locale let date = new Date(); @@ -25,7 +24,7 @@ function renderBar(l) { function timeText(date) { - if (!is12Hour) { + if (!clock.is12Hour) { return locale.time(date, true); } const date12 = new Date(date.getTime()); @@ -38,7 +37,7 @@ function timeText(date) { return locale.time(date12, true); } function ampmText(date) { - return (is12Hour && locale.hasMeridian)? locale.meridian(date) : ""; + return (clock.is12Hour && locale.hasMeridian) ? locale.meridian(date) : ""; } function dateText(date) { const dayName = locale.dow(date, true), @@ -69,7 +68,7 @@ const ClockFace = require("ClockFace"), }, {lazy: true}); // adjustments based on screen size and whether we display am/pm let thickness; // bar thickness, same as time font "pixel block" size - if (is12Hour) { + if (this.is12Hour) { // Maximum font size = ( - ) / (5chars * 6px) thickness = Math.floor((Bangle.appRect.w-24)/(5*6)); } else { diff --git a/apps/barclock/metadata.json b/apps/barclock/metadata.json index 3ee7ccb3a..7bc61096d 100644 --- a/apps/barclock/metadata.json +++ b/apps/barclock/metadata.json @@ -1,7 +1,7 @@ { "id": "barclock", "name": "Bar Clock", - "version": "0.10", + "version": "0.11", "description": "A simple digital clock showing seconds as a bar", "icon": "clock-bar.png", "screenshots": [{"url":"screenshot.png"},{"url":"screenshot_pm.png"}], diff --git a/apps/boot/ChangeLog b/apps/boot/ChangeLog index e3f492d3b..a43ecf86e 100644 --- a/apps/boot/ChangeLog +++ b/apps/boot/ChangeLog @@ -51,3 +51,4 @@ 0.45: Fix 0.44 regression (auto-add semi-colon between each boot code chunk) 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) diff --git a/apps/boot/bootupdate.js b/apps/boot/bootupdate.js index 119cd2c2c..4cb3c52e4 100644 --- a/apps/boot/bootupdate.js +++ b/apps/boot/bootupdate.js @@ -197,8 +197,18 @@ bootFiles.forEach(bootFile=>{ require('Storage').write('.boot0',"//"+bootFile+"\n",fileOffset); fileOffset+=2+bootFile.length+1; var bf = require('Storage').read(bootFile); - require('Storage').write('.boot0',bf,fileOffset); - fileOffset+=bf.length; + // we can't just write 'bf' in one go because at least in 2v13 and earlier + // Espruino wants to read the whole file into RAM first, and on Bangle.js 1 + // it can be too big (especially BTHRM). + var bflen = bf.length; + var bfoffset = 0; + while (bflen) { + var bfchunk = Math.min(bflen, 2048); + require('Storage').write('.boot0',bf.substr(bfoffset, bfchunk),fileOffset); + fileOffset+=bfchunk; + bfoffset+=bfchunk; + bflen-=bfchunk; + } require('Storage').write('.boot0',";\n",fileOffset); fileOffset+=2; }); diff --git a/apps/boot/metadata.json b/apps/boot/metadata.json index d1bf2edde..62adc4db1 100644 --- a/apps/boot/metadata.json +++ b/apps/boot/metadata.json @@ -1,7 +1,7 @@ { "id": "boot", "name": "Bootloader", - "version": "0.47", + "version": "0.48", "description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings", "icon": "bootloader.png", "type": "bootloader", diff --git a/apps/f9lander/ChangeLog b/apps/f9lander/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/f9lander/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/f9lander/README.md b/apps/f9lander/README.md new file mode 100644 index 000000000..16202f166 --- /dev/null +++ b/apps/f9lander/README.md @@ -0,0 +1,33 @@ +# F9 Lander + +Land a Falcon 9 booster on a drone ship. + +## Game play + +Attempt to land your Falcon 9 booster on a drone ship before running out of fuel. +A successful landing requires: + * setting down on the ship + * the booster has to be mostly vertical + * the landing speed cannot be too high + +## Controls + +The angle of the booster is controlled by tilting the watch side-to-side. The +throttle level is controlled by tilting the watch forward and back: + * screen horizontal (face up) means no throttle + * screen vertical corresponds to full throttle + +The fuel burn rate is proportional to the throttle level. + +## Creators +Liam Kl. B. + +Marko Kl. B. + +## Screenshots + +![](f9lander_screenshot1.png) + +![](f9lander_screenshot2.png) + +![](f9lander_screenshot3.png) diff --git a/apps/f9lander/app-icon.js b/apps/f9lander/app-icon.js new file mode 100644 index 000000000..572768a28 --- /dev/null +++ b/apps/f9lander/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwcA/4AD/P8yVJkgCCye27dt2wRE//kCIuSuwRIBwgCCpwRQpIRRnYRQkmdCIvPCJICBEZ4RG/IRP/15CJ/z5IRPz4RM/gQB/n+BxICCn/z/P/BxQCDz7mIAX4Cq31/CJ+ebpiYE/IR/CNP/5IROnn//4jP5DFQ5sJCKAjPk3oCMMk4QRQAX4Ckn7jBAA/5CK8nCJPJNHA")) diff --git a/apps/f9lander/app.js b/apps/f9lander/app.js new file mode 100644 index 000000000..7e52104c0 --- /dev/null +++ b/apps/f9lander/app.js @@ -0,0 +1,150 @@ +const falcon9 = Graphics.createImage(` + xxxxx + xxxxx xxxxx + x x + x x + xxxxx + xxxxx + xxxxx + xxxxx + xxxxx + xxxxx + xxxxx + xxxxx + xxxxx + xxxxx + xxxxx + xxxxx + xxxxx + xxxxx + xxxxxxxxx + xx xxxxx xx +xx xx`); + +const droneShip = Graphics.createImage(` +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +`); + +const droneX = Math.floor(Math.random()*(g.getWidth()-droneShip.width-40) + 20) +const cloudOffs = Math.floor(Math.random()*g.getWidth()/2); + +const oceanHeight = g.getHeight()*0.1; + +const targetY = g.getHeight()-oceanHeight-falcon9.height/2; + +var booster = { x : g.getWidth()/4 + Math.random()*g.getWidth()/2, + y : 20, + vx : 0, + vy : 0, + mass : 100, + fuel : 100 }; + +var exploded = false; +var nExplosions = 0; +var landed = false; + +const gravity = 4; +const dt = 0.1; +const fuelBurnRate = 20*(176/g.getHeight()); +const maxV = 12; + +function flameImageGen (throttle) { + var str = " xxx \n xxx \n"; + str += "xxxxx\n".repeat(throttle); + str += " xxx \n x \n"; + return Graphics.createImage(str); +} + +function drawFalcon(x, y, throttle, angle) { + g.setColor(1, 1, 1).drawImage(falcon9, x, y, {rotate:angle}); + if (throttle>0) { + var flameImg = flameImageGen(throttle); + var r = falcon9.height/2 + flameImg.height/2-1; + var xoffs = -Math.sin(angle)*r; + var yoffs = Math.cos(angle)*r; + if (Math.random()>0.7) g.setColor(1, 0.5, 0); + else g.setColor(1, 1, 0); + g.drawImage(flameImg, x+xoffs, y+yoffs, {rotate:angle}); + } +} + +function drawBG() { + g.setBgColor(0.2, 0.2, 1).clear(); + g.setColor(0, 0, 1).fillRect(0, g.getHeight()-oceanHeight, g.getWidth()-1, g.getHeight()-1); + g.setColor(0.5, 0.5, 1).fillCircle(cloudOffs+34, 30, 15).fillCircle(cloudOffs+60, 35, 20).fillCircle(cloudOffs+75, 20, 10); + g.setColor(1, 1, 0).fillCircle(g.getWidth(), 0, 20); + g.setColor(1, 1, 1).drawImage(droneShip, droneX, g.getHeight()-oceanHeight-1); +} + +function showFuel() { + g.setColor(0, 0, 0).setFont("4x6:2").setFontAlign(-1, -1, 0).drawString("Fuel: "+Math.abs(booster.fuel).toFixed(0), 4, 4); +} + +function renderScreen(input) { + drawBG(); + showFuel(); + drawFalcon(booster.x, booster.y, Math.floor(input.throttle*12), input.angle); +} + +function getInputs() { + var accel = Bangle.getAccel(); + var a = Math.PI/2 + Math.atan2(accel.y, accel.x); + var t = (1+accel.z); + if (t > 1) t = 1; + if (t < 0) t = 0; + if (booster.fuel<=0) t = 0; + return {throttle: t, angle: a}; +} + +function epilogue(str) { + g.setFont("Vector", 24).setFontAlign(0, 0, 0).setColor(0, 0, 0).drawString(str, g.getWidth()/2, g.getHeight()/2).flip(); + g.setFont("Vector", 16).drawString("<= again exit =>", g.getWidth()/2, g.getHeight()/2+20); + clearInterval(stepInterval); + Bangle.on("swipe", (d) => { if (d>0) load(); else load('f9lander.app.js'); }); +} + +function gameStep() { + if (exploded) { + if (nExplosions++ < 15) { + var r = Math.random()*25; + var x = Math.random()*30 - 15; + var y = Math.random()*30 - 15; + g.setColor(1, Math.random()*0.5+0.5, 0).fillCircle(booster.x+x, booster.y+y, r); + if (nExplosions==1) Bangle.buzz(600); + } + else epilogue("You crashed!"); + } + else { + var input = getInputs(); + if (booster.y >= targetY) { +// console.log(booster.x + " " + booster.y + " " + booster.vy + " " + droneX + " " + input.angle); + if (Math.abs(booster.x-droneX-droneShip.width/2) { + stepInterval = setInterval(gameStep, Math.floor(1000*dt)); + Bangle.removeListener("swipe"); +}); diff --git a/apps/f9lander/f9lander.png b/apps/f9lander/f9lander.png new file mode 100644 index 000000000..f03cc1645 Binary files /dev/null and b/apps/f9lander/f9lander.png differ diff --git a/apps/f9lander/f9lander_screenshot1.png b/apps/f9lander/f9lander_screenshot1.png new file mode 100644 index 000000000..ea7d8a834 Binary files /dev/null and b/apps/f9lander/f9lander_screenshot1.png differ diff --git a/apps/f9lander/f9lander_screenshot2.png b/apps/f9lander/f9lander_screenshot2.png new file mode 100644 index 000000000..a2f13d6c7 Binary files /dev/null and b/apps/f9lander/f9lander_screenshot2.png differ diff --git a/apps/f9lander/f9lander_screenshot3.png b/apps/f9lander/f9lander_screenshot3.png new file mode 100644 index 000000000..61b8be82f Binary files /dev/null and b/apps/f9lander/f9lander_screenshot3.png differ diff --git a/apps/f9lander/metadata.json b/apps/f9lander/metadata.json new file mode 100644 index 000000000..75c6a0164 --- /dev/null +++ b/apps/f9lander/metadata.json @@ -0,0 +1,15 @@ +{ "id": "f9lander", + "name": "Falcon9 Lander", + "shortName":"F9lander", + "version":"0.01", + "description": "Land a rocket booster", + "icon": "f9lander.png", + "screenshots" : [ { "url":"f9lander_screenshot1.png" }, { "url":"f9lander_screenshot2.png" }, { "url":"f9lander_screenshot3.png" }], + "readme": "README.md", + "tags": "game", + "supports" : ["BANGLEJS", "BANGLEJS2"], + "storage": [ + {"name":"f9lander.app.js","url":"app.js"}, + {"name":"f9lander.img","url":"app-icon.js","evaluate":true} + ] +} diff --git a/apps/health/ChangeLog b/apps/health/ChangeLog index 74be2faec..62d93e606 100644 --- a/apps/health/ChangeLog +++ b/apps/health/ChangeLog @@ -13,3 +13,4 @@ 0.12: Add setting for Daily Step Goal 0.13: Add support for internationalization 0.14: Move settings +0.15: Fix charts (fix #1366) diff --git a/apps/health/app.js b/apps/health/app.js index a1fe1a4f7..c0a40bd93 100644 --- a/apps/health/app.js +++ b/apps/health/app.js @@ -46,7 +46,7 @@ function menuHRM() { function stepsPerHour() { E.showMessage(/*LANG*/"Loading..."); - let data = new Uint16Array(24); + var data = new Uint16Array(24); require("health").readDay(new Date(), h=>data[h.hr]+=h.steps); g.clear(1); Bangle.drawWidgets(); @@ -57,7 +57,7 @@ function stepsPerHour() { function stepsPerDay() { E.showMessage(/*LANG*/"Loading..."); - let data = new Uint16Array(31); + var data = new Uint16Array(31); require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.steps); g.clear(1); Bangle.drawWidgets(); @@ -68,8 +68,8 @@ function stepsPerDay() { function hrmPerHour() { E.showMessage(/*LANG*/"Loading..."); - let data = new Uint16Array(24); - let cnt = new Uint8Array(23); + var data = new Uint16Array(24); + var cnt = new Uint8Array(23); require("health").readDay(new Date(), h=>{ data[h.hr]+=h.bpm; if (h.bpm) cnt[h.hr]++; @@ -84,8 +84,8 @@ function hrmPerHour() { function hrmPerDay() { E.showMessage(/*LANG*/"Loading..."); - let data = new Uint16Array(31); - let cnt = new Uint8Array(31); + var data = new Uint16Array(31); + var cnt = new Uint8Array(31); require("health").readDailySummaries(new Date(), h=>{ data[h.day]+=h.bpm; if (h.bpm) cnt[h.day]++; @@ -100,7 +100,7 @@ function hrmPerDay() { function movementPerHour() { E.showMessage(/*LANG*/"Loading..."); - let data = new Uint16Array(24); + var data = new Uint16Array(24); require("health").readDay(new Date(), h=>data[h.hr]+=h.movement); g.clear(1); Bangle.drawWidgets(); @@ -111,7 +111,7 @@ function movementPerHour() { function movementPerDay() { E.showMessage(/*LANG*/"Loading..."); - let data = new Uint16Array(31); + var data = new Uint16Array(31); require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.movement); g.clear(1); Bangle.drawWidgets(); @@ -183,7 +183,7 @@ function drawBarChart() { } // draw a fake 0 height bar if chart_index is outside the bounds of the array - if ((chart_index + bar - 1) >= 0 && (chart_index + bar - 1) < data_len) + if ((chart_index + bar - 1) >= 0 && (chart_index + bar - 1) < data_len && chart_max_datum > 0) bar_top = bar_bot - 100 * (chart_data[chart_index + bar - 1]) / chart_max_datum; else bar_top = bar_bot; diff --git a/apps/health/metadata.json b/apps/health/metadata.json index 58762c77a..a038f67b5 100644 --- a/apps/health/metadata.json +++ b/apps/health/metadata.json @@ -1,7 +1,7 @@ { "id": "health", "name": "Health Tracking", - "version": "0.14", + "version": "0.15", "description": "Logs health data and provides an app to view it", "icon": "app.png", "tags": "tool,system,health", diff --git a/lang/it_IT.json b/lang/it_IT.json index 07545e1e7..4bc36ee48 100644 --- a/lang/it_IT.json +++ b/lang/it_IT.json @@ -45,6 +45,7 @@ "Messages": "Messaggi", "No Messages": "Nessun messaggio", "Keep Msgs": "Tieni i messaggi", + "Mark all read": "Segna tutto come letto", "Mark Unread": "Segna come non letto", "Vibrate": "Vibrazione", "Are you sure": "Sei sicuro/a", @@ -83,8 +84,8 @@ "Background": "Sfondo", "Foreground 2": "Primo piano 2", "Background 2": "Sfondo 2", - "Highlight FG": "Selezione PP", - "Highlight BG": "Selezione Sf", + "Highlight FG": "Primo piano selezione", + "Highlight BG": "Sfondo selezione", "Utilities": "Utilità", "Storage": "Memoria", "Compact Storage": "Compatta memoria", @@ -110,7 +111,7 @@ "Loading": "Caricamento", "Launcher Settings": "Impostazioni Launcher", "Font": "Font", - "Show clocks": "Mostra orologi", + "Show Clocks": "Mostra orologi", "Log": "Log", "Steps": "Passi", "steps": "passi", @@ -151,19 +152,76 @@ "start&lap/reset, BTN1: EXIT": "start&lap/reset, BTN1: EXIT", "back": "indietro", "color": "colore", - "BACK": "INDIETRO" + "BACK": "INDIETRO", + "Select App": "Seleziona app", + "No Apps Found": "Nessuna app trovata", + "Edit Alarm": "Modifica sveglia", + "Auto Snooze": "Ripeti automaticamente", + "Delete Alarm": "Cancella sveglia", + "Repeat Alarm": "Ripeti sveglia", + "Once": "Una volta", + "Workdays": "Giorni lavorativi", + "Weekends": "Weekend", + "Every Day": "Ogni giorno", + "Custom": "Personalizza", + "Edit Timer": "Modifica timer", + "Delete Timer": "Cancella timer", + "Scheduler Settings": "Impostazioni schedulatore", + "Nothing to Enable": "Niente da attivare", + "Nothing to Disable": "Niente da disattivare", + "Enable All": "Attiva tutto", + "Disable All": "Disattiva tutto", + "Delete All": "Cancella tutto", + "Updating boot0": "Aggiornamento boot0 in corso", + "Reloading": "Ricaricamento in corso", + "Date & Time": "Data & ora", + "Small": "Piccolo", + "Medium": "Medio", + "Big": "Grande", + "Text size": "Dimensione testo", + "Find Phone": "Trova telefono", + "Movement": "Movimento", + "Heart Rate": "Frequenza cardiaca", + "Step Counting": "Conteggio passi", + "Daily Step Goal": "Obiettivo passi giornalieri", + "Fullscreen": "Schermo intero", + "Unlock Watch": "Sblocca orologio", + "Flash Icon": "Icona lampeggiante", + "Auto-Open Music": "Apri modalità musica automaticamente", + "Colour": "Colore", + "Notifications": "Notifiche", + "Scheduler": "Schedulatore", + "Stop": "Stop", + "Min Font": "Dimensione minima del font" }, "//2": "App-specific overrides", + "alarm": { + "//": "'Crea' instead of 'Nuovo' because 'Nuovo sveglia' would be weird (and wrong)", + "New": "Crea", + "Custom Days": "Personalizza i giorni", + "Advanced": "Altre funzionalità" + }, + "health": { + "Health Tracking": "Monitoraggio salute", + "HRM Interval": "Intervallo monitoraggio cardiaco", + "3 min": "3 min", + "10 min": "10 min", + "Always": "Sempre" + }, "launch": { - "Vector font size": "Dim. font vett.", + "Vector Font Size": "Dim. font vett.", "App Source\nNot found": "Codice app\nnon trovato" }, "messages": { - "Unread timer": "Timer msg non letti" + "Unread timer": "Timer messaggi non letti" }, "run": { "Record Run": "Registra corsa" }, + "sched": { + "Unlock at Buzz": "Sblocca quando vibra", + "s": "s" + }, "setting": { "Clock Style": "Formato ora", "Compacting...\nTakes approx\n1 minute": "Compattamento in corso...\nCi vorrà circa un minuto", diff --git a/modules/ClockFace.js b/modules/ClockFace.js index 9818ae4e3..25e2430bf 100644 --- a/modules/ClockFace.js +++ b/modules/ClockFace.js @@ -32,6 +32,8 @@ function ClockFace(options) { if (dir>0 && options.down) options.down.apply(this); }; if (options.upDown) this._upDown = options.upDown; + + this.is12Hour = !!(require("Storage").readJSON("setting.json", 1) || {})["12hour"]; } ClockFace.prototype.tick = function() { @@ -69,6 +71,7 @@ ClockFace.prototype.start = function() { if (this._upDown) Bangle.setUI("clockupdown", d=>this._upDown.apply(this,[d])); else Bangle.setUI("clock"); delete this._last; + this.paused = false; this.tick(); Bangle.on("lcdPower", on => { @@ -87,7 +90,7 @@ ClockFace.prototype.pause = function() { ClockFace.prototype.resume = function() { if (this._timeout) return; // not paused delete this._last; - delete this.paused; + this.paused = false; if (this._resume) this._resume.apply(this); this.tick(true); }; diff --git a/modules/ClockFace.md b/modules/ClockFace.md index 1da6e6020..e760c3e74 100644 --- a/modules/ClockFace.md +++ b/modules/ClockFace.md @@ -59,6 +59,7 @@ var clock = new ClockFace({ draw: function(time, changed) { // at least draw or update is required // (re)draw entire clockface, time is a Date object // `changed` is the same format as for update() below, but always all true + // You can use `this.is12Hour` to test if the 'Time Format' setting is set to "12h" or "24h" }, // The difference between draw() and update() is that the screen is cleared // before draw() is called, so it needs to always redraw the entire clock @@ -107,4 +108,29 @@ var clock = new ClockFace(function(time) { }); clock.start(); +``` + +Properties +---------- +The following properties are automatically set on the clock: +* `is12Hour`: `true` if the "Time Format" setting is set to "12h", `false` for "24h". +* `paused`: `true` while the clock is paused. (You don't need to check this inside your `draw()` code) + +Inside the `draw()`/`update()` function you can access these using `this`: + +```js + +var ClockFace = require("ClockFace"); +var clock = new ClockFace({ + draw: function (time) { + if (this.is12Hour) // draw 12h time + else // use 24h format + } +}); +clock.start(); + +Bangle.on('step', function(steps) { + if (clock.paused === false) // draw step count +}); + ``` \ No newline at end of file