diff --git a/apps/cc_astro/ChangeLog b/apps/cc_astro/ChangeLog new file mode 100644 index 000000000..507df8921 --- /dev/null +++ b/apps/cc_astro/ChangeLog @@ -0,0 +1 @@ +0.01: copied from cc_abstract (V0.01) diff --git a/apps/cc_astro/README.md b/apps/cc_astro/README.md new file mode 100644 index 000000000..b328251b5 --- /dev/null +++ b/apps/cc_astro/README.md @@ -0,0 +1,17 @@ +# Analog Clock With Abstract Face + +## Features + +* inspired from the abstract face of the google smartwatch +* second hand (only on unlocked screen) +* date +* battery percentage (showing charge status with color) +* turned off or swipeable widgets (choose in settings) + +![logo](cc_clock24_screen.png) + +## Settings + +* whether to load widgets, or not; if widgets are loaded, they are swipeable from the top; if not, NO ACTIONS of widgets are available +* date and battery can be printed both below hands (as if hands were physical) and above (more readable) +* hour hand can be made slighly shorter to improve readability when minute hand is behind a number diff --git a/apps/cc_astro/app.js b/apps/cc_astro/app.js new file mode 100644 index 000000000..3e437bd30 --- /dev/null +++ b/apps/cc_astro/app.js @@ -0,0 +1,202 @@ +// ----- const ----- + +const defaultSettings = { + loadWidgets : false, + textAboveHands : false, + shortHrHand : false, + show24HourMode : false +}; + +const settings = Object.assign(defaultSettings, require('Storage').readJSON('cc_abstract.json', 1) || {}); + +const center = { + "x": g.getWidth()/2, + "y": g.getHeight()/2 +}; + +const parameters = { + "earthOrbitRadius": 80, + "venusOrbitRadius": 60, + "mercuryOrbitRadius": 40, + "earthRadius": 8, + "venusRadius": 6, + "mercuryRadius": 4, + "sunRadius": 12, + "maxSunRadius": 115 +}; + +// ----- global vars ----- + +let drawTimeout; +let queueMillis = 1000; +let unlock = true; +let lastBatteryStates = [E.getBattery()]; + +// ----- functions ----- + +function updateState() { + updateBatteryStates(); + + if (Bangle.isLCDOn()) { + if (!Bangle.isLocked()) { + queueMillis = 1000; + unlock = true; + } + else { + queueMillis = 60000; + unlock = false; + } + draw(); + } + else { + if (drawTimeout) + clearTimeout(drawTimeout); + drawTimeout = undefined; + } +} + +function updateBatteryStates() { + lastBatteryStates.push(E.getBattery()); + if (lastBatteryStates.length > 5) + lastBatteryStates.shift(); // remove 1st item +} + +function draw() { + drawBackground(); + drawHands(); + queueDraw(); +} + +function drawBackground() { + clearScreen(); + drawSun(); +} + +function clearScreen() { + g.setBgColor(0, 0, 0); + g.clear(); +} + +function drawSun() { + const batteryState = calcAvgBatteryState(); + + if (batteryState <= 25) + g.setColor(1, 0, 0); // red sun, if battery low + else + g.setColor(1, 1, 0); + + let r = parameters.sunRadius; + if (batteryState <= 20) { + const relSize = (20 - batteryState) / 20; + const dr = parameters.maxSunRadius - parameters.sunRadius; + r = parameters.sunRadius + relSize * dr; + } + + g.fillCircle(center.x, center.y, r); +} + +function drawHands() { + const date = new Date(); + + drawHourHand(date.getHours(), date.getMinutes()); + drawMinuteHand(date.getMinutes()); + + if (unlock) { + drawSecondHand(date.getSeconds()); + } +} + +function drawHourHand(hours, minutes) { + const r = parameters.earthOrbitRadius; + const phi = 30 * (hours + minutes/60) * (Math.PI / 180) - Math.PI/2; + const x = center.x + r * Math.cos(phi); + const y = center.y + r * Math.sin(phi); + + g.setColor(1, 1, 1); + g.drawCircle(center.x, center.y, r); + + g.setColor(0, 1, 1); + g.fillCircle(x, y, parameters.earthRadius); +} + +function drawMinuteHand(minutes) { + const r = parameters.venusOrbitRadius; + const phi = 6 * minutes * (Math.PI / 180) - Math.PI/2; + const x = center.x + r * Math.cos(phi); + const y = center.y + r * Math.sin(phi); + + g.setColor(1, 1, 1); + g.drawCircle(center.x, center.y, r); + + g.setColor(1, 1, 1); + g.fillCircle(x, y, parameters.venusRadius); +} + +function drawSecondHand(seconds) { + const r = parameters.mercuryOrbitRadius; + const phi = 6 * seconds * (Math.PI / 180) - Math.PI/2; + const x = center.x + r * Math.cos(phi); + const y = center.y + r * Math.sin(phi); + + g.setColor(1, 1, 1); + g.drawCircle(center.x, center.y, r); + + g.setColor(1, 0, 1); + g.fillCircle(x, y, parameters.mercuryRadius); +} + +function calcAvgBatteryState() { + const n = lastBatteryStates.length; + if (n == 0) + return 100; + + let sum = lastBatteryStates.reduce((acc, value) => acc + value, 0); + return Math.round(sum / n); +} + +function queueDraw() { + if (drawTimeout) + clearTimeout(drawTimeout); + + drawTimeout = setTimeout(function() { + drawTimeout = undefined; + draw(); + }, queueMillis - (Date.now() % queueMillis)); +} + + +//// main running sequence //// + +// Show launcher when middle button pressed, and widgets that we're clock +Bangle.setUI({ + mode: "clock", + remove: function() { + Bangle.removeListener('lcdPower', updateState); + Bangle.removeListener('lock', updateState); + Bangle.removeListener('charging', draw); + + // We clear drawTimout after removing all listeners, because they can add one again + if (drawTimeout) + clearTimeout(drawTimeout); + + drawTimeout = undefined; + require("widget_utils").show(); + } +}); + +// Load widgets if needed, and make them show swipeable +if (settings.loadWidgets) { + Bangle.loadWidgets(); + require("widget_utils").swipeOn(); +} +else if (global.WIDGETS) { + require("widget_utils").hide(); +} + +// Stop updates when LCD is off, restart when on +Bangle.on('lcdPower', updateState); +Bangle.on('lock', updateState); +Bangle.on('charging', draw); // Immediately redraw when charger (dis)connected + +updateState(); +draw(); diff --git a/apps/cc_astro/app_icon.js b/apps/cc_astro/app_icon.js new file mode 100644 index 000000000..b213fe5c8 --- /dev/null +++ b/apps/cc_astro/app_icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwgIEBoUAiAKCgUCBQUEColEAYUQhAmKCwgeCAAcCgEDjwEBkEAg8TBocNgYFDh8GAYMDxkPjEA8EAwkHJgIcBAoPfAoYWCBYYFIgfvAoX4FYRJEAp9gAomYNAOAArPwAogAC4AFiRoIFJLgIFJuADCg//Q4U//4FDj4FEAAV4Aoi0CSxBsCA==")) \ No newline at end of file diff --git a/apps/cc_astro/cc_astro_icon.png b/apps/cc_astro/cc_astro_icon.png new file mode 100644 index 000000000..cded02071 Binary files /dev/null and b/apps/cc_astro/cc_astro_icon.png differ diff --git a/apps/cc_astro/cc_astro_screen.png b/apps/cc_astro/cc_astro_screen.png new file mode 100644 index 000000000..1f0e5b089 Binary files /dev/null and b/apps/cc_astro/cc_astro_screen.png differ diff --git a/apps/cc_astro/metadata.json b/apps/cc_astro/metadata.json new file mode 100644 index 000000000..2c053d2c6 --- /dev/null +++ b/apps/cc_astro/metadata.json @@ -0,0 +1,18 @@ +{ "id": "cc_astro", + "name": "CC Astro", + "shortName":"CC-Astro", + "version":"0.01", + "description": "astronomy clock face", + "icon": "cc_astro_icon.png", + "type": "clock", + "tags": "clock", + "supports" : ["BANGLEJS2"], + "screenshots": [{"url":"cc_astro_screen.png"}], + "readme": "README.md", + "storage": [ + {"name":"cc_astro.app.js","url":"app.js"}, + {"name":"cc_astro.settings.js","url":"settings.js"}, + {"name":"cc_astro.img","url":"app_icon.js","evaluate":true} + ], + "data": [{"name":"cc_astro.json"}] +} diff --git a/apps/cc_astro/settings.js b/apps/cc_astro/settings.js new file mode 100644 index 000000000..4aa19215d --- /dev/null +++ b/apps/cc_astro/settings.js @@ -0,0 +1,33 @@ +(function(back) { + const defaultSettings = { + loadWidgets : false, + textAboveHands : false, + shortHrHand : false, + show24HourMode : false + } + let settings = Object.assign(defaultSettings, require('Storage').readJSON('cc_clock24.json',1) || {}); + + const save = () => require('Storage').write('cc_clock24.json', settings); + + const appMenu = { + '': {title: 'cc_clock24'}, '< Back': back, + /*LANG*/'Load widgets': { + value : !!settings.loadWidgets, + onchange : v => { settings.loadWidgets=v; save();} + }, + /*LANG*/'Text above hands': { + value : !!settings.textAboveHands, + onchange : v => { settings.textAboveHands=v; save();} + }, + /*LANG*/'Short hour hand': { + value : !!settings.shortHrHand, + onchange : v => { settings.shortHrHand=v; save();} + }, + /*LANG*/'Show 24 hour mode': { + value : !!settings.show24HourMode, + onchange : v => { settings.show24HourMode=v; save();} + }, + }; + + E.showMenu(appMenu); +})