diff --git a/apps/burn/ChangeLog b/apps/burn/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/burn/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/burn/bangle1-burn-screenshot.png b/apps/burn/bangle1-burn-screenshot.png new file mode 100644 index 000000000..fef0e701e Binary files /dev/null and b/apps/burn/bangle1-burn-screenshot.png differ diff --git a/apps/burn/burn-icon.js b/apps/burn/burn-icon.js new file mode 100644 index 000000000..04a5287db --- /dev/null +++ b/apps/burn/burn-icon.js @@ -0,0 +1,5 @@ +require("heatshrink").decompress( + atob( + "AJhMOJ/4NEf//6VQsf+/57fl/ZRWcv5xGfIgAlJQGEGU6DQguHAgaNJIoFzBKRI4jEwSMCA4FEQAFUDQIBwakXRAiGFxZAFwicDR7vFQuTALMAAkwGFwavd7uMBLNQYtZjuEsSAuFdUeUAB5MFgQACEAhwDNBsEFyZGFw7kEA4MhAgsRArLVSQFcwgMFyYNVQADd4jFLEJRguRYwuEoBAAbBpqFganFKJRKMFzrGFwptVAuhBpnM5rVrNZAuDQguhM3sDBguDQ7ARjQsDNgiFNAwCF0BcCAASQEF0AgHBAuCjjguPQwusjzoGFxZJRKApzqmFxC8DIHedZVOCLgyMcMAqKMAopFBceeQqqFwaJMAsgAK4LRV0HQoEjQXcFAqCZCJjEQIEDgYLEQAFIJCAwYqFhYECQRcFEYoKQQuFUKDFRMCMBaiFwwIDFFWDZrIEgYjQIGFxR8GMwwuCAAZCBAQuCEAVcIQQRWCEAYiXwTIHEhwDMAWCRxZFBKCEAYnUQuENQaABAxCGMRE6CRgoiKwRACKQT6EHAguOugxMDVAQCUQom1xwuFJAuFQg6CAgQGDQW4EFyAADVHoDBaIQQXoHQagFypIKwQuCiAguGjxQACBw4EBApEnQ5nM5kYBwBXA5nMLHM5rValAruAAqglwqDQm8IFxJnVarBQuHcKi4AXCDBHEEFwamLxKmFxKg1QQADFReCFwoNMwEEAAZABApEjwYEAwZEmAUuEA" + ) +); diff --git a/apps/burn/burn.js b/apps/burn/burn.js new file mode 100644 index 000000000..44ed7d146 --- /dev/null +++ b/apps/burn/burn.js @@ -0,0 +1,254 @@ +/* + * Burn: Calories Counter for Bangle.js (Espruino). Based on the original Counter app. + * Features: + * - Persistent counter: saved to a file. + * - Daily reset: counter resets each day. + * - Adjustable increment value. + * + * Bangle.js 1 Controls: + * - BTN1: Increase (or tap right) + * - BTN3: Decrease (or tap left) + * - Press BTN2: Change increment + * + * Bangle.js 2 Controls: + * - Swipe up: Increase + * - Swipe down: Decrease + * - Press BTN: Change increment + */ + +// File variable to handle file operations +let file; + +// Importing the Storage module for file operations +const Storage = require("Storage"); + +// File path for the counter data +const PATH = "kcal.txt"; + +// Function to get the current date as a string +const dayString = () => { + const date = new Date(); + // Month is 0-indexed, so we add 1 to get the correct month number + return `${date.getMonth() + 1}-${date.getDate()}-${date.getFullYear()}`; +}; + +// Counter object to keep track of the count and the date +let counter = { count: 0, date: dayString() }; + +// Function to read the counter from the file +function readCounterFromFile() { + try { + // Open the file in read mode + file = Storage.open(PATH, "r"); + let line = file.readLine(); + + // If the file has content, parse it and update the counter + if (line) { + let splitLine = line.trim().split(","); + counter = { count: parseInt(splitLine[0]), date: splitLine[1] }; + } + } catch (err) { + // If the file does not exist, the counter will remain 0 + } +} + +// Function to write the counter to the file +function writeCounterToFile() { + // Open the file in write mode + file = Storage.open(PATH, "w"); + // Write the counter and date to the file + file.write(counter.count.toString() + "," + counter.date + "\n"); +} + +// Function to reset the counter +function resetCounter() { + // Reset the counter to 0 and update the date + counter = { count: 0, date: dayString() }; +} + +// Function to update the counter value +function updateCounterValue(value) { + // Update the counter with the new value, ensuring it's not less than 0 + counter = { count: Math.max(0, value), date: dayString() }; +} + +// Function to update the counter +function updateCounter(value) { + // If the date has changed, reset the counter + if (counter.date != dayString()) { + resetCounter(); + } else { + // Otherwise, update the counter value + updateCounterValue(value); + } + + // Write the updated counter to the file + writeCounterToFile(); + // Update the screen with the new counter value + updateScreen(); +} + +// Function to set a watch on a button to update the counter when pressed +function counterButtonWatch(button, increment) { + setWatch( + () => { + // If the button is for incrementing, or the counter is greater than 0, update the counter + if (increment || counter.count > 0) { + updateCounter( + counter.count + (increment ? getInterval() : -getInterval()) + ); + // Update the screen with the new counter value + updateScreen(); + } + }, + button, + { repeat: true } + ); +} + +// Function to create interval functions +const createIntervalFunctions = function () { + // Array of intervals + const intervals = [50, 100, 200, 10]; + // Current location in the intervals array + let location = 0; + + // Function to get the current interval + const getInterval = function () { + return intervals[location]; + }; + + // Function to rotate the increment + const rotateIncrement = function () { + // Update the location to the next index in the intervals array, wrapping around if necessary + location = (location + 1) % intervals.length; + // Update the screen with the new increment + updateScreen(); + }; + + // Return the getInterval and rotateIncrement functions + return { getInterval, rotateIncrement }; +}; + +// Create the interval functions +const intervalFunctions = createIntervalFunctions(); +const getInterval = intervalFunctions.getInterval; +const rotateIncrement = intervalFunctions.rotateIncrement; + +// Check if the hardware version is Bangle.js 2 +const BANGLEJS2 = process.env.HWVERSION == 2; + +// Variables for drag handling and screen coordinates +let drag, y, x; + +// If the hardware version is Bangle.js 2, set the coordinates accordingly +if (BANGLEJS2) { + drag = undefined; + y = 45; + x = 5; +} else { + // If the hardware version is Bangle.js 1, set the coordinates accordingly + y = 100; + x = 25; +} + +// Function to update the screen +function updateScreen() { + // Clear the screen area for the counter + g.clearRect(0, 50, 250, BANGLEJS2 ? 130 : 150) + .setBgColor(g.theme.bg) + .setColor(g.theme.fg) + .setFont("Vector", 40) + .setFontAlign(0, 0) + // Draw the counter value + .drawString(Math.floor(counter.count), g.getWidth() / 2, 100) + .setFont("6x8") + // Clear the screen area for the increment + .clearRect(g.getWidth() / 2 - 50, 140, g.getWidth() / 2 + 50, 160) + // Draw the increment value + .drawString("Increment: " + getInterval(), g.getWidth() / 2, 150); + + // If the hardware version is Bangle.js 1, draw the increment and decrement buttons + if (!BANGLEJS2) { + g.drawString("-", 45, 100).drawString("+", 185, 100); + } +} + +// If the hardware version is Bangle.js 2, set up the drag handling and button watch +if (BANGLEJS2) { + // Set up drag handling + Bangle.on("drag", (e) => { + // If this is the start of a drag, record the initial coordinates + if (!drag) { + drag = { x: e.x, y: e.y }; + return; + } + + // If the button is still being pressed, ignore this event + if (e.b) return; + + // Calculate the change in x and y from the start of the drag + const dx = e.x - drag.x; + const dy = e.y - drag.y; + // Reset the drag start coordinates + drag = null; + + // Determine if the drag is primarily horizontal or vertical + const isHorizontalDrag = Math.abs(dx) > Math.abs(dy) + 10; + const isVerticalDrag = Math.abs(dy) > Math.abs(dx) + 10; + + // If the drag is primarily horizontal, ignore it + if (isHorizontalDrag) { + return; + } + + // If the drag is primarily vertical, update the counter + if (isVerticalDrag) { + // If the drag is downwards and the counter is greater than 0, decrease the counter + if (dy > 0 && counter.count > 0) { + updateCounter(counter.count - getInterval()); + } else if (dy < 0) { + // If the drag is upwards, increase the counter + updateCounter(counter.count + getInterval()); + } + // Update the screen with the new counter value + updateScreen(); + } + }); + + // Set a watch on the button to rotate the increment when pressed + setWatch(rotateIncrement, BTN1, { repeat: true }); +} else { + // If the hardware version is Bangle.js 1, set up the button watches + + // Set watch on button to increase the counter + counterButtonWatch(BTN1, true); + counterButtonWatch(BTN5, true); // screen tap + // Set watch on button to decrease the counter + counterButtonWatch(BTN3, false); + counterButtonWatch(BTN4, false); // screen tap + + // Set a watch on button to rotate the increment when pressed + setWatch( + () => { + rotateIncrement(); + }, + BTN2, + { repeat: true } + ); +} + +// clear the screen +g.clear(); + +// Set the background and foreground colors +g.setBgColor(g.theme.bg).setColor(g.theme.fg); + +// Load and draw the widgets +Bangle.loadWidgets(); +Bangle.drawWidgets(); + +// Read the counter from the file +readCounterFromFile(); +// Update the screen with the counter value +updateScreen(); diff --git a/apps/burn/burn_icon.png b/apps/burn/burn_icon.png new file mode 100644 index 000000000..23d4a13e6 Binary files /dev/null and b/apps/burn/burn_icon.png differ diff --git a/apps/burn/metadata.json b/apps/burn/metadata.json new file mode 100644 index 000000000..576d6ef9e --- /dev/null +++ b/apps/burn/metadata.json @@ -0,0 +1,15 @@ +{ + "id": "burn", + "name": "Burn", + "version": "0.01", + "description": "Calorie Counter", + "icon": "burn_icon.png", + "tags": "tool", + "supports": ["BANGLEJS", "BANGLEJS2"], + "screenshots": [{"url":"bangle1-burn-screenshot.png"}], + "allow_emulator": true, + "storage": [ + {"name":"burn.app.js","url":"burn.js"}, + {"name":"burn.img","url":"burn-icon.js","evaluate":true} + ] +}